musica 0.11.1.4__cp312-cp312-win_amd64.whl → 0.12.1__cp312-cp312-win_amd64.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 +28 -2
- musica/__init__.py +9 -49
- musica/_musica.cp312-win_amd64.pyd +0 -0
- musica/_version.py +1 -1
- musica/backend.py +41 -0
- musica/binding_common.cpp +23 -6
- musica/carma.cpp +911 -0
- musica/carma.py +1729 -0
- musica/constants.py +3 -0
- musica/cpu_binding.cpp +2 -1
- musica/cuda.py +4 -1
- musica/examples/__init__.py +1 -0
- musica/examples/carma_aluminum.py +123 -0
- musica/examples/carma_sulfate.py +245 -0
- musica/examples/examples.py +165 -0
- musica/examples/sulfate_box_model.py +439 -0
- musica/examples/ts1_latin_hypercube.py +245 -0
- musica/gpu_binding.cpp +2 -1
- musica/main.py +89 -0
- musica/mechanism_configuration/__init__.py +1 -0
- musica/mechanism_configuration/aqueous_equilibrium.py +274 -0
- musica/mechanism_configuration/arrhenius.py +307 -0
- musica/mechanism_configuration/branched.py +299 -0
- musica/mechanism_configuration/condensed_phase_arrhenius.py +309 -0
- musica/mechanism_configuration/condensed_phase_photolysis.py +88 -0
- musica/mechanism_configuration/emission.py +71 -0
- musica/mechanism_configuration/first_order_loss.py +174 -0
- musica/mechanism_configuration/henrys_law.py +44 -0
- musica/mechanism_configuration/mechanism_configuration.py +234 -0
- musica/mechanism_configuration/phase.py +47 -0
- musica/mechanism_configuration/photolysis.py +88 -0
- musica/mechanism_configuration/reactions.py +73 -0
- musica/mechanism_configuration/simpol_phase_transfer.py +217 -0
- musica/mechanism_configuration/species.py +91 -0
- musica/mechanism_configuration/surface.py +94 -0
- musica/mechanism_configuration/ternary_chemical_activation.py +352 -0
- musica/mechanism_configuration/troe.py +352 -0
- musica/mechanism_configuration/tunneling.py +250 -0
- musica/mechanism_configuration/user_defined.py +88 -0
- musica/mechanism_configuration/utils.py +10 -0
- musica/mechanism_configuration/wet_deposition.py +52 -0
- musica/mechanism_configuration.cpp +184 -96
- musica/musica.cpp +48 -61
- musica/test/examples/v1/full_configuration/full_configuration.json +67 -35
- musica/test/examples/v1/full_configuration/full_configuration.yaml +44 -20
- musica/test/{test_analytical.py → integration/test_analytical.py} +1 -2
- musica/test/integration/test_carma.py +227 -0
- musica/test/integration/test_carma_aluminum.py +11 -0
- musica/test/integration/test_carma_sulfate.py +16 -0
- musica/test/integration/test_sulfate_box_model.py +34 -0
- musica/test/integration/test_tuvx.py +62 -0
- musica/test/unit/test_parser.py +64 -0
- musica/test/unit/test_serializer.py +69 -0
- musica/test/{test_parser.py → unit/test_util_full_mechanism.py} +409 -404
- musica/tools/prepare_build_environment_linux.sh +39 -32
- musica/tools/prepare_build_environment_macos.sh +1 -0
- musica/tuvx.cpp +93 -0
- musica/tuvx.py +199 -0
- musica/types.py +104 -63
- {musica-0.11.1.4.dist-info → musica-0.12.1.dist-info}/METADATA +100 -84
- musica-0.12.1.dist-info/RECORD +69 -0
- {musica-0.11.1.4.dist-info → musica-0.12.1.dist-info}/WHEEL +1 -1
- musica-0.12.1.dist-info/entry_points.txt +3 -0
- musica-0.12.1.dist-info/licenses/AUTHORS.md +59 -0
- musica/mechanism_configuration.py +0 -1291
- musica/test/examples/v0/config.json +0 -7
- musica/test/examples/v0/config.yaml +0 -3
- musica/test/examples/v0/reactions.json +0 -193
- musica/test/examples/v0/reactions.yaml +0 -142
- musica/test/examples/v0/species.json +0 -40
- musica/test/examples/v0/species.yaml +0 -19
- musica/test/tuvx.py +0 -10
- musica/tools/prepare_build_environment_windows.sh +0 -22
- musica-0.11.1.4.dist-info/RECORD +0 -33
- /musica/test/{test_chapman.py → integration/test_chapman.py} +0 -0
- {musica-0.11.1.4.dist-info → musica-0.12.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import musica
|
|
3
|
+
|
|
4
|
+
available = musica.backend.carma_available()
|
|
5
|
+
pytestmark = pytest.mark.skipif(
|
|
6
|
+
not available, reason="CARMA backend is not available")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_carma_version():
|
|
10
|
+
version = musica.carma.version
|
|
11
|
+
assert version is not None
|
|
12
|
+
assert isinstance(version, str)
|
|
13
|
+
print(f"CARMA version: {version}")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_carma_with_all_components():
|
|
17
|
+
"""Test CARMA with multiple groups, elements, solutes, and gases"""
|
|
18
|
+
params = musica.CARMAParameters()
|
|
19
|
+
params.nz = 1
|
|
20
|
+
params.ny = 1
|
|
21
|
+
params.nx = 1
|
|
22
|
+
params.nbin = 3
|
|
23
|
+
params.dtime = 900.0
|
|
24
|
+
params.deltaz = 500.0
|
|
25
|
+
params.zmin = 1000.0
|
|
26
|
+
|
|
27
|
+
# Set up wavelength bins
|
|
28
|
+
params.wavelength_bins = [
|
|
29
|
+
musica.carma.CARMAWavelengthBin(
|
|
30
|
+
center=550e-9, width=50e-9, do_emission=True), # 550 nm ± 25 nm
|
|
31
|
+
musica.carma.CARMAWavelengthBin(
|
|
32
|
+
center=850e-9, width=100e-9, do_emission=True) # 850 nm ± 50 nm
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Group 1: Aluminum particles (sphere)
|
|
36
|
+
aluminum_group = musica.carma.CARMAGroupConfig(
|
|
37
|
+
name="aluminum",
|
|
38
|
+
shortname="ALUM",
|
|
39
|
+
rmin=1e-8,
|
|
40
|
+
rmrat=2.0,
|
|
41
|
+
ishape=musica.carma.ParticleShape.SPHERE,
|
|
42
|
+
eshape=1.0,
|
|
43
|
+
is_fractal=False,
|
|
44
|
+
do_vtran=True,
|
|
45
|
+
do_drydep=True,
|
|
46
|
+
df=[1.8, 1.8, 1.8] # fractal dimension per bin
|
|
47
|
+
)
|
|
48
|
+
params.groups.append(aluminum_group)
|
|
49
|
+
|
|
50
|
+
# Group 2: Sulfate particles (sphere, with swelling)
|
|
51
|
+
sulfate_group = musica.carma.CARMAGroupConfig(
|
|
52
|
+
name="sulfate",
|
|
53
|
+
shortname="SULF",
|
|
54
|
+
rmin=5e-9,
|
|
55
|
+
rmrat=2.5,
|
|
56
|
+
ishape=musica.carma.ParticleShape.SPHERE,
|
|
57
|
+
eshape=1.0,
|
|
58
|
+
swelling_approach={
|
|
59
|
+
"algorithm": musica.carma.ParticleSwellingAlgorithm.FITZGERALD,
|
|
60
|
+
"composition": musica.carma.ParticleSwellingComposition.AMMONIUM_SULFATE
|
|
61
|
+
},
|
|
62
|
+
is_sulfate=True,
|
|
63
|
+
do_wetdep=True,
|
|
64
|
+
do_vtran=True,
|
|
65
|
+
solfac=0.8,
|
|
66
|
+
df=[2.0, 2.0, 2.0]
|
|
67
|
+
)
|
|
68
|
+
params.groups.append(sulfate_group)
|
|
69
|
+
|
|
70
|
+
# Group 3: Ice particles (hexagon)
|
|
71
|
+
ice_group = musica.carma.CARMAGroupConfig(
|
|
72
|
+
name="ice",
|
|
73
|
+
shortname="ICE",
|
|
74
|
+
rmin=2e-8,
|
|
75
|
+
rmrat=3.0,
|
|
76
|
+
ishape=musica.carma.ParticleShape.HEXAGON,
|
|
77
|
+
eshape=2.0, # aspect ratio
|
|
78
|
+
is_ice=True,
|
|
79
|
+
is_cloud=True,
|
|
80
|
+
do_vtran=True,
|
|
81
|
+
df=[1.5, 1.5, 1.5]
|
|
82
|
+
)
|
|
83
|
+
params.groups.append(ice_group)
|
|
84
|
+
|
|
85
|
+
# Element 1: Aluminum core (Group 1)
|
|
86
|
+
aluminum_element = musica.carma.CARMAElementConfig(
|
|
87
|
+
igroup=1,
|
|
88
|
+
name="Aluminum",
|
|
89
|
+
shortname="AL",
|
|
90
|
+
rho=2.70, # g/cm³
|
|
91
|
+
itype=musica.carma.ParticleType.INVOLATILE,
|
|
92
|
+
icomposition=musica.carma.ParticleComposition.ALUMINUM,
|
|
93
|
+
kappa=0.0,
|
|
94
|
+
is_shell=False # core
|
|
95
|
+
)
|
|
96
|
+
params.elements.append(aluminum_element)
|
|
97
|
+
|
|
98
|
+
# Element 2: Sulfate (Group 2)
|
|
99
|
+
sulfate_element = musica.carma.CARMAElementConfig(
|
|
100
|
+
igroup=2,
|
|
101
|
+
isolute=1, # linked to first solute
|
|
102
|
+
name="Sulfate",
|
|
103
|
+
shortname="SO4",
|
|
104
|
+
rho=1.84, # g/cm³
|
|
105
|
+
itype=musica.carma.ParticleType.VOLATILE,
|
|
106
|
+
icomposition=musica.carma.ParticleComposition.SULFURIC_ACID,
|
|
107
|
+
kappa=0.61, # hygroscopicity
|
|
108
|
+
is_shell=True
|
|
109
|
+
)
|
|
110
|
+
params.elements.append(sulfate_element)
|
|
111
|
+
|
|
112
|
+
# Element 3: Water on sulfate (Group 2)
|
|
113
|
+
water_element = musica.carma.CARMAElementConfig(
|
|
114
|
+
igroup=2,
|
|
115
|
+
name="Water",
|
|
116
|
+
shortname="H2O",
|
|
117
|
+
rho=1.0, # g/cm³
|
|
118
|
+
itype=musica.carma.ParticleType.CORE_MASS,
|
|
119
|
+
icomposition=musica.carma.ParticleComposition.WATER,
|
|
120
|
+
kappa=0.0,
|
|
121
|
+
is_shell=True
|
|
122
|
+
)
|
|
123
|
+
params.elements.append(water_element)
|
|
124
|
+
|
|
125
|
+
# Element 4: Ice (Group 3)
|
|
126
|
+
ice_element = musica.carma.CARMAElementConfig(
|
|
127
|
+
igroup=3,
|
|
128
|
+
name="Ice",
|
|
129
|
+
shortname="ICE",
|
|
130
|
+
rho=0.92, # g/cm³
|
|
131
|
+
itype=musica.carma.ParticleType.INVOLATILE,
|
|
132
|
+
icomposition=musica.carma.ParticleComposition.ICE,
|
|
133
|
+
kappa=0.0,
|
|
134
|
+
is_shell=False
|
|
135
|
+
)
|
|
136
|
+
params.elements.append(ice_element)
|
|
137
|
+
|
|
138
|
+
# Solute: Sulfate
|
|
139
|
+
sulfate_solute = musica.carma.CARMASoluteConfig(
|
|
140
|
+
name="Sulfate",
|
|
141
|
+
shortname="SO4",
|
|
142
|
+
ions=2,
|
|
143
|
+
wtmol=0.1324, # kg mol-1
|
|
144
|
+
rho=1840.0 # kg m-3
|
|
145
|
+
)
|
|
146
|
+
params.solutes.append(sulfate_solute)
|
|
147
|
+
|
|
148
|
+
# Gas: Water vapor
|
|
149
|
+
water_gas = musica.carma.CARMAGasConfig(
|
|
150
|
+
name="Water Vapor",
|
|
151
|
+
shortname="H2O",
|
|
152
|
+
wtmol=0.01801528, # kg mol-1
|
|
153
|
+
ivaprtn=musica.carma.VaporizationAlgorithm.H2O_MURPHY_2005,
|
|
154
|
+
icomposition=musica.carma.GasComposition.H2O,
|
|
155
|
+
dgc_threshold=1.0e-6,
|
|
156
|
+
ds_threshold=1.0e-4
|
|
157
|
+
)
|
|
158
|
+
params.gases.append(water_gas)
|
|
159
|
+
|
|
160
|
+
# Gas: Sulfuric acid
|
|
161
|
+
h2so4_gas = musica.carma.CARMAGasConfig(
|
|
162
|
+
name="Sulfuric Acid",
|
|
163
|
+
shortname="H2SO4",
|
|
164
|
+
wtmol=0.098079, # kg mol-1
|
|
165
|
+
ivaprtn=musica.carma.VaporizationAlgorithm.H2O_BUCK_1981,
|
|
166
|
+
icomposition=musica.carma.GasComposition.H2SO4,
|
|
167
|
+
dgc_threshold=0.05,
|
|
168
|
+
ds_threshold=0.1
|
|
169
|
+
)
|
|
170
|
+
params.gases.append(h2so4_gas)
|
|
171
|
+
|
|
172
|
+
# Gas: Sulfur dioxide
|
|
173
|
+
so2_gas = musica.carma.CARMAGasConfig(
|
|
174
|
+
name="Sulfur Dioxide",
|
|
175
|
+
shortname="SO2",
|
|
176
|
+
wtmol=0.064066, # kg mol-1
|
|
177
|
+
ivaprtn=musica.carma.VaporizationAlgorithm.H2O_BUCK_1981,
|
|
178
|
+
icomposition=musica.carma.GasComposition.SO2,
|
|
179
|
+
dgc_threshold=0.05,
|
|
180
|
+
ds_threshold=0.1
|
|
181
|
+
)
|
|
182
|
+
params.gases.append(so2_gas)
|
|
183
|
+
|
|
184
|
+
# Create CARMA instance and run
|
|
185
|
+
carma = musica.CARMA(params)
|
|
186
|
+
|
|
187
|
+
assert carma is not None
|
|
188
|
+
assert isinstance(carma, musica.CARMA)
|
|
189
|
+
|
|
190
|
+
state = carma.create_state(
|
|
191
|
+
vertical_center=[16500.0],
|
|
192
|
+
vertical_levels=[16500.0, 17000.0],
|
|
193
|
+
pressure=[90000.0],
|
|
194
|
+
pressure_levels=[101325.0, 90050.0],
|
|
195
|
+
temperature=[280.0],
|
|
196
|
+
time=0.0,
|
|
197
|
+
time_step=900.0, # 15 minutes
|
|
198
|
+
longitude=0.0,
|
|
199
|
+
latitude=0.0,
|
|
200
|
+
coordinates=musica.carma.CarmaCoordinates.CARTESIAN
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
assert state is not None
|
|
204
|
+
assert isinstance(state, musica.CARMAState)
|
|
205
|
+
|
|
206
|
+
state.set_bin(1, 1, 1.0)
|
|
207
|
+
state.set_detrain(1, 1, 1.0)
|
|
208
|
+
state.set_gas(1, 1.4e-3)
|
|
209
|
+
state.set_temperature(300.0)
|
|
210
|
+
state.set_air_density(1.2)
|
|
211
|
+
state.step(land=musica.carma.CARMASurfaceProperties(surface_friction_velocity=0.42, area_fraction=0.3),
|
|
212
|
+
ocean=musica.carma.CARMASurfaceProperties(
|
|
213
|
+
aerodynamic_resistance=0.1),
|
|
214
|
+
ice=musica.carma.CARMASurfaceProperties(area_fraction=0.2))
|
|
215
|
+
print(state.get_step_statistics())
|
|
216
|
+
print(state.get_bins())
|
|
217
|
+
print(state.get_detrained_masses())
|
|
218
|
+
print(state.get_environmental_values())
|
|
219
|
+
print(state.get_gases())
|
|
220
|
+
print(carma.get_group_properties())
|
|
221
|
+
print(carma.get_element_properties())
|
|
222
|
+
print(carma.get_gas_properties())
|
|
223
|
+
print(carma.get_solute_properties())
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == '__main__':
|
|
227
|
+
pytest.main([__file__])
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import musica
|
|
3
|
+
|
|
4
|
+
available = musica.backend.carma_available()
|
|
5
|
+
pytestmark = pytest.mark.skipif(
|
|
6
|
+
not available, reason="CARMA backend is not available")
|
|
7
|
+
|
|
8
|
+
def test_carma_aluminum():
|
|
9
|
+
from musica.examples import carma_aluminum
|
|
10
|
+
state = carma_aluminum.run_carma_aluminum_example()
|
|
11
|
+
assert state is not None, "State should not be None"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import musica
|
|
3
|
+
|
|
4
|
+
available = musica.backend.carma_available()
|
|
5
|
+
pytestmark = pytest.mark.skipif(
|
|
6
|
+
not available, reason="CARMA backend is not available")
|
|
7
|
+
|
|
8
|
+
def test_carma_sulfate():
|
|
9
|
+
from musica.examples import carma_sulfate
|
|
10
|
+
env_state, gas_state, bin_state = carma_sulfate.run_carma_sulfate_example()
|
|
11
|
+
# Basic assertions to verify the simulation ran successfully
|
|
12
|
+
assert env_state is not None, "Environmental state should not be None"
|
|
13
|
+
assert gas_state is not None, "Gas state should not be None"
|
|
14
|
+
assert bin_state is not None, "Bin state should not be None"
|
|
15
|
+
# Optionally, check expected dimensions or variables
|
|
16
|
+
assert hasattr(bin_state, "mass_mixing_ratio"), "Bin state should have mass_mixing_ratio"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import musica
|
|
3
|
+
|
|
4
|
+
available = musica.backend.carma_available()
|
|
5
|
+
pytestmark = pytest.mark.skipif(
|
|
6
|
+
not available, reason="CARMA backend is not available")
|
|
7
|
+
|
|
8
|
+
def test_sulfate_box_model():
|
|
9
|
+
"""Test the sulfate box model implementation."""
|
|
10
|
+
from musica.examples import sulfate_box_model
|
|
11
|
+
concentrations, times, sulfate_data = sulfate_box_model.run_box_model()
|
|
12
|
+
|
|
13
|
+
# Basic assertions to verify the simulation ran successfully
|
|
14
|
+
assert concentrations is not None, "Concentrations should not be None"
|
|
15
|
+
assert len(concentrations) > 0, "Should have concentration data"
|
|
16
|
+
assert times is not None, "Times should not be None"
|
|
17
|
+
assert len(times) > 0, "Should have time data"
|
|
18
|
+
assert sulfate_data is not None, "CARMA sulfate data should not be None"
|
|
19
|
+
|
|
20
|
+
# Check that we have the expected species
|
|
21
|
+
expected_species = ["HO2", "H2O2", "SO2", "SO3", "H2SO4", "H2O"]
|
|
22
|
+
for species in expected_species:
|
|
23
|
+
assert species in concentrations.columns, f"Missing species: {species}"
|
|
24
|
+
|
|
25
|
+
# Check that CARMA data has expected structure
|
|
26
|
+
assert hasattr(sulfate_data, "mass_mixing_ratio"), "CARMA data should have mass_mixing_ratio"
|
|
27
|
+
assert "time" in sulfate_data.dims, "CARMA data should have time dimension"
|
|
28
|
+
assert "bin" in sulfate_data.dims, "CARMA data should have bin dimension"
|
|
29
|
+
|
|
30
|
+
print(f"✅ Test passed! Simulated {len(times)} time steps over {times[-1]:.2f} hours")
|
|
31
|
+
print(f" Chemical species tracked: {list(concentrations.columns)}")
|
|
32
|
+
print(f" CARMA bins: {len(sulfate_data.bin)}")
|
|
33
|
+
print(f" Vertical levels: {len(sulfate_data.vertical_center)}")
|
|
34
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import musica
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
available = musica.backend.tuvx_available()
|
|
6
|
+
pytestmark = pytest.mark.skipif(not available, reason="TUV-x backend is not available")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_tuvx_version():
|
|
10
|
+
version = musica.tuvx.version
|
|
11
|
+
assert version is not None
|
|
12
|
+
assert isinstance(version, str)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_full_tuvx(monkeypatch):
|
|
16
|
+
monkeypatch.chdir("configs/tuvx")
|
|
17
|
+
file = "tuv_5_4.json"
|
|
18
|
+
tuvx = musica.TUVX(file)
|
|
19
|
+
assert tuvx is not None
|
|
20
|
+
|
|
21
|
+
heating_rates = tuvx.heating_rate_names
|
|
22
|
+
photolysis_rates = tuvx.photolysis_rate_names
|
|
23
|
+
rates = tuvx.run()
|
|
24
|
+
|
|
25
|
+
assert len(rates) > 0, "No photolysis rates found"
|
|
26
|
+
assert len(heating_rates) == 0, "Heating rates should be empty for this config"
|
|
27
|
+
assert len(photolysis_rates) > 0, "No photolysis rates found"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_fixed_tuvx(monkeypatch):
|
|
31
|
+
monkeypatch.chdir("src")
|
|
32
|
+
file = "test/data/tuvx/fixed/config.json"
|
|
33
|
+
tuvx = musica.TUVX(file)
|
|
34
|
+
assert tuvx is not None
|
|
35
|
+
|
|
36
|
+
# Access properties multiple times
|
|
37
|
+
photolysis_names_1 = tuvx.photolysis_rate_names
|
|
38
|
+
photolysis_names_2 = tuvx.photolysis_rate_names
|
|
39
|
+
heating_names_1 = tuvx.heating_rate_names
|
|
40
|
+
heating_names_2 = tuvx.heating_rate_names
|
|
41
|
+
|
|
42
|
+
# Verify they return the same object (cached)
|
|
43
|
+
assert photolysis_names_1 is photolysis_names_2
|
|
44
|
+
assert heating_names_1 is heating_names_2
|
|
45
|
+
|
|
46
|
+
assert len(heating_names_1) > 0, "No heating rates found"
|
|
47
|
+
assert len(photolysis_names_1) > 0, "No photolysis rates found"
|
|
48
|
+
|
|
49
|
+
# these fail to run due to missing solar zenith angle, but that's not required and these should be able to run
|
|
50
|
+
# rates = tuvx.run()
|
|
51
|
+
# print(f"Rates: {rates}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_tuvx_initialization_errors():
|
|
55
|
+
"""Test error handling during TUVX initialization."""
|
|
56
|
+
# Test with non-existent file
|
|
57
|
+
with pytest.raises(FileNotFoundError):
|
|
58
|
+
musica.TUVX("non_existent_config.json")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == '__main__':
|
|
62
|
+
pytest.main([__file__])
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import musica.mechanism_configuration as mc
|
|
3
|
+
from musica.mechanism_configuration import Parser
|
|
4
|
+
from test_util_full_mechanism import get_fully_defined_mechanism, validate_full_v1_mechanism
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_parsed_full_v1_configuration():
|
|
8
|
+
parser = Parser()
|
|
9
|
+
extensions = [".yaml", ".json"]
|
|
10
|
+
for extension in extensions:
|
|
11
|
+
path = f"musica/test/examples/v1/full_configuration/full_configuration{extension}"
|
|
12
|
+
mechanism = parser.parse(path)
|
|
13
|
+
validate_full_v1_mechanism(mechanism)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_parser_reports_bad_files():
|
|
17
|
+
parser = Parser()
|
|
18
|
+
extensions = [".yaml", ".json"]
|
|
19
|
+
for extension in extensions:
|
|
20
|
+
path = f"musica/test/examples/_missing_configuration{extension}"
|
|
21
|
+
with pytest.raises(Exception):
|
|
22
|
+
parser.parse(path)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_hard_coded_full_v1_configuration():
|
|
26
|
+
MECHANISM_FULLY_DEFINED = get_fully_defined_mechanism()
|
|
27
|
+
validate_full_v1_mechanism(MECHANISM_FULLY_DEFINED)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_hard_coded_default_constructed_types():
|
|
31
|
+
arrhenius = mc.Arrhenius()
|
|
32
|
+
assert arrhenius.type == mc.ReactionType.Arrhenius
|
|
33
|
+
condensed_phase_arrhenius = mc.CondensedPhaseArrhenius()
|
|
34
|
+
assert condensed_phase_arrhenius.type == mc.ReactionType.CondensedPhaseArrhenius
|
|
35
|
+
condensed_phase_photolysis = mc.CondensedPhasePhotolysis()
|
|
36
|
+
assert condensed_phase_photolysis.type == mc.ReactionType.CondensedPhasePhotolysis
|
|
37
|
+
emission = mc.Emission()
|
|
38
|
+
assert emission.type == mc.ReactionType.Emission
|
|
39
|
+
first_order_loss = mc.FirstOrderLoss()
|
|
40
|
+
assert first_order_loss.type == mc.ReactionType.FirstOrderLoss
|
|
41
|
+
henrys_law = mc.HenrysLaw()
|
|
42
|
+
assert henrys_law.type == mc.ReactionType.HenrysLaw
|
|
43
|
+
photolysis = mc.Photolysis()
|
|
44
|
+
assert photolysis.type == mc.ReactionType.Photolysis
|
|
45
|
+
simpol_phase_transfer = mc.SimpolPhaseTransfer()
|
|
46
|
+
assert simpol_phase_transfer.type == mc.ReactionType.SimpolPhaseTransfer
|
|
47
|
+
surface = mc.Surface()
|
|
48
|
+
assert surface.type == mc.ReactionType.Surface
|
|
49
|
+
troe = mc.Troe()
|
|
50
|
+
assert troe.type == mc.ReactionType.Troe
|
|
51
|
+
ternary_chemical_activation = mc.TernaryChemicalActivation()
|
|
52
|
+
assert ternary_chemical_activation.type == mc.ReactionType.TernaryChemicalActivation
|
|
53
|
+
tunneling = mc.Tunneling()
|
|
54
|
+
assert tunneling.type == mc.ReactionType.Tunneling
|
|
55
|
+
wet_deposition = mc.WetDeposition()
|
|
56
|
+
assert wet_deposition.type == mc.ReactionType.WetDeposition
|
|
57
|
+
branched = mc.Branched()
|
|
58
|
+
assert branched.type == mc.ReactionType.Branched
|
|
59
|
+
user_defined = mc.UserDefined()
|
|
60
|
+
assert user_defined.type == mc.ReactionType.UserDefined
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
pytest.main([__file__])
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
from musica.mechanism_configuration import MechanismSerializer, Mechanism, Parser
|
|
4
|
+
from test_util_full_mechanism import get_fully_defined_mechanism, validate_full_v1_mechanism
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_mechanism_export_loop(tmp_path):
|
|
8
|
+
parser = Parser()
|
|
9
|
+
MECHANISM_FULLY_DEFINED = get_fully_defined_mechanism()
|
|
10
|
+
extensions = [".yml", ".yaml", ".json"]
|
|
11
|
+
for extension in extensions:
|
|
12
|
+
path = f"{tmp_path}/test_mechanism{extension}"
|
|
13
|
+
MECHANISM_FULLY_DEFINED.export(path)
|
|
14
|
+
mechanism = parser.parse(path)
|
|
15
|
+
validate_full_v1_mechanism(mechanism)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_serialize_parser_loop(tmp_path):
|
|
19
|
+
parser = Parser()
|
|
20
|
+
MECHANISM_FULLY_DEFINED = get_fully_defined_mechanism()
|
|
21
|
+
extensions = [".yml", ".yaml", ".json"]
|
|
22
|
+
for extension in extensions:
|
|
23
|
+
path = f"{tmp_path}/test_mechanism{extension}"
|
|
24
|
+
MechanismSerializer.serialize(MECHANISM_FULLY_DEFINED, path)
|
|
25
|
+
mechanism = parser.parse(path)
|
|
26
|
+
validate_full_v1_mechanism(mechanism)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_serialize_to_file(tmp_path):
|
|
30
|
+
MECHANISM_FULLY_DEFINED = get_fully_defined_mechanism()
|
|
31
|
+
extensions = [".yml", ".yaml", ".json"]
|
|
32
|
+
for extension in extensions:
|
|
33
|
+
file_path = f'{tmp_path}/test_mechanism{extension}'
|
|
34
|
+
assert not os.path.exists(file_path)
|
|
35
|
+
MechanismSerializer.serialize(MECHANISM_FULLY_DEFINED, file_path)
|
|
36
|
+
assert os.path.exists(file_path)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_bad_inputs():
|
|
40
|
+
with pytest.raises(TypeError):
|
|
41
|
+
MechanismSerializer.serialize(None)
|
|
42
|
+
with pytest.raises(TypeError):
|
|
43
|
+
MechanismSerializer.serialize('not a mechanism')
|
|
44
|
+
with pytest.raises(Exception):
|
|
45
|
+
MechanismSerializer.serialize(get_fully_defined_mechanism(), 'unsupported.txt')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_path_creation(tmp_path):
|
|
49
|
+
mechanism = Mechanism(name="Full Configuration")
|
|
50
|
+
path = f"{tmp_path}/non_existant_path/"
|
|
51
|
+
assert not os.path.exists(path)
|
|
52
|
+
MechanismSerializer.serialize(mechanism, f"{path}test_mechanism.json")
|
|
53
|
+
assert os.path.exists(path)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_overwrite_file(tmp_path):
|
|
57
|
+
mechanism = Mechanism(name="Full Configuration")
|
|
58
|
+
file_path = f'{tmp_path}/test_mechanism.json'
|
|
59
|
+
assert not os.path.exists(file_path)
|
|
60
|
+
|
|
61
|
+
# write first file
|
|
62
|
+
MechanismSerializer.serialize(mechanism, file_path)
|
|
63
|
+
files = list(tmp_path.iterdir())
|
|
64
|
+
assert len(files) == 1
|
|
65
|
+
|
|
66
|
+
# overwrite file
|
|
67
|
+
MechanismSerializer.serialize(mechanism, file_path)
|
|
68
|
+
files = list(tmp_path.iterdir())
|
|
69
|
+
assert len(files) == 1
|