Skip to content

Commit 4078990

Browse files
committed
Version matching
2 parents 5b301a6 + 337cf40 commit 4078990

File tree

7 files changed

+135
-23
lines changed

7 files changed

+135
-23
lines changed

docs/source/changelog.rst

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
1+
opencosmo 0.9.4 (2025-09-26)
2+
============================
3+
4+
Bugfixes
5+
--------
6+
7+
- Fix a bug that could cause opening halos and galaxies only to fail
8+
9+
10+
opencosmo 0.9.3 (2025-09-25)
11+
============================
12+
13+
Bugfixes
14+
--------
15+
16+
- Fix an issue that could cause opens with multiple files to fail in MPI contexts
17+
18+
19+
opencosmo 0.9.2 (2025-09-25)
20+
============================
21+
22+
Bugfixes
23+
--------
24+
25+
- Fix a but that could cause opening properties and profiles without particles to fail.
26+
27+
128
opencosmo 0.9.1 (2025-09-16)
229
============================
330

@@ -7,7 +34,7 @@ Bugfixes
734
- Re-add license file to package for conda-forge compatability
835

936

10-
Opencosmo 0.9.0 (2025-09-15)
37+
opencosmo 0.9.0 (2025-09-15)
1138
============================
1239

1340
Features
@@ -50,7 +77,7 @@ Misc
5077
- Update evaluate to respect default parameters
5178

5279

53-
Opencosmo 0.8.0 (2025-07-22)
80+
opencosmo 0.8.0 (2025-07-22)
5481
============================
5582

5683
Features

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
project = "OpenCosmo"
1010
copyright = "2025, OpenCosmo Team"
1111
author = "OpenCosmo Team"
12-
release = "0.9.1"
12+
release = "0.9.4"
1313

1414
# -- General configuration ---------------------------------------------------
1515
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

