Skip to content

Commit cc33d89

Browse files
rwestbjkreitz
authored andcommitted
Estimating thermo of an adsorbate Species now re-orders Species.molecule.
We do this in the gas phase, so now also adsorbates: If you have a Species with multiple Molecules (resonance structures) then the list of Molecules gets re-ordered when you estimate the thermo so that the most stable (lowest H298) goes first. This will then be used for drawing pictures, printing SMILES strings, etc. which might mean it's no the one that you started with. This may surprise some people in some situations, but I think is the way the gas phase works, and it has some benefits, especially for machine-generated species. One thing we don't yet do in adsorbates is prioritize resonance structures coming from thermo libraries, over those that are merely estimated. Not sure of the consequences of this. Other changes in this commit are mostly for clarity.
1 parent 37b31eb commit cc33d89

File tree

1 file changed

+21
-17
lines changed

1 file changed

+21
-17
lines changed

rmgpy/data/thermo.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,8 +1561,7 @@ def get_thermo_data_for_surface_species(self, species):
15611561
"""
15621562

15631563
# define the comparison function to find the lowest energy
1564-
1565-
def lowest_energy(species):
1564+
def species_enthalpy(species):
15661565
if hasattr(species.thermo, 'H298'):
15671566
return species.thermo.H298.value_si
15681567
else:
@@ -1577,9 +1576,7 @@ def lowest_energy(species):
15771576
resonance_data = []
15781577

15791578
# iterate over all resonance structures of the species
1580-
1581-
for i in range(len(species.molecule)):
1582-
molecule = species.molecule[i]
1579+
for molecule in species.molecule:
15831580
# store any labeled atoms to reapply at the end
15841581
labeled_atoms = molecule.get_all_labeled_atoms()
15851582
molecule.clear_labeled_atoms()
@@ -1591,7 +1588,10 @@ def lowest_energy(species):
15911588
if len(dummy_molecules) == 0:
15921589
raise RuntimeError(f"Cannot get thermo for gas-phase molecule. No valid dummy molecules from original molecule:\n{molecule.to_adjacency_list()}")
15931590

1594-
# if len(molecule) > 1, it will assume all resonance structures have already been generated when it tries to generate them, so evaluate each configuration separately and pick the lowest energy one by H298 value
1591+
# if len(molecule) > 1, it will assume all resonance structures
1592+
# have already been generated when it tries to generate them, so
1593+
# evaluate each configuration separately and pick the lowest energy
1594+
# one by H298 value
15951595
gas_phase_species_from_libraries = []
15961596
gas_phase_species_estimates = []
15971597
for dummy_molecule in dummy_molecules:
@@ -1605,21 +1605,20 @@ def lowest_energy(species):
16051605
gas_phase_species_estimates.append(dummy_species)
16061606

16071607
if gas_phase_species_from_libraries:
1608-
gas_phase_species = min(gas_phase_species_from_libraries, key=lowest_energy)
1608+
gas_phase_species = min(gas_phase_species_from_libraries, key=species_enthalpy)
16091609
else:
1610-
gas_phase_species = min(gas_phase_species_estimates, key=lowest_energy)
1610+
gas_phase_species = min(gas_phase_species_estimates, key=species_enthalpy)
16111611

16121612
thermo = gas_phase_species.thermo
16131613
thermo.comment = f"Gas phase thermo for {thermo.label or gas_phase_species.molecule[0].to_smiles()} from {thermo.comment}. Adsorption correction:"
1614-
logging.debug("Using thermo from gas phase for species {}\n".format(gas_phase_species.label) + repr(thermo))
1614+
logging.debug("Using thermo from gas phase for species %s: %r", gas_phase_species.label, thermo)
16151615

16161616
if not isinstance(thermo, ThermoData):
16171617
thermo = thermo.to_thermo_data()
16181618
find_cp0_and_cpinf(gas_phase_species, thermo)
16191619

16201620
# Get the adsorption energy
16211621
# Create the ThermoData object
1622-
16231622
adsorption_thermo = ThermoData(
16241623
Tdata=([300, 400, 500, 600, 800, 1000, 1500], "K"),
16251624
Cpdata=([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "J/(mol*K)"),
@@ -1639,7 +1638,6 @@ def lowest_energy(species):
16391638
# (group_additivity=True means it appends the comments)
16401639
add_thermo_data(thermo, adsorption_thermo, group_additivity=True)
16411640

1642-
resonance_data.append(thermo)
16431641
# if the molecule had labels, reapply them
16441642
for label, atom in labeled_atoms.items():
16451643
if isinstance(atom,list):
@@ -1648,15 +1646,21 @@ def lowest_energy(species):
16481646
else:
16491647
atom.label = label
16501648

1651-
# Get the enthalpy of formation of every adsorbate at 298 K and determine the resonance structure with the lowest enthalpy of formation
1652-
# We assume that the lowest enthalpy of formation is the correct thermodynamic property for the adsorbate
1649+
# a tuple of the H298, the ThermoData object, and the molecule
1650+
resonance_data.append((thermo.get_enthalpy(298), thermo, molecule))
1651+
1652+
# Get the enthalpy of formation of every adsorbate at 298 K and
1653+
# determine the resonance structure with the lowest enthalpy of formation.
1654+
# We assume that the lowest enthalpy of formation is the correct
1655+
# thermodynamic property for the adsorbate.
16531656

1654-
enthalpy298 = []
1657+
# first element of each tuple is H298, so sort by that
1658+
resonance_data = sorted(resonance_data)
16551659

1656-
for i in range(len(resonance_data)):
1657-
enthalpy298.append(resonance_data[i].get_enthalpy(298))
1660+
# reorder the resonance structures (molecules) by their H298
1661+
species.molecule = [mol for h, thermo, mol in resonance_data]
16581662

1659-
thermo = resonance_data[np.argmin(enthalpy298)]
1663+
thermo = resonance_data[0][1]
16601664

16611665
if thermo.label:
16621666
thermo.label += 'X' * len(surface_sites)

0 commit comments

Comments
 (0)