musica 0.10.1__cp311-cp311-macosx_11_0_arm64.whl → 0.11.1.0__cp311-cp311-macosx_11_0_arm64.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.

@@ -0,0 +1,323 @@
1
+ import pytest
2
+ import numpy as np
3
+ import musica
4
+ import random
5
+ import musica.mechanism_configuration as mc
6
+ from _musica._core import _is_cuda_available
7
+
8
+
9
+ def TestSingleGridCell(solver, state, time_step, places=5):
10
+ temperature = 272.5
11
+ pressure = 101253.3
12
+ GAS_CONSTANT = 8.31446261815324
13
+ air_density = pressure / (GAS_CONSTANT * temperature)
14
+
15
+ rate_constants = {
16
+ "USER.reaction 1": 0.001,
17
+ "USER.reaction 2": 0.002
18
+ }
19
+
20
+ concentrations = {
21
+ "A": 0.75,
22
+ "B": 0,
23
+ "C": 0.4,
24
+ "D": 0.8,
25
+ "E": 0,
26
+ "F": 0.1
27
+ }
28
+ state.set_conditions(temperature, pressure, air_density)
29
+ state.set_concentrations(concentrations)
30
+ state.set_user_defined_rate_parameters(rate_constants)
31
+
32
+ # test to make sure a second call to set_conditions with an empty dictionary does not change the values
33
+ state.set_concentrations({})
34
+ state.set_user_defined_rate_parameters({})
35
+
36
+ initial_concentrations = state.get_concentrations()
37
+ initial_rate_parameters = state.get_user_defined_rate_parameters()
38
+ initial_temperatures = state.get_conditions()["temperature"]
39
+ initial_pressures = state.get_conditions()["pressure"]
40
+ initial_air_density = state.get_conditions()["air_density"]
41
+ assert np.isclose(initial_concentrations["A"][0], concentrations["A"], atol=1e-13)
42
+ assert np.isclose(initial_concentrations["B"][0], concentrations["B"], atol=1e-13)
43
+ assert np.isclose(initial_concentrations["C"][0], concentrations["C"], atol=1e-13)
44
+ assert np.isclose(initial_concentrations["D"][0], concentrations["D"], atol=1e-13)
45
+ assert np.isclose(initial_concentrations["E"][0], concentrations["E"], atol=1e-13)
46
+ assert np.isclose(initial_concentrations["F"][0], concentrations["F"], atol=1e-13)
47
+ assert np.isclose(initial_rate_parameters["USER.reaction 1"][0], rate_constants["USER.reaction 1"], atol=1e-13)
48
+ assert np.isclose(initial_rate_parameters["USER.reaction 2"][0], rate_constants["USER.reaction 2"], atol=1e-13)
49
+ assert np.isclose(initial_temperatures[0], temperature, atol=1e-13)
50
+ assert np.isclose(initial_pressures[0], pressure, atol=1e-13)
51
+ assert np.isclose(initial_air_density[0], air_density, atol=1e-13)
52
+
53
+ time_step = 1
54
+ sim_length = 100
55
+
56
+ curr_time = time_step
57
+ initial_A = initial_concentrations["A"][0]
58
+ initial_C = initial_concentrations["C"][0]
59
+ initial_D = initial_concentrations["D"][0]
60
+ initial_F = initial_concentrations["F"][0]
61
+ # Gets analytical concentrations
62
+ while curr_time <= sim_length:
63
+ solver.solve(state, time_step)
64
+ concentrations = state.get_concentrations()
65
+ k1 = rate_constants["USER.reaction 1"]
66
+ k2 = rate_constants["USER.reaction 2"]
67
+ k3 = 0.004 * np.exp(50.0 / temperature)
68
+ k4 = 0.012 * np.exp(75.0 / temperature) * \
69
+ (temperature / 50.0)**(-2) * (1.0 + 1.0e-6 * pressure)
70
+ A_conc = initial_A * np.exp(-(k3) * curr_time)
71
+ B_conc = initial_A * (k3 / (k4 - k3)) * \
72
+ (np.exp(-k3 * curr_time) - np.exp(-k4 * curr_time))
73
+ C_conc = initial_C + initial_A * \
74
+ (1.0 + (k3 * np.exp(-k4 * curr_time) - k4 * np.exp(-k3 * curr_time)) / (k4 - k3))
75
+ D_conc = initial_D * np.exp(-(k1) * curr_time)
76
+ E_conc = initial_D * (k1 / (k2 - k1)) * \
77
+ (np.exp(-k1 * curr_time) - np.exp(-k2 * curr_time))
78
+ F_conc = initial_F + initial_D * \
79
+ (1.0 + (k1 * np.exp(-k2 * curr_time) - k2 * np.exp(-k1 * curr_time)) / (k2 - k1))
80
+
81
+ assert np.isclose(concentrations["A"][0], A_conc, atol=10**-places)
82
+ assert np.isclose(concentrations["B"][0], B_conc, atol=10**-places)
83
+ assert np.isclose(concentrations["C"][0], C_conc, atol=10**-places)
84
+ assert np.isclose(concentrations["D"][0], D_conc, atol=10**-places)
85
+ assert np.isclose(concentrations["E"][0], E_conc, atol=10**-places)
86
+ assert np.isclose(concentrations["F"][0], F_conc, atol=10**-places)
87
+
88
+ curr_time += time_step
89
+
90
+
91
+ def TestMultipleGridCell(solver, state, num_grid_cells, time_step, places=5):
92
+ concentrations = {
93
+ "A": [],
94
+ "B": [],
95
+ "C": [],
96
+ "D": [],
97
+ "E": [],
98
+ "F": []
99
+ }
100
+ rate_constants = {
101
+ "USER.reaction 1": [],
102
+ "USER.reaction 2": []
103
+ }
104
+ temperatures = []
105
+ pressures = []
106
+
107
+ for i in range(num_grid_cells):
108
+ temperatures.append(275.0 + random.uniform(-50.0, 50.0))
109
+ pressures.append(101253.3 + random.uniform(-500.0, 500.0))
110
+ concentrations["A"].append(0.75 + random.uniform(-0.05, 0.05))
111
+ concentrations["B"].append(0)
112
+ concentrations["C"].append(0.4 + random.uniform(-0.05, 0.05))
113
+ concentrations["D"].append(0.8 + random.uniform(-0.05, 0.05))
114
+ concentrations["E"].append(0)
115
+ concentrations["F"].append(0.1 + random.uniform(-0.05, 0.05))
116
+ rate_constants["USER.reaction 1"].append(
117
+ 0.001 + random.uniform(-0.0001, 0.0001))
118
+ rate_constants["USER.reaction 2"].append(
119
+ 0.002 + random.uniform(-0.0001, 0.0001))
120
+
121
+ state.set_conditions(temperatures, pressures) # Air density should be calculated in the state
122
+ state.set_concentrations(concentrations)
123
+ state.set_user_defined_rate_parameters(rate_constants)
124
+
125
+ initial_concentrations = state.get_concentrations()
126
+ initial_rate_parameters = state.get_user_defined_rate_parameters()
127
+ initial_temperatures = state.get_conditions()["temperature"]
128
+ initial_pressures = state.get_conditions()["pressure"]
129
+ initial_air_density = state.get_conditions()["air_density"]
130
+ for i in range(num_grid_cells):
131
+ assert np.isclose(initial_concentrations["A"][i], concentrations["A"][i], atol=1e-13)
132
+ assert np.isclose(initial_concentrations["B"][i], concentrations["B"][i], atol=1e-13)
133
+ assert np.isclose(initial_concentrations["C"][i], concentrations["C"][i], atol=1e-13)
134
+ assert np.isclose(initial_concentrations["D"][i], concentrations["D"][i], atol=1e-13)
135
+ assert np.isclose(initial_concentrations["E"][i], concentrations["E"][i], atol=1e-13)
136
+ assert np.isclose(initial_concentrations["F"][i], concentrations["F"][i], atol=1e-13)
137
+ assert np.isclose(
138
+ initial_rate_parameters["USER.reaction 1"][i],
139
+ rate_constants["USER.reaction 1"][i],
140
+ atol=1e-13)
141
+ assert np.isclose(
142
+ initial_rate_parameters["USER.reaction 2"][i],
143
+ rate_constants["USER.reaction 2"][i],
144
+ atol=1e-13)
145
+ assert np.isclose(initial_temperatures[i], temperatures[i], atol=1e-13)
146
+ assert np.isclose(initial_pressures[i], pressures[i], atol=1e-13)
147
+ assert np.isclose(initial_air_density[i], pressures[i] / (8.31446261815324 * temperatures[i]), atol=1e-13)
148
+
149
+ time_step = 1
150
+ sim_length = 100
151
+
152
+ curr_time = time_step
153
+ initial_A = num_grid_cells * [0.0]
154
+ initial_C = num_grid_cells * [0.0]
155
+ initial_D = num_grid_cells * [0.0]
156
+ initial_F = num_grid_cells * [0.0]
157
+ for i in range(num_grid_cells):
158
+ initial_A[i] = initial_concentrations["A"][i]
159
+ initial_C[i] = initial_concentrations["C"][i]
160
+ initial_D[i] = initial_concentrations["D"][i]
161
+ initial_F[i] = initial_concentrations["F"][i]
162
+
163
+ k1 = num_grid_cells * [0.0]
164
+ k2 = num_grid_cells * [0.0]
165
+ k3 = num_grid_cells * [0.0]
166
+ k4 = num_grid_cells * [0.0]
167
+ for i in range(num_grid_cells):
168
+ k1[i] = rate_constants["USER.reaction 1"][i]
169
+ k2[i] = rate_constants["USER.reaction 2"][i]
170
+ k3[i] = 0.004 * np.exp(50.0 / temperatures[i])
171
+ k4[i] = 0.012 * np.exp(75.0 / temperatures[i]) * \
172
+ (temperatures[i] / 50.0)**(-2) * (1.0 + 1.0e-6 * pressures[i])
173
+
174
+ while curr_time <= sim_length:
175
+ solver.solve(state, time_step)
176
+ concentrations = state.get_concentrations()
177
+
178
+ for i in range(num_grid_cells):
179
+ A_conc = initial_A[i] * np.exp(-(k3[i]) * curr_time)
180
+ B_conc = initial_A[i] * (k3[i] / (k4[i] - k3[i])) * \
181
+ (np.exp(-k3[i] * curr_time) - np.exp(-k4[i] * curr_time))
182
+ C_conc = initial_C[i] + initial_A[i] * (1.0 + (
183
+ k3[i] * np.exp(-k4[i] * curr_time) - k4[i] * np.exp(-k3[i] * curr_time)) / (k4[i] - k3[i]))
184
+ D_conc = initial_D[i] * np.exp(-(k1[i]) * curr_time)
185
+ E_conc = initial_D[i] * (k1[i] / (k2[i] - k1[i])) * \
186
+ (np.exp(-k1[i] * curr_time) - np.exp(-k2[i] * curr_time))
187
+ F_conc = initial_F[i] + initial_D[i] * (1.0 + (
188
+ k1[i] * np.exp(-k2[i] * curr_time) - k2[i] * np.exp(-k1[i] * curr_time)) / (k2[i] - k1[i]))
189
+
190
+ assert np.isclose(
191
+ concentrations["A"][i],
192
+ A_conc,
193
+ atol=10**-places), f"Grid cell {i} of {num_grid_cells}: A concentration mismatch. Initial A: {initial_concentrations['A'][i]}"
194
+ assert np.isclose(
195
+ concentrations["B"][i],
196
+ B_conc,
197
+ atol=10**-places), f"Grid cell {i} of {num_grid_cells}: B concentration mismatch. Initial B: {initial_concentrations['B'][i]}"
198
+ assert np.isclose(
199
+ concentrations["C"][i],
200
+ C_conc,
201
+ atol=10**-places), f"Grid cell {i} of {num_grid_cells}: C concentration mismatch. Initial C: {initial_concentrations['C'][i]}"
202
+ assert np.isclose(
203
+ concentrations["D"][i],
204
+ D_conc,
205
+ atol=10**-places), f"Grid cell {i} of {num_grid_cells}: D concentration mismatch. Initial D: {initial_concentrations['D'][i]}"
206
+ assert np.isclose(
207
+ concentrations["E"][i],
208
+ E_conc,
209
+ atol=10**-places), f"Grid cell {i} of {num_grid_cells}: E concentration mismatch. Initial E: {initial_concentrations['E'][i]}"
210
+ assert np.isclose(
211
+ concentrations["F"][i],
212
+ F_conc,
213
+ atol=10**-places), f"Grid cell {i} of {num_grid_cells}: F concentration mismatch. Initial F: {initial_concentrations['F'][i]}"
214
+
215
+ curr_time += time_step
216
+
217
+
218
+ def GetMechanism():
219
+ A = mc.Species(name="A")
220
+ B = mc.Species(name="B")
221
+ C = mc.Species(name="C")
222
+ D = mc.Species(name="D")
223
+ E = mc.Species(name="E")
224
+ F = mc.Species(name="F")
225
+ gas = mc.Phase(name="gas", species=[A, B, C, D, E, F])
226
+ arr1 = mc.Arrhenius(name="A->B", A=0.004, C=50,
227
+ gas_phase=gas, reactants=[A], products=[B])
228
+ arr2 = mc.Arrhenius(name="B->C", A=0.012, B=-2, C=75, D=50, E=1.0e-6,
229
+ gas_phase=gas, reactants=[B], products=[C])
230
+ user1 = mc.UserDefined(name="reaction 1", gas_phase=gas,
231
+ reactants=[D], products=[E])
232
+ user2 = mc.UserDefined(name="reaction 2", gas_phase=gas,
233
+ reactants=[E], products=[F])
234
+ mechanism = mc.Mechanism(
235
+ name="analytical test",
236
+ species=[A, B, C, D, E, F],
237
+ phases=[gas],
238
+ reactions=[arr1, arr2, user1, user2],
239
+ )
240
+ return mechanism
241
+
242
+
243
+ def test_single_grid_cell_standard_rosenbrock():
244
+ solver = musica.MICM(
245
+ config_path="configs/analytical",
246
+ solver_type=musica.SolverType.rosenbrock_standard_order)
247
+ state = solver.create_state()
248
+ TestSingleGridCell(solver, state, 200.0, 5)
249
+
250
+
251
+ def test_multiple_grid_cells_standard_rosenbrock():
252
+ for i in range(1, 11):
253
+ solver = musica.MICM(
254
+ config_path="configs/analytical",
255
+ solver_type=musica.SolverType.rosenbrock_standard_order)
256
+ state = solver.create_state(i)
257
+ TestMultipleGridCell(solver, state, i, 200.0, 5)
258
+
259
+
260
+ def test_cuda_rosenbrock():
261
+ if _is_cuda_available():
262
+ solver = musica.MICM(
263
+ config_path="configs/analytical",
264
+ solver_type=musica.SolverType.cuda_rosenbrock)
265
+ state = solver.create_state()
266
+ TestSingleGridCell(solver, state, 200.0, 5)
267
+ else:
268
+ pytest.skip("CUDA is not available.")
269
+
270
+
271
+ def test_single_grid_cell_backward_euler():
272
+ solver = musica.MICM(
273
+ config_path="configs/analytical",
274
+ solver_type=musica.SolverType.backward_euler_standard_order)
275
+ state = solver.create_state()
276
+ TestSingleGridCell(solver, state, 10.0, places=2)
277
+
278
+
279
+ def test_multiple_grid_cells_backward_euler():
280
+ for i in range(1, 11):
281
+ solver = musica.MICM(
282
+ config_path="configs/analytical",
283
+ solver_type=musica.SolverType.backward_euler_standard_order)
284
+ state = solver.create_state(i)
285
+ TestMultipleGridCell(solver, state, i, 10.0, places=2)
286
+
287
+
288
+ def test_single_grid_cell_rosenbrock():
289
+ solver = musica.MICM(
290
+ config_path="configs/analytical",
291
+ solver_type=musica.SolverType.rosenbrock)
292
+ state = solver.create_state()
293
+ TestSingleGridCell(solver, state, 200.0, 5)
294
+
295
+
296
+ def test_multiple_grid_cells_rosenbrock():
297
+ for i in range(1, 11):
298
+ solver = musica.MICM(
299
+ config_path="configs/analytical",
300
+ solver_type=musica.SolverType.rosenbrock)
301
+ state = solver.create_state(i)
302
+ TestMultipleGridCell(solver, state, i, 200.0, 5)
303
+
304
+
305
+ def test_single_grid_cell_backward_euler_standard_order():
306
+ solver = musica.MICM(
307
+ config_path="configs/analytical",
308
+ solver_type=musica.SolverType.backward_euler_standard_order)
309
+ state = solver.create_state()
310
+ TestSingleGridCell(solver, state, 10.0, places=2)
311
+
312
+
313
+ def test_multiple_grid_cells_backward_euler_standard_order():
314
+ for i in range(1, 11):
315
+ solver = musica.MICM(
316
+ config_path="configs/analytical",
317
+ solver_type=musica.SolverType.backward_euler_standard_order)
318
+ state = solver.create_state(i)
319
+ TestMultipleGridCell(solver, state, i, 10.0, places=2)
320
+
321
+
322
+ if __name__ == '__main__':
323
+ pytest.main()
@@ -0,0 +1,123 @@
1
+ import pytest
2
+ import musica
3
+ import musica.mechanism_configuration as mc
4
+
5
+
6
+ def test_solve_with_config_path():
7
+ solver = musica.MICM(
8
+ config_path="configs/chapman",
9
+ solver_type=musica.SolverType.rosenbrock_standard_order,
10
+ )
11
+ TestSolve(solver)
12
+
13
+
14
+ def test_solve_with_mechanism():
15
+ solver = musica.MICM(
16
+ mechanism=GetMechanism(),
17
+ solver_type=musica.SolverType.rosenbrock_standard_order,
18
+ )
19
+ TestSolve(solver)
20
+
21
+
22
+ def TestSolve(solver):
23
+ state = solver.create_state()
24
+
25
+ time_step = 200.0
26
+ temperature = 272.5
27
+ pressure = 101253.3
28
+
29
+ rate_constants = {
30
+ "PHOTO.jO2": 2.42e-17,
31
+ "PHOTO.jO3->O": 1.15e-5,
32
+ "PHOTO.jO3->O1D": 6.61e-9
33
+ }
34
+
35
+ initial_concentrations = {
36
+ "O2": 0.75,
37
+ "O": 0.0,
38
+ "O1D": 0.0,
39
+ "O3": 0.0000081
40
+ }
41
+
42
+ # Test setting int values
43
+ state.set_conditions(temperatures=272, pressures=101325)
44
+
45
+ # Set actual test conditions
46
+ state.set_conditions(temperatures=temperature, pressures=pressure)
47
+ state.set_concentrations(initial_concentrations)
48
+ state.set_user_defined_rate_parameters(rate_constants)
49
+
50
+ solver.solve(state, time_step)
51
+ concentrations = state.get_concentrations()
52
+
53
+ assert pytest.approx(concentrations["O2"][0], rel=1e-5) == 0.75
54
+ assert concentrations["O"][0] > 0.0
55
+ assert concentrations["O1D"][0] > 0.0
56
+ assert concentrations["O3"][0] != 0.0000081
57
+
58
+
59
+ def GetMechanism():
60
+ M = mc.Species(tracer_type="THIRD_BODY")
61
+ O2 = mc.Species(name="O2", tracer_type="CONSTANT")
62
+ O = mc.Species(name="O", other_properties={
63
+ "__absolute_tolerance": "1e-12"})
64
+ O1D = mc.Species(name="O1D", other_properties={
65
+ "__absolute_tolerance": "1e-12"})
66
+ O3 = mc.Species(
67
+ name="O3",
68
+ molecular_weight_kg_mol=0.048,
69
+ other_properties={
70
+ "__absolute_tolerance": "1e-12",
71
+ "__long_name": "ozone",
72
+ "__atmos": "3",
73
+ "__do_advect": "True",
74
+ })
75
+ gas = mc.Phase(
76
+ name="gas",
77
+ species=[O2, O, O1D, O3, M],
78
+ )
79
+ jO2 = mc.Photolysis(
80
+ name="jO2",
81
+ reactants=[O2],
82
+ products=[(2, O)],
83
+ )
84
+ R2 = mc.Arrhenius(
85
+ name="R2",
86
+ A=8.018e-17,
87
+ reactants=[O, O2],
88
+ products=[O3],
89
+ )
90
+ jO31 = mc.Photolysis(
91
+ name="jO3->O",
92
+ reactants=[O3],
93
+ products=[O, O2],
94
+ )
95
+ R4 = mc.Arrhenius(
96
+ name="R4",
97
+ A=1.576e-15,
98
+ reactants=[O, O3],
99
+ products=[(2, O2)],
100
+ )
101
+ jO32 = mc.Photolysis(
102
+ name="jO3->O1D",
103
+ reactants=[O3],
104
+ products=[O1D, O2],
105
+ )
106
+ R6 = mc.Arrhenius(
107
+ name="R6",
108
+ A=7.11e-11,
109
+ reactants=[O1D, M],
110
+ products=[O, M],
111
+ )
112
+ R7 = mc.Arrhenius(
113
+ name="R7",
114
+ A=1.2e-10,
115
+ reactants=[O1D, O3],
116
+ products=[(2, O2)],
117
+ )
118
+ return mc.Mechanism(
119
+ name="Chapman",
120
+ species=[O2, O, O1D, O3, M],
121
+ phases=[gas],
122
+ reactions=[jO2, R2, jO31, R4, jO32, R6, R7],
123
+ )