Skip to content

Commit c6e2c19

Browse files
authored
Merge pull request #2525 from rwest/pytest
2 parents 32715ad + e946ec2 commit c6e2c19

19 files changed

Lines changed: 574 additions & 442 deletions

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ addopts =
2424
--cov-report html
2525
testpaths = test
2626
python_files = *Test.py
27+
python_classes = *Test Test*
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
#!/usr/bin/env python3
2+
3+
###############################################################################
4+
# #
5+
# RMG - Reaction Mechanism Generator #
6+
# #
7+
# Copyright (c) 2002-2023 Prof. William H. Green (whgreen@mit.edu), #
8+
# Prof. Richard H. West (r.west@neu.edu) and the RMG Team (rmg_dev@mit.edu) #
9+
# #
10+
# Permission is hereby granted, free of charge, to any person obtaining a #
11+
# copy of this software and associated documentation files (the 'Software'), #
12+
# to deal in the Software without restriction, including without limitation #
13+
# the rights to use, copy, modify, merge, publish, distribute, sublicense, #
14+
# and/or sell copies of the Software, and to permit persons to whom the #
15+
# Software is furnished to do so, subject to the following conditions: #
16+
# #
17+
# The above copyright notice and this permission notice shall be included in #
18+
# all copies or substantial portions of the Software. #
19+
# #
20+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
21+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
22+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
23+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
24+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #
25+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER #
26+
# DEALINGS IN THE SOFTWARE. #
27+
# #
28+
###############################################################################
29+
30+
"""
31+
This module contains unit tests of the :mod:`arkane.input` module.
32+
"""
33+
34+
import os
35+
36+
from rmgpy.pdep.collision import SingleExponentialDown
37+
import pytest
38+
39+
import rmgpy
40+
from rmgpy.exceptions import InputError
41+
from rmgpy.kinetics.tunneling import Eckart
42+
from rmgpy.statmech.rotation import NonlinearRotor
43+
from rmgpy.statmech.translation import IdealGasTranslation
44+
from rmgpy.statmech.vibration import HarmonicOscillator
45+
from rmgpy.thermo.nasa import NASAPolynomial, NASA
46+
from rmgpy.transport import TransportData
47+
48+
from arkane.input import (
49+
species,
50+
transitionState,
51+
reaction,
52+
SMILES,
53+
load_input_file,
54+
process_model_chemistry,
55+
)
56+
from arkane.modelchem import LevelOfTheory, CompositeLevelOfTheory
57+
58+
59+
ADMONITION = (
60+
"This unit test fails in the new pytest framework despite other similar tests passing. "
61+
"This is likely due to a global state issue that was implicitly handled differently by the old nose testing "
62+
"framework. \nThe best solution for this problem is to remove the abuse of the global "
63+
"state in Arkane and RMG-Py rather than try and fix this single test."
64+
)
65+
66+
67+
class FunctionalInputTest:
68+
"""
69+
Contains unit tests for the Arkane input module
70+
"""
71+
72+
def test_species(self):
73+
"""
74+
Test loading a species from input file-like kew word arguments
75+
"""
76+
label0 = "CH2O"
77+
kwargs = {
78+
"E0": (28.69, "kcal/mol"),
79+
"structure": SMILES("C=O"),
80+
"collisionModel": TransportData(sigma=(3.69e-10, "m"), epsilon=(4.0, "kJ/mol")),
81+
"energyTransferModel": SingleExponentialDown(alpha0=(0.956, "kJ/mol"), T0=(300, "K"), n=0.95),
82+
"spinMultiplicity": 1,
83+
"opticalIsomers": 1,
84+
"modes": [
85+
HarmonicOscillator(frequencies=([1180, 1261, 1529, 1764, 2931, 2999], "cm^-1")),
86+
NonlinearRotor(
87+
rotationalConstant=(
88+
[1.15498821005263, 1.3156969584727, 9.45570474524524],
89+
"cm^-1",
90+
),
91+
symmetry=2,
92+
quantum=False,
93+
),
94+
IdealGasTranslation(mass=(30.0106, "g/mol")),
95+
],
96+
}
97+
98+
spc0 = species(label0, **kwargs)
99+
assert spc0.label == "CH2O"
100+
assert spc0.smiles == "C=O"
101+
assert round(abs(spc0.conformer.E0.value_si - 120038.96), 7) == 0
102+
assert spc0.conformer.spin_multiplicity == 1
103+
assert spc0.conformer.optical_isomers == 1
104+
assert len(spc0.conformer.modes) == 3
105+
assert isinstance(spc0.transport_data, TransportData)
106+
assert isinstance(spc0.energy_transfer_model, SingleExponentialDown)
107+
108+
@pytest.mark.skip(reason=ADMONITION)
109+
def test_species_atomic_nasa_polynomial(self):
110+
"""
111+
Test loading a atom with NASA polynomials
112+
"""
113+
label0 = "H(1)"
114+
kwargs = {
115+
"structure": SMILES("[H]"),
116+
"thermo": NASA(
117+
polynomials=[
118+
NASAPolynomial(
119+
coeffs=[2.5, 0, 0, 0, 0, 25473.7, -0.446683],
120+
Tmin=(200, "K"),
121+
Tmax=(1000, "K"),
122+
),
123+
NASAPolynomial(
124+
coeffs=[2.5, 0, 0, 0, 0, 25473.7, -0.446683],
125+
Tmin=(1000, "K"),
126+
Tmax=(6000, "K"),
127+
),
128+
],
129+
Tmin=(200, "K"),
130+
Tmax=(6000, "K"),
131+
comment="""Thermo library: FFCM1(-)""",
132+
),
133+
"energyTransferModel": SingleExponentialDown(alpha0=(3.5886, "kJ/mol"), T0=(300, "K"), n=0.85),
134+
}
135+
spc0 = species(label0, **kwargs)
136+
assert spc0.label == label0
137+
assert spc0.smiles == "[H]"
138+
assert spc0.has_statmech()
139+
assert spc0.thermo == kwargs["thermo"]
140+
141+
@pytest.mark.skip(reason=ADMONITION)
142+
def test_species_polyatomic_nasa_polynomial(self):
143+
"""
144+
Test loading a species with NASA polynomials
145+
"""
146+
label0 = "benzyl"
147+
kwargs = {
148+
"structure": SMILES("[c]1ccccc1"),
149+
"thermo": NASA(
150+
polynomials=[
151+
NASAPolynomial(
152+
coeffs=[
153+
2.78632,
154+
0.00784632,
155+
7.97887e-05,
156+
-1.11617e-07,
157+
4.39429e-11,
158+
39695,
159+
11.5114,
160+
],
161+
Tmin=(100, "K"),
162+
Tmax=(943.73, "K"),
163+
),
164+
NASAPolynomial(
165+
coeffs=[
166+
13.2455,
167+
0.0115667,
168+
-2.49996e-06,
169+
4.66496e-10,
170+
-4.12376e-14,
171+
35581.1,
172+
-49.6793,
173+
],
174+
Tmin=(943.73, "K"),
175+
Tmax=(5000, "K"),
176+
),
177+
],
178+
Tmin=(100, "K"),
179+
Tmax=(5000, "K"),
180+
comment="""Thermo library: Fulvene_H + radical(CbJ)""",
181+
),
182+
"energyTransferModel": SingleExponentialDown(alpha0=(3.5886, "kJ/mol"), T0=(300, "K"), n=0.85),
183+
}
184+
spc0 = species(label0, **kwargs)
185+
assert spc0.label == label0
186+
assert spc0.has_statmech()
187+
assert spc0.thermo == kwargs["thermo"]
188+
189+
def test_transition_state(self):
190+
"""
191+
Test loading a transition state from input file-like kew word arguments
192+
"""
193+
label0 = "TS1"
194+
kwargs = {
195+
"E0": (39.95, "kcal/mol"),
196+
"spinMultiplicity": 2,
197+
"opticalIsomers": 1,
198+
"frequency": (-1934, "cm^-1"),
199+
"modes": [
200+
HarmonicOscillator(
201+
frequencies=(
202+
[792, 987, 1136, 1142, 1482, 2441, 3096, 3183],
203+
"cm^-1",
204+
)
205+
),
206+
NonlinearRotor(
207+
rotationalConstant=([0.928, 0.962, 5.807], "cm^-1"),
208+
symmetry=1,
209+
quantum=False,
210+
),
211+
IdealGasTranslation(mass=(31.01843, "g/mol")),
212+
],
213+
}
214+
215+
ts0 = transitionState(label0, **kwargs)
216+
assert ts0.label == "TS1"
217+
assert round(abs(ts0.conformer.E0.value_si - 167150.8), 7) == 0
218+
assert ts0.conformer.spin_multiplicity == 2
219+
assert ts0.conformer.optical_isomers == 1
220+
assert ts0.frequency.value_si == -1934.0
221+
assert len(ts0.conformer.modes) == 3
222+
223+
def test_reaction(self):
224+
"""
225+
Test loading a reaction from input file-like kew word arguments
226+
"""
227+
228+
species(
229+
label="methoxy",
230+
structure=SMILES("C[O]"),
231+
E0=(9.44, "kcal/mol"),
232+
modes=[
233+
HarmonicOscillator(
234+
frequencies=(
235+
[758, 960, 1106, 1393, 1403, 1518, 2940, 3019, 3065],
236+
"cm^-1",
237+
)
238+
),
239+
NonlinearRotor(
240+
rotationalConstant=([0.916, 0.921, 5.251], "cm^-1"),
241+
symmetry=3,
242+
quantum=False,
243+
),
244+
IdealGasTranslation(mass=(31.01843, "g/mol")),
245+
],
246+
spinMultiplicity=2,
247+
opticalIsomers=1,
248+
molecularWeight=(31.01843, "amu"),
249+
collisionModel=TransportData(sigma=(3.69e-10, "m"), epsilon=(4.0, "kJ/mol")),
250+
energyTransferModel=SingleExponentialDown(alpha0=(0.956, "kJ/mol"), T0=(300, "K"), n=0.95),
251+
)
252+
253+
species(
254+
label="formaldehyde",
255+
E0=(28.69, "kcal/mol"),
256+
molecularWeight=(30.0106, "g/mol"),
257+
collisionModel=TransportData(sigma=(3.69e-10, "m"), epsilon=(4.0, "kJ/mol")),
258+
energyTransferModel=SingleExponentialDown(alpha0=(0.956, "kJ/mol"), T0=(300, "K"), n=0.95),
259+
spinMultiplicity=1,
260+
opticalIsomers=1,
261+
modes=[
262+
HarmonicOscillator(frequencies=([1180, 1261, 1529, 1764, 2931, 2999], "cm^-1")),
263+
NonlinearRotor(
264+
rotationalConstant=(
265+
[1.15498821005263, 1.3156969584727, 9.45570474524524],
266+
"cm^-1",
267+
),
268+
symmetry=2,
269+
quantum=False,
270+
),
271+
IdealGasTranslation(mass=(30.0106, "g/mol")),
272+
],
273+
)
274+
275+
species(
276+
label="H",
277+
E0=(0.000, "kcal/mol"),
278+
molecularWeight=(1.00783, "g/mol"),
279+
collisionModel=TransportData(sigma=(3.69e-10, "m"), epsilon=(4.0, "kJ/mol")),
280+
energyTransferModel=SingleExponentialDown(alpha0=(0.956, "kJ/mol"), T0=(300, "K"), n=0.95),
281+
modes=[IdealGasTranslation(mass=(1.00783, "g/mol"))],
282+
spinMultiplicity=2,
283+
opticalIsomers=1,
284+
)
285+
286+
transitionState(
287+
label="TS3",
288+
E0=(34.1, "kcal/mol"),
289+
spinMultiplicity=2,
290+
opticalIsomers=1,
291+
frequency=(-967, "cm^-1"),
292+
modes=[
293+
HarmonicOscillator(
294+
frequencies=(
295+
[466, 581, 1169, 1242, 1499, 1659, 2933, 3000],
296+
"cm^-1",
297+
)
298+
),
299+
NonlinearRotor(
300+
rotationalConstant=([0.970, 1.029, 3.717], "cm^-1"),
301+
symmetry=1,
302+
quantum=False,
303+
),
304+
IdealGasTranslation(mass=(31.01843, "g/mol")),
305+
],
306+
)
307+
308+
reactants = ["formaldehyde", "H"]
309+
products = ["methoxy"]
310+
tunneling = "Eckart"
311+
312+
rxn = reaction("CH2O+H=Methoxy", reactants, products, "TS3", tunneling=tunneling)
313+
assert rxn.label == "CH2O+H=Methoxy"
314+
assert len(rxn.reactants) == 2
315+
assert len(rxn.products) == 1
316+
assert round(abs(rxn.reactants[0].conformer.E0.value_si - 0), 7) == 0
317+
assert round(abs(rxn.reactants[1].conformer.E0.value_si - 120038.96), 7) == 0
318+
assert round(abs(rxn.products[0].conformer.E0.value_si - 39496.96), 7) == 0
319+
assert round(abs(rxn.transition_state.conformer.E0.value_si - 142674.4), 7) == 0
320+
assert round(abs(rxn.transition_state.frequency.value_si - -967.0), 7) == 0
321+
assert isinstance(rxn.transition_state.tunneling, Eckart)
322+
323+
def test_load_input_file(self):
324+
"""Test loading an Arkane input file"""
325+
path = os.path.join(
326+
os.path.dirname(os.path.dirname(rmgpy.__file__)),
327+
"examples",
328+
"arkane",
329+
"networks",
330+
"acetyl+O2",
331+
"input.py",
332+
)
333+
(
334+
job_list,
335+
reaction_dict,
336+
species_dict,
337+
transition_state_dict,
338+
network_dict,
339+
model_chemistry,
340+
) = load_input_file(path)
341+
342+
assert len(job_list) == 1
343+
344+
assert len(reaction_dict) == 5
345+
assert "entrance1" in reaction_dict
346+
assert "exit2" in reaction_dict
347+
348+
assert len(species_dict) == 9
349+
assert "acetyl" in species_dict
350+
assert "hydroperoxyl" in species_dict
351+
352+
assert len(transition_state_dict) == 5
353+
assert "entrance1" in transition_state_dict
354+
assert "isom1" in transition_state_dict
355+
356+
assert len(network_dict) == 1
357+
assert "acetyl + O2" in network_dict
358+
359+
assert model_chemistry is None
360+
361+
def test_process_model_chemistry(self):
362+
"""
363+
Test processing the model chemistry to derive the sp and freq levels
364+
"""
365+
mc = "ccsd(t)-f12a/aug-cc-pvtz//b3lyp/6-311++g(3df,3pd)"
366+
lot = process_model_chemistry(mc)
367+
assert isinstance(lot, CompositeLevelOfTheory)
368+
assert lot.energy == LevelOfTheory("ccsd(t)-f12a", "aug-cc-pvtz")
369+
assert lot.freq == LevelOfTheory("b3lyp", "6-311++g(3df,3pd)")
370+
371+
mc = "b3lyp-d3/def2-tzvp"
372+
lot = process_model_chemistry(mc)
373+
assert isinstance(lot, LevelOfTheory)
374+
assert lot == LevelOfTheory("b3lyp-d3", "def2-tzvp")
375+
376+
mc = "cbs-qb3"
377+
lot = process_model_chemistry(mc)
378+
assert isinstance(lot, LevelOfTheory)
379+
assert lot == LevelOfTheory("cbs-qb3")
380+
381+
mc = LevelOfTheory("test")
382+
lot = process_model_chemistry(mc)
383+
assert mc is lot
384+
385+
with pytest.raises(InputError):
386+
process_model_chemistry("CCSD(T)-F12a/aug-cc-pVTZ//CCSD(T)-F12a/aug-cc-pVTZ//B3LYP/6-311++G(3df,3pd)")

0 commit comments

Comments
 (0)