from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
# from __future__ import unicode_literals
#
# SASMOL: Copyright (C) 2011 Joseph E. Curtis, Ph.D.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# SYSTEM
#
# 12/4/2009 -- initial coding : jc
# 12/10/2009 -- doc strings : jc
# 01/11/2010 -- new design pattern : jc
# 12/25/2015 -- refactored for release : jc
# 07/23/2016 -- refactored for Python 3 : jc
# 08/19/2016 -- added doc strings : jc
#
# 1 2 3 4 5 6 7
# LC4567890123456789012345678901234567890123456789012345678901234567890123456789
# * **
'''
System is the main module that contains the base classes that
describe the objects in the system. An instance of the system
can be created of Atom, Molecule, or System classes.
For purely atomic based systems, a set of utilities are provided
to hold information, to calculate properties, and to manipulate
the structures in space. In addition, basic file input / output
is provided in the Mol class (and inherited up the classes
as dictated by the calling code).
'''
import sys
import os
import numpy
import sasmol.file_io as file_io
import sasmol.calculate as calculate
import sasmol.operate as operate
import sasmol.subset as subset
import sasmol.properties as properties
import sasmol.topology as topology
import sasmol.view as view
import sasmol.config as config
[docs]class Atom(
file_io.Files,
calculate.Calculate,
operate.Move,
subset.Mask,
properties.Atomic,
topology.Topology,
view.View):
""" Base class containing methods to define system objects.
Atom is the base class to build and deal with molecular systems.
The class inherits file input/output, calcuation, manipulation, subset,
atom properties, topology, and viewing from other samsol classes.
Class has several initialization options
Parameters
----------
args
optional integer : self._id
kwargs
optional keyword arguments
string filename (filename = 'hiv1_gag.pdb') : default = None
integet id (id=3) : default = 0
boolean debug (debug = True) : default = None
Returns
-------
system object
if called with string (or with filename kwarg) returns
an initialized system object with data read in using
file_io.read_pdb()
Examples
-------
Since subsequent classes in this file inherit from Atom and the
common use case involves molecules, examples will use the Molecule()
class instead of Atom.
Define instance of class and read in PDB file at same time
>>> import sasmol.system as system
>>> molecule = system.Molecule(filename='hiv1_gag.pdb')
Other instance definition examples (not-exhaustive)
>>> molecule = system.Molecule()
>>> molecule = system.Molecule(id=7)
>>> molecule = system.Molecule(debug=True)
>>> molecule = system.Molecule('hiv1_gag.pdb')
>>> molecule = system.Molecule(filename='hiv1_gag.pdb', id=0, debug=False)
Example of how setters and getters work. Below, the original index
values are adjusted by a value (-10) and setIndex() is used to assign
the new list to the molecule
>>> molecule.index()[0]
1
>>> offset = -10
>>> index = [x + offset for x in range(molecule.natoms())]
>>> molecule.setIndex(index)
>>> molecule.index()[0]
-10
Note
----
`self` parameter is not shown in the ``Parameters`` section in the documentation
Many "setters" and "getters" methods are defined in this class. See source
code for details.
### attributes of len(natoms):
# python lists:
list_keys = ['_residue_flag', '_occupancy', '_charge', '_atom', '_chain', '_segname', '_beta', '_loc', '_element', '_name', '_rescode', '_moltype', '_resname', '_charmm_type', '_atom_charge', '_atom_vdw', '_residue_charge', '_one_letter_resname' ]
# numpy arrays: (note that _coor has array dimensions (number of frames, natoms, 3)
numpy_keys = ['_original_index', '_original_resid', '_index', '_resid', '_mass', '_coor', '_minimum', '_maximum', '_pmi']
### attributes with variable list lenghts:
#python lists:
short_keys = ['_resnames', '_chains', '_resids', '_elements', '_segnames', '_betas', '_names', '_moltypes', '_occupancies' ]
# numpy arrays
numpy_short_keys = ['_names_mask', '_resnames_mask', '_resids_mask', '_chains_mask', '_occupancies_mask', '_betas_mask', '_elements_mask', '_segnames_mask']
### integer attributes
int_keys = ['_number_of_chains', '_number_of_betas', '_number_of_resids', '_number_of_names', '_number_of_moltypes', '_number_of_resnames', '_number_of_segnames', '_number_of_elements', '_id', '_number_of_occupancies', '_number_of_frames', '_natoms' ]
### float attributes
float_keys = ['_total_mass', '_center_of_mass', '_rg']
### strings, dictionaries, and flags
other_keys = ['_fasta', '_header', '_conect', '_debug', '_formula', '_unitcell']
"""
def __init__(self, *args, **kwargs):
self._filename = kwargs.pop('filename', None)
self._id = kwargs.pop('id', 0)
self._debug = kwargs.pop('debug', None)
self._total_mass = 0.0
self._natoms = 0
self._mass = None
self._coor = None
self._center_of_mass = None
self._conect = []
if config.__logging_level__ == 'DEBUG':
self._debug = True
self._defined_with_input_file = False
self._argument_flag = False
self._id_flag = False
try:
if self._filename is not None:
if (os.path.isfile(self._filename)):
self.read_pdb(self._filename)
self._defined_with_input_file = True
else:
for argument in args:
self._argument_flag = True
if (os.path.isfile(str(argument))):
try:
self.read_pdb(argument)
self._defined_with_input_file = True
self._filename = argument
break
except BaseException:
pass
else:
try:
self._id = int(argument)
self._id_flag = True
break
except BaseException:
pass
except BaseException:
pass
def __repr__(self):
if self._defined_with_input_file:
return "sasmol object initialied with filename = " + self._filename
elif self._argument_flag and not self._id_flag:
return "sasmol object (no file found)"
else:
return "sasmol object"
[docs] def setFilename(self, newValue):
self._filename = newValue
[docs] def filename(self):
return self._filename
def __add__(self, other, **kwargs):
'''
Override the python __add__ method to combine other molecule into instance molecule
Parameters
----------
other
system object
kwargs
optional keyword arguments
Returns
-------
None
modified system object
Examples
-------
>>> import sasmol.system as system
>>> molecule_1 = system.Molecule(filename='hiv1_gag.pdb')
>>> molecule_2 = system.Molecule(filename='lysozyme.pdb')
>>> molecule_1.natoms()
6730
>>> molecule_2.natoms()
1960
>>> molecule_1 + molecule_2
>>> molecule_1.natoms()
8690
>>> molecule_1.index()[-1]
8690
Note
____
If an item is missing in the other molecule then the original item is not altered
self._natoms is updated based on the len(self._names)
self._index is reset to start at 1 and end at self._natoms
Currently no check is made on the number of frames in each molecule that are being
added together.
'''
# print(self.__dict__)
for key, value in self.__dict__.items():
# print(key)
try:
if type(value) is list:
self.__dict__[key].extend(other.__dict__[key])
elif type(value) is numpy.ndarray:
# print 'sdk = ',self.__dict__[key], 'odk =',
# other.__dict__[key]
if key == '_coor':
self.__dict__[key] = numpy.concatenate(
(self.__dict__[key], other.__dict__[key]), axis=1)
elif len(value.shape) == 1:
# print(key)
self.__dict__[key] = numpy.concatenate(
(self.__dict__[key], other.__dict__[key]))
else:
print(
'numpy array not added for self.__dict__[key]: ' +
str(key))
except BaseException:
pass
self._natoms = len(self._name)
self._index = numpy.array(
[x + 1 for x in range(self._natoms)], numpy.int32)
[docs] def setId(self, newValue):
self._id = newValue
[docs] def id(self):
return self._id
# properties
[docs] def number_of_frames(self):
return self._coor[:, 0, 0].shape[0]
[docs] def setNumber_of_frames(self, newValue):
self._number_of_frames = newValue
[docs] def atom(self):
return self._atom
[docs] def setAtom(self, newValue):
self._atom = newValue
[docs] def index(self):
return self._index
[docs] def setIndex(self, newValue):
self._index = newValue
[docs] def original_index(self):
return self._original_index
[docs] def setOriginal_index(self, newValue):
self._original_index = newValue
[docs] def original_resid(self):
return self._original_resid
[docs] def setOriginal_resid(self, newValue):
self._original_resid = newValue
[docs] def name(self):
return self._name
[docs] def setName(self, newValue):
self._name = newValue
[docs] def loc(self):
return self._loc
[docs] def setLoc(self, newValue):
self._loc = newValue
[docs] def resname(self):
return self._resname
[docs] def setResname(self, newValue):
self._resname = newValue
[docs] def chain(self):
return self._chain
[docs] def setChain(self, newValue):
self._chain = newValue
[docs] def resid(self):
return self._resid
[docs] def setResid(self, newValue):
self._resid = newValue
[docs] def coor(self):
return self._coor
[docs] def setCoor(self, newValue):
self._coor = newValue
[docs] def rescode(self):
return self._rescode
[docs] def setRescode(self, newValue):
self._rescode = newValue
[docs] def occupancy(self):
return self._occupancy
[docs] def setOccupancy(self, newValue):
self._occupancy = newValue
[docs] def beta(self):
return self._beta
[docs] def setBeta(self, newValue):
self._beta = newValue
[docs] def segname(self):
return self._segname
[docs] def setSegname(self, newValue):
self._segname = newValue
[docs] def element(self):
return self._element
[docs] def setElement(self, newValue):
self._element = newValue
[docs] def charge(self):
return self._charge
[docs] def setCharge(self, newValue):
self._charge = newValue
[docs] def atom_charge(self):
return self._atom_charge
[docs] def setAtom_charge(self, newValue):
self._atom_charge = newValue
[docs] def atom_vdw(self):
return self._atom_vdw
[docs] def setAtom_vdw(self, newValue):
self._atom_vdw = newValue
[docs] def mass(self):
return self._mass
[docs] def setMass(self, newValue):
self._mass = newValue
[docs] def total_mass(self):
if (self._total_mass is None):
self._total_mass = calculate.Calculate.calculate_mass(self)
return self._total_mass
[docs] def setTotal_mass(self, newValue):
self._total_mass = newValue
[docs] def natoms(self):
return self._natoms
[docs] def setNatoms(self, newValue):
self._natoms = newValue
[docs] def number_of_atoms(self):
return self._number_of_atoms
[docs] def setNumber_of_atoms(self, newValue):
self._number_of_atoms = newValue
[docs] def moltype(self):
return self._moltype
[docs] def setMoltype(self, newValue):
self._moltype = newValue
# --- diagnostics ---
[docs] def check_integrity(self, fast_check=False):
import sasmol.utilities as utilities
return utilities.check_integrity(self, fast_check=fast_check)
[docs] def validate_integrity(self, fast_check=False):
import sasmol.utilities as utilities
results = utilities.check_integrity(
self, fast_check=fast_check, warn=False)
natoms = self.natoms()
errors = []
for key, length in results.items():
if length is None:
errors.append('%s is missing' % key)
elif length == 'N/A':
errors.append('%s has no length' % key)
elif length != natoms:
errors.append('%s has length %s, expected %s' %
(key, length, natoms))
if errors:
raise ValueError('Integrity check failed: ' + '; '.join(errors))
return results
[docs] def record(self):
self._moltype = newValue
[docs] def setRecord(self, newValue):
self._atom = newValue
[docs] def altloc(self):
return self._loc
[docs] def setAltloc(self, newValue):
self._loc = newValue
[docs] def icode(self):
return self._rescode
[docs] def setIcode(self, newValue):
self._rescode = newValue
[docs]class Molecule(Atom):
""" Molecule is a class that is used to describe molecules. It inherits
all of attributes from Atom. An example of a molecule is
a single protein, a single nucleic acid strand.
Class has several initialization options
Parameters
----------
args
optional integer : self._id
kwargs
optional keyword arguments
string filename (filename = 'hiv1_gag.pdb') : default = None
integet id (id=3) : default = 0
boolean debug (debug = True) : default = None
Returns
-------
system object
if called with string (or with filename kwarg) returns
an initialized system object with data read in using
file_io.read_pdb()
Examples
--------
>>> import sasmol.system as system
>>> molecule = system.Molecule(filename='hiv1_gag.pdb')
>>> molecule = system.Molecule()
>>> molecule = system.Molecule(id=7)
>>> molecule = system.Molecule(debug=True)
>>> molecule = system.Molecule('hiv1_gag.pdb')
>>> molecule = system.Molecule(filename='hiv1_gag.pdb', id=0, debug=False)
"""
def __init__(self, *args, **kwargs):
Atom.__init__(self, *args, **kwargs)
[docs] def fasta(self):
return self._fasta
[docs] def setFasta(self, newValue):
self._fasta = newValue
[docs] def setCharmm_type(self, newValue):
self._charmm_type = newValue
[docs] def charmm_type(self):
return self._charmm_type
[docs] def unitcell(self):
return self._unitcell
[docs] def setUnitcell(self, newValue):
self._unitcell = newValue
[docs] def rg(self):
return self._rg
[docs] def setRg(self, newValue):
self._rg = newValue
[docs] def pmi(self):
return self._pmi
[docs] def setPmi(self, newValue):
self._pmi = newValue
[docs] def minimum(self):
return self._minimum
[docs] def setMinimum(self, newValue):
self._minimum = newValue
[docs] def maximum(self):
return self._maximum
[docs] def setMaximum(self, newValue):
self._maximum = newValue
[docs] def one_letter_resname(self):
return self._one_letter_resname
[docs] def setOne_letter_resname(self, newValue):
self._one_letter_resname = newValue
[docs] def center_of_mass(self):
return self._center_of_mass
[docs] def setCenter_of_mass(self, newValue):
self._center_of_mass = newValue
[docs] def names(self):
return self._names
[docs] def setNames(self, newValue):
self._names = newValue
[docs] def resnames(self):
return self._resnames
[docs] def setResnames(self, newValue):
self._resnames = newValue
[docs] def resids(self):
return self._resids
[docs] def setResids(self, newValue):
self._resids = newValue
[docs] def chains(self):
return self._chains
[docs] def setChains(self, newValue):
self._chains = newValue
[docs] def segnames(self):
return self._segnames
[docs] def setSegnames(self, newValue):
self._segnames = newValue
[docs] def occupancies(self):
return self._occupancies
[docs] def setOccupancies(self, newValue):
self._occupancies = newValue
[docs] def betas(self):
return self._betas
[docs] def setBetas(self, newValue):
self._betas = newValue
[docs] def moltypes(self):
return self._moltypes
[docs] def setMoltypes(self, newValue):
self._moltypes = newValue
[docs] def elements(self):
return self._elements
[docs] def setElements(self, newValue):
self._elements = newValue
[docs] def names_mask(self):
return self._names_mask
[docs] def setNames_mask(self, newValue):
self._names_mask = newValue
[docs] def resnames_mask(self):
return self._resnames_mask
[docs] def setResnames_mask(self, newValue):
self._resnames_mask = newValue
[docs] def resids_mask(self):
return self._resids_mask
[docs] def setResids_mask(self, newValue):
self._resids_mask = newValue
[docs] def chains_mask(self):
return self._chains_mask
[docs] def setChains_mask(self, newValue):
self._chains_mask = newValue
[docs] def occupancies_mask(self):
return self._occupancies_mask
[docs] def setOccupancies_mask(self, newValue):
self._occupancies_mask = newValue
[docs] def betas_mask(self):
return self._betas_mask
[docs] def setBetas_mask(self, newValue):
self._betas_mask = newValue
[docs] def elements_mask(self):
return self._elements_mask
[docs] def setElements_mask(self, newValue):
self._elements_mask = newValue
[docs] def segnames_mask(self):
return self._segnames_mask
[docs] def setSegnames_mask(self, newValue):
self._segnames_mask = newValue
[docs] def number_of_names(self):
return self._number_of_names
[docs] def setNumber_of_names(self, newValue):
self._number_of_names = newValue
[docs] def number_of_resnames(self):
return self._number_of_resnames
[docs] def setNumber_of_resnames(self, newValue):
self._number_of_resnames = newValue
[docs] def number_of_resids(self):
return self._number_of_resids
[docs] def setNumber_of_resids(self, newValue):
self._number_of_resids = newValue
[docs] def number_of_chains(self):
return self._number_of_chains
[docs] def setNumber_of_chains(self, newValue):
self._number_of_chains = newValue
[docs] def number_of_segnames(self):
return self._number_of_segnames
[docs] def setNumber_of_segnames(self, newValue):
self._number_of_segnames = newValue
[docs] def number_of_occupancies(self):
return self._number_of_occupancies
[docs] def setNumber_of_occupancies(self, newValue):
self._number_of_occupancies = newValue
[docs] def number_of_betas(self):
return self._number_of_betas
[docs] def setNumber_of_betas(self, newValue):
self._number_of_betas = newValue
[docs] def number_of_moltypes(self):
return self._number_of_moltypes
[docs] def setNumber_of_moltypes(self, newValue):
self._number_of_moltypes = newValue
[docs] def number_of_elements(self):
return self._number_of_elements
[docs] def setNumber_of_elements(self, newValue):
self._number_of_elements = newValue
[docs] def residue_charge(self):
return self._residue_charge
[docs] def setResidue_charge(self, newValue):
self._residue_charge = newValue
[docs] def conect(self):
return self._conect
[docs] def setConect(self, newValue):
self._conect = newValue
[docs] def residue_flag(self):
try:
return self._residue_flag
except BaseException:
self._residue_flag = False
return self._residue_flag
[docs] def setResidue_flag(self, newValue):
self._residue_flag = newValue
[docs] def set_average_vdw(self):
import sasmol.operate as operate
operate.set_average_vdw(self)
return
[docs]class System(Atom):
""" System is a class that is used to aggregate all components. It inherits
all of attributes from Atom.
Class has several initialization options
Parameters
----------
args
optional integer : self._id
kwargs
optional keyword arguments
string filename (filename = 'hiv1_gag.pdb') : default = None
integet id (id=3) : default = 0
boolean debug (debug = True) : default = None
Returns
-------
system object
if called with string (or with filename kwarg) returns
an initialized system object with data read in using
file_io.read_pdb()
Examples
-------
>>> import sasmol.system as system
>>> molecule = system.Molecule(filename='hiv1_gag.pdb')
>>> molecule = system.Molecule()
>>> molecule = system.Molecule(id=7)
>>> molecule = system.Molecule(debug=True)
>>> molecule = system.Molecule('hiv1_gag.pdb')
>>> molecule = system.Molecule(filename='hiv1_gag.pdb', id=0, debug=False)
"""
def __init__(self, *args, **kwargs):
Atom.__init__(self, *args, **kwargs)
[docs]class Molecule_Maker(Atom):
"""
This class is used to define the minimum number of fields required to
use within sasmol to use read_pdb() and write_pdb() methods in file_io.
Default inputs are listed in the variables in __init__ and itemized below.
Scalar values are assigned to all atoms in the molecule. Per-atom
lists, tuples, or arrays may also be supplied, and must have one value
for each atom.
Once defined, attributes can be set using setters in the Atom class.
Class has several initialization options
For most atom fields, pass a scalar value to use the same value for
every atom, or pass a list, tuple, or numpy array with one value per
atom. Per-atom inputs must match natoms exactly. Partial lists are
not expanded or repeated. The coor argument remains a coordinate
array and is not interpreted as a per-atom constructor template. Use
validate_integrity() after later setter calls to raise a ValueError if
core per-atom fields no longer match natoms.
Parameters
----------
natoms
integer : number of atoms in the molecule
atom
string : name of ATOM keyword, typically either ATOM or HETATM
index
integer list : atom number
name
string : atom name
loc
string : alt loc
resname
string : residue name
chain
string : chain name
resid
integer list : residue number
rescode
string : residue code
coor
numpy float array : x, y, z coordinates
occupancy
string : occupancy value
beta
string : beta value
segname
string : segment name
element
string : element name
charge
string : element charge
kwargs
optional future arguments
Returns
-------
system object
Examples
-------
>>> import sasmol.system as system
>>> molecule = system.Molecule_Maker(2048)
>>> molecule = system.Molecule_Maker(2048, name='Ar')
>>> molecule = system.Molecule_Maker(2048, name='Ar', segname='ARG0')
>>> molecule = system.Molecule_Maker(2, name=['N', 'CA'])
>>> index = [x for x in range(340,1000)]
>>> molecule = system.Molecule_Maker(660, name='Ar', index=index)
>>> molecule.index()[0]
340
"""
def _expand_constructor_value(self, field_name, value, natoms):
if isinstance(value, (list, tuple, numpy.ndarray)):
length = len(value)
if length != natoms:
raise ValueError(
'%s must be a scalar value or have length %d' %
(field_name, natoms))
return list(value)
return [value for x in range(natoms)]
def _validate_constructor_length(self, field_name, value, natoms):
if not isinstance(value, (list, tuple, numpy.ndarray)):
raise ValueError('%s must have length %d' % (field_name, natoms))
if len(value) != natoms:
raise ValueError('%s must have length %d' % (field_name, natoms))
def __init__(
self,
natoms,
atom='ATOM',
index=None,
name='C',
loc=' ',
resname='DUM',
chain='A',
resid=None,
rescode=' ',
coor=None,
occupancy='0.00',
beta='0.00',
segname='DUM',
element='C',
charge=' ',
moltype='protein',
residue_flag=False,
**kwargs):
Atom.__init__(self)
self._natoms = natoms
self._number_of_atoms = natoms
self._atom = self._expand_constructor_value('atom', atom, natoms)
if index is not None:
self._validate_constructor_length('index', index, natoms)
self._index = index
else:
self._index = numpy.array(
[x + 1 for x in range(natoms)], numpy.int32)
self._name = self._expand_constructor_value('name', name, natoms)
self._loc = self._expand_constructor_value('loc', loc, natoms)
self._resname = self._expand_constructor_value(
'resname', resname, natoms)
self._chain = self._expand_constructor_value('chain', chain, natoms)
if resid is not None:
self._validate_constructor_length('resid', resid, natoms)
self._resid = resid
else:
self._resid = numpy.array(
[x + 1 for x in range(natoms)], numpy.int32)
self._rescode = self._expand_constructor_value(
'rescode', rescode, natoms)
if coor is not None:
self._coor = coor
else:
self._coor = numpy.zeros((1, natoms, 3), config.COORD_DTYPE)
self._occupancy = self._expand_constructor_value(
'occupancy', occupancy, natoms)
self._beta = self._expand_constructor_value('beta', beta, natoms)
self._charge = self._expand_constructor_value(
'charge', charge, natoms)
self._segname = self._expand_constructor_value(
'segname', segname, natoms)
self._element = self._expand_constructor_value(
'element', element, natoms)
self.setOriginal_index(self.index())
self.setOriginal_resid(self.resid())
self._residue_flag = self._expand_constructor_value(
'residue_flag', residue_flag, natoms)
self._moltype = self._expand_constructor_value(
'moltype', moltype, natoms)
self._total_mass = calculate.Calculate.calculate_mass(self)
[docs] def residue_flag(self):
return self._residue_flag