Source code for sasmol.system

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 formula(self): return self._formula
[docs] def setFormula(self, newValue): self._formula = 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 header(self): return self._header
[docs] def setHeader(self, newValue): self._header = newValue
[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