src/opencosmo/collection/io.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def get_collection_type(
4747
2. The files contain a single non-lightcone datatype.
4848
3. The files are linked together into a structure collection
4949
"""
50+
5051
handles_by_type = {target.data_type: target.group for target in targets}
5152
is_lightcone = [target.header.file.is_lightcone for target in targets]
5253
unique_data_types = set(handles_by_type.keys())
@@ -60,9 +61,16 @@ def get_collection_type(
6061

6162
elif len(unique_data_types) == 1 and all(not il for il in is_lightcone):
6263
return oc.SimulationCollection
63-
elif unique_file_types == set([FILE_TYPE.PARTICLES, FILE_TYPE.DATASET]):
64+
65+
elif FILE_TYPE.HALO_PROPERTIES in unique_file_types and set(
66+
[FILE_TYPE.HALO_PARTICLES, FILE_TYPE.SOD_BINS, FILE_TYPE.GALAXY_PROPERTIES]
67+
).intersection(unique_file_types):
6468
validate_linked_groups(handles_by_type)
6569
return oc.StructureCollection
70+
71+
elif unique_file_types == {FILE_TYPE.GALAXY_PROPERTIES, FILE_TYPE.GALAXY_PARTICLES}:
72+
return oc.StructureCollection
73+
6674
elif (
6775
len(unique_file_types) == 1
6876
and unique_file_types.pop() == FILE_TYPE.STRUCTURE_COLLECTION

src/opencosmo/collection/structure/io.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,16 @@ def build_structure_collection(targets: list[io.io.OpenTarget], ignore_empty: bo
151151
else:
152152
return collection
153153

154+
if (
155+
link_sources["halo_properties"]
156+
and len(link_sources["galaxy_properties"]) == 1
157+
and not link_targets["galaxy_targets"]
158+
):
159+
galaxy_properties = io.io.open_single_dataset(
160+
link_sources["galaxy_properties"][0]
161+
)
162+
link_targets["halo_targets"]["galaxy_properties"] = galaxy_properties
163+
154164
if len(link_sources["halo_properties"]) == 1 and link_targets["halo_targets"]:
155165
handlers = get_link_handlers(
156166
link_sources["halo_properties"][0].group,

src/opencosmo/io/io.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,15 @@
5959

6060

6161
class FILE_TYPE(Enum):
62-
DATASET = 0
63-
PARTICLES = 1
64-
LIGHTCONE = 2
65-
STRUCTURE_COLLECTION = 3
66-
SIMULATION_COLLECTION = 4
62+
HALO_PROPERTIES = 0
63+
HALO_PARTICLES = 1
64+
GALAXY_PROPERTIES = 2
65+
GALAXY_PARTICLES = 3
66+
SOD_BINS = 4
67+
LIGHTCONE = 5
68+
STRUCTURE_COLLECTION = 6
69+
SIMULATION_COLLECTION = 7
70+
SYNTHETIC_CATALOG = 8
6771

6872

6973
class COLLECTION_TYPE(Enum):
@@ -83,17 +87,22 @@ def data_type(self):
8387

8488

8589
def get_file_type(file: h5py.File) -> FILE_TYPE:
86-
file_keys = set(file.keys())
87-
if len(set(["header", "data"]).intersection(file_keys)) == 2:
88-
return FILE_TYPE.DATASET
89-
90-
elif all("particles" in fk or fk == "header" for fk in file_keys):
91-
return FILE_TYPE.PARTICLES
92-
93-
elif "header" in file.keys():
94-
groups = {name: group for name, group in file.items() if name != "header"}
95-
collection.structure.io.validate_linked_groups(groups)
96-
return FILE_TYPE.STRUCTURE_COLLECTION
90+
if "header" in file.keys():
91+
dtype = file["header"]["file"].attrs["data_type"]
92+
if dtype == "halo_particles":
93+
return FILE_TYPE.HALO_PARTICLES
94+
elif dtype == "halo_profiles":
95+
return FILE_TYPE.SOD_BINS
96+
elif dtype == "halo_properties":
97+
return FILE_TYPE.HALO_PROPERTIES
98+
elif dtype == "galaxy_properties":
99+
return FILE_TYPE.GALAXY_PROPERTIES
100+
elif dtype == "galaxy_particles":
101+
return FILE_TYPE.GALAXY_PARTICLES
102+
elif dtype == "diffsky_fits":
103+
return FILE_TYPE.SYNTHETIC_CATALOG
104+
else:
105+
raise ValueError(f"Unknown file type {dtype}")
97106

98107
if not all("header" in group.keys() for group in file.values()):
99108
for subgroup in file.values():
@@ -206,8 +215,11 @@ def open(
206215
207216
"""
208217
if len(files) == 1 and isinstance(files[0], list):
209-
return oc.open(*files[0], **open_kwargs)
210-
handles = [h5py.File(f) for f in files]
218+
file_list = files[0]
219+
else:
220+
file_list = list(files)
221+
file_list.sort()
222+
handles = [h5py.File(f) for f in file_list]
211223
file_types = list(map(get_file_type, handles))
212224
targets = make_all_targets(handles)
213225
targets = evaluate_load_conditions(targets, open_kwargs)

test/parallel/test_mpi.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import mpi4py
88
import numpy as np
99
import pytest
10+
from mpi4py import MPI
1011
from pytest_mpi.parallel_assert import parallel_assert
1112

1213
import opencosmo as oc
@@ -22,6 +23,11 @@ def particle_path(snapshot_path):
2223
return snapshot_path / "haloparticles.hdf5"
2324

2425

26+
@pytest.fixture
27+
def profile_path(snapshot_path):
28+
return snapshot_path / "sodproperties.hdf5"
29+
30+
2531
@pytest.fixture
2632
def malformed_header_path(input_path, tmp_path):
2733
update = {"n_dm": "foo"}
@@ -72,6 +78,20 @@ def test_mpi(input_path):
7278
parallel_assert(len(data) != 0)
7379

7480

81+
@pytest.mark.parallel(nprocs=4)
82+
def test_structure_collection_open(input_path, profile_path):
83+
oc.open(input_path, profile_path)
84+
85+
86+
@pytest.mark.parallel(nprocs=4)
87+
def test_structure_collection_open_2(input_path, profile_path):
88+
comm = MPI.COMM_WORLD
89+
if comm.Get_rank() in [0, 2]:
90+
oc.open(profile_path, input_path)
91+
else:
92+
oc.open(input_path, profile_path)
93+
94+
7595
@pytest.mark.parallel(nprocs=4)
7696
def test_partitioning_includes_all(input_path):
7797
with oc.open(input_path) as f:

test/test_collection.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def multi_path(snapshot_path):
1919

2020
@pytest.fixture
2121
def halo_paths(snapshot_path: Path):
22-
files = ["haloparticles.hdf5", "haloproperties.hdf5", "sodproperties.hdf5"]
22+
files = ["haloproperties.hdf5", "haloparticles.hdf5", "sodproperties.hdf5"]
2323
hdf_files = [snapshot_path / file for file in files]
2424
return list(hdf_files)
2525

@@ -48,6 +48,27 @@ def conditional_path(multi_path, tmp_path):
4848
return path
4949

5050

51+
def test_open_structures(halo_paths, galaxy_paths):
52+
c1 = oc.open(galaxy_paths)
53+
assert isinstance(c1, oc.StructureCollection)
54+
c2 = oc.open(halo_paths[0], galaxy_paths[1])
55+
assert isinstance(c2, oc.StructureCollection)
56+
c3 = oc.open(halo_paths[0], *galaxy_paths)
57+
assert isinstance(c3, oc.StructureCollection)
58+
c3 = oc.open(halo_paths[0], halo_paths[1])
59+
assert isinstance(c3, oc.StructureCollection)
60+
c3 = oc.open(halo_paths[0], halo_paths[2])
61+
assert isinstance(c3, oc.StructureCollection)
62+
c3 = oc.open(*halo_paths)
63+
assert isinstance(c3, oc.StructureCollection)
64+
c3 = oc.open(*halo_paths, galaxy_paths[1])
65+
assert isinstance(c3, oc.StructureCollection)
66+
c3 = oc.open(*halo_paths, *galaxy_paths)
67+
assert isinstance(c3, oc.StructureCollection)
68+
c3 = oc.open(halo_paths[0], halo_paths[1], *galaxy_paths)
69+
assert isinstance(c3, oc.StructureCollection)
70+
71+
5172
def test_multi_filter(multi_path):
5273
collection = oc.open(multi_path)
5374
collection = collection.filter(oc.col("sod_halo_mass") > 0)
@@ -56,6 +77,20 @@ def test_multi_filter(multi_path):
5677
assert all(ds.data["sod_halo_mass"] > 0)
5778

5879

80+
def test_link_particles_only(halo_paths):
81+
collection = oc.open(halo_paths[0], halo_paths[1])
82+
assert isinstance(collection, oc.StructureCollection)
83+
for key in collection.keys():
84+
assert "particles" in key or key == "halo_properties"
85+
86+
87+
def test_link_profiles_only(halo_paths):
88+
print(halo_paths)
89+
collection = oc.open(halo_paths[0], halo_paths[2])
90+
assert isinstance(collection, oc.StructureCollection)
91+
assert set(collection.keys()) == {"halo_properties", "halo_profiles"}
92+
93+
5994
def test_galaxy_alias_fails_for_halos(halo_paths):
6095
ds = oc.open(halo_paths)
6196
with pytest.raises(AttributeError):

0 commit comments

Comments
 (0)