musica 0.12.2__cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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.

Files changed (108) hide show
  1. musica/CMakeLists.txt +68 -0
  2. musica/__init__.py +11 -0
  3. musica/_musica.cpython-39-x86_64-linux-gnu.so +0 -0
  4. musica/_musica_gpu.cpython-39-x86_64-linux-gnu.so +0 -0
  5. musica/_version.py +1 -0
  6. musica/backend.py +41 -0
  7. musica/binding_common.cpp +33 -0
  8. musica/binding_common.hpp +7 -0
  9. musica/carma.cpp +911 -0
  10. musica/carma.py +1729 -0
  11. musica/constants.py +3 -0
  12. musica/cpu_binding.cpp +11 -0
  13. musica/cuda.cpp +12 -0
  14. musica/cuda.py +13 -0
  15. musica/examples/__init__.py +1 -0
  16. musica/examples/carma_aluminum.py +124 -0
  17. musica/examples/carma_sulfate.py +246 -0
  18. musica/examples/examples.py +165 -0
  19. musica/examples/sulfate_box_model.py +439 -0
  20. musica/examples/ts1_latin_hypercube.py +245 -0
  21. musica/gpu_binding.cpp +11 -0
  22. musica/main.py +89 -0
  23. musica/mechanism_configuration/__init__.py +1 -0
  24. musica/mechanism_configuration/aqueous_equilibrium.py +274 -0
  25. musica/mechanism_configuration/arrhenius.py +307 -0
  26. musica/mechanism_configuration/branched.py +299 -0
  27. musica/mechanism_configuration/condensed_phase_arrhenius.py +309 -0
  28. musica/mechanism_configuration/condensed_phase_photolysis.py +88 -0
  29. musica/mechanism_configuration/emission.py +71 -0
  30. musica/mechanism_configuration/first_order_loss.py +174 -0
  31. musica/mechanism_configuration/henrys_law.py +44 -0
  32. musica/mechanism_configuration/mechanism_configuration.py +234 -0
  33. musica/mechanism_configuration/phase.py +47 -0
  34. musica/mechanism_configuration/photolysis.py +88 -0
  35. musica/mechanism_configuration/reactions.py +73 -0
  36. musica/mechanism_configuration/simpol_phase_transfer.py +217 -0
  37. musica/mechanism_configuration/species.py +91 -0
  38. musica/mechanism_configuration/surface.py +94 -0
  39. musica/mechanism_configuration/ternary_chemical_activation.py +352 -0
  40. musica/mechanism_configuration/troe.py +352 -0
  41. musica/mechanism_configuration/tunneling.py +250 -0
  42. musica/mechanism_configuration/user_defined.py +88 -0
  43. musica/mechanism_configuration/utils.py +10 -0
  44. musica/mechanism_configuration/wet_deposition.py +52 -0
  45. musica/mechanism_configuration.cpp +607 -0
  46. musica/musica.cpp +201 -0
  47. musica/test/examples/v1/full_configuration/full_configuration.json +466 -0
  48. musica/test/examples/v1/full_configuration/full_configuration.yaml +295 -0
  49. musica/test/integration/test_analytical.py +324 -0
  50. musica/test/integration/test_carma.py +227 -0
  51. musica/test/integration/test_carma_aluminum.py +12 -0
  52. musica/test/integration/test_carma_sulfate.py +17 -0
  53. musica/test/integration/test_chapman.py +139 -0
  54. musica/test/integration/test_sulfate_box_model.py +34 -0
  55. musica/test/integration/test_tuvx.py +62 -0
  56. musica/test/unit/test_parser.py +64 -0
  57. musica/test/unit/test_serializer.py +69 -0
  58. musica/test/unit/test_state.py +325 -0
  59. musica/test/unit/test_util_full_mechanism.py +698 -0
  60. musica/tools/prepare_build_environment_linux.sh +32 -0
  61. musica/tools/prepare_build_environment_macos.sh +1 -0
  62. musica/tools/repair_wheel_gpu.sh +40 -0
  63. musica/tuvx.cpp +93 -0
  64. musica/tuvx.py +199 -0
  65. musica/types.py +407 -0
  66. musica-0.12.2.dist-info/METADATA +473 -0
  67. musica-0.12.2.dist-info/RECORD +111 -0
  68. musica-0.12.2.dist-info/WHEEL +6 -0
  69. musica-0.12.2.dist-info/entry_points.txt +3 -0
  70. musica-0.12.2.dist-info/licenses/AUTHORS.md +59 -0
  71. musica-0.12.2.dist-info/licenses/LICENSE +201 -0
  72. musica.libs/libaec-91f0ca0f.so.0.0.8 +0 -0
  73. musica.libs/libblas-fe34f726.so.3.8.0 +0 -0
  74. musica.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  75. musica.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  76. musica.libs/libcom_err-448cbf49.so.2.1 +0 -0
  77. musica.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  78. musica.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  79. musica.libs/libcurl-c96f124b.so.4.5.0 +0 -0
  80. musica.libs/libdf-2848fafa.so.0.0.0 +0 -0
  81. musica.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
  82. musica.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  83. musica.libs/libhdf5-b5e70f84.so.103.1.0 +0 -0
  84. musica.libs/libhdf5_hl-0b60eabd.so.100.1.2 +0 -0
  85. musica.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  86. musica.libs/libjpeg-e87d2658.so.62.2.0 +0 -0
  87. musica.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  88. musica.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  89. musica.libs/libkrb5-a55300e8.so.3.3 +0 -0
  90. musica.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  91. musica.libs/liblapack-31d7d384.so.3.8.0 +0 -0
  92. musica.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  93. musica.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  94. musica.libs/libmfhdf-38b34047.so.0.0.0 +0 -0
  95. musica.libs/libmvec-2-583a17db.28.so +0 -0
  96. musica.libs/libnetcdf-3915b03a.so.15.0.1 +0 -0
  97. musica.libs/libnetcdff-8cc3a1b0.so.7.0.0 +0 -0
  98. musica.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  99. musica.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  100. musica.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  101. musica.libs/libquadmath-2284e583.so.0.0.0 +0 -0
  102. musica.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  103. musica.libs/libselinux-d0805dcb.so.1 +0 -0
  104. musica.libs/libssh-c11d285b.so.4.8.7 +0 -0
  105. musica.libs/libssl-60250281.so.1.1.1k +0 -0
  106. musica.libs/libsz-0deeef7d.so.2.0.1 +0 -0
  107. musica.libs/libtirpc-cff449a4.so.3.0.0 +0 -0
  108. musica.libs/libunistring-05abdd40.so.2.1.0 +0 -0
