musica 0.12.1__cp311-cp311-win32.whl → 0.13.0__cp311-cp311-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of musica might be problematic. Click here for more details.
- musica/CMakeLists.txt +4 -0
- musica/_musica.cp311-win32.pyd +0 -0
- musica/_version.py +1 -1
- musica/binding_common.cpp +6 -9
- musica/binding_common.hpp +17 -1
- musica/examples/__init__.py +1 -1
- musica/examples/carma_aluminum.py +1 -0
- musica/examples/carma_sulfate.py +1 -0
- musica/examples/sulfate_box_model.py +2 -2
- musica/examples/ts1_latin_hypercube.py +1 -1
- musica/grid.cpp +206 -0
- musica/grid.py +98 -0
- musica/grid_map.cpp +117 -0
- musica/grid_map.py +167 -0
- musica/mechanism_configuration/__init__.py +18 -1
- musica/mechanism_configuration/ancillary.py +6 -0
- musica/mechanism_configuration/arrhenius.py +111 -269
- musica/mechanism_configuration/branched.py +116 -275
- musica/mechanism_configuration/emission.py +63 -52
- musica/mechanism_configuration/first_order_loss.py +73 -157
- musica/mechanism_configuration/mechanism.py +93 -0
- musica/mechanism_configuration/phase.py +44 -33
- musica/mechanism_configuration/phase_species.py +58 -0
- musica/mechanism_configuration/photolysis.py +77 -67
- musica/mechanism_configuration/reaction_component.py +54 -0
- musica/mechanism_configuration/reactions.py +17 -58
- musica/mechanism_configuration/species.py +45 -71
- musica/mechanism_configuration/surface.py +78 -74
- musica/mechanism_configuration/taylor_series.py +136 -0
- musica/mechanism_configuration/ternary_chemical_activation.py +138 -330
- musica/mechanism_configuration/troe.py +138 -330
- musica/mechanism_configuration/tunneling.py +105 -229
- musica/mechanism_configuration/user_defined.py +79 -68
- musica/mechanism_configuration.cpp +54 -162
- musica/musica.cpp +2 -5
- musica/profile.cpp +294 -0
- musica/profile.py +93 -0
- musica/profile_map.cpp +117 -0
- musica/profile_map.py +167 -0
- musica/test/examples/v1/full_configuration/full_configuration.json +91 -233
- musica/test/examples/v1/full_configuration/full_configuration.yaml +191 -290
- musica/test/integration/test_carma_aluminum.py +2 -1
- musica/test/integration/test_carma_sulfate.py +2 -1
- musica/test/integration/test_chapman.py +2 -2
- musica/test/integration/test_sulfate_box_model.py +1 -1
- musica/test/integration/test_tuvx.py +72 -15
- musica/test/unit/test_grid.py +137 -0
- musica/test/unit/test_grid_map.py +126 -0
- musica/test/unit/test_parser.py +10 -10
- musica/test/unit/test_profile.py +169 -0
- musica/test/unit/test_profile_map.py +137 -0
- musica/test/unit/test_serializer.py +17 -16
- musica/test/unit/test_state.py +338 -0
- musica/test/unit/test_util_full_mechanism.py +78 -298
- musica/tools/prepare_build_environment_linux.sh +7 -25
- musica/tuvx.cpp +94 -15
- musica/tuvx.py +92 -22
- musica/types.py +28 -17
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/METADATA +15 -14
- musica-0.13.0.dist-info/RECORD +80 -0
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/WHEEL +1 -1
- musica/mechanism_configuration/aqueous_equilibrium.py +0 -274
- musica/mechanism_configuration/condensed_phase_arrhenius.py +0 -309
- musica/mechanism_configuration/condensed_phase_photolysis.py +0 -88
- musica/mechanism_configuration/henrys_law.py +0 -44
- musica/mechanism_configuration/mechanism_configuration.py +0 -234
- musica/mechanism_configuration/simpol_phase_transfer.py +0 -217
- musica/mechanism_configuration/wet_deposition.py +0 -52
- musica-0.12.1.dist-info/RECORD +0 -69
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/entry_points.txt +0 -0
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/licenses/AUTHORS.md +0 -0
- {musica-0.12.1.dist-info → musica-0.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,55 +1,52 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
import os
|
|
3
|
-
from musica.mechanism_configuration import
|
|
3
|
+
from musica.mechanism_configuration import Mechanism, Parser
|
|
4
4
|
from test_util_full_mechanism import get_fully_defined_mechanism, validate_full_v1_mechanism
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def test_mechanism_export_loop(tmp_path):
|
|
8
8
|
parser = Parser()
|
|
9
|
-
|
|
9
|
+
mechanism = get_fully_defined_mechanism()
|
|
10
10
|
extensions = [".yml", ".yaml", ".json"]
|
|
11
11
|
for extension in extensions:
|
|
12
12
|
path = f"{tmp_path}/test_mechanism{extension}"
|
|
13
|
-
|
|
13
|
+
mechanism.export(path)
|
|
14
14
|
mechanism = parser.parse(path)
|
|
15
15
|
validate_full_v1_mechanism(mechanism)
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def test_serialize_parser_loop(tmp_path):
|
|
19
19
|
parser = Parser()
|
|
20
|
-
|
|
21
|
-
extensions = [".
|
|
20
|
+
mechanism = get_fully_defined_mechanism()
|
|
21
|
+
extensions = [".json", ".yml", ".yaml"]
|
|
22
22
|
for extension in extensions:
|
|
23
23
|
path = f"{tmp_path}/test_mechanism{extension}"
|
|
24
|
-
|
|
24
|
+
mechanism.export(path)
|
|
25
25
|
mechanism = parser.parse(path)
|
|
26
26
|
validate_full_v1_mechanism(mechanism)
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def test_serialize_to_file(tmp_path):
|
|
30
|
-
|
|
30
|
+
mechanism = get_fully_defined_mechanism()
|
|
31
31
|
extensions = [".yml", ".yaml", ".json"]
|
|
32
32
|
for extension in extensions:
|
|
33
33
|
file_path = f'{tmp_path}/test_mechanism{extension}'
|
|
34
34
|
assert not os.path.exists(file_path)
|
|
35
|
-
|
|
35
|
+
mechanism.export(file_path)
|
|
36
36
|
assert os.path.exists(file_path)
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def test_bad_inputs():
|
|
40
|
-
|
|
41
|
-
MechanismSerializer.serialize(None)
|
|
42
|
-
with pytest.raises(TypeError):
|
|
43
|
-
MechanismSerializer.serialize('not a mechanism')
|
|
40
|
+
mechanism = Mechanism(name="Full Configuration")
|
|
44
41
|
with pytest.raises(Exception):
|
|
45
|
-
|
|
42
|
+
mechanism.export("unsupported.txt")
|
|
46
43
|
|
|
47
44
|
|
|
48
45
|
def test_path_creation(tmp_path):
|
|
49
46
|
mechanism = Mechanism(name="Full Configuration")
|
|
50
47
|
path = f"{tmp_path}/non_existant_path/"
|
|
51
48
|
assert not os.path.exists(path)
|
|
52
|
-
|
|
49
|
+
mechanism.export(f"{path}test_mechanism.json")
|
|
53
50
|
assert os.path.exists(path)
|
|
54
51
|
|
|
55
52
|
|
|
@@ -59,11 +56,15 @@ def test_overwrite_file(tmp_path):
|
|
|
59
56
|
assert not os.path.exists(file_path)
|
|
60
57
|
|
|
61
58
|
# write first file
|
|
62
|
-
|
|
59
|
+
mechanism.export(file_path)
|
|
63
60
|
files = list(tmp_path.iterdir())
|
|
64
61
|
assert len(files) == 1
|
|
65
62
|
|
|
66
63
|
# overwrite file
|
|
67
|
-
|
|
64
|
+
mechanism.export(file_path)
|
|
68
65
|
files = list(tmp_path.iterdir())
|
|
69
66
|
assert len(files) == 1
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
pytest.main([__file__])
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""Tests for the State class."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import pytest # type: ignore # pylint: disable=import-error
|
|
4
|
+
import numpy as np # type: ignore # pylint: disable=import-error
|
|
5
|
+
from musica.types import State, MICM
|
|
6
|
+
import musica.mechanism_configuration as mc
|
|
7
|
+
from musica.mechanism_configuration import Mechanism
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_test_mechanism() -> Mechanism:
|
|
11
|
+
"""Helper function to create a test mechanism."""
|
|
12
|
+
# Chemical species
|
|
13
|
+
A = mc.Species(
|
|
14
|
+
name="A",
|
|
15
|
+
molecular_weight_kg_mol=32.1,
|
|
16
|
+
other_properties={
|
|
17
|
+
"__absolute tolerance": "1.0e-30"})
|
|
18
|
+
B = mc.Species(name="B")
|
|
19
|
+
C = mc.Species(name="C")
|
|
20
|
+
M = mc.Species(name="M", is_third_body=True)
|
|
21
|
+
|
|
22
|
+
# Chemical phases
|
|
23
|
+
gas = mc.Phase(name="gas", species=[mc.PhaseSpecies(name=A.name, diffusion_coefficient_m2_s=1.0), B, C, M])
|
|
24
|
+
|
|
25
|
+
# Reactions
|
|
26
|
+
my_arrhenius = mc.Arrhenius(
|
|
27
|
+
name="my arrhenius",
|
|
28
|
+
A=32.1, B=-2.3, C=102.3, D=63.4, E=-1.3,
|
|
29
|
+
gas_phase=gas,
|
|
30
|
+
reactants=[B],
|
|
31
|
+
products=[C],
|
|
32
|
+
other_properties={"__irrelevant": "2"},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
my_other_arrhenius = mc.Arrhenius(
|
|
36
|
+
name="my other arrhenius",
|
|
37
|
+
A=29.3, B=-1.5, Ea=101.2, D=82.6, E=-0.98,
|
|
38
|
+
gas_phase=gas,
|
|
39
|
+
reactants=[A],
|
|
40
|
+
products=[(1.2, B)]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
my_troe = mc.Troe(
|
|
44
|
+
name="my troe",
|
|
45
|
+
gas_phase=gas,
|
|
46
|
+
k0_A=1.2e-12,
|
|
47
|
+
k0_B=167,
|
|
48
|
+
k0_C=3,
|
|
49
|
+
kinf_A=136,
|
|
50
|
+
kinf_B=5,
|
|
51
|
+
kinf_C=24,
|
|
52
|
+
Fc=0.9,
|
|
53
|
+
N=0.8,
|
|
54
|
+
reactants=[B, A],
|
|
55
|
+
products=[C],
|
|
56
|
+
other_properties={"__irrelevant": "2"},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
my_ternary = mc.TernaryChemicalActivation(
|
|
60
|
+
name="my ternary chemical activation",
|
|
61
|
+
gas_phase=gas,
|
|
62
|
+
k0_A=32.1,
|
|
63
|
+
k0_B=-2.3,
|
|
64
|
+
k0_C=102.3,
|
|
65
|
+
kinf_A=63.4,
|
|
66
|
+
kinf_B=-1.3,
|
|
67
|
+
kinf_C=908.5,
|
|
68
|
+
Fc=1.3,
|
|
69
|
+
N=32.1,
|
|
70
|
+
reactants=[B, A],
|
|
71
|
+
products=[C],
|
|
72
|
+
other_properties={"__irrelevant": "2"},
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
my_branched = mc.Branched(
|
|
76
|
+
name="my branched",
|
|
77
|
+
gas_phase=gas,
|
|
78
|
+
reactants=[A],
|
|
79
|
+
alkoxy_products=[B],
|
|
80
|
+
nitrate_products=[C],
|
|
81
|
+
X=1.2e-4,
|
|
82
|
+
Y=167,
|
|
83
|
+
a0=0.15,
|
|
84
|
+
n=9,
|
|
85
|
+
other_properties={"__irrelevant": "2"},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
my_tunneling = mc.Tunneling(
|
|
89
|
+
name="my tunneling",
|
|
90
|
+
gas_phase=gas,
|
|
91
|
+
reactants=[B],
|
|
92
|
+
products=[C],
|
|
93
|
+
A=123.45,
|
|
94
|
+
B=1200.0,
|
|
95
|
+
C=1.0e8,
|
|
96
|
+
other_properties={"__irrelevant": "2"},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
my_surface = mc.Surface(
|
|
100
|
+
name="my surface",
|
|
101
|
+
gas_phase=gas,
|
|
102
|
+
gas_phase_species=A,
|
|
103
|
+
reaction_probability=2.0e-2,
|
|
104
|
+
gas_phase_products=[B, C],
|
|
105
|
+
other_properties={"__irrelevant": "2"},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
photo_b = mc.Photolysis(
|
|
109
|
+
name="photo B",
|
|
110
|
+
gas_phase=gas,
|
|
111
|
+
reactants=[B],
|
|
112
|
+
products=[C],
|
|
113
|
+
scaling_factor=12.3,
|
|
114
|
+
other_properties={"__irrelevant": "2"},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
my_emission = mc.Emission(
|
|
118
|
+
name="my emission",
|
|
119
|
+
gas_phase=gas,
|
|
120
|
+
products=[B],
|
|
121
|
+
scaling_factor=12.3,
|
|
122
|
+
other_properties={"__irrelevant": "2"},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
my_first_order_loss = mc.FirstOrderLoss(
|
|
126
|
+
name="my first order loss",
|
|
127
|
+
gas_phase=gas,
|
|
128
|
+
reactants=[C],
|
|
129
|
+
scaling_factor=12.3,
|
|
130
|
+
other_properties={"__irrelevant": "2"},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
user_defined = mc.UserDefined(
|
|
134
|
+
name="my user defined",
|
|
135
|
+
gas_phase=gas,
|
|
136
|
+
reactants=[A, B],
|
|
137
|
+
products=[(1.3, C)],
|
|
138
|
+
scaling_factor=12.3,
|
|
139
|
+
other_properties={"__irrelevant": "2"}
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Mechanism
|
|
143
|
+
return mc.Mechanism(
|
|
144
|
+
name="Full Configuration",
|
|
145
|
+
species=[A, B, C, M],
|
|
146
|
+
phases=[gas],
|
|
147
|
+
reactions=[my_arrhenius, my_other_arrhenius, my_troe, my_ternary,
|
|
148
|
+
my_branched, my_tunneling,
|
|
149
|
+
my_surface, photo_b,
|
|
150
|
+
my_emission, my_first_order_loss, user_defined],
|
|
151
|
+
version=mc.Version(1, 0, 0),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def get_test_solver(mech: Mechanism) -> MICM:
|
|
156
|
+
"""Helper function to create a test solver."""
|
|
157
|
+
return MICM(mechanism=mech)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_state_initialization():
|
|
161
|
+
"""Test State initialization with various grid cell configurations."""
|
|
162
|
+
# Use the test mechanism
|
|
163
|
+
mech = create_test_mechanism()
|
|
164
|
+
|
|
165
|
+
# Create MICM instance
|
|
166
|
+
solver = get_test_solver(mech)
|
|
167
|
+
|
|
168
|
+
# Test with valid input
|
|
169
|
+
state = solver.create_state(number_of_grid_cells=1)
|
|
170
|
+
assert isinstance(state, State)
|
|
171
|
+
|
|
172
|
+
# Test with multiple grid cells
|
|
173
|
+
state_multi = solver.create_state(number_of_grid_cells=3)
|
|
174
|
+
assert isinstance(state_multi, State)
|
|
175
|
+
|
|
176
|
+
# Test with invalid input
|
|
177
|
+
with pytest.raises(ValueError, match="number_of_grid_cells must be greater than 0"):
|
|
178
|
+
solver.create_state(number_of_grid_cells=0)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_set_get_concentrations():
|
|
182
|
+
"""Test setting and getting concentrations."""
|
|
183
|
+
# Use the test mechanism
|
|
184
|
+
mech = create_test_mechanism()
|
|
185
|
+
solver = get_test_solver(mech)
|
|
186
|
+
|
|
187
|
+
# Test single grid cell
|
|
188
|
+
state = solver.create_state(number_of_grid_cells=1)
|
|
189
|
+
concentrations = {"A": 1.0, "B": 2.0, "C": 3.0}
|
|
190
|
+
state.set_concentrations(concentrations)
|
|
191
|
+
result = state.get_concentrations()
|
|
192
|
+
assert result["A"][0] == 1.0
|
|
193
|
+
assert result["B"][0] == 2.0
|
|
194
|
+
assert result["C"][0] == 3.0
|
|
195
|
+
|
|
196
|
+
# Test multiple grid cells
|
|
197
|
+
state_multi = solver.create_state(number_of_grid_cells=2)
|
|
198
|
+
concentrations_multi = {"A": [1.0, 2.0], "B": [3.0, 4.0], "C": [5.0, 6.0]}
|
|
199
|
+
state_multi.set_concentrations(concentrations_multi)
|
|
200
|
+
result_multi = state_multi.get_concentrations()
|
|
201
|
+
assert result_multi["A"] == [1.0, 2.0]
|
|
202
|
+
assert result_multi["B"] == [3.0, 4.0]
|
|
203
|
+
assert result_multi["C"] == [5.0, 6.0]
|
|
204
|
+
|
|
205
|
+
# Test invalid species
|
|
206
|
+
with pytest.raises(ValueError, match="Species D not found in the mechanism"):
|
|
207
|
+
state.set_concentrations({"D": 1.0})
|
|
208
|
+
|
|
209
|
+
# Test invalid length
|
|
210
|
+
with pytest.raises(ValueError, match="must have length"):
|
|
211
|
+
state_multi.set_concentrations({"A": [1.0]})
|
|
212
|
+
|
|
213
|
+
# Test cannot set third-body species
|
|
214
|
+
with pytest.raises(ValueError, match="Species M not found in the mechanism"):
|
|
215
|
+
state.set_concentrations({"M": 1.0})
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_set_get_conditions():
|
|
219
|
+
"""Test setting and getting environmental conditions."""
|
|
220
|
+
# Use the test mechanism
|
|
221
|
+
mech = create_test_mechanism()
|
|
222
|
+
solver = get_test_solver(mech)
|
|
223
|
+
|
|
224
|
+
# Test single grid cell
|
|
225
|
+
state = solver.create_state(number_of_grid_cells=1)
|
|
226
|
+
state.set_conditions(temperatures=300.0, pressures=101325.0)
|
|
227
|
+
conditions = state.get_conditions()
|
|
228
|
+
assert conditions["temperature"][0] == 300.0
|
|
229
|
+
assert conditions["pressure"][0] == 101325.0
|
|
230
|
+
assert np.isclose(conditions["air_density"][0], 40.9, rtol=0.1) # Approximate value from ideal gas law
|
|
231
|
+
|
|
232
|
+
# Test multiple grid cells
|
|
233
|
+
state_multi = solver.create_state(number_of_grid_cells=2)
|
|
234
|
+
state_multi.set_conditions(
|
|
235
|
+
temperatures=[300.0, 310.0],
|
|
236
|
+
pressures=[101325.0, 101325.0],
|
|
237
|
+
air_densities=[40.9, 39.5]
|
|
238
|
+
)
|
|
239
|
+
conditions_multi = state_multi.get_conditions()
|
|
240
|
+
assert conditions_multi["temperature"] == [300.0, 310.0]
|
|
241
|
+
assert conditions_multi["pressure"] == [101325.0, 101325.0]
|
|
242
|
+
assert conditions_multi["air_density"] == [40.9, 39.5]
|
|
243
|
+
|
|
244
|
+
# Test invalid input length
|
|
245
|
+
with pytest.raises(ValueError, match="must be a list of length"):
|
|
246
|
+
state_multi.set_conditions(temperatures=[300.0])
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_set_get_user_defined_rate_parameters():
|
|
250
|
+
"""Test setting and getting user-defined rate parameters."""
|
|
251
|
+
# Use the test mechanism which includes a user-defined reaction
|
|
252
|
+
mech = create_test_mechanism()
|
|
253
|
+
solver = get_test_solver(mech)
|
|
254
|
+
|
|
255
|
+
# Test single grid cell
|
|
256
|
+
state = solver.create_state(number_of_grid_cells=1)
|
|
257
|
+
params = {"EMIS.my emission": 1.0}
|
|
258
|
+
state.set_user_defined_rate_parameters(params)
|
|
259
|
+
result = state.get_user_defined_rate_parameters()
|
|
260
|
+
assert result["EMIS.my emission"][0] == 1.0
|
|
261
|
+
|
|
262
|
+
params = {
|
|
263
|
+
"SURF.my surface.effective radius [m]": 0.5,
|
|
264
|
+
"SURF.my surface.particle number concentration [# m-3]": 1000.0,
|
|
265
|
+
}
|
|
266
|
+
state.set_user_defined_rate_parameters(params)
|
|
267
|
+
result = state.get_user_defined_rate_parameters()
|
|
268
|
+
assert result["SURF.my surface.effective radius [m]"][0] == 0.5
|
|
269
|
+
assert result["SURF.my surface.particle number concentration [# m-3]"][0] == 1000.0
|
|
270
|
+
|
|
271
|
+
# Test multiple grid cells
|
|
272
|
+
state_multi = solver.create_state(number_of_grid_cells=2)
|
|
273
|
+
params_multi = {"PHOTO.photo B": [1.0, 2.0]}
|
|
274
|
+
state_multi.set_user_defined_rate_parameters(params_multi)
|
|
275
|
+
result_multi = state_multi.get_user_defined_rate_parameters()
|
|
276
|
+
assert result_multi["PHOTO.photo B"] == [1.0, 2.0]
|
|
277
|
+
|
|
278
|
+
# Test invalid parameter
|
|
279
|
+
with pytest.raises(ValueError, match="User-defined rate parameter invalid_param not found"):
|
|
280
|
+
state.set_user_defined_rate_parameters({"invalid_param": 1.0})
|
|
281
|
+
|
|
282
|
+
solver = get_test_solver(mech)
|
|
283
|
+
|
|
284
|
+
state = solver.create_state(number_of_grid_cells=1)
|
|
285
|
+
|
|
286
|
+
# Test species ordering
|
|
287
|
+
species_ordering = state.get_species_ordering()
|
|
288
|
+
assert isinstance(species_ordering, dict)
|
|
289
|
+
assert len(species_ordering) == 3 # A, B, C (M is third-body and not included)
|
|
290
|
+
|
|
291
|
+
# Dictionary style access
|
|
292
|
+
assert species_ordering["A"] >= 0
|
|
293
|
+
assert species_ordering["B"] >= 0
|
|
294
|
+
assert species_ordering["C"] >= 0
|
|
295
|
+
|
|
296
|
+
# Using get() method with default value
|
|
297
|
+
assert species_ordering.get("A", -1) >= 0 # returns value if key exists
|
|
298
|
+
assert species_ordering.get("Z", -1) == -1 # returns -1 since Z doesn't exist
|
|
299
|
+
|
|
300
|
+
# Test key membership
|
|
301
|
+
assert "A" in species_ordering
|
|
302
|
+
assert "B" in species_ordering
|
|
303
|
+
assert "C" in species_ordering
|
|
304
|
+
assert "M" not in species_ordering # third-body not included
|
|
305
|
+
|
|
306
|
+
# Test parameter ordering
|
|
307
|
+
param_ordering = state.get_user_defined_rate_parameters_ordering()
|
|
308
|
+
assert isinstance(param_ordering, dict)
|
|
309
|
+
assert len(param_ordering) == 6
|
|
310
|
+
|
|
311
|
+
# Convert dict keys to list using list() function
|
|
312
|
+
param_names = list(param_ordering.keys())
|
|
313
|
+
assert len(param_names) == 6
|
|
314
|
+
assert isinstance(param_names[0], str)
|
|
315
|
+
|
|
316
|
+
# Alternative way using list comprehension
|
|
317
|
+
param_names_alt = [key for key in param_ordering]
|
|
318
|
+
assert param_names_alt == param_names
|
|
319
|
+
|
|
320
|
+
# Sort the keys if needed - useful for consistent ordering in tests
|
|
321
|
+
sorted_param_names = sorted(param_ordering.keys())
|
|
322
|
+
assert len(sorted_param_names) == 6
|
|
323
|
+
assert all(isinstance(name, str) for name in sorted_param_names)
|
|
324
|
+
|
|
325
|
+
# Verify all expected keys are present
|
|
326
|
+
expected_params = [
|
|
327
|
+
"PHOTO.photo B",
|
|
328
|
+
"EMIS.my emission",
|
|
329
|
+
"LOSS.my first order loss",
|
|
330
|
+
"SURF.my surface.effective radius [m]",
|
|
331
|
+
"SURF.my surface.particle number concentration [# m-3]",
|
|
332
|
+
"USER.my user defined"
|
|
333
|
+
]
|
|
334
|
+
assert sorted(expected_params) == sorted(param_names)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if __name__ == "__main__":
|
|
338
|
+
pytest.main([__file__])
|