<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

################################################################################
#
#	RMG - Reaction Mechanism Generator
#
#	Copyright (c) 2002-2009 Prof. <NAME> (<EMAIL>) and the
#	RMG Team (<EMAIL>)
#
#	Permission is hereby granted, free of charge, to any person obtaining a
#	copy of this software and associated documentation files (the 'Software'),
#	to deal in the Software without restriction, including without limitation
#	the rights to use, copy, modify, merge, publish, distribute, sublicense,
#	and/or sell copies of the Software, and to permit persons to whom the
#	Software is furnished to do so, subject to the following conditions:
#
#	The above copyright notice and this permission notice shall be included in
#	all copies or substantial portions of the Software.
#
#	THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#	DEALINGS IN THE SOFTWARE.
#
################################################################################

"""
Contains classes describing chemical species.
"""

import logging
import math
import pybel

import constants
import settings
import structure
import data
import thermo
import os

import ctml_writer

################################################################################

class ThermoSnapshot:
	"""
	A set of thermodynamic state variables for a given state under a single
	set of conditions. During dynamic simulations, this class enables the
	simulator to update the thermodynamics of each species one per iteration.
	The attributes are:
	
	============== ========================================================
	Attribute      Description
	============== ========================================================
	`temperature`  the temperature at which the snapshot was taken in K
	`heatCapacity` the heat capacity at the current conditions in J/mol*K
	`enthalpy`     the enthalpy at the current conditions in J/mol
	`entropy`      the entropy at the current conditions in J/mol*K
	`freeEnergy`   the Gibbs free energy at the current conditions in J/mol
	============== ========================================================
	
	"""

	def __init__(self, temperature=0.0, heatCapacity=0.0, enthalpy=0.0, entropy=0.0):
		self.temperature = temperature
		self.heatCapacity = heatCapacity
		self.enthalpy = enthalpy
		self.entropy = entropy
		self.freeEnergy = enthalpy - temperature * entropy

	def isValid(self, temperature):
		"""
		Return :data:`True` if the current snapshot is still valid at the new
		`temperature` (in K), or :data:`False` if it needs to be recalculated.
		The snapshot is considered valid if the	temperatures are equal to within
		five signficiant figures. The primary benefit is a speedup of simulations
		that are isothermal or steady-state in temperature.
		"""
		if self.temperature == 0: return False
		if temperature==self.temperature: return True
		
		ratio =  temperature / self.temperature
		return (ratio > 0.99999 and ratio < 1.00001)
		

	def update(self, temperature, thermoData):
		"""
		Update the thermodynamics snapshot to a new `temperature` (in K) using
		the information in `thermoData`. The latter can be any object that
		provides the necessary getHeatCapacity(), getEnthalpy(), getEntropy(),
		and getFreeEnergy() methods.
		"""
		self.temperature = temperature
		self.heatCapacity = thermoData.getHeatCapacity(temperature)
		self.enthalpy = thermoData.getEnthalpy(temperature)
		self.entropy = thermoData.getEntropy(temperature)
		self.freeEnergy = self.enthalpy - self.temperature * self.entropy

################################################################################

