modules/chempy/__init__.py (636 lines of code) (raw):
#A* -------------------------------------------------------------------
#B* This file contains source code for the PyMOL computer program
#C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
#D* -------------------------------------------------------------------
#E* It is unlawful to modify or remove this copyright notice.
#F* -------------------------------------------------------------------
#G* Please see the accompanying LICENSE file for further information.
#H* -------------------------------------------------------------------
#I* Additional authors of this source file include:
#-*
#-*
#-*
#Z* -------------------------------------------------------------------
from __future__ import print_function
import os
import copy
import functools
#
# Basic chempy types
#
@functools.total_ordering
class Atom(object):
def __reduce__(self):
# for loading new-style class pickle with PyMOL <= 2.2
return (self.__class__, (), self.__dict__)
def __setstate__(self, state):
# for loading PyMOL <= 2.2 pickles
self.__dict__.update(state)
if 'resi' in state:
self.resi = self.__dict__.pop('resi')
@property
def resi(self):
return str(self.resi_number) + self.ins_code
@resi.setter
def resi(self, resi):
if isinstance(resi, str) and resi and not resi[-1].isdigit():
self.ins_code = resi[-1]
resi = resi[:-1]
else:
self.__dict__.pop('ins_code', None)
self.resi_number = int(resi or 0)
# these must be immutables
symbol = 'X'
name = ''
resn = 'UNK'
resn_code = 'X'
ins_code = ''
resi_number = 1
b = 0.0
q = 1.0
vdw = 0.0
alt = ''
hetatm = 1
segi = ''
chain = ''
coord = (9999.999,9999.999,9999.999)
formal_charge = 0.0
partial_charge = 0.0
elec_radius = 0.0
custom = ''
# Flags
flags = 0
# Force-fields
numeric_type = -9999
text_type = '??'
# MDL Mol-files
stereo = 0
# Macromodel files
color_code = 2
# Secondary structure
ss = ''
def get_mass(self):
'''Given the chemical symbol the atomic mass is returned'''
return atomic_mass[self.symbol]
def get_number(self):
'''Given the chemical symbol the atomic number is returned'''
return atomic_number[self.symbol]
def get_implicit_valence(self):
return implicit_valence[self.symbol]
def get_free_valence(self, npaired):
try:
maxfree = implicit_valence[self.symbol][0]
except KeyError:
print("unknown implicit_valence for", self.symbol)
return 0
return max(0, maxfree - npaired + self.formal_charge)
def has(self,attr):
return attr in self.__dict__
def in_same_residue(self,other):
if self.resi == other.resi:
if self.chain == other.chain:
if self.segi == other.segi:
return 1
return 0
def new_in_residue(self):
newat = Atom()
if self.has('segi'): newat.segi = self.segi
if self.has('chain'): newat.chain = self.chain
if self.has('resn'): newat.resn = self.resn
if self.has('resn_code'): newat.resn_code = self.resn_code
if self.has('ins_code'): newat.ins_code = self.ins_code
if self.has('resi_number'): newat.resi_number = self.resi_number
if self.has('hetatm'): newat.hetatm = self.hetatm
return newat
def get_signature(self):
return ':'.join([self.segi, self.chain, self.resn,
self.resi, self.symbol, self.name])
def __cmp__(self,other):
return \
cmp(type(self), type(other)) or \
cmp(self.segi, other.segi) or \
cmp(self.chain, other.chain) or \
cmp(self.resi_number, other.resi_number) or \
cmp(self.ins_code, other.ins_code) or \
cmp(self.resn, other.resn) or \
cmp(self.symbol, other.symbol) or \
cmp(self.name, other.name) or \
cmp(id(self), id(other))
@staticmethod
def _order(obj):
return (
obj.segi,
obj.chain,
obj.resi_number,
obj.ins_code,
obj.resn,
obj.symbol,
obj.name,
id(obj),
)
def __gt__(self, other):
return Atom._order(self) > Atom._order(other)
def __eq__(self, other):
return Atom._order(self) == Atom._order(other)
class Bond:
order = 1
stereo = 0
def has(self,attr):
return attr in self.__dict__
class Molecule:
defaults = {
'dim_code' : '3D',
'title' : 'untitled',
'comments' : '',
'chiral' : 1,
'spacegroup' : 'P 1',
'cell' : [1.0, 1.0, 1.0, 90.0, 90.0, 90.0],
}
def __getattr__(self,attr):
if attr in Molecule.defaults:
return Molecule.defaults[attr]
else:
raise AttributeError(attr)
def has(self,attr):
return attr in self.__dict__
class Storage:
def my_open(self,fname,mode='r'):
if 'r' in mode and '://' in fname:
try:
import urllib.request as urllib
except ImportError:
import urllib
return urllib.urlopen(fname)
elif fname.endswith(".gz") or fname.endswith(".pze") or fname.endswith("pzw"):
import gzip
return gzip.open(fname, mode)
else:
return open(fname,mode)
def updateFromList(self,indexed,**params):
return NotImplementedError
def fromList(self,**params):
return NotImplementedError
def toList(self,indexed,**params):
return NotImplementedError
def updateFromFile(self,indexed,fname,**params):
fp = open(fname)
result = self.updateFromList(indexed, fp.readlines(), **params)
fp.close()
def fromFile(self,fname,**params):
if feedback['io']:
print(' chempy: reading "%s".' % fname)
fp = self.my_open(fname)
result = self.fromList(fp.readlines(), **params)
fp.close()
return result
def toFile(self,indexed,fname,**params):
lines = self.toList(indexed, **params)
if feedback['io']:
print(' chempy: writing "%s".' % fname)
fp = open(fname,'w')
result = fp.writelines(lines)
fp.close()
class PseudoFile:
def __init__(self,list=[]):
self.list = copy.deepcopy(list)
def write(self,st):
self.list.append(str(st))
def readline(self):
try:
return self.list.pop(0)
except:
return None
def close(self):
self.list = None
feedback = { 'warnings': 1,
'terse' : 1,
'io' : 1,
'actions' : 1,
'tinker' : 1,
'gamess' : 1,
'atoms' : 0,
'bonds' : 0,
'verbose' : 0,
'bmin' : 1,
}
if 'CHEMPY_DATA' in os.environ: #
path = os.environ['CHEMPY_DATA'] + '/'
elif 'PYMOL_DATA' in os.environ:
path = os.environ['PYMOL_DATA'] + '/chempy/'
elif 'PYMOL_PATH' in os.environ:
path = os.environ['PYMOL_PATH'] + '/data/chempy/'
elif 'FREEMOL_MODULES' in os.environ:
path = os.environ['FREEMOL_MODULES'] + '/chempy/'
else:
path = ''
# double check these values...
#hvd values obtained from http://www.webelements.com/ and recorded to their
# known accuracy.
atomic_mass = {
'H' : 1.00794,
'He' : 4.002602,
'HE' : 4.002602,
'Li' : 6.941,
'LI' : 6.941,
'Be' : 9.012182,
'BE' : 9.012182,
'B' : 10.811,
'C' : 12.0107,
'N' : 14.0067,
'O' : 15.9994,
'F' : 18.9984032,
'Ne' : 20.1797,
'NE' : 20.1797,
'Na' : 22.989770,
'NA' : 22.989770,
'Mg' : 24.3050,
'MG' : 24.3050,
'Al' : 26.981538,
'AL' : 26.981538,
'Si' : 28.0855,
'SI' : 28.0855,
'P' : 30.973761,
'S' : 32.065,
'Cl' : 35.453,
'CL' : 35.453,
'Ar' : 39.948,
'AR' : 39.948,
'K' : 39.0983,
'Ca' : 40.078,
'CA' : 40.078,
'Sc' : 44.955910,
'SC' : 44.955910,
'Ti' : 47.867,
'TI' : 47.867,
'V' : 50.9415,
'Cr' : 51.9961,
'CR' : 51.9961,
'Mn' : 54.938049,
'MN' : 54.938049,
'Fe' : 55.845,
'FE' : 55.845,
'Co' : 58.933200,
'CO' : 58.933200,
'Ni' : 58.6934,
'NI' : 58.6934,
'Cu' : 63.546,
'CU' : 63.546,
'Zn' : 65.39,
'ZN' : 65.39,
'Ga' : 69.723,
'GA' : 69.723,
'Ge' : 72.64,
'GE' : 72.64,
'As' : 74.92160,
'AS' : 74.92160,
'Se' : 78.96,
'SE' : 78.96,
'Br' : 79.904,
'BR' : 79.904,
'Kr' : 83.80,
'KR' : 83.80,
'Rb' : 85.4678,
'RB' : 85.4678,
'Sr' : 87.62,
'SR' : 87.62,
'Y' : 88.90585,
'Zr' : 91.224,
'ZR' : 91.224,
'Nb' : 92.90638,
'NB' : 92.90638,
'Mo' : 95.94,
'MO' : 95.94,
'Tc' : 98,
'TC' : 98,
'Ru' : 101.07,
'RU' : 101.07,
'Rh' : 102.90550,
'RH' : 102.90550,
'Pd' : 106.42,
'PD' : 106.42,
'Ag' : 107.8682,
'AG' : 107.8682,
'Cd' : 112.411,
'CD' : 112.411,
'In' : 114.818,
'IN' : 114.818,
'Sn' : 118.710,
'SN' : 118.710,
'Sb' : 121.760,
'SB' : 121.760,
'Te' : 127.60,
'TE' : 127.60,
'I' : 126.90447,
'Xe' : 131.293,
'XE' : 131.293,
'Cs' : 132.90545,
'CS' : 132.90545,
'Ba' : 137.327,
'BA' : 137.327,
'La' : 138.9055,
'LA' : 138.9055,
'Ce' : 140.116,
'CE' : 140.116,
'Pr' : 140.90765,
'PR' : 140.90765,
'Nd' : 144.24,
'ND' : 144.24,
'Pm' : 145,
'PM' : 145,
'Sm' : 150.36,
'SM' : 150.36,
'Eu' : 151.964,
'EU' : 151.964,
'Gd' : 157.25,
'GD' : 157.25,
'Tb' : 158.92534,
'TB' : 158.92534,
'Dy' : 162.50,
'DY' : 162.50,
'Ho' : 164.93032,
'HO' : 164.93032,
'Er' : 167.259,
'ER' : 167.259,
'Tm' : 168.93421,
'TM' : 168.93421,
'Yb' : 173.04,
'YB' : 173.04,
'Lu' : 174.967,
'LU' : 174.967,
'Hf' : 178.49,
'HF' : 178.49,
'Ta' : 180.9479,
'TA' : 180.9479,
'W' : 183.84,
'Re' : 186.207,
'RE' : 186.207,
'Os' : 190.23,
'OS' : 190.23,
'Ir' : 192.217,
'IR' : 192.217,
'Pt' : 195.078,
'PT' : 195.078,
'Au' : 196.96655,
'AU' : 196.96655,
'Hg' : 200.59,
'HG' : 200.59,
'Tl' : 204.3833,
'TL' : 204.3833,
'Pb' : 207.2,
'PB' : 207.2,
'Bi' : 208.98038,
'BI' : 208.98038,
'Po' : 208.98,
'PO' : 208.98,
'At' : 209.99,
'AT' : 209.99,
'Rn' : 222.02,
'RN' : 222.02,
'Fr' : 223.02,
'FR' : 223.02,
'Ra' : 226.03,
'RA' : 226.03,
'Ac' : 227.03,
'AC' : 227.03,
'Th' : 232.0381,
'TH' : 232.0381,
'Pa' : 231.03588,
'PA' : 231.03588,
'U' : 238.02891,
'Np' : 237.05,
'NP' : 237.05,
'Pu' : 244.06,
'PU' : 244.06,
'Am' : 243.06,
'AM' : 243.06,
'Cm' : 247.07,
'CM' : 247.07,
'Bk' : 247.07,
'BK' : 247.07,
'Cf' : 251.08,
'CF' : 251.08,
'Es' : 252.08,
'ES' : 252.08,
'Fm' : 257.10,
'FM' : 257.10,
'Md' : 258.10,
'MD' : 258.10,
'No' : 259.10,
'NO' : 259.10,
'Lr' : 262.11,
'LR' : 262.11,
'Rf' : 261.11,
'RF' : 261.11,
'Db' : 262.11,
'DB' : 262.11,
'Sg' : 266.12,
'SG' : 266.12,
'Bh' : 264.12,
'BH' : 264.12,
'Hs' : 269.13,
'HS' : 269.13,
'Mt' : 268.14,
'MT' : 268.14,
}
atomic_number = {
'H' : 1,
'He' : 2,
'HE' : 2,
'Li' : 3,
'LI' : 3,
'Be' : 4,
'BE' : 4,
'B' : 5,
'C' : 6,
'N' : 7,
'O' : 8,
'F' : 9,
'Ne' : 10,
'NE' : 10,
'Na' : 11,
'NA' : 11,
'Mg' : 12,
'MG' : 12,
'Al' : 13,
'AL' : 13,
'Si' : 14,
'SI' : 14,
'P' : 15,
'S' : 16,
'Cl' : 17,
'CL' : 17,
'Ar' : 18,
'AR' : 18,
'K' : 19,
'Ca' : 20,
'CA' : 20,
'Sc' : 21,
'SC' : 21,
'Ti' : 22,
'TI' : 22,
'V' : 23,
'Cr' : 24,
'CR' : 24,
'Mn' : 25,
'MN' : 25,
'Fe' : 26,
'FE' : 26,
'Co' : 27,
'CO' : 27,
'Ni' : 28,
'NI' : 28,
'Cu' : 29,
'CU' : 29,
'Zn' : 30,
'ZN' : 30,
'Ga' : 31,
'GA' : 31,
'Ge' : 32,
'GE' : 32,
'As' : 33,
'AS' : 33,
'Se' : 34,
'SE' : 34,
'Br' : 35,
'BR' : 35,
'Kr' : 36,
'KR' : 36,
'Rb' : 37,
'RB' : 37,
'Sr' : 38,
'SR' : 38,
'Y' : 39,
'Zr' : 40,
'ZR' : 40,
'Nb' : 41,
'NB' : 41,
'Mo' : 42,
'MO' : 42,
'Tc' : 43,
'TC' : 43,
'Ru' : 44,
'RU' : 44,
'Rh' : 45,
'RH' : 45,
'Pd' : 46,
'PD' : 46,
'Ag' : 47,
'AG' : 47,
'Cd' : 48,
'CD' : 48,
'In' : 49,
'IN' : 49,
'Sn' : 50,
'SN' : 50,
'Sb' : 51,
'SB' : 51,
'Te' : 52,
'TE' : 52,
'I' : 53,
'Xe' : 54,
'XE' : 54,
'Cs' : 55,
'CS' : 55,
'Ba' : 56,
'BA' : 56,
'La' : 57,
'LA' : 57,
'Ce' : 58,
'CE' : 58,
'Pr' : 59,
'PR' : 59,
'Nd' : 60,
'ND' : 60,
'Pm' : 61,
'PM' : 61,
'Sm' : 62,
'SM' : 62,
'Eu' : 63,
'EU' : 63,
'Gd' : 64,
'GD' : 64,
'Tb' : 65,
'TB' : 65,
'Dy' : 66,
'DY' : 66,
'Ho' : 67,
'HO' : 67,
'Er' : 68,
'ER' : 68,
'Tm' : 69,
'TM' : 69,
'Yb' : 70,
'YB' : 70,
'Lu' : 71,
'LU' : 71,
'Hf' : 72,
'HF' : 72,
'Ta' : 73,
'TA' : 73,
'W' : 74,
'Re' : 75,
'RE' : 75,
'Os' : 76,
'OS' : 76,
'Ir' : 77,
'IR' : 77,
'Pt' : 78,
'PT' : 78,
'Au' : 79,
'AU' : 79,
'Hg' : 80,
'HG' : 80,
'Tl' : 81,
'TL' : 81,
'Pb' : 82,
'PB' : 82,
'Bi' : 83,
'BI' : 83,
'Po' : 84,
'PO' : 84,
'At' : 85,
'AT' : 85,
'Rn' : 86,
'RN' : 86,
'Fr' : 87,
'FR' : 87,
'Ra' : 88,
'RA' : 88,
'Ac' : 89,
'AC' : 89,
'Th' : 90,
'TH' : 90,
'Pa' : 91,
'PA' : 91,
'U' : 92,
'Np' : 93,
'NP' : 93,
'Pu' : 94,
'PU' : 94,
'Am' : 95,
'AM' : 95,
'Cm' : 96,
'CM' : 96,
'Bk' : 97,
'BK' : 97,
'Cf' : 98,
'CF' : 98,
'Es' : 99,
'ES' : 99,
'Fm' : 100,
'FM' : 100,
'Md' : 101,
'MD' : 101,
'No' : 102,
'NO' : 102,
'Lr' : 103,
'LR' : 103,
'Rf' : 104,
'RF' : 104,
'Db' : 105,
'DB' : 105,
'Sg' : 106,
'SG' : 106,
'Bh' : 107,
'BH' : 107,
'Hs' : 108,
'HS' : 108,
'Mt' : 109,
'MT' : 109
}
implicit_valence = {
'H' : {0:1,1:0},
'C' : {0:4,1:3,2:2,3:1,4:0},
'N' : {0:3,1:2,2:1,3:0,4:0},
'O' : {0:2,1:1,2:0},
'F' : {0:1,1:0},
'Cl' : {0:1,1:0},
'CL' : {0:1,1:0},
'Br' : {0:1,1:0},
'BR' : {0:1,1:0},
'I' : {0:1,1:0},
'S' : {0:2,1:2,2:0,3:1,4:0,5:1,6:0}, # ambiguity?
'K' : {0:0,1:0}, # as drawn
'Cu' : {0:0,1:0,2:0}, # as drawn
'CU' : {0:0,1:0,2:0}, # as drawn
'Zn' : {0:0,1:0,2:0,4:0}, # as drawn
'ZN' : {0:0,1:0,2:0,4:0}, # as drawn
'Mg' : {0:1,1:0},
'MG' : {0:1,1:0},
'Ca' : {0:1,1:0},
'CA' : {0:1,1:0},
'P' : {0:3,1:2,2:1,3:0,4:0,5:0,6:0},
}