|
8 | 8 |
|
9 | 9 | mpirun -- python convert_eagle.py \ |
10 | 10 | --snapshot-basename=SNAPSHOT \ |
| 11 | + --particledata-basename=PARTICLE_DATA \ |
| 12 | + --subfind-basename=SUBFIND \ |
11 | 13 | --output-basename=OUTPUT \ |
12 | 14 | --membership-basename=MEMBERSHIP |
13 | 15 |
|
14 | | -where SNAPSHOT is the EAGLE snapshot (use the particledata_*** files, |
15 | | -since the normal snapshots don't store SubGroupNumber), and OUTPUT & |
| 16 | +where SNAPSHOT is the EAGLE snapshot (the snapshots_*** files), |
| 17 | +PARTICLE_DATA are the EAGLE membership files (the particledata_*** files, |
| 18 | +since the normal snapshots don't store SubGroupNumber), SUBFIND are |
| 19 | +the SubFind catalogues (the subfind_tab_*** files), and OUTPUT & |
16 | 20 | MEMBERSHIP are the names of the output files. You must run with |
17 | 21 | the same number of ranks as input files. |
18 | 22 |
|
|
82 | 86 | "name without the .{file_nr}.hdf5 suffix)" |
83 | 87 | ), |
84 | 88 | ) |
| 89 | +parser.add_argument( |
| 90 | + "--particledata-basename", |
| 91 | + type=str, |
| 92 | + required=True, |
| 93 | + help=( |
| 94 | + "The basename for the particle data files (the files which " |
| 95 | + "contain the GroupNumber and SubGroupNumber values for bound " |
| 96 | + "particles. For EAGLE these are separate to the snapshots." |
| 97 | + ), |
| 98 | +) |
85 | 99 | parser.add_argument( |
86 | 100 | "--subfind-basename", |
87 | 101 | type=str, |
88 | 102 | required=True, |
89 | | - help=("The basename for the subfind files"), |
| 103 | + help="The basename for the subfind files", |
90 | 104 | ) |
91 | 105 | parser.add_argument( |
92 | 106 | "--output-basename", |
|
102 | 116 | ) |
103 | 117 | args = parser.parse_args() |
104 | 118 | snap_filename = args.snap_basename + ".{file_nr}.hdf5" |
| 119 | +particledata_filename = args.particledata_basename + ".{file_nr}.hdf5" |
105 | 120 | subfind_filename = args.subfind_basename + ".{file_nr}.hdf5" |
106 | 121 | output_filename = args.output_basename + ".{file_nr}.hdf5" |
107 | 122 | membership_filename = args.membership_basename + ".{file_nr}.hdf5" |
|
132 | 147 | h = comm.bcast(h) |
133 | 148 | box_size_cmpc = comm.bcast(box_size_cmpc) |
134 | 149 |
|
135 | | -assert comm_size == n_file |
| 150 | +assert comm_size <= n_file |
136 | 151 |
|
137 | 152 | # Specify the unit system of the output SWIFT snapshot |
138 | 153 | if comm_rank == 0: |
|
281 | 296 | "description": None, |
282 | 297 | "conversion_factor": None, |
283 | 298 | }, |
284 | | - "GroupNumber": { |
285 | | - "swift_name": "FOFGroupIDs", |
286 | | - "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
287 | | - "a_exponent": None, |
288 | | - "description": None, |
289 | | - "conversion_factor": None, |
290 | | - }, |
291 | 299 | "ParticleIDs": { |
292 | 300 | "swift_name": "ParticleIDs", |
293 | 301 | "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
|
380 | 388 | "description": "Particle mass", |
381 | 389 | "conversion_factor": None, |
382 | 390 | }, |
383 | | - "GroupNumber": { |
384 | | - "swift_name": "FOFGroupIDs", |
385 | | - "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
386 | | - "a_exponent": None, |
387 | | - "description": None, |
388 | | - "conversion_factor": None, |
389 | | - }, |
390 | 391 | "ParticleIDs": { |
391 | 392 | "swift_name": "ParticleIDs", |
392 | 393 | "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
|
417 | 418 | "description": None, |
418 | 419 | "conversion_factor": None, |
419 | 420 | }, |
420 | | - "GroupNumber": { |
421 | | - "swift_name": "FOFGroupIDs", |
422 | | - "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
423 | | - "a_exponent": None, |
424 | | - "description": None, |
425 | | - "conversion_factor": None, |
426 | | - }, |
427 | 421 | "ParticleIDs": { |
428 | 422 | "swift_name": "ParticleIDs", |
429 | 423 | "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
|
506 | 500 | "description": None, |
507 | 501 | "conversion_factor": None, |
508 | 502 | }, |
509 | | - "GroupNumber": { |
510 | | - "swift_name": "FOFGroupIDs", |
511 | | - "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
512 | | - "a_exponent": None, |
513 | | - "description": None, |
514 | | - "conversion_factor": None, |
515 | | - }, |
516 | 503 | "ParticleIDs": { |
517 | 504 | "swift_name": "ParticleIDs", |
518 | 505 | "exponents": {"L": 0, "M": 0, "T": 0, "t": 0}, |
|
607 | 594 | snap_file = phdf5.MultiFile( |
608 | 595 | snap_filename, file_nr_attr=("Header", "NumFilesPerSnapshot"), comm=comm |
609 | 596 | ) |
| 597 | +particledata_file = phdf5.MultiFile( |
| 598 | + particledata_filename, file_nr_attr=("Header", "NumFilesPerSnapshot"), comm=comm |
| 599 | +) |
610 | 600 | # Load the SubFind catalogue and create an array that links the GroupNumber |
611 | 601 | # and SubGroupNumber of a subhalo to its index within the subhalo catalogue |
612 | 602 | # (for creating the membership files) |
|
656 | 646 | elements_per_file[1:] -= elements_per_file[:-1] |
657 | 647 | assert np.sum(elements_per_file) == np.sum(cell_counts[ptype]) |
658 | 648 |
|
| 649 | + # Each rank writes the files assigned to it by MultiFile, so it |
| 650 | + # must hold the concatenation of the data of those files. |
| 651 | + elements_per_rank = np.zeros(comm_size, dtype=elements_per_file.dtype) |
| 652 | + for rank in range(comm_size): |
| 653 | + first = snap_file.first_file_on_rank[rank] |
| 654 | + num = snap_file.num_files_on_rank[rank] |
| 655 | + elements_per_rank[rank] = np.sum(elements_per_file[first : first + num]) |
| 656 | + assert np.sum(elements_per_rank) == np.sum(elements_per_file) |
| 657 | + |
659 | 658 | # Calculate offsets of the first particle in each cell |
660 | 659 | cell_files[ptype] = np.repeat(np.arange(n_file), cells_per_file) |
661 | 660 | absolute_offset = np.cumsum(cell_counts[ptype]) - cell_counts[ptype] |
|
703 | 702 | attrs.update(unit_attrs) |
704 | 703 |
|
705 | 704 | # Write to the output file |
706 | | - arr = psort.repartition(arr, elements_per_file, comm=comm) |
| 705 | + arr = psort.repartition(arr, elements_per_rank, comm=comm) |
707 | 706 | if create_output_file: |
708 | 707 | mode = "w" |
709 | 708 | create_output_file = False |
|
750 | 749 | attrs.update(unit_attrs) |
751 | 750 |
|
752 | 751 | # Write to the output file |
753 | | - arr = psort.repartition(arr, elements_per_file, comm=comm) |
| 752 | + arr = psort.repartition(arr, elements_per_rank, comm=comm) |
754 | 753 | if create_output_file: |
755 | 754 | mode = "w" |
756 | 755 | create_output_file = False |
|
765 | 764 | attrs={"ElementMassFractions": attrs}, |
766 | 765 | ) |
767 | 766 |
|
| 767 | + |
| 768 | + # Load the GroupNumber and SubGroupNumber of particles by matching |
| 769 | + # the snapshot_* files with the particledata_* files |
| 770 | + snap_ids = snap_file.read(f"PartType{ptype}/ParticleIDs") |
| 771 | + particledata_ids = particledata_file.read(f"PartType{ptype}/ParticleIDs") |
| 772 | + idx = psort.parallel_match(snap_ids, particledata_ids, comm=comm) |
| 773 | + |
| 774 | + # EAGLE uses a value of 2^30 to indicate unbound particles |
| 775 | + # Particles missing from the particledata_* files are always unbound |
| 776 | + particledata_sub_group_nr = particledata_file.read(f"PartType{ptype}/SubGroupNumber") |
| 777 | + sub_group_nr = 1073741824 * np.ones(snap_ids.shape[0], dtype=np.int32) |
| 778 | + sub_group_nr[idx != -1] = psort.fetch_elements( |
| 779 | + particledata_sub_group_nr, |
| 780 | + idx[idx != -1], |
| 781 | + comm=comm, |
| 782 | + ) |
| 783 | + |
| 784 | + # Negative values indicate that the particle is not part of a FoF, |
| 785 | + # but is within the SO group of a FoF (the FoF it is part of is the positive |
| 786 | + # value, e.g. -10 means it is within the SO of FoF 10) |
| 787 | + particledata_group_nr = particledata_file.read(f"PartType{ptype}/GroupNumber") |
| 788 | + particledata_group_nr[particledata_group_nr < 0] = 1073741824 |
| 789 | + group_nr = 1073741824 * np.ones(snap_ids.shape[0], dtype=np.int32) |
| 790 | + group_nr[idx != -1] = psort.fetch_elements( |
| 791 | + particledata_group_nr, |
| 792 | + idx[idx != -1], |
| 793 | + comm=comm, |
| 794 | + ) |
| 795 | + |
768 | 796 | # Create a subhalo id for each particle by combining the |
769 | 797 | # group number and subgroup number |
770 | | - sub_group = snap_file.read(f"PartType{ptype}/SubGroupNumber") |
771 | | - subhalo = snap_file.read(f"PartType{ptype}/GroupNumber").astype(np.int64) |
| 798 | + subhalo = group_nr.astype(np.int64) |
772 | 799 | subhalo <<= 32 |
773 | | - subhalo += sub_group.astype(np.int64) |
774 | | - # Indicate unbound particles with -1 |
775 | | - bound = sub_group != 1073741824 |
| 800 | + subhalo += sub_group_nr.astype(np.int64) |
| 801 | + # For SOAP we want unbound particles to be indicated with a value of -1 |
| 802 | + bound = sub_group_nr != 1073741824 |
776 | 803 | subhalo[np.logical_not(bound)] = -1 |
777 | 804 | # Get SubFind index of bound particles |
778 | 805 | subhalo[bound] = psort.parallel_match(subhalo[bound], subfind_id, comm=comm) |
779 | 806 | assert np.all(subhalo[bound] != -1) |
780 | 807 |
|
781 | | - # Sort, add units, and write to file (same as for other properties) |
| 808 | + # Sort values spatially (same as for other properties) |
782 | 809 | subhalo = psort.fetch_elements(subhalo, order, comm=comm) |
| 810 | + group_nr = psort.fetch_elements(group_nr, order, comm=comm) |
| 811 | + # Add units, and write to file |
783 | 812 | units = unyt.Unit("dimensionless", registry=reg) |
784 | 813 | unit_attrs = swift_units.attributes_from_units(units, False, 0) |
785 | | - attrs = { |
| 814 | + subhalo_attrs = { |
786 | 815 | "Description": ( |
787 | 816 | "Unique identifier of the subhalo this particle is " |
788 | 817 | "bound to. This is a combination of the GroupNumber and" |
789 | 818 | "the SubGroupNumber. -1 if the particle is not bound" |
790 | 819 | ) |
791 | 820 | } |
792 | | - attrs.update(unit_attrs) |
793 | | - subhalo = psort.repartition(subhalo, elements_per_file, comm=comm) |
| 821 | + subhalo_attrs.update(unit_attrs) |
| 822 | + fof_attrs = {"Description": "FoF group number particle is in"} |
| 823 | + fof_attrs.update(unit_attrs) |
| 824 | + subhalo = psort.repartition(subhalo, elements_per_rank, comm=comm) |
| 825 | + group_nr = psort.repartition(group_nr, elements_per_rank, comm=comm) |
794 | 826 | if create_membership_file: |
795 | 827 | mode = "w" |
796 | 828 | create_membership_file = False |
797 | 829 | else: |
798 | 830 | mode = "r+" |
799 | 831 | snap_file.write( |
800 | | - {"GroupNr_bound": subhalo}, |
| 832 | + {"GroupNr_bound": subhalo, "FOFGroupIDs": group_nr}, |
801 | 833 | elements_per_file, |
802 | 834 | filenames=membership_filename, |
803 | 835 | mode=mode, |
804 | 836 | group=f"PartType{ptype}", |
805 | | - attrs={"GroupNr_bound": attrs}, |
| 837 | + attrs={"GroupNr_bound": subhalo_attrs, "FOFGroupIDs": fof_attrs}, |
806 | 838 | ) |
807 | 839 |
|
808 | 840 | # Add headers to the snapshots |
|
813 | 845 | header = outfile.create_group("Header") |
814 | 846 | for name, value in swift_header.items(): |
815 | 847 | header.attrs[name] = value |
816 | | - n_part = np.zeros(max(ptypes) + 1) |
| 848 | + n_part = np.zeros(max(ptypes) + 1, dtype=np.int64) |
817 | 849 | for ptype in ptypes: |
818 | 850 | n_part[ptype] = outfile[f"PartType{ptype}/Coordinates"].shape[0] |
819 | 851 | header.attrs["NumPart_ThisFile"] = n_part |
|
0 commit comments