# 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