Source code for Chemistry.base.reactants

# Copyright (c) 2014 Dan Obermiller
#
# 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.
#
# You should have received a copy of the MIT License along with this program.
# If not, see <http://opensource.org/licenses/MIT>


"""This module provides the classes used to form various reactants in a chemical
reaction.
"""


from copy import deepcopy

import Chemistry.base.periodic_table as pt
from Chemistry.base.compounds import _CompoundWrapper


[docs]class Reactant(_CompoundWrapper): """The base Reactant object. All subclasses of this are things that are commonly found in a reaction. Parameters ---------- compound : Compound The molecule being considered as a reactant. Attributes ---------- pka """ _pka = None @classmethod def _new_key(cls, compound, atom=True): """Generates a new atom/bond key for a compound. Parameters ---------- compound : Compound, _CompoundWrapper The compound that needs a new key atom : bool, optional True if a new atom key is needed, False for new bond key. Defaults to True. Returns ------- string The new key. """ if atom: max_key = max(compound.atoms) letter = 'a' else: max_key = max(compound.bonds) letter = 'b' number = int(max_key[1:])+1 return "{}{}".format(letter, number) def __init__(self, compound): super(Reactant, self).__init__(compound) @property def pka(self): """The pka of a molecule. If the wrapped molecule already has a known pka then the value passed by the constructor is checked against that. """ return self._pka @pka.setter
[docs] def pka(self, pka_): if hasattr(self.compound, 'pka'): if pka_ != self.pka: raise ValueError("pKa must equal {}".format(self.pka)) self._pka = pka_
def __str__(self): return "{} of {}".format(self.__class__.__name__, self.compound.__class__.__name__) def __repr__(self): return str(self)
[docs]class Acid(Reactant): """A subclass of Reactant, represents acidic compounds in a reaction. Parameters ---------- compound : Compound The molecule being treated as an acid. acidic_point : string The key of the 'acidic point' of the molecule, or the most acidic H+. pka : float The pKa of the aforementioned most acidic H+. """ def __init__(self, compound, acidic_point, pka): super(Acid, self).__init__(compound) self.acidic_point = acidic_point self.pka = pka
[docs] def to_conjugate_base(self): """Transforms the current acid into its conjugate base.""" raise NotImplementedError
[docs]class Base(Reactant): """A subclass of Reactant, represents basic compounds in a reaction. Parameters ---------- compound : Compound The molecule being treated as a base. basic_point : string The key of the 'basic point' of the molecule, or the location that is most accepting of H+. pka : float The pKa of the conjugate acid. """ def __init__(self, compound, basic_point, pka): super(Base, self).__init__(compound) self.basic_point = basic_point self.pka = pka
[docs] def to_conjugate_acid(self): """Transforms the current base into its conjugate acid. Is side-effect free; all changes happen on a copy of this base. Returns ------- Acid The conjugate acid of the base.""" conjugate = deepcopy(self.compound) a_key = Reactant._new_key(conjugate) b_key = Reactant._new_key(conjugate, False) hydrogen = pt.get_element('H') conjugate._add_node(a_key, hydrogen) conjugate._add_edge(b_key, a_key, self.basic_point) try: conjugate.other_info['id'] = \ "Conjugate acid of {}".format(self.other_info['id']) except KeyError: conjugate.other_info['id'] = "Unknown acid" return Acid(conjugate, a_key, self.pka)
[docs]class LewisAcid(Acid): pass
[docs]class LewisBase(Base): pass
[docs]class BronstedAcid(Acid): pass
[docs]class BronstedBase(Base): pass
[docs]class Electrophile(Reactant): pass
[docs]class Nucleophile(Reactant): pass