class Species:
	"""
	Represent a chemical species (including all of its resonance forms). The
	attributes are:

	================  ==========================================================
	Attributes        Description
	================  ==========================================================
	`id`              A unique integer identifier
	`label`           A more descriptive (but not necessarily unique) string
	                  label
	`lennardJones`    The Lennard-Jones parameters for the species
	`reactive`        :data:`True` if the species is reactive, :data:`False` if
	                  inert
	`spectralData`    The spectral data (degrees of freedom) for the species
	`structure`       A list of :class:`structure.Structure` objects
	                  representing the set of resonance isomers
	`thermoData`      The thermodynamic parameters for the species, always a
	                  derived class of `thermo.ThermoData`
	`thermoSnapshot`  The thermodynamic parameters at the current point in the
	                  simulation
	================  ==========================================================

	"""
	def __repr__(self):
		"""How it looks on the console"""
		return "<Species %d '%s'>"%(self.id, self.label)

	def __init__(self, id=0, label='', structure=None, reactive=True, SMILES=None):
		"""
		Initialize a Species object.
		"""
		self.id = id
		
		self.label = label
		if structure is not None:
			structure.simplifyAtomTypes()
			self.structure = [structure]
		else:
			self.structure = []
		self.reactive = reactive

		self.thermoData = None
		self.thermoSnapshot = ThermoSnapshot()
		self.lennardJones = None
		self.spectralData = None
		
		if SMILES is not None:
			self.fromSMILES(SMILES)

	def __cmp__(self, other):
		"""
		A comparison function that can be used to sort lists of Species objects.
		Currently the sorting method is by increasing ID.
		"""
		return cmp(self.id, other.id)

	def __hash__(self):
		"""
		A hash function that allows for use in dictionaries et al. Currently the
		species ID is used.
		"""
		return self.id
		
	def __str__(self):
		"""
		Return a string representation of the species, in the form 'label(id)'.
		"""
		return self.label + '(' + str(self.id) + ')'
		
	def toCantera(self):
		"""Return a Cantera ctml_writer instance"""
		# contrivedly get a list like ['C', '3', 'H', '9', 'Si', '1']
		atoms = self.structure[0].toOBMol().GetSpacedFormula().split()
		# magically turn that lst into a string like 'C:3 H:9 Si:1'
		atoms = ' '.join([i+':'+j for i,j in zip(*[iter(atoms)]*2)])
		return ctml_writer.species(name = str(self),
		    atoms = " %s "%atoms,
		    thermo = self.thermoData.toCantera(),
		    note = "%s (%s)"%(self.label,self.thermoData.comment)
		       )
		
	def getFormula(self):
		"""
		Return the chemical formula for the species.
		"""
		return self.structure[0].getFormula()

	def fromAdjacencyList(self, adjstr):
		"""
		Convert an adjacency list string `adjstr` to a Species object.
		"""
		self.structure = [structure.Structure()]
		self.structure[0].fromAdjacencyList(adjstr)

	def fromCML(self, cmlstr):
		"""
		Convert a string of CML `cmlstr` to a Species object.
		"""
		self.structure = [structure.Structure()]
		self.structure[0].fromCML(cmlstr)
	
	def fromInChI(self, inchistr):
		"""
		Convert an InChI string `inchistr` to a Species object.
		"""
		self.structure = [structure.Structure()]
		self.structure[0].fromInChI(inchistr)

	def fromSMILES(self, smilesstr):<commit_msg>Escape thermo comments for cantera to avoid xml problems with <> characters.<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

################################################################################
#
#	RMG - Reaction Mechanism Generator
#
#	Copyright (c) 2002-2009 Prof. <NAME> (<EMAIL>) and the
#	RMG Team (<EMAIL>)
#
#	Permission is hereby granted, free of charge, to any person obtaining a
#	copy of this software and associated documentation files (the 'Software'),
#	to deal in the Software without restriction, including without limitation
#	the rights to use, copy, modify, merge, publish, distribute, sublicense,
#	and/or sell copies of the Software, and to permit persons to whom the
#	Software is furnished to do so, subject to the following conditions:
#
#	The above copyright notice and this permission notice shall be included in
#	all copies or substantial portions of the Software.
#
#	THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#	DEALINGS IN THE SOFTWARE.
#
################################################################################

"""
Contains classes describing chemical species.
"""

import logging
import math
import pybel

import constants
import settings
import structure
import data
import thermo
import os

import ctml_writer
import xml.sax.saxutils

################################################################################

class ThermoSnapshot:
	"""
	A set of thermodynamic state variables for a given state under a single
	set of conditions. During dynamic simulations, this class enables the
	simulator to update the thermodynamics of each species one per iteration.
	The attributes are:
	
	============== ========================================================
	Attribute      Description
	============== ========================================================
	`temperature`  the temperature at which the snapshot was taken in K
	`heatCapacity` the heat capacity at the current conditions in J/mol*K
	`enthalpy`     the enthalpy at the current conditions in J/mol
	`entropy`      the entropy at the current conditions in J/mol*K
	`freeEnergy`   the Gibbs free energy at the current conditions in J/mol
	============== ========================================================
	
	"""

	def __init__(self, temperature=0.0, heatCapacity=0.0, enthalpy=0.0, entropy=0.0):
		self.temperature = temperature
		self.heatCapacity = heatCapacity
		self.enthalpy = enthalpy
		self.entropy = entropy
		self.freeEnergy = enthalpy - temperature * entropy

	def isValid(self, temperature):
		"""
		Return :data:`True` if the current snapshot is still valid at the new
		`temperature` (in K), or :data:`False` if it needs to be recalculated.
		The snapshot is considered valid if the	temperatures are equal to within
		five signficiant figures. The primary benefit is a speedup of simulations
		that are isothermal or steady-state in temperature.
		"""
		if self.temperature == 0: return False
		if temperature==self.temperature: return True
		
		ratio =  temperature / self.temperature
		return (ratio > 0.99999 and ratio < 1.00001)
		

	def update(self, temperature, thermoData):
		"""
		Update the thermodynamics snapshot to a new `temperature` (in K) using
		the information in `thermoData`. The latter can be any object that
		provides the necessary getHeatCapacity(), getEnthalpy(), getEntropy(),
		and getFreeEnergy() methods.
		"""
		self.temperature = temperature
		self.heatCapacity = thermoData.getHeatCapacity(temperature)
		self.enthalpy = thermoData.getEnthalpy(temperature)
		self.entropy = thermoData.getEntropy(temperature)
		self.freeEnergy = self.enthalpy - self.temperature * self.entropy