@@ -0,0 +1,325 @@
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
+ diffusion_coefficient_m2_s=20.3,
16
+ molecular_weight_kg_mol=13.2,
17
+ other_properties={
18
+ "__absolute tolerance": "1.0e-30"})
19
+ B = mc.Species(name="B")
20
+ C = mc.Species(name="C")
21
+ M = mc.Species(name="M", is_third_body=True)
22
+
23
+ # Chemical phases
24
+ gas = mc.Phase(name="gas", species=[A, B, C, M])
25
+
26
+ # Reactions
27
+ my_arrhenius = mc.Arrhenius(
28
+ name="my arrhenius",
29
+ A=32.1, B=-2.3, C=102.3, D=63.4, E=-1.3,
30
+ gas_phase=gas,
31
+ reactants=[B],
32
+ products=[C],
33
+ other_properties={"__irrelevant": "2"},
34
+ )
35
+
36
+ my_other_arrhenius = mc.Arrhenius(
37
+ name="my other arrhenius",
38
+ A=29.3, B=-1.5, Ea=101.2, D=82.6, E=-0.98,
39
+ gas_phase=gas,
40
+ reactants=[A],
41
+ products=[(1.2, B)]
42
+ )
43
+
44
+ my_troe = mc.Troe(
45
+ name="my troe",
46
+ gas_phase=gas,
47
+ k0_A=1.2e-12,
48
+ k0_B=167,
49
+ k0_C=3,
50
+ kinf_A=136,
51
+ kinf_B=5,
52
+ kinf_C=24,
53
+ Fc=0.9,
54
+ N=0.8,
55
+ reactants=[B, A],
56
+ products=[C],
57
+ other_properties={"__irrelevant": "2"},
58
+ )
59
+
60
+ my_ternary = mc.TernaryChemicalActivation(
61
+ name="my ternary chemical activation",
62
+ gas_phase=gas,
63
+ k0_A=32.1,
64
+ k0_B=-2.3,
65
+ k0_C=102.3,
66
+ kinf_A=63.4,
67
+ kinf_B=-1.3,
68
+ kinf_C=908.5,
69
+ Fc=1.3,
70
+ N=32.1,
71
+ reactants=[B, A],
72
+ products=[C],
73
+ other_properties={"__irrelevant": "2"},
74
+ )
75
+
76
+ my_branched = mc.Branched(
77
+ name="my branched",
78
+ gas_phase=gas,
79
+ reactants=[A],
80
+ alkoxy_products=[B],
81
+ nitrate_products=[C],
82
+ X=1.2e-4,
83
+ Y=167,
84
+ a0=0.15,
85
+ n=9,
86
+ other_properties={"__irrelevant": "2"},
87
+ )
88
+
89
+ my_tunneling = mc.Tunneling(
90
+ name="my tunneling",
91
+ gas_phase=gas,
92
+ reactants=[B],
93
+ products=[C],
94
+ A=123.45,
95
+ B=1200.0,
96
+ C=1.0e8,
97
+ other_properties={"__irrelevant": "2"},
98
+ )
99
+
100
+ my_surface = mc.Surface(
101
+ name="my surface",
102
+ gas_phase=gas,
103
+ gas_phase_species=A,
104
+ reaction_probability=2.0e-2,
105
+ gas_phase_products=[B, C],
106
+ other_properties={"__irrelevant": "2"},
107
+ )
108
+
109
+ photo_b = mc.Photolysis(
110
+ name="photo B",
111
+ gas_phase=gas,
112
+ reactants=[B],
113
+ products=[C],
114
+ scaling_factor=12.3,
115
+ other_properties={"__irrelevant": "2"},
116
+ )
117
+
118
+ my_emission = mc.Emission(
119
+ name="my emission",
120
+ gas_phase=gas,
121
+ products=[B],
122
+ scaling_factor=12.3,
123
+ other_properties={"__irrelevant": "2"},
124
+ )
125
+
126
+ my_first_order_loss = mc.FirstOrderLoss(
127
+ name="my first order loss",
128
+ gas_phase=gas,
129
+ reactants=[C],
130
+ scaling_factor=12.3,
131
+ other_properties={"__irrelevant": "2"},
132
+ )
133
+
134
+ user_defined = mc.UserDefined(
135
+ name="my user defined",
136
+ gas_phase=gas,
137
+ reactants=[A, B],
138
+ products=[(1.3, C)],
139
+ scaling_factor=12.3,
140
+ other_properties={"__irrelevant": "2"}
141
+ )
142
+
143
+ # Mechanism
144
+ return mc.Mechanism(
145
+ name="Full Configuration",
146
+ species=[A, B, C, M],
147
+ phases=[gas],
148
+ reactions=[my_arrhenius, my_other_arrhenius, my_troe, my_ternary,
149
+ my_branched, my_tunneling, 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
+ # Test multiple grid cells
263
+ state_multi = solver.create_state(number_of_grid_cells=2)
264
+ params_multi = {"PHOTO.photo B": [1.0, 2.0]}
265
+ state_multi.set_user_defined_rate_parameters(params_multi)
266
+ result_multi = state_multi.get_user_defined_rate_parameters()
267
+ assert result_multi["PHOTO.photo B"] == [1.0, 2.0]
268
+
269
+ # Test invalid parameter
270
+ with pytest.raises(ValueError, match="User-defined rate parameter invalid_param not found"):
271
+ state.set_user_defined_rate_parameters({"invalid_param": 1.0})
272
+
273
+ solver = get_test_solver(mech)
274
+
275
+ state = solver.create_state(number_of_grid_cells=1)
276
+
277
+ # Test species ordering
278
+ species_ordering = state.get_species_ordering()
279
+ assert isinstance(species_ordering, dict)
280
+ assert len(species_ordering) == 3 # A, B, C (M is third-body and not included)
281
+
282
+ # Dictionary style access
283
+ assert species_ordering["A"] >= 0
284
+ assert species_ordering["B"] >= 0
285
+ assert species_ordering["C"] >= 0
286
+
287
+ # Using get() method with default value
288
+ assert species_ordering.get("A", -1) >= 0 # returns value if key exists
289
+ assert species_ordering.get("Z", -1) == -1 # returns -1 since Z doesn't exist
290
+
291
+ # Test key membership
292
+ assert "A" in species_ordering
293
+ assert "B" in species_ordering
294
+ assert "C" in species_ordering
295
+ assert "M" not in species_ordering # third-body not included
296
+
297
+ # Test parameter ordering
298
+ param_ordering = state.get_user_defined_rate_parameters_ordering()
299
+ assert isinstance(param_ordering, dict)
300
+ assert len(param_ordering) == 6
301
+
302
+ # Convert dict keys to list using list() function
303
+ param_names = list(param_ordering.keys())
304
+ assert len(param_names) == 6
305
+ assert isinstance(param_names[0], str)
306
+
307
+ # Alternative way using list comprehension
308
+ param_names_alt = [key for key in param_ordering]
309
+ assert param_names_alt == param_names
310
+
311
+ # Sort the keys if needed - useful for consistent ordering in tests
312
+ sorted_param_names = sorted(param_ordering.keys())
313
+ assert len(sorted_param_names) == 6
314
+ assert all(isinstance(name, str) for name in sorted_param_names)
315
+
316
+ # Verify all expected keys are present
317
+ expected_params = [
318
+ "PHOTO.photo B",
319
+ "EMIS.my emission",
320
+ "LOSS.my first order loss",
321
+ "SURF.my surface.effective radius [m]",
322
+ "SURF.my surface.particle number concentration [# m-3]",
323
+ "USER.my user defined"
324
+ ]
325
+ assert sorted(expected_params) == sorted(param_names)