################################################################################

class Species:
	"""
	Represent a chemical species (including all of its resonance forms). The
	attributes are:

	================  ==========================================================
	Attributes        Description
	================  ==========================================================
	`id`              A unique integer identifier
	`label`           A more descriptive (but not necessarily unique) string
	                  label
	`lennardJones`    The Lennard-Jones parameters for the species
	`reactive`        :data:`True` if the species is reactive, :data:`False` if
	                  inert
	`spectralData`    The spectral data (degrees of freedom) for the species
	`structure`       A list of :class:`structure.Structure` objects
	                  representing the set of resonance isomers
	`thermoData`      The thermodynamic parameters for the species, always a
	                  derived class of `thermo.ThermoData`
	`thermoSnapshot`  The thermodynamic parameters at the current point in the
	                  simulation
	================  ==========================================================

	"""
	def __repr__(self):
		"""How it looks on the console"""
		return "<Species %d '%s'>"%(self.id, self.label)

	def __init__(self, id=0, label='', structure=None, reactive=True, SMILES=None):
		"""
		Initialize a Species object.
		"""
		self.id = id
		
		self.label = label
		if structure is not None:
			structure.simplifyAtomTypes()
			self.structure = [structure]
		else:
			self.structure = []
		self.reactive = reactive

		self.thermoData = None
		self.thermoSnapshot = ThermoSnapshot()
		self.lennardJones = None
		self.spectralData = None
		
		if SMILES is not None:
			self.fromSMILES(SMILES)

	def __cmp__(self, other):
		"""
		A comparison function that can be used to sort lists of Species objects.
		Currently the sorting method is by increasing ID.
		"""
		return cmp(self.id, other.id)

	def __hash__(self):
		"""
		A hash function that allows for use in dictionaries et al. Currently the
		species ID is used.
		"""
		return self.id
		
	def __str__(self):
		"""
		Return a string representation of the species, in the form 'label(id)'.
		"""
		return self.label + '(' + str(self.id) + ')'
		
	def toCantera(self):
		"""Return a Cantera ctml_writer instance"""
		# contrivedly get a list like ['C', '3', 'H', '9', 'Si', '1']
		atoms = self.structure[0].toOBMol().GetSpacedFormula().split()
		# magically turn that lst into a string like 'C:3 H:9 Si:1'
		atoms = ' '.join([i+':'+j for i,j in zip(*[iter(atoms)]*2)])
		return ctml_writer.species(name = str(self),
		    atoms = " %s "%atoms,
		    thermo = self.thermoData.toCantera(),
			# this escaping should really be done by ctml_writer, but it doesn't do it
		    note = xml.sax.saxutils.escape("%s (%s)"%(self.label,self.thermoData.comment))
		       )
		
	def getFormula(self):
		"""
		Return the chemical formula for the species.
		"""
		return self.structure[0].getFormula()

	def fromAdjacencyList(self, adjstr):
		"""
		Convert an adjacency list string `adjstr` to a Species object.
		"""
		self.structure = [structure.Structure()]
		self.structure[0].fromAdjacencyList(adjstr)

	def fromCML(self, cmlstr):
		"""
		Convert a string of CML `cmlstr` to a Species object.
		"""
		self.structure = [structure.Structure()]
		self.structure[0].fromCML(cmlstr)
	
	def fromInChI(self, inchistr):
		"""
		Convert an InChI string `inchistr` to a Species object.
		"""
		self.structure = [structure.Structure()]
		self.structure[0].fromInChI(inchistr)

	def fromSMILES(self, smilesstr):
{'fraction_non_alphanumeric': 0.18264690451206717, 'fraction_numerical': 0.005640083945435467, 'mean_word_length': 4.184971098265896, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 3}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3060980', 'n_tokens_mistral': 4627, 'n_tokens_neox': 4067, 'n_words': 1538}