MMTK-2.7.9/0000755000076600000240000000000012156626561012723 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/0000755000076600000240000000000012156626561013430 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/CHANGELOG0000644000076600000240000007244012156626006014643 0ustar hinsenstaff000000000000002.7.8 --> 2.7.9 =============== Improvements: - Allow a Collection to be constructed from a generator - Make Amber file parser more tolerant to accept more mod_files - More pdb_alternative atom names: GROMACS-style OC1/OC2 in C-terminal peptide group - Compile C versions of extension modules if Cython is not installed Bug fixes: - Remove long delays when deallocating thread-based energy evaluators 2.7.7 --> 2.7.8 =============== Improvements: - Collections can be used in place of ChemicalObjects as arguments to HarmonicDistanceTerm() and Universe.distance(). - MMForceField and its components can be used directly in user code, in addition to serving as subclasses (e.g. for AmberForceField). Bug fixes: - Building the Spinx documentation now works as part of a build procedure, without requiring installation of MMTK. - No more test case failures on some Linux systems. 2.7.6 --> 2.7.7 =============== New features: - The Amber12SB force field is implemented in addition to Amber94 and Amber99. - New restraint term: HarmonicTrapForceField restrains the center of mass of an object to a fixed point in space using a spring term. - HarmonicDistanceRestraint now accepts non-atom objects, applying the restraint between their centers of mass. A distance restraint can optionally be treated like a chemical bond, suppressing any non-bonded interactions between the same pair of atoms. - The Amber parameter files have been updated to the versions contained in AmberTools 12. Removed features: - The DPMTA library and its MMTK interface code, implementing the multipole method for electrostatic interactions, has been removed. The MMTK interface has been unusable for years due to a typo that made it crash. No one ever complained, so it seems safe to assume that nobody uses it. DPMTA has become an obstacle to the distribution and packaging of MMTK because it doesn't have a clear licence. Improvements: - The visualization interface under MacOSX and Linux has been modified. When no PDBVIEWER is defined, it defaults to the system mechanism for opening PDB or VRML files, which is "open" under MacOSX and "xdg-open" under Linux. 2.7.5 --> 2.7.6 =============== Improvements: - Allow unicode strings in filenames. - New methods precedingResidue() and nextResidue() for peptide and nucleotide chains. - NormalModes: use scipy.linalg if available, don't use symeig which is no longer maintained. - Use PDB chain_id as name for nucleotide chains (just like for peptide chains) if segment_id is missing. - Make read-only trajectories picklable (the pickle contains just the file name). - Added database files for bromine and iodine. Bug fixes: - Strip spaces from chemical element names determined from a PDB file. - Universe attributes of type string could cause MMTK to hang when updated or removed. - Reading the trajectory variable box_size yielded a wrong result for trajectories with block_size > 1. 2.7.4 --> 2.7.5 =============== Improvements: - Adding items to a universe became significantly faster. This is most noticeable when opening a trajectory file for a big system. - New example script NormalModes/conformational_change_analysis.py - Added plotting to the example script NormalModes/calpha_modes.py - New subcommand "test" for setup.py runs all the test cases and works before installation. - The default visulization module was changed from VRML to VRML2. - General cleanup of the code and the installation process. There are many fewer warnings now. - Definition for He added to the database. Bug fixes: - NormalModes.effectiveMassAndForceConstant crashed due to a forgotten name change in a method call. - Writing DCD trajectories could crash on 64-bit machines. - Distance constraints were handled incorrectly in peptide chains connected by disulfide bridges. - Universes using the SPCE forcefield could not be read back from a trajectory file. - Creation of graphics representatios of Collection objects crashed. - Interators and minimizers could hang if an error occured during trajectory output. - Energy calculations on configurations containing NaN values crashed Python with a segment fault. 2.7.3 --> 2.7.4 =============== Improvements: - MMTK.Dynamics.Heater now accepts negative temperature gradients. - More comprehensive test suite. - New example script Visualization/vector_field_chimera.py Bug fixes: - Dipole calculation (method dipole) used to crash. - Methods boundingBox and boundingSphere used to crash. - Indexing a subset of a trajectory crashed with NumPy. (I hope this is the last NumPy-related bug!) - Replacing residues in a peptide chain could crash. - Corrections in the manual. 2.7.2 --> 2.7.3 =============== New features: - Trajectory actions in Cython - Trajectory generators (MD, minimization) run as a background task provide access to their current state in a thread-safe way. - New method atomIterator() complements atomList() and may become more efficient in future versions. New examples: - MDintegrator (a simple Velocity Verlet written in Cython) - Visualization/additional_objects.py Improvements: - Documentation updated, completed, and built with Sphinx. - Universe description string in trajectories made unambiguous. - Cython replaces Pyrex everywhere in MMTK. - Name conflicts in the database cause a warning to be printed. Bug fixes: - Memory leak in trajectory output removed. - The force contribution from bond terms was NaN at zero bond length. - Elastic Network Model force fields now work correctly with subsets. - A universe is marked as updated when the mass of one of its atoms changes. - Subspace.RigidMotionSubspace was wrong for linked rigid bodies. - Internal coordinate objects detected cyclic bond structures in cases where there are none. - Opening a trajectory whose universe contains named objects could fail. 2.7.1 --> 2.7.2 =============== Improvements: - ParticleProperty objects (including ParticleScalar, ParticleVector, and Configuration objects) have a new method selectAtoms. Its argument is a function that is called with the property of one particle and returns a boolean deciding whether or not that particle is part of the selection. - CalphaForceField has a new option (version=2) for the pair force constants. For reasonable input structures, it is equivalent to the default, version=1. It adds a threshold for small nearest-neighbour C-alpha distances: for distances less than 0.37 nm, the force constant does not decrease below its value at 0.37 nm. This ensures that the Hessian remains positive semi-definite for proteins with unrealistically short C-alpha distances. - MMTK.Subspae.Subspace has a new method complement() which returns the orthogonal complement. - Two new example scripts: - Miscellaneous/bad_contacts.py - Miscellaneous/two_models.py - Protein friction constants for Brownian Dynamics (module MMTK.ProteinFriction) are calculated with a threshold that assures a minimal value of 1000/ps. Without this threshold, friction constants could become negative for solvent-exposed loops. Bug fixes: - The VelocityVerletIntegrator could crash, or produce NaN results, for systems with distance constraints and a thermostat but no barostat. - All examples now work with recent releases of NumPy. - Installation could break on systems whose C compiler's sizeof returns another type than "int". - Trajectory actions of type "function" could crash integrators. 2.7.0 --> 2.7.1 =============== Improvements: - The identification of protonation states when creating molecules from a PDB file now takes into account the common situation of crystallographic PDB files that contain no hydrogens at all. In that case, the most frequent protonation is used for each individual residue. - Ewald summation works for non-orthogonal universes. Bug fixes: - Universe.distanceVector raised an exception when called with the optional configuration argument and for an InfiniteUniverse. - MMTK.Universe.contiguousObjectOffset could crash when input arrays do not use contiguous storage (very unlikely in normal MMTK usage) - NumPy compatibility problem: indexing trajectory variables did not work with indices coming from a NumPy array. - Removal of objects from a universe (e.g. by calling replaceResidue() on a biopolymer chain) could cause incorrect atom indices in the universe. - A force field containing more than one restraint term did not permit the retrieval of the evaluator parameters. 2.5.25 --> 2.7.0 ================ Improvements: - Better support of pickle and cPickle. Until now, MMTK's specialized pickler had to be used to pickle MMTK's chemical objects. From this version, the standard pickler and unpickler can be used as well, although MMTK's pickler is more efficient in terms of pickle size and memory use of unpickled objects. - AtomReference objects implement the rich comparison protocol. Bug fixes: - The use of force field terms implemented in Python with an integrator or minimizer crashed Python on some platforms. - Modifying a universe after initializing its velocities could lead to an infinite recursion. - Reading a ParticleTrajectory for atoms outside of the trajectory's universe now raises an exception. - Fixed PDB names of oxygens in phosphate groups of DNA - Added missing database file MMTK/Database/Groups/lysine_neutral_noh 2.5.24 --> 2.5.25 ================= Improvements: - General code revision - Conversion of docstrings to epytext - NumPy 1.2 compatibility fixes - AtomCluster objects have a default name (the empty string) - PDBMoleculeFactory guarantees that ADP tensors are exactly symmetric - New method Trajectory.flush() Bug fixes: - Dihedral restraints lead to a segmentation fault. - Dihedral restraints for angles close to pi/-pi caused instabilities. - Multiple assignments to the same attribute of a universe lead to all the objects being added to the universe, with the last assignment defininge the value of the attribute. For consistency with assignment semantics, an assignment now removes the previous value of the attribute from the universe. - Under certain conditions, nucleic acid chains in PDB files were wrongly identified as peptide chains. - Wrong import in MMTK.InternalCoordinates - Visualization of periodic universes with undefined atom positions didn't work. - Creation of AtomCluster objects from PDB files was sometimes incomplete (mission positions) - Opening a trajectory with undefined atom positions lead to a crash. 2.5.23 --> 2.5.24 ================= License change: MMTK is now distributed under the CeCILL-C license, which is an adaptation of the LGPL to French law. The previously used CeCILL license, similar to the GPL, was considered too restrictive. New features: - PDBConfiguration: new method createUnitCellUniverse() creates an empty universe that has the shape of the unit cell from the PDB file. - PDBConfiguration: new method asuToUnitCell() applies crystallographic symmetry operations to the molecules in the asymmetric unit. - PDBConfiguration: accept file objects as well as file names. Improvements: - The residue name list in Scientific.IO.PDB is now synchronized with the one in MMTK, meaning that all residues defined as such in MMTK will also be recognized when reading PDB files. Bug fixes: - NormalModes objects handle temperature=None correctly when calculating fluctuations. - PDBMoleculeFactory now works with PDB configurations that have no symmetry information. - Opening a trajectory for reading could crash depending on the force field used in the system. - Proteins with disulphide bonds between chains had an inconsistent internal representation that would cause a crash when writing to a PDB file. - More alternative PDB atom names for ribose. - Fixed installation with NumPy under Windows. 2.5.22 --> 2.5.23 ================= Bug fixes: - NumPy compatibility fixes. - Residue name conflicts were not handled correctly in PDBMoleculeFactory. - VMD support in MMTK.Visualization didn't work under Windows. - ParallelepipedicPeriodicUniverse.largestDistance() returned wrong values. - Cell parameter handling in ParallelepipedicPeriodicUniverse was fixed. - LennardJonesForceField would not initialize correctly in rare circumstances. - Fixed setup.py to work with ScientificPython < 2.7.8. Improvements: - Better handling of add-on forcefields in trajectory descriptions. - Support for atom-dependent force fields. - Atom and residue filters were added to PDBMoleculeFactory. 2.5.21 --> 2.5.22 ================= Bug fixes: - The Amber atom type of HD in N-terminal proline was wrong. - Make CYM a known amino acid residue (useable in PDB files). - Universe.randomPoint() returned points shifted by half the box size. - When opening a PDB file in a PDB viewer fails under Windows, the error message given mentioned VRML instead of PDB. Improvements: - Hydrogen placement extended to more situations. - Universe objects have a few new methods introduced for CDTK compatibility. 2.5.20 --> 2.5.21 ================= Incompatible changes: - PDB.PDBConfiguration stores atom positions and temperature factors (isotropic and anisotropic) in internal units (nm, nm**2) rather than in PDB units (Ang, Ang**2). Scripts that access these parameters directly must be updated! New features: - New module PDBMoleculeFactory permits to work with molecule objects that represent exactly the contents of a PDB file. Improvements: - The universe method contiguousObjectOffset behaves more reasonably for Collection arguments. It doesn't try to make the whole Collection contiguous, which for large Collections is impossible anyway. - New method Universe.configurationDifference. - PDB.PDBConfiguration: new attributes basis and reciprocal_basis describe the crystallographic unit cell and the reciprocal lattice cell. - New method ParticleTensor.trace. - Additional pressure units kbar, MPa and GPa in module Units. Bug fixes: - Hierarchical molecule definitions (using groups) in a MoleculeFactory lacked the bonds from the subgroups. - HarmonicForceField now works correctly with periodic boundary conditions. - NumPy compatibility fixes. 2.5.19 --> 2.5.20 ================= New features: - ParallelepipedicPeriodicUniverse makes it possible to work with non-orthogonal universes that are periodic in three dimensions. At the moment, energy evaluation is slow (the nonbonded list iterates over all atom pairs), and the reciprocal part of the Ewald sum is not yet implemented. - When the method view() is called on a universe object that represents a periodic universe, the simulation box is shown as twelve lines representing its edges, if VMD has been defined as PDBVIEWER. - New force field for C-alpha models of proteins: AnisotropicNetworkForceField. This is very similar to DeformationForceField, the only difference being that the pair force constant as a function of the pair distance is a step function rather than an exponential. Modifications: - The method Universe.configuration() no longer folds the atomic coordinates into the central box of a periodic universe. Sometimes this is not desired, and when it it, it can always be achieved with an explicit call to Universe.foldCoordinatesIntoBox(). Improvements: - The methods that perform calculations on normal modes now all take an optional argument first_mode (default: 6) that defines the first mode to be taken into account. - Database entries for C- and N-terminal neutral lysine. - More unt tests. Bug fixes: - When Configuration objects were copied, the cell parameters were not copied. The copy contained the cell parameters of the current configuration of the universe instead. - Nonbonded list generation for periodic universes could in rare cases miss atom pairs inside the cutoff. - Multi-threaded energy evaluation got stuck for more than two threads. - CompoundForceField objects could not be pickled. - Dihedral energy terms were wrong for phase offsets other than 0 and pi. Note: this has no incidence on AMBER and OPLS force fields because they use only offsets of 0 and pi. - MoleculeFactory.retrieveMolecule had a left-over print statement. - TrajectoryViewer crashed when single-precision trajectories had a discontinuous time axis. - The charges for the Amber force field in the database files for nucleotides were lacking the last digit. - NumPy compatibility fixes. 2.5.18 --> 2.5.19 ================= New features: - PDB.PDBConfiguration contains crystallographic information read from the PDB file: the edge lengths of the cell (a, b, c, in units of nm), the unit cell angles (alpha, beta, gamma, in units of radians), the space group (space_group, a string), the crystallographic symmetry transformations corresponding to the space group (cs_transformations, a list of transformation objects), and the non-crystallographic symmetry transformations (ncs_transformations, a list of transformation objects). The transformations are transformed to Cartesian coordinates, so they can be applied directly to the atom positions. This information is available only if ScientificPython >= 2.7.5 is installed. Improvements: - Improved NumPy compatibility. - Use the symeig package for normal modes if it is installed. - universe.configuration() has been accelerated. The speedup can be very important (a factor of ten), but in most situations it is rather modest. - AMBER atom types are handled as case-sensitive, as is done by the AMBER program. - Better identification of protonation states in PDB files that contain hydrogens. 2.5.17 --> 2.5.18 ================= Bug fixes: - The module lapack_mmtk that consisted of CLAPACK2 code and caused compilation problems with recent GCC versions was removed. Instead, CLAPACK3 routines from Numeric/numpy are used. They are also faster, but use more working memory. 2.5.16 --> 2.5.17 ================= Bug fixes: - PartitionedCollection.selectShell() used wrong distance criteria - Fixed iteration over force constant matrices > 5 GB - Improved NumPy compatibility - AtomCluster objects were not correctly written to trajectory files - Compilation/linking issues under Fedora 6 (GCC 4.1) Improvements: - New atom definitions and updates to existing ones to include scattering lengths - Methods setBondAttributes and clearBondAttributes available also on complexes (and thus proteins) New features: - Method anisotropicFluctuations for normal modes. 2.5.15 --> 2.5.16 ================= Bug fixes: - Removed the dependency on Scientific 2.7.2 introduced into Random.py. MMTK 2.5.16 should again work with ScientificPython > 2.5. - Updated README 2.5.14 --> 2.5.15 ================= New features: - New method energyEvaluatorTerms() on universes. It returns a data structure (dictionary of dictionaries) that contains all the force field parameters for the given universe. It can be used to use MMTK as an energy term generator for other simulation programs. - There is a first draft for an interface to the CCPN data model. Since the data model itself is still evolving, changes to the MMTK interface are very likely as well. - New method GroupOfAtoms.normalizingTransformation. Improvements: - MMTK should now work with NumPy as an alternative to Numeric, though this combination still requires a lot of testing. See README for installation instructions. Bug fixes: - Minor bug fixes in MMTK.ProteinFriction and MMTK.NormalModes.BrownianModes. 2.5.13 --> 2.5.14 ================= Bug fixes: - MMTK.NormalModes.Core.reduceToRange() didn't work correctly for VibrationalModes. - MMTK.Subspace.RigidMotionSubspace could fail for some object collections. - Various 64-bit issues. - Crashes at termination of Python when running under Python 2.5. - Molecule creation through MoleculeFactory didn't work. Improvements: - MMTK.PDB.PDBConfiguration accepts optional arguments for model and alternate code. - The C modules are adapted to the new 64-bit features in Python 2.5. - Installation on Linux makes CLAPACK code compile correctly with GCC 4. - Improved handling of PDB residues with non-unique atom names. - Trajectory files contain a netCDF attributes "Conventions" that identifies them as MMTK trajectories. 2.5.12 --> 2.5.13 ================= License change: MMTK is now distributed under the CeCILL license. See LICENSE (English) or LICENCE (French) for the license text, or www.cecill.info for more information. In short, CeCILL is an OpenSource license based on French law. It is compatible with the GPL, so the change from GPL to CeCILL should not make a difference in practice. 2.5.11 --> 2.5.12 ================= New features: - MMTK.Geometry.Box has new method cornerPoints() and implements intersectWith() for box-box-intersections. 2.5.10 --> 2.5.11 ================= Bug fixes: - Some pickle files from pre-2.5 could not be read any more. - MMTK_energy_term.c wouldn't compile with GCC 4, it has been regenerated with a corrected Pyrex 2.5.9 --> 2.5.10 ================ Bug fixes: - MMTK.Proteins.PeptideChain.replaceResidue() did not update the atom list of the universe. - Compiler errors with gcc 4.0 Changes: - Module "Collection" is now named "Collections" to avoid confusion with the class "Collection". Application code should not import that module directly, so there should be no compatibility issues. 2.5.8 --> 2.5.9 =============== Improvements: - Subspace.RigidMotionSubspace avoids the costly SVD calculation by constructing an orthonormal basis immediately. - Src/lapack_subset.c should compile with gcc 4 now. - MMTK.Proteins.PeptideChain.replaceResidue() could only be used prior to the inclusion of the chain into a protein. Now it can be used later as well, permitting mutations. 2.5.7 --> 2.5.8 =============== Bug fix: - Bugs concerning storing certain force field terms in trajectory descriptions. - Universe descriptions didn't contain distance constraints in AtomCluster objects. 2.5.6 --> 2.5.7 =============== New features: - Support for force field development in Python 2.5.5 --> 2.5.6 =============== New features: - Support for force field development with Pyrex. Bug fixes: - LAPACK error -13 with certain normal mode and subspace operations fixed. - MMTK.Proteins.Residue.phiPsi() could fail for proline residues. 2.5.4 --> 2.5.5 =============== New features: - New module MoleculeFactory permits the construction of molecules from Python code, without requiring database entries. - Any object can be written to an XML file that uses CML conventions as much as possible, the method to call is writeXML(). These files can also be read in using XML.XMLMoleculeFactory. Bug fixes: - A few C modules crashed on Opteron systems running Linux in 64 bit mode. 2.5.3 --> 2.5.4 =============== New features: - Reorganization of the normal mode module into a package providing vibrational, energetic, and Brownian normal modes. - New module MMTK.ProteinFriction to go with Brownian modes. 2.4.2 --> 2.5.3 =============== New features: - Amber 99 force field - New module MMTK.InternalCoordinates - New methods phiAngle, psiAngle, chiAngle in MMTK.Proteins.Residue 2.4.1 --> 2.4.2 =============== Bug fixes: - TrajectorySet with a list of tuples as argument would crash. 2.4 --> 2.4.1 ============= Bug fixes: - A bug in Dynamics.RotationRemover could destabilize an MD run. - Visualization of periodic universes could produce strange results (some molecules out of the box) Portability: - MMTK now compiles without errors under Windows using Microsoft's VisualStudio. It should still compile with MinGW, of course. 2.2 --> 2.4 =========== New features: - Interface to PyMOL. Running an MMTK script from within PyMOL automatically makes all visualization use PyMOL. For more explicit control, see the module MMTK.PyMol. Bug fixes: - Rotation removal during simulation had some wrong formulas. - Compound force fields could not be used in trajectory generation. - Wrong forces from Ewald reciprocal when run with more than one thread. 2.1.3 --> 2.2 ============= New module: - MolecularSurface, a reimplementation by Peter McCluskey, which does not have the license restrictions of the NSC code. The NSC-based MolecularSurface (which according to Peter is faster and more accurate) will remain available as an add-on that replaces the standard module. New features: - New method "rotateAroundAxis()" in class GroupOfAtoms. - New protein model "polar_oldopls", which defines polar hydrogens according to the old OPLS conventions. The model "polar" follows the new OPLS conventions (i.e. includes the hydrogens on aromatic rings). - TrajectorySet class permits the treatment of a sequence of trajectory files as a single trajectory. Bug fixes: - Nonbonded list updated could crash for non-periodic universes under certain (rare) circumstances. - Method "normalizeConfiguration" sometimes performed a reflection in addition to rotation and translation. - Multi-threaded Ewald summation tended to hang after a couple of energy evaluations. 2.1.2 --> 2.1.3 =============== New features: - New method "contiguousObjectConfiguration" for universes. - Three independent scale factors (bonded, Lennard-Jones, electrostatic) can be specified for the Amber force field, all are 1. by default. Improvements: - Snapshots can now specify arbitrary energy terms and pressure (pressure should have worked before as well, but didn't). Bug fixes: - The Amber94 charges for the N-terminal versions of THR, ARG, HIP, and LYS in the MMTK database were wrong, due to mistakes in early versions of the Amber parameter files from which the database entries were generated. For more information see http://www.amber.ucsf.edu/amber/bugfixes41.html and then bugfix.76 and bugfix.91. - molecule.view() failed for non-protein molecules (this bug was introduced in 2.1.2). - RigidBodyTrajectory generation crashed due to a typo. - ParticleTrajectory reads from trajectories with block_size > 1 could return wrong data. 2.1.1 --> 2.1.2 =============== New features: - The TrajectoryViewer tool (in Tools/TrajectoryViewer) was extended by normal mode projections for proteins. - The structure of trajectory files can be influenced by specifying a "block size", which can be used to optimize I/O performance on very large files. - Reading of single-atom and rigid-body trajectories has been optimized. Bug fixes: - The functions MMTK.Biopolymers.defineAminoAcidResidue and MMTK.Biopolymers.defineNucleicAcidResidue didn't work. - The method MMTK.Collection.GroupOfAtoms.findTransformationAsQuaternion returned randomly one of the two equivalent quaternions that desribe the rigid-body rotation. This doesn't matter for most purposes, but it creates non-continuous quaternion trajectories when applied to a sequence of configurations. The method has been changed to return the quaternion that has a positive real part. - Rigid-body trajectories (in module Trajectory) were wrong in certain circumstances. 2.1.0 --> 2.1.1 =============== Modifications: - Improved load balance for shared memory parallelization. - The united-atom models for amino acids were changed from Amber 91 conventions to OPLS by removing the fake "lone pair" atoms. Note that there was never proper support for lone pair atoms, so this model wasn't functional, and therefore nothing should be broken by this change. Now the united-atom model is actually fully usable together with the OPLS force field. (Thanks to Krzysztof Murzyn for fixing this!) Additions: - Basic MPI support. Only energy evaluation has been parallelized, using a data-replication approach. All processors execute the same code and cooperate only during energy evaluation. See the Example MPI/md.py for more information. - New force fields: HarmonicForceField, CalphaForceField, SPCEForceField. The first two are designed for proteins, the last one is only for water. 2.0 --> 2.1.0 ============= Bug fixes: - Addition of force fields didn't work when one of the terms was already a compound force field. - Memory allocation bug in DCD output. - Restarting NPT dynamics simulations didn't work due to a reported universe mismatch. This was caused by the different box size. - MMTK.DCD.writeDCDPDB produced a PDB file with non-contiguous molecules for periodic universes. - In the electrostatic options for the Amber force field, the "screened" option was misinterpreted as "ewald". Modifications: - The method objectList() for collections and universes now takes an optional argument specifying a class; only the objects corresponding to this class are returned. This permits a simple identification of proteins etc. - The universe description that is stored in a trajectory now contains the force field and environment objects (thermostats and barostats). - Optional name argument for C evaluator objects for bonded interactions. Additions: - Thread support. - Module MMTK.ForceFields.Restraints. - New function MMTK.DCD.writeVelocityDCDPDB for exporting velocities to velocity DCD files. - New method pairIndices for nonbonded list objects. 2.0b1 --> 2.0 ============= Bug fixes: - Subtraction of a ParticleVector from a Configuration returned a ParticleVector (now a Configuration). - The Amber atom types for two hydrogens in proline were wrong. Additions: - group definitions for neutral versions of aspartic acid, glutamic acid, and lysine (provided by Alan Grossfield) MMTK-2.7.9/Doc/conf.py0000644000076600000240000001625011662461637014735 0ustar hinsenstaff00000000000000# -*- coding: utf-8 -*- # # MMTK User Guide documentation build configuration file, created by # sphinx-quickstart on Thu Oct 21 18:14:14 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) #sys.path.insert(0, os.path.abspath('../Documents/mmtk_with_pi2_branch/Src')) #sys.path.insert(0, os.path.abspath('../Documents/mmtk_with_pi2_branch')) import MMTK # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] autodoc_default_flags=['members', 'show-inheritance'] autoclass_content="both" # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'MMTK User Guide' copyright = u'2010, Konrad Hinsen' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = MMTK.__version__ # The full version, including alpha/beta/rc tags. release = MMTK.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'MMTKUserGuidedoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'MMTKUserGuide.tex', u'MMTK User Guide Documentation', u'Konrad Hinsen', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'mmtkuserguide', u'MMTK User Guide Documentation', [u'Konrad Hinsen'], 1) ] MMTK-2.7.9/Doc/Examples/0000755000076600000240000000000012156626561015206 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/DNA/0000755000076600000240000000000012156626561015610 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/DNA/construction.py.rst0000644000076600000240000000027012041514572021511 0ustar hinsenstaff00000000000000:orphan: Constructing a DNA strand with a ligand ####################################### .. literalinclude:: ../../../Examples/DNA/construction.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Forcefield/0000755000076600000240000000000012156626561017250 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/ElectricField/0000755000076600000240000000000012156626561021746 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/ElectricField/Cython/0000755000076600000240000000000012156626561023212 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/ElectricField/Cython/ElectricField.py.rst0000644000076600000240000000032512041514572027060 0ustar hinsenstaff00000000000000:orphan: An electric field term (Python part) #################################### .. literalinclude:: ../../../../../Examples/Forcefield/ElectricField/Cython/ElectricField.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Forcefield/ElectricField/Cython/MMTK_electric_field.pyx.rst0000644000076600000240000000033412041514572030337 0ustar hinsenstaff00000000000000:orphan: An electric field term (Cython part) #################################### .. literalinclude:: ../../../../../Examples/Forcefield/ElectricField/Cython/MMTK_electric_field.pyx :language: cython :linenos: MMTK-2.7.9/Doc/Examples/Forcefield/ElectricField/Python/0000755000076600000240000000000012156626561023227 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/ElectricField/Python/ElectricField.py.rst0000644000076600000240000000035712041514572027102 0ustar hinsenstaff00000000000000:orphan: An electric field term implemented in pure Python ################################################# .. literalinclude:: ../../../../../Examples/Forcefield/ElectricField/Python/ElectricField.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Forcefield/HarmonicOscillator/0000755000076600000240000000000012156626561023044 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/HarmonicOscillator/Cython/0000755000076600000240000000000012156626561024310 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py.rst0000644000076600000240000000037712041514572031477 0ustar hinsenstaff00000000000000:orphan: An efficient harmonic oscillator term (Python part) ################################################### .. literalinclude:: ../../../../../Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.pyx.rst0000644000076600000240000000040412041514572032531 0ustar hinsenstaff00000000000000:orphan: An efficient harmonic oscillator term (Cython part) ################################################### .. literalinclude:: ../../../../../Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.pyx :language: cython :linenos: MMTK-2.7.9/Doc/Examples/Forcefield/HarmonicOscillator/Python/0000755000076600000240000000000012156626561024325 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py.rst0000644000076600000240000000035312041514572031506 0ustar hinsenstaff00000000000000:orphan: A harmonic oscillator term in pure Python ######################################### .. literalinclude:: ../../../../../Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/LangevinDynamics/0000755000076600000240000000000012156626561020441 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/LangevinDynamics/LangevinDynamics.py.rst0000644000076600000240000000033512041514572025045 0ustar hinsenstaff00000000000000:orphan: An integrator for Langevin dynamics (Python part) ################################################# .. literalinclude:: ../../../Examples/LangevinDynamics/LangevinDynamics.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/LangevinDynamics/MMTK_langevin.c.rst0000644000076600000240000000031212041514572024032 0ustar hinsenstaff00000000000000:orphan: An integrator for Langevin dynamics (C part) ############################################ .. literalinclude:: ../../../Examples/LangevinDynamics/MMTK_langevin.c :language: c :linenos: MMTK-2.7.9/Doc/Examples/MDIntegrator/0000755000076600000240000000000012156626561017545 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/MDIntegrator/VelocityVerlet.pyx.rst0000644000076600000240000000027412041514572024070 0ustar hinsenstaff00000000000000:orphan: A simple Velocity Verlet integrator ################################### .. literalinclude:: ../../../Examples/MDIntegrator/VelocityVerlet.pyx :language: cython :linenos: MMTK-2.7.9/Doc/Examples/Miscellaneous/0000755000076600000240000000000012156626561020011 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Miscellaneous/charge_fit.py.rst0000644000076600000240000000035012041514572023252 0ustar hinsenstaff00000000000000:orphan: Fitting point charges to an electrostatic potential surface ########################################################### .. literalinclude:: ../../../Examples/Miscellaneous/charge_fit.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Miscellaneous/construct_from_pdb.py.rst0000644000076600000240000000032212041514572025052 0ustar hinsenstaff00000000000000:orphan: Building a complete universe from a PDB file ############################################ .. literalinclude:: ../../../Examples/Miscellaneous/construct_from_pdb.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Miscellaneous/lattice.py.rst0000644000076600000240000000024712041514572022611 0ustar hinsenstaff00000000000000:orphan: Place molecules on a lattice ############################ .. literalinclude:: ../../../Examples/Miscellaneous/lattice.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Miscellaneous/vector_field.py.rst0000644000076600000240000000037012041514572023626 0ustar hinsenstaff00000000000000:orphan: Vector fields for analysis and visualization of collective motions ################################################################## .. literalinclude:: ../../../Examples/Miscellaneous/vector_field.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/MolecularDynamics/0000755000076600000240000000000012156626561020621 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/MolecularDynamics/argon.py.rst0000644000076600000240000000024412041514572023077 0ustar hinsenstaff00000000000000:orphan: Simulation of liquid argon ########################## .. literalinclude:: ../../../Examples/MolecularDynamics/argon.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/MolecularDynamics/protein.py.rst0000644000076600000240000000024012041514572023445 0ustar hinsenstaff00000000000000:orphan: Simulation of a protein ####################### .. literalinclude:: ../../../Examples/MolecularDynamics/protein.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/MolecularDynamics/restart.py.rst0000644000076600000240000000026412041514572023457 0ustar hinsenstaff00000000000000:orphan: Restarting the protein simulation ################################# .. literalinclude:: ../../../Examples/MolecularDynamics/restart.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/MolecularDynamics/solvation.py.rst0000644000076600000240000000026212041514572024007 0ustar hinsenstaff00000000000000:orphan: Solvation of a protein in water ############################### .. literalinclude:: ../../../Examples/MolecularDynamics/solvation.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/MonteCarlo/0000755000076600000240000000000012156626561017251 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/MonteCarlo/backbone.py.rst0000644000076600000240000000031212041514572022161 0ustar hinsenstaff00000000000000:orphan: Sampling a backbone-only configuration ensemble ############################################### .. literalinclude:: ../../../Examples/MonteCarlo/backbone.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/MPI/0000755000076600000240000000000012156626561015633 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/MPI/md.py.rst0000644000076600000240000000023212041514572017400 0ustar hinsenstaff00000000000000:orphan: Protein solvation in parallel ############################# .. literalinclude:: ../../../Examples/MPI/md.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/NormalModes/0000755000076600000240000000000012156626561017426 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/NormalModes/calpha_modes.py.rst0000644000076600000240000000033212041514572023213 0ustar hinsenstaff00000000000000:orphan: Slow normal modes of a protein using a C-alpha model #################################################### .. literalinclude:: ../../../Examples/NormalModes/calpha_modes.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/NormalModes/constrained_modes.py.rst0000644000076600000240000000034112041514572024274 0ustar hinsenstaff00000000000000:orphan: Normal modes of a protein using a rigid-residue model ##################################################### .. literalinclude:: ../../../Examples/NormalModes/constrained_modes.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/NormalModes/harmonic_force_field.py.rst0000644000076600000240000000035212041514572024717 0ustar hinsenstaff00000000000000:orphan: Normal modes of a protein using a simplified force field ######################################################## .. literalinclude:: ../../../Examples/NormalModes/harmonic_force_field.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/NormalModes/modes.py.rst0000644000076600000240000000023512041514572021705 0ustar hinsenstaff00000000000000:orphan: Normal modes of a protein ######################### .. literalinclude:: ../../../Examples/NormalModes/modes.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Proteins/0000755000076600000240000000000012156626561017011 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Proteins/analysis.py.rst0000644000076600000240000000025312041514572022004 0ustar hinsenstaff00000000000000:orphan: Comparing protein configurations ################################ .. literalinclude:: ../../../Examples/Proteins/analysis.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Proteins/construction.py.rst0000644000076600000240000000030712041514572022713 0ustar hinsenstaff00000000000000:orphan: Protein construction beyond the simple cases ############################################ .. literalinclude:: ../../../Examples/Proteins/construction.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/0000755000076600000240000000000012156626562017645 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Trajectories/calpha_trajectory.py.rst0000644000076600000240000000034712041514572024516 0ustar hinsenstaff00000000000000:orphan: Extract a C-alpha trajectory from an all-atom trajectory ######################################################## .. literalinclude:: ../../../Examples/Trajectories/calpha_trajectory.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/dcd_export.py.rst0000644000076600000240000000026412041514572023151 0ustar hinsenstaff00000000000000:orphan: Convert a trajectory to DCD format ################################## .. literalinclude:: ../../../Examples/Trajectories/dcd_export.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/dcd_import.py.rst0000644000076600000240000000027012041514572023137 0ustar hinsenstaff00000000000000:orphan: Importing a trajectory in DCD format #################################### .. literalinclude:: ../../../Examples/Trajectories/dcd_import.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/fluctuations.py.rst0000644000076600000240000000032412041514572023533 0ustar hinsenstaff00000000000000:orphan: Calculating atomic fluctuations from a trajectory ################################################# .. literalinclude:: ../../../Examples/Trajectories/fluctuations.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/pdb_export.py.rst0000644000076600000240000000032412041514572023161 0ustar hinsenstaff00000000000000:orphan: Converting a trajectory to a sequence of PDB files ################################################## .. literalinclude:: ../../../Examples/Trajectories/pdb_export.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/snapshot.py.rst0000644000076600000240000000026612041514572022657 0ustar hinsenstaff00000000000000:orphan: Assembling a trajectory step by step #################################### .. literalinclude:: ../../../Examples/Trajectories/snapshot.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/trajectory_average.py.rst0000644000076600000240000000032412041514572024673 0ustar hinsenstaff00000000000000:orphan: Compute an average structure from a trajectory ############################################## .. literalinclude:: ../../../Examples/Trajectories/trajectory_average.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/trajectory_extraction.py.rst0000644000076600000240000000027712041514572025450 0ustar hinsenstaff00000000000000:orphan: Extract a subset from a trajectory ################################## .. literalinclude:: ../../../Examples/Trajectories/trajectory_extraction.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Trajectories/view_trajectory.py.rst0000644000076600000240000000026712041514572024241 0ustar hinsenstaff00000000000000:orphan: Show an animation of a trajectory ################################# .. literalinclude:: ../../../Examples/Trajectories/view_trajectory.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Visualization/0000755000076600000240000000000012156626562020050 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/Examples/Visualization/additional_objects.py.rst0000644000076600000240000000035612041514572025044 0ustar hinsenstaff00000000000000:orphan: Adding custom graphics to a molecular system visualization ########################################################## .. literalinclude:: ../../../Examples/Visualization/additional_objects.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Visualization/graphics_data.py.rst0000644000076600000240000000032612041514572024011 0ustar hinsenstaff00000000000000:orphan: Extracting numerical values from graphics objects ################################################# .. literalinclude:: ../../../Examples/Visualization/graphics_data.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Visualization/vector_field_chimera.py.rst0000644000076600000240000000036112041514572025354 0ustar hinsenstaff00000000000000:orphan: Vector field visualization of a normal mode (using Chimera) ########################################################### .. literalinclude:: ../../../Examples/Visualization/vector_field_chimera.py :language: python :linenos: MMTK-2.7.9/Doc/Examples/Visualization/vector_field_vmd.py.rst0000644000076600000240000000034512041514572024534 0ustar hinsenstaff00000000000000:orphan: Vector field visualization of a normal mode (using VMD) ####################################################### .. literalinclude:: ../../../Examples/Visualization/vector_field_vmd.py :language: python :linenos: MMTK-2.7.9/Doc/examples.rst0000644000076600000240000002044112117632051015765 0ustar hinsenstaff00000000000000.. _Examples: .. |C_alpha| replace:: C\ :sub:`α` Code Examples ############# One of the best ways to learn how to use a new tool is to look at examples. The examples given in this manual were adapted from real-life MMTK applications. They are also contained in the MMTK distribution (directory "Examples") for direct use and modification. The example molecules, system sizes, parameters, etc., were chosen to reduce execution time as much as possible, in order to enable you to run the examples interactively step by step to see how they work. If you plan to modify an example program for your own use, don't forget to check all parameters carefully to make sure that you obtain reasonable results. .. _Example-MolecularDynamics: - Molecular Dynamics examples - The program :doc:`argon.py ` contains a simulation of liquid argon at constant temperature and pressure. - The program :doc:`protein.py ` contains a simulation of a small (very small) protein in vacuum. - The program :doc:`restart.py ` shows how the simulation started in :doc:`protein.py ` can be continued. - The program :doc:`solvation.py ` contains the solvation of a protein by water molecules. .. _Example-MonteCarlo: - Monte-Carlo examples - The program :doc:`backbone.py <../Examples/MonteCarlo/backbone.py>` generates an ensemble of backbone configuration (C-alpha atoms only) for a protein. .. _Example-Trajectories: - Trajectory examples - The program :doc:`snapshot.py <../Examples/Trajectories/snapshot.py>` shows how a trajectory can be built up step by step from arbitrary data. - The program :doc:`dcd_import.py <../Examples/Trajectories/dcd_import.py>` converts a trajectory in DCD format (used by the programs CHARMM, X-Plor, and NAMD) to MMTK's format. - The program :doc:`dcd_export.py <../Examples/Trajectories/dcd_export.py>` converts an MMTK trajectory to DCD format (used by the programs CHARMM, X-Plor, and NAMD). - The program :doc:`pdb_export.py <../Examples/Trajectories/pdb_export.py>` converts an MMTK trajectory to a sequence of PDB files. - The program :doc:`trajectory_average.py <../Examples/Trajectories/trajectory_average.py>` calculates an average structure from a trajectory. - The program :doc:`trajectory_extraction.py <../Examples/Trajectories/trajectory_extraction.py>` reads a trajectory and writes a new one containing only a subset of the original universe. - The program :doc:`view_trajectory.py <../Examples/Trajectories/view_trajectory.py>` shows an animation of a trajectory, provided that an external molecule viewer with animation is available. - The program :doc:`calpha_trajectory.py <../Examples/Trajectories/calpha_trajectory.py>` shows how a much smaller |C_alpha|-only trajectory can be extracted from a trajectory containing one or more proteins. - The program :doc:`fluctuations.py <../Examples/Trajectories/fluctuations.py>` shows how to calculate atomic fluctuations from a trajectory file. .. _Example-NormalModes: - Normal mode examples - The program :doc:`modes.py <../Examples/NormalModes/modes.py>` contains a standard normal mode calculation for a small protein. - The program :doc:`constrained_modes.py <../Examples/NormalModes/constrained_modes.py>` contains a normal mode calculation for a small protein using a model in which each amino acid residue is rigid. - The program :doc:`calpha_modes.py <../Examples/NormalModes/calpha_modes.py>` contains a normal mode calculation for a mid-size protein using a |C_alpha| model and an elastic network model. - The program :doc:`harmonic_force_field.py <../Examples/NormalModes/harmonic_force_field.py>` contains a normal mode calculation for a protein using a detailed but still simple harmonic force field. .. _Example-Proteins: - Protein examples - The program :doc:`construction.py <../Examples/Proteins/construction.py>` shows some more complex examples of protein construction from PDB files. - The program :doc:`analysis.py <../Examples/Proteins/analysis.py>` demonstrates a few analysis techniques for comparing protein conformations. .. _Example-DNA: - DNA examples - The program :doc:`construction.py <../Examples/DNA/construction.py>` contains the construction of a DNA strand with a ligand. .. _Example-Forcefield: - Forcefield examples - Electric field term - A pure Python implementation (rather slow in general, but tolerable for a simple term like this one) is given in :doc:`Python/ElectricField.py <../Examples/Forcefield/ElectricField/Python/ElectricField.py>`. - A more efficient implementation has the evaluation code written in Cython (:doc:`Cython/MMTK_electric_field.pyx <../Examples/Forcefield/ElectricField/Cython/MMTK_electric_field.pyx>`) while the bookkeeping part remains in Python (:doc:`Cython/ElectricField.py <../Examples/Forcefield/ElectricField/Cython/ElectricField.py>`). - Harmonic oscillator term - A pure Python implementation (rather slow in general, but tolerable for a simple term like this one) is given in :doc:`Python/HarmonicOscillatorFF.py <../Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py>`. - A more efficient implementation has the evaluation code written in Cython (:doc:`Cython/MMTK_harmonic_oscillator.pyx <../Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.pyx>`) while the bookkeeping part remains in Python (:doc:`Cython/HarmonicOscillatorFF.py <../Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py>`). .. _Example-MPI: - MPI examples (parallelization) - The program :doc:`md.py <../Examples/MPI/md.py>` contains a parallelized version of :doc:`solvation.py <../Examples/MolecularDynamics/solvation.py>`. .. _Example-MDIntegrator: - Molecular Dynamics integrators - The program :doc:`md.py <../Examples/MDIntegrator/VelocityVerlet.pyx>` illustrates how Molecular Dynamics integrators can be implemented in Cython. .. _Example-LangevinDynamics: - Langevin dynamics integrator - The programs :doc:`LangevinDynamics.py <../Examples/LangevinDynamics/LangevinDynamics.py>` and :doc:`MMTK_langevinmodule.c <../Examples/LangevinDynamics/MMTK_langevin.c>` implement a simple integrator for Langevin dynamics. It is meant as an example of how to write integrators etc. in C, but of course it can also be used directly. .. _Example-Visualization: - Visualization examples - The program :doc:`additional_objects.py <../Examples/Visualization/additional_objects.py>` describes the addition of custom graphics objects to the representation of a molecular system. - The program :doc:`vector_field_chimera.py <../Examples/Visualization/vector_field_chimera.py>` shows how to create a vector-field visualization of a normal mode using the graphics program Chimera for visualization. The program :doc:`vector_field_vmd.py <../Examples/Visualization/vector_field_vmd.py>` does the same but uses the graphics program VMD. - The program :doc:`graphics_data.py <../Examples/Visualization/graphics_data.py>` shows how to use a fake graphics module to extract numerical values from a vector field object. .. _Example-Miscellaneous: - Micellaneous examples - The example :doc:`charge_fit.py <../Examples/Miscellaneous/charge_fit.py>` demonstrates fitting point charges to an electrostatic potential energy surface. - The program :doc:`construct_from_pdb.py <../Examples/Miscellaneous/construct_from_pdb.py>` shows how a universe can be built from a PDB file in such a way that the internal atom ordering is compatible. This is important for exchanging data with other programs. - The program :doc:`lattice.py <../Examples/Miscellaneous/lattice.py>` constructs molecules placed on a lattice. - The program :doc:`vector_field.py <../Examples/Miscellaneous/vector_field.py>` shows how vector fields can be used in the analysis and visualization of collective motions. MMTK-2.7.9/Doc/glossary.rst0000644000076600000240000000330411662461637016027 0ustar hinsenstaff00000000000000Glossary ======== .. glossary:: Abstract base class A :term:`Base Class` that is not directly usable by itself, but which defines the common properties of several subclasses. Example: the class :class:`MMTK.ChemicalObjects.ChemicalObject` is an abstract base class which defines the common properties of its subclasses :class:`MMTK.ChemicalObjects.Atom`, :class:`MMTK.ChemicalObjects.Group`, :class:`MMTK.ChemicalObjects.Molecule`, :class:`MMTK.ChemicaObjects.Complex`, and :class:`MMTK.ChemicalObjects.AtomCluster`. A :term:`Mix-in class` is a special kind of abstract base class. Base class A class from which another class inherits. In most cases, the inheriting class is a specialization of the base class. For example, the class :class:`MMTK.ChemicalObjects.Molecule` is a base class of :class:`MMTK.Proteins.PeptideChain`, because peptide chains are special molecules. Another common application is the :term:`Abstract base class`. Mix-in class A class that is used as a :term:`Base class` in other classes with the sole intention of providing methods that are common to these classes. Mix-in classes cannot be used to create instances. They are a special kind of :term:`Abstract base class`. Example: class :class:`MMTK.Collections.GroupOfAtoms`. Subclass A class that has another class as its :term:`Base class`. The subclass is usually a specialization of the base class, and can use all of the methods defined in the base class. Example: class :class:`MMTK.Proteins.Residue` is a subclass of :class:`MMTK.ChemicalObjects.Group`. MMTK-2.7.9/Doc/HTML/0000755000076600000240000000000012156626562014175 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/.buildinfo0000644000076600000240000000034612013143456016140 0ustar hinsenstaff00000000000000# Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. config: e2db96d3c837982ac06a36b828a4b432 tags: fbb0d17656682115ca4d033fb2f83ba1 MMTK-2.7.9/Doc/HTML/_modules/0000755000076600000240000000000012156626562016004 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_modules/index.html0000644000076600000240000001344412013143456017773 0ustar hinsenstaff00000000000000 Overview: module code — MMTK User Guide 2.7.7 documentation MMTK-2.7.9/Doc/HTML/_modules/MMTK/0000755000076600000240000000000012156626562016554 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_modules/MMTK/Biopolymers.html0000644000076600000240000013340012013143454021731 0ustar hinsenstaff00000000000000 MMTK.Biopolymers — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Biopolymers

# This module implements the base classes for proteins and
# nucleic acid chains.
#
# Written by Konrad Hinsen
#

"""
Base classes for proteins and nucleic acids
"""

from MMTK import Bonds, ChemicalObjects, Collections, Database, PDB
import Scientific.IO.PDB

[docs]class Residue(ChemicalObjects.Group): """ Base class for aminoacid and nucleic acid residues """ def __setstate__(self, state): self.__dict__.update(state) try: self.model = self.hydrogens except AttributeError: pass self._init() def _init(self): # construct PDB map and alternative names type = self.type if not hasattr(type, 'pdbmap'): pdb_dict = {} type.pdbmap = [(type.symbol, pdb_dict)] offset = 0 for g in type.groups: for name, atom in g.pdbmap[0][1].items(): pdb_dict[name] = Database.AtomReference(atom.number + \ offset) offset = offset + len(g.atoms) if not hasattr(type, 'pdb_alternative'): alt_dict = {} for g in type.groups: if hasattr(g, 'pdb_alternative'): for key, value in g.pdb_alternative.items(): alt_dict[key] = value setattr(type, 'pdb_alternative', alt_dict) def _residueThroughLinkAtom(self, link_atom): if link_atom is None: return None levels = 0 obj = link_atom while obj is not None and obj != self: obj = obj.parent levels += 1 for atom in link_atom.bondedTo(): if atom.parent is not link_atom.parent: obj = atom while levels > 0: obj = obj.parent levels -= 1 return obj return None
[docs] def precedingResidue(self): """ :returns the preceding residue in the chain """ return self._residueThroughLinkAtom(self.chain_links[0])
[docs] def nextResidue(self): """ :returns the next residue in the chain """ return self._residueThroughLinkAtom(self.chain_links[1])
[docs]class ResidueChain(ChemicalObjects.Molecule): """ Chain of residues Base class for peptide chains and nucleotide chains """ is_chain = 1 def _setupChain(self, circular, properties, conf): self.atoms = [] self.bonds = [] for g in self.groups: self.atoms.extend(g.atoms) self.bonds.extend(g.bonds) for i in range(len(self.groups)-1): link1 = self.groups[i].chain_links[1] link2 = self.groups[i+1].chain_links[0] self.bonds.append(Bonds.Bond((link1, link2))) if circular: link1 = self.groups[-1].chain_links[1] link2 = self.groups[0].chain_links[0] self.bonds.append(Bonds.Bond((link1, link2))) self.bonds = Bonds.BondList(self.bonds) self.parent = None self.type = None self.configurations = {} try: self.name = properties['name'] del properties['name'] except KeyError: self.name = '' if conf: conf.applyTo(self) try: self.translateTo(properties['position']) del properties['position'] except KeyError: pass self.addProperties(properties) def __len__(self): return len(self.groups) def __getitem__(self, item): return self.groups[item] def __setitem__(self, item, value): self.replaceResidue(self.groups[item], value)
[docs] def residuesOfType(self, *types): """ :param types: residue type codes :type types: str :returns: a collection that contains all residues whose type (residue code) is contained in types :rtype: :class:`~MMTK.Collections.Collection` """ types = [t.lower() for t in types] rlist = [r for r in self.groups if r.type.symbol.lower() in types] return Collections.Collection(rlist)
[docs] def residues(self): """ :returns: a collection containing all residues :rtype: :class:`~MMTK.Collections.Collection` """ return Collections.Collection(self.groups)
[docs] def sequence(self): """ :returns: the residue sequence as a list of residue codes :rtype: list of str """ return [r.type.symbol.lower() for r in self.groups] # # Find the full name of a residue #
def _fullName(residue): residue = residue.lower() try: return _aa_residue_names[residue] except KeyError: return _na_residue_names[residue] _aa_residue_names = {'ala': 'alanine', 'a': 'alanine', 'arg': 'arginine', 'r': 'arginine', 'asn': 'asparagine', 'n': 'asparagine', 'asp': 'aspartic_acid', 'd': 'aspartic_acid', 'cys': 'cysteine', 'c': 'cysteine', 'gln': 'glutamine', 'q': 'glutamine', 'glu': 'glutamic_acid', 'e': 'glutamic_acid', 'gly': 'glycine', 'g': 'glycine', 'his': 'histidine', 'h': 'histidine', 'ile': 'isoleucine', 'i': 'isoleucine', 'leu': 'leucine', 'l': 'leucine', 'lys': 'lysine', 'k': 'lysine', 'met': 'methionine', 'm': 'methionine', 'phe': 'phenylalanine', 'f': 'phenylalanine', 'pro': 'proline', 'p': 'proline', 'ser': 'serine', 's': 'serine', 'thr': 'threonine', 't': 'threonine', 'trp': 'tryptophan', 'w': 'tryptophan', 'tyr': 'tyrosine', 'y': 'tyrosine', 'val': 'valine', 'v': 'valine', 'cyx': 'cystine_ss', 'cym': 'cysteine_with_negative_charge', 'app': 'aspartic_acid_neutral', 'glp': 'glutamic_acid_neutral', 'hsd': 'histidine_deltah', 'hse': 'histidine_epsilonh', 'hsp': 'histidine_plus', 'hid': 'histidine_deltah', 'hie': 'histidine_epsilonh', 'hip': 'histidine_plus', 'lyp': 'lysine_neutral', 'ace': 'ace_beginning', 'nme': 'nmethyl', 'nhe': 'amide', } _na_residue_names = {'da': 'd-adenosine', 'da5': 'd-adenosine_5ter', 'da3': 'd-adenosine_3ter', 'dan': 'd-adenosine_5ter_3ter', 'dc': 'd-cytosine', 'dc5': 'd-cytosine_5ter', 'dc3': 'd-cytosine_3ter', 'dcn': 'd-cytosine_5ter_3ter', 'dg': 'd-guanosine', 'dg5': 'd-guanosine_5ter', 'dg3': 'd-guanosine_3ter', 'dgn': 'd-guanosine_5ter_3ter', 'dt': 'd-thymine', 'dt5': 'd-thymine_5ter', 'dt3': 'd-thymine_3ter', 'dtn': 'd-thymine_5ter_3ter', 'ra': 'r-adenosine', 'ra5': 'r-adenosine_5ter', 'ra3': 'r-adenosine_3ter', 'ran': 'r-adenosine_5ter_3ter', 'rc': 'r-cytosine', 'rc5': 'r-cytosine_5ter', 'rc3': 'r-cytosine_3ter', 'rcn': 'r-cytosine_5ter_3ter', 'rg': 'r-guanosine', 'rg5': 'r-guanosine_5ter', 'rg3': 'r-guanosine_3ter', 'rgn': 'r-guanosine_5ter_3ter', 'ru': 'r-uracil', 'ru5': 'r-uracil_5ter', 'ru3': 'r-uracil_3ter', 'run': 'r-uracil_5ter_3ter', } for code in _aa_residue_names: if len(code) == 3: Scientific.IO.PDB.defineAminoAcidResidue(code) for code in _na_residue_names: Scientific.IO.PDB.defineNucleicAcidResidue(code) # # Add a residue to the residue list #
[docs]def defineAminoAcidResidue(full_name, code3, code1 = None): """ Add a non-standard amino acid residue to the internal residue table. Once added to the residue table, the new residue can be used like any of the standard residues in the creation of peptide chains. :param full_name: the name of the group definition in the chemical database :type full_name: str :param code3: the three-letter residue code :type code3: str :param code1: an optionel one-letter residue code :type code1: str """ code3 = code3.lower() if code1 is not None: code1 = code1.lower() if _aa_residue_names.has_key(code3) or _na_residue_names.has_key(code3): raise ValueError("residue name " + code3 + " already used") if _aa_residue_names.has_key(code1): raise ValueError("residue name " + code1 + " already used") _aa_residue_names[code3] = full_name if code1 is not None: _aa_residue_names[code1] = full_name Scientific.IO.PDB.defineAminoAcidResidue(code3)
[docs]def defineNucleicAcidResidue(full_name, code): """ Add a non-standard nucleic acid residue to the internal residue table. Once added to the residue table, the new residue can be used like any of the standard residues in the creation of nucleotide chains. :param full_name: the name of the group definition in the chemical database :type full_name: str :param code: the residue code :type code3: str """ code = code.lower() if _aa_residue_names.has_key(code) or _na_residue_names.has_key(code): raise ValueError("residue name " + code + " already used") _na_residue_names[code] = full_name Scientific.IO.PDB.defineNucleicAcidResidue(code)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Bonds.html0000644000076600000240000017114412013143454020501 0ustar hinsenstaff00000000000000 MMTK.Bonds — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Bonds

# This module implements classes that represent lists of bonds,
# bond angles, and dihedral angles.
#
# Written by Konrad Hinsen
#

"""
Bonds, bond lists, bond angle lists, and dihedral angle lists

The classes in this module are normally not used directly from
client code. They are used by the classes in ChemicalObjects and
ForceField.
"""

__docformat__ = 'restructuredtext'

from MMTK import Database, Utility
from copy import copy

#
# Bonds
#
# Bond objects are created from the specifications in the data base.
#
[docs]class Bond(object): """ Chemical bond A bond links two atoms (attributes a1 and a2) """ def __init__(self, blueprint, memo = None): if type(blueprint) is type(()): self.a1 = blueprint[0] self.a2 = blueprint[1] else: self.a1 = Database.instantiate(blueprint.a1, memo) self.a2 = Database.instantiate(blueprint.a2, memo) if Utility.uniqueID(self.a2) < Utility.uniqueID(self.a1): self.a1, self.a2 = self.a2, self.a1 Utility.uniqueID.registerObject(self) blueprintclass = Database.BlueprintBond __safe_for_unpickling__ = 1 __had_initargs__ = 1 def __repr__(self): return 'Bond(' + `self.a1` + ', ' + `self.a2` + ')' __str__ = __repr__
[docs] def hasAtom(self, a): """ :param a: an atom :type a: :class:`~MMTK.ChemicalObjects.Atom` :returns: True if a participates in the bond """ return a is self.a1 or a is self.a2
[docs] def otherAtom(self, a): """ :param a: an atom involved in the bond :type a: :class:`~MMTK.ChemicalObjects.Atom` :returns: the atom at the other end of the bond :rtype: :class:`~MMTK.ChemicalObjects.Atom` :raises ValueError: if a does not belong to the bond """ if a is self.a1: return self.a2 elif a is self.a2: return self.a1 else: raise ValueError('atom not in bond')
def _graphics(self, conf, distance_fn, model, module, options): objects = [] if model == 'ball_and_stick' or model == 'vdw_and_stick': radius = options.get('stick_radius', 0.01) elif model == 'wireframe': radius = None else: return [] p1 = self.a1.position(conf) p2 = self.a2.position(conf) if p1 is not None and p2 is not None: bond_vector = 0.5*distance_fn(self.a1, self.a2, conf) cut = bond_vector != 0.5*(p2-p1) color1 = self.a1._atomColor(self.a1, options) color2 = self.a2._atomColor(self.a2, options) material1 = module.EmissiveMaterial(color1) material2 = module.EmissiveMaterial(color2) if color1 == color2 and not cut: if radius is None: objects.append(module.Line(p1, p2, material = material1)) else: objects.append(module.Cylinder(p1, p2, radius, material = material1)) else: if radius is None: objects.append(module.Line(p1, p1+bond_vector, material = material1)) objects.append(module.Line(p2, p2-bond_vector, material = material2)) else: objects.append(module.Cylinder(p1, p1+bond_vector, radius, material = material1)) objects.append(module.Cylinder(p2, p2-bond_vector, radius, material = material2)) return objects
Database.registerInstanceClass(Bond.blueprintclass, Bond) # # Bond angles #
[docs]class BondAngle(object): """ Bond angle A bond angle is the angle between two bonds that share a common atom. It is defined by two bond objects (attributes b1 and b2) and an atom object (the common atom, attribute ca). """ def __init__(self, b1, b2, ca): self.b1 = b1 # bond 1 self.b2 = b2 # bond 2 self.ca = ca # common atom if Utility.uniqueID(self.b2) < Utility.uniqueID(self.b1): self.b1, self.b2 = self.b2, self.b1 self.a1 = b1.otherAtom(ca) self.a2 = b2.otherAtom(ca) Utility.uniqueID.registerObject(self) def __repr__(self): return 'BondAngle(' + `self.a1` +',' + `self.ca` +','+ `self.a2` +')'
[docs] def otherBond(self, bond): """ :param bond: a bond involved in the angle :type bond: :class:`~MMTK.Bonds.Bond` :returns: the other bond involved in the angle :rtype: :class:`~MMTK.Bonds.Bond` :raises ValueError: if bond does not belong to the angle """ if bond is self.b1: return self.b2 elif bond is self.b2: return self.b1 else: raise ValueError('bond not in bond angle') # # Dihedral angles #
[docs]class DihedralAngle(object): """ Dihedral angle A dihedral angle is the angle between two planes that are defined by BondAngle objects (attributes ba1 and ba2) and their common bond (attribute cb). There are proper dihedrals (four atoms linked by three bonds in sequence) and improper dihedrals (a central atom linked to three surrounding atoms by three bonds). The boolean attribute improper indicates whether a dihedral is an improper one. """ def __init__(self, ba1, ba2, cb): self.ba1 = ba1 # bond angle 1 self.ba2 = ba2 # bond angle 2 # cb is the common bond, i.e. the central bond for a proper dihedral if Utility.uniqueID(self.ba2) < Utility.uniqueID(self.ba1): self.ba1, self.ba2 = self.ba2, self.ba1 self.improper = (self.ba1.ca is self.ba2.ca) if self.improper: self.b1 = self.ba1.otherBond(cb) self.b2 = cb self.b3 = self.ba2.otherBond(cb) self.a1 = self.ba1.ca # central atom self.a2 = self.b1.otherAtom(self.ba1.ca) self.a3 = cb.otherAtom(self.ba1.ca) self.a4 = self.b3.otherAtom(self.ba2.ca) # each improper dihedral will come in three versions; # identify an arbitrary unique one for constructing the list self.normalized = Utility.uniqueID(cb) < Utility.uniqueID(self.b1)\ and Utility.uniqueID(cb) < \ Utility.uniqueID(self.b3) else: self.b1 = self.ba1.otherBond(cb) self.b2 = cb self.b3 = self.ba2.otherBond(cb) self.a1 = self.b1.otherAtom(self.ba1.ca) self.a2 = self.ba1.ca # these two are self.a3 = self.ba2.ca # on the common bond self.a4 = self.b3.otherAtom(self.ba2.ca) self.normalized = self.a1 is not self.a4 def __repr__(self): if self.improper: return 'ImproperDihedral(' + `self.a1` +','+ `self.a2` +','+ \ `self.a3` +','+ `self.a4` +')' else: return 'Dihedral(' + `self.a1` +','+ `self.a2` +','+ \ `self.a3` +','+ `self.a4` +')' # # Bond lists # # Bond lists can create bond angle and dihedral angle lists # for themselves. These are cached for efficiency. The cached # copy is deleted whenever the bond list is modified. #
class BondList(list): def __init__(self, initlist=None): list.__init__(self, initlist) self._clearCache() __safe_for_unpickling__ = 1 def _clearCache(self): self.bond_angles = None self.dihedral_angles = None def __getinitargs__(self): return (None,) def __getstate__(self): self._clearCache() return self.__dict__ def __setitem__(self, i, item): list.__setitem__(self, i, item) self._clearCache() def __setslice__(self, i, j, data): list.__setslice__(self, i, j, data) self._clearCache() def __delslice__(self, i, j): list.__delslice__(self, i, j) self._clearCache() def append(self, item): list.append(self, item) self._clearCache() def extend(self, data): list.extend(self, data) self._clearCache() def insert(self, i, item): list.insert(i, item) self._clearCache() def remove(self, item): list.remove(self, item) self._clearCache() def bondAngles(self): """ :returns: a list of all bond angles that can be formed from the bonds in the list :rtype: :class:`~MMTK.Bonds.BondAngleList` """ if self.bond_angles is None: # find all atoms that are involved in more than one bond bonds = {} atom_list = [] for bond in self: try: bl = bonds[bond.a1] except KeyError: bl = [] bonds[bond.a1] = bl atom_list.append(bond.a1) bl.append(bond) try: bl = bonds[bond.a2] except KeyError: bl = [] bonds[bond.a2] = bl atom_list.append(bond.a2) bl.append(bond) angles = [] for atom in atom_list: # each pair of bonds at the same atom defines a bond angle for p in Utility.pairs(bonds[atom]): angles.append(BondAngle(p[0], p[1], atom)) self.bond_angles = BondAngleList(angles) return self.bond_angles def dihedralAngles(self): """ :returns: a list of all dihedral angles that can be formed from the bonds in the list :rtype: :class:`~MMTK.Bonds.DihedralAngleList` """ if self.dihedral_angles is None: self.dihedral_angles = self.bondAngles().dihedralAngles() return self.dihedral_angles def bondedTo(self, atom): """ :param atom: an atom :type atom: :class:`~MMTK.ChemicalObjects.Atom` :returns: a list of all atoms to which the given atom is bound :rtype: list """ return [b.otherAtom(atom) for b in self if b.hasAtom(atom)] def bondsOf(self, atom): """ :param atom: an atom :type atom: :class:`~MMTK.ChemicalObjects.Atom` :returns: a list of all bonds in which the given atom is involved :rtype: list """ return [b for b in self if b.hasAtom(atom)] def setBondAttributes(self): """ Create an attribute in all atoms of all bonds that points to the bonded atom. :note: Bond attributes are only set temporarily for optimization purposes. """ for b in self: b.a1.setBondAttribute(b.a2) b.a2.setBondAttribute(b.a1) # # Bond angle lists #
[docs]class BondAngleList(object): """ Bond angle list """ def __init__(self, angles): self.data = angles def __repr__(self): return repr(self.data) def __len__(self): return len(self.data) def __getitem__(self, i): return self.data[i]
[docs] def dihedralAngles(self): """ :returns: a list of all dihedral angles that can be formed from the bond angles in the list :rtype: :class:`~MMTK.Bonds.DihedralAngleList` """ # find all bonds that are involved in more than one bond angle angles = {} bond_list = [] for angle in self.data: try: al = angles[angle.b1] except KeyError: al = [] angles[angle.b1] = al bond_list.append(angle.b1) al.append(angle) try: al = angles[angle.b2] except KeyError: al = [] angles[angle.b2] = al bond_list.append(angle.b2) al.append(angle) dihedrals = [] for bond in bond_list: # each pair of bond angles with a common bond defines a dihedral for p in Utility.pairs(angles[bond]): d = DihedralAngle(p[0], p[1], bond) if d.normalized: dihedrals.append(d) return DihedralAngleList(dihedrals) # # Dihedral angle lists #
[docs]class DihedralAngleList(object): """ Dihedral angle list """ def __init__(self, dihedrals): self.data = dihedrals def __repr__(self): return repr(self.data) def __len__(self): return len(self.data) def __getitem__(self, i): return self.data[i] # # Dummy bond length database, for constraints without a force field #
class DummyBondLengthDatabase(object): def __init__(self, universe): pass def bondLength(self, bond): return None def bondAngle(self, angle): return None
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ChargeFit.html0000644000076600000240000013133512013143454021266 0ustar hinsenstaff00000000000000 MMTK.ChargeFit — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ChargeFit

# This module contains code for charge fitting.
#
# Written by Konrad Hinsen
#

"""
Fit of point chages to an electrostatic potential surface

This module implements a numerically stable method (based on
Singular Value Decomposition) to fit point charges to values of an
electrostatic potential surface. Two types of constraints are
avaiable: a constraint on the total charge of the system or a subset
of the system, and constraints that force the charges of several atoms
to be equal. There is also a utility function that selects suitable
evaluation points for the electrostatic potential surface. For the
potential evaluation itself, some quantum chemistry program is needed.

The charge fitting method is described in:

  | K. Hinsen and B. Roux,
  | An accurate potential for simulating proton transfer in acetylacetone,
  | J. Comp. Chem. 18, 1997: 368

See also Examples/Miscellaneous/charge_fit.py.
"""

__docformat__ = 'restructuredtext'

from MMTK import Random, Units, Utility
from Scientific.Geometry import Vector
from Scientific import N
from Scientific import LA


[docs]class ChargeFit(object): """ Fit of point charges to an electrostatic potential surface A ChargeFit object acts like a dictionary that stores the fitted charge value for each atom in the system. """ def __init__(self, system, points, constraints = None): """ :param system: any chemical object (usually a molecule) :param points: a list of point/potential pairs (a vector for the evaluation point, a number for the potential), or a dictionary whose keys are Configuration objects and whose values are lists of point/potential pairs. The latter case permits combined fits for several conformations of the system. :param constraints: an optional list of constraint objects (:class:`~MMTK.ChargeFit.TotalChargeConstraint` and/or :class:`~MMTK.ChargeFit.EqualityConstraint` objects). If the constraints are inconsistent, a warning is printed and the result will satisfy the constraints only in a least-squares sense. """ self.atoms = system.atomList() if type(points) != type({}): points = {None: points} if constraints is not None: constraints = ChargeConstraintSet(self.atoms, constraints) npoints = sum([len(v) for v in points.values()]) natoms = len(self.atoms) if npoints < natoms: raise ValueError("Not enough data points for fit") m = N.zeros((npoints, natoms), N.Float) phi = N.zeros((npoints,), N.Float) i = 0 for conf, pointlist in points.items(): for r, p in pointlist: for j in range(natoms): m[i, j] = 1./(r-self.atoms[j].position(conf)).length() phi[i] = p i = i + 1 m = m*Units.electrostatic_energy m_test = m phi_test = phi if constraints is not None: phi -= N.dot(m, constraints.bi_c) m = N.dot(m, constraints.p) c_rank = constraints.rank else: c_rank = 0 u, s, vt = LA.singular_value_decomposition(m) s_test = s[:len(s)-c_rank] cutoff = 1.e-10*N.maximum.reduce(s_test) nonzero = N.repeat(s_test, N.not_equal(s_test, 0.)) self.rank = len(nonzero) self.condition = N.maximum.reduce(nonzero) / \ N.minimum.reduce(nonzero) self.effective_rank = N.add.reduce(N.greater(s, cutoff)) if self.effective_rank < self.rank: self.effective_condition = N.maximum.reduce(nonzero) / cutoff else: self.effective_condition = self.condition if self.effective_rank < natoms-c_rank: Utility.warning('Not all charges are uniquely determined' + ' by the available data') for i in range(natoms): if s[i] > cutoff: s[i] = 1./s[i] else: s[i] = 0. q = N.dot(N.transpose(vt), s*N.dot(N.transpose(u)[:natoms, :], phi)) if constraints is not None: q = constraints.bi_c + N.dot(constraints.p, q) deviation = N.dot(m_test, q)-phi_test self.rms_error = N.sqrt(N.dot(deviation, deviation)) deviation = N.fabs(deviation/phi_test) self.relative_rms_error = N.sqrt(N.dot(deviation, deviation)) self.charges = {} for i in range(natoms): self.charges[self.atoms[i]] = q[i] def __getitem__(self, item): return self.charges[item]
[docs]class TotalChargeConstraint(object): """ Constraint on the total system charge To be used with :class:`~MMTK.ChargeFit.ChargeFit` """ def __init__(self, system, charge): """ :param system: any chamical object whose total charge is to be constrained :param charge: the total charge value :type charge: number """ self.atoms = system.atomList() self.charge = charge def __len__(self): return 1 def setCoefficients(self, atoms, b, c, i): for a in self.atoms: j = atoms.index(a) b[i, j] = 1. c[i] = self.charge
[docs]class EqualityConstraint(object): """ Constraint forcing two charges to be equal To be used with :class:`~MMTK.ChargeFit.ChargeFit` Any atom may occur in more than one EqualityConstraint object, in order to keep the charges of more than two atoms equal. """ def __init__(self, atom1, atom2): """ :param atom1: the first atom in the equality relation :type atom1: :class:`~MMTK.ChemicalObjects.Atom` :param atom2: the second atom in the equality relation :type atom2: :class:`~MMTK.ChemicalObjects.Atom` """ self.a1 = atom1 self.a2 = atom2 def __len__(self): return 1 def setCoefficients(self, atoms, b, c, i): b[i, atoms.index(self.a1)] = 1. b[i, atoms.index(self.a2)] = -1. c[i] = 0.
class ChargeConstraintSet(object): def __init__(self, atoms, constraints): self.atoms = atoms natoms = len(self.atoms) nconst = sum([len(c) for c in constraints]) b = N.zeros((nconst, natoms), N.Float) c = N.zeros((nconst,), N.Float) i = 0 for cons in constraints: cons.setCoefficients(self.atoms, b, c, i) i = i + len(cons) u, s, vt = LA.singular_value_decomposition(b) self.rank = 0 for i in range(min(natoms, nconst)): if s[i] > 0.: self.rank = self.rank + 1 self.b = b self.bi = LA.generalized_inverse(b) self.p = N.identity(natoms)-N.dot(self.bi, self.b) self.c = c self.bi_c = N.dot(self.bi, c) c_test = N.dot(self.b, self.bi_c) if N.add.reduce((c_test-c)**2)/nconst > 1.e-12: Utility.warning("The charge constraints are inconsistent." " They will be applied as a least-squares" " condition.")
[docs]def evaluationPoints(system, n, smallest = 0.3, largest = 0.5): """ Generate points in space around a molecule that are suitable for potential evaluation in view of a subsequent charge fit. The points are chosen at random and uniformly in a shell around the system. :param system: the chemical object for which the charges will be fitted :param n: the number of evaluation points to be generated :param smallest: the smallest allowed distance of any evaluation point from any non-hydrogen atom :param largest: the largest allowed value for the distance from an evaluation point to the nearest atom :returns: a list of evaluation points :rtype: list of Scientific.Geometry.Vector """ atoms = system.atomList() p1, p2 = system.boundingBox() margin = Vector(largest, largest, largest) p1 -= margin p2 += margin a, b, c = tuple(p2-p1) offset = 0.5*Vector(a, b, c) points = [] while len(points) < n: p = p1 + Random.randomPointInBox(a, b, c) + offset m = 2*largest ok = 1 for atom in atoms: d = (p-atom.position()).length() m = min(m, d) if d < smallest and atom.symbol != 'H': ok = 0 if not ok: break if ok and m <= largest: points.append(p) return points
if __name__ == '__main__': from MMTK import * a1 = Atom('C', position=Vector(-0.05,0.,0.)) a2 = Atom('C', position=Vector( 0.05,0.,0.)) system = Collection(a1, a2) a1.charge = -0.75 a2.charge = 0.15 points = [] for r in evaluationPoints(system, 50): p = 0. for atom in system.atomList(): p = p + atom.charge/(r-atom.position()).length() points.append((r, p*Units.electrostatic_energy)) constraints = [TotalChargeConstraint(system, 0.)] constraints = [EqualityConstraint(a1, a2)] constraints = None f = ChargeFit(system, points, constraints) print f[a1], a1.charge print f[a2], a2.charge
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ChemicalObjects.html0000644000076600000240000063671112013143456022463 0ustar hinsenstaff00000000000000 MMTK.ChemicalObjects — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ChemicalObjects

# This module implements classes that represent atoms, molecules, and
# complexes. They are made as copies from blueprints in the database.
#
# Written by Konrad Hinsen
#

"""
Atoms, groups, molecules and similar objects
"""

__docformat__ = 'restructuredtext'

from MMTK import Bonds, Collections, Database, \
                 Units, Utility, Visualization
from Scientific.Geometry import Vector
from Scientific.Geometry import Objects3D
from Scientific import N
import copy

#
# The base class for all chemical objects.
#
[docs]class ChemicalObject(Collections.GroupOfAtoms, Visualization.Viewable): """ General chemical object This is an abstract base class that implements methods which are applicable to any chemical object (atom, molecule, etc.). """ def __init__(self, blueprint, memo): if isinstance(blueprint, basestring): blueprint = self.blueprintclass(blueprint) self.type = blueprint.type if hasattr(blueprint, 'name'): self.name = blueprint.name if memo is None: memo = {} memo[id(blueprint)] = self for attr in blueprint.instance: setattr(self, attr, Database.instantiate(getattr(blueprint, attr), memo)) is_chemical_object = True is_incomplete = False is_modified = False __safe_for_unpickling__ = True __had_initargs__ = True def __hash__(self): return id(self) def __getattr__(self, attr): if attr[:1] == '_' or attr[:3] == 'is_': raise AttributeError else: return getattr(self.type, attr) def isSubsetModel(self): return False def addProperties(self, properties): if properties: for item in properties.items(): if hasattr(self, item[0]) and item[0] != 'name': raise TypeError('attribute '+item[0]+' already defined') setattr(self, item[0], item[1]) def binaryProperty(self, properties, name, default): value = default try: value = properties[name] del properties[name] except KeyError: pass return value
[docs] def topLevelChemicalObject(self): """Returns the highest-level chemical object of which the current object is a part.""" if self.parent is None or not isChemicalObject(self.parent): return self else: return self.parent.topLevelChemicalObject()
[docs] def universe(self): """ :returns: the universe to which the object belongs, or None if the object does not belong to any universe :rtype: :class:`~MMTK.Universe.Universe` """ if self.parent is None: return None else: return self.parent.universe()
[docs] def bondedUnits(self): """ :returns: the largest subobjects which can contain bonds. There are no bonds between any of the subobjects in the list. :rtype: list """ return [self]
[docs] def atomList(self): """ :returns: a list containing all atoms in the object :rtype: list """ pass
[docs] def atomIterator(self): """ :returns: an iterator over all atoms in the object :rtype: iterator """ pass
[docs] def fullName(self): """ :returns: the full name of the object. The full name consists of the proper name of the object preceded by the full name of its parent separated by a dot. :rtype: str """ if self.parent is None or not isChemicalObject(self.parent): return self.name else: return self.parent.fullName() + '.' + self.name
[docs] def degreesOfFreedom(self): """ :returns: the number of degrees of freedom of the object :rtype: int """ return Collections.GroupOfAtoms.degreesOfFreedom(self) \ - self.numberOfDistanceConstraints()
[docs] def distanceConstraintList(self): """ :returns: the distance constraints of the object :rtype: list """ return []
def _distanceConstraintList(self): return [] def traverseBondTree(self, function = None): return []
[docs] def numberOfDistanceConstraints(self): """ :returns: the number of distance constraints of the object :rtype: int """ return 0
[docs] def setBondConstraints(self, universe=None): """ Sets distance constraints for all bonds. """ pass
[docs] def removeDistanceConstraints(self, universe=None): """ Removes all distance constraints. """ pass
[docs] def setRigidBodyConstraints(self, universe = None): """ Sets distance constraints that make the object fully rigid. """ if universe is None: universe = self.universe() if universe is None: import Universe universe = Universe.InfiniteUniverse() atoms = self.atomList() if len(atoms) > 1: self.addDistanceConstraint(atoms[0], atoms[1], universe.distance(atoms[0], atoms[1])) if len(atoms) > 2: self.addDistanceConstraint(atoms[0], atoms[2], universe.distance(atoms[0], atoms[2])) self.addDistanceConstraint(atoms[1], atoms[2], universe.distance(atoms[1], atoms[2])) if len(atoms) > 3: for a in atoms[3:]: self.addDistanceConstraint(atoms[0], a, universe.distance(atoms[0], a)) self.addDistanceConstraint(atoms[1], a, universe.distance(atoms[1], a)) self.addDistanceConstraint(atoms[2], a, universe.distance(atoms[2], a))
[docs] def getAtomProperty(self, atom, property): """ Retrieve atom properties from the chemical database. Note: the property is first looked up in the database entry for the object on which the method is called. If the lookup fails, the complete hierarchy from the atom to the top-level object is constructed and traversed starting from the top-level object until the property is found. This permits database entries for higher-level objects to override property definitions in its constituents. At the atom level, the property is retrieved from an attribute with the same name. This means that properties at the atom level can be defined both in the chemical database and for each atom individually by assignment to the attribute. :returns: the value of the specified property for the given atom from the chemical database. """
def writeXML(self, file, memo, toplevel=1): if self.type is None: name = 'm' + `memo['counter']` memo['counter'] = memo['counter'] + 1 memo[id(self)] = name atoms = copy.copy(self.atoms) bonds = copy.copy(self.bonds) for group in self.groups: group.writeXML(file, memo, 0) for atom in group.atoms: atoms.remove(atom) for bond in group.bonds: bonds.remove(bond) file.write('<molecule id="%s">\n' % name) for group in self.groups: file.write(' <molecule ref="%s" title="%s"/>\n' % (memo[id(group.type)], group.name)) if atoms: file.write(' <atomArray>\n') for atom in atoms: file.write(' <atom title="%s" elementType="%s"/>\n' % (atom.name, atom.type.symbol)) file.write(' </atomArray>\n') if bonds: file.write(' <bondArray>\n') for bond in bonds: a1n = self.relativeName(bond.a1) a2n = self.relativeName(bond.a2) file.write(' <bond atomRefs2="%s %s"/>\n' % (a1n, a2n)) file.write(' </bondArray>\n') file.write('</molecule>\n') else: name = self.name self.type.writeXML(file, memo) atom_names = self.type.getXMLAtomOrder() if toplevel: return ['<molecule ref="%s"/>' % name] else: return None def getXMLAtomOrder(self): if self.type is None: atoms = [] for group in self.groups: atoms.extend(group.getXMLAtomOrder()) for atom in self.atoms: if atom not in atoms: atoms.append(atom) else: atom_names = self.type.getXMLAtomOrder() atoms = [] for name in atom_names: parts = name.split(':') object = self for attr in parts: object = getattr(object, attr) atoms.append(object) return atoms def relativeName(self, object): name = '' while object is not None and object != self: name = object.name + ':' + name object = object.parent return name[:-1] def relativeName_unused(self, object): name = '' while object is not None and object != self: for attr, value in object.parent.__dict__.items(): if value is object: name = attr + ':' + name break object = object.parent return name[:-1] def description(self, index_map = None): tag = Utility.uniqueAttribute() s = self._description(tag, index_map, 1) for a in self.atomList(): delattr(a, tag) return s def __repr__(self): return self.__class__.__name__ + ' ' + self.fullName() __str__ = __repr__ def __copy__(self): return copy.deepcopy(self, {id(self.parent): None}) # Type check
[docs]def isChemicalObject(object): """ :returns: True if object is a chemical object """ return hasattr(object, 'is_chemical_object') # # The second base class for all composite chemical objects. #
[docs]class CompositeChemicalObject(object): """ Chemical object containing subobjects This is an abstract base class that implements methods which can be used with any composite chemical object, i.e. any chemical object that is not an atom. """ def __init__(self, properties): if properties.has_key('configuration'): conf = properties['configuration'] self.configurations[conf].applyTo(self) del properties['configuration'] elif hasattr(self, 'configurations') and \ self.configurations.has_key('default'): self.configurations['default'].applyTo(self) if properties.has_key('position'): self.translateTo(properties['position']) del properties['position'] self.addProperties(properties) def atomList(self): return self.atoms def atomIterator(self): return iter(self.atoms) def setPosition(self, atom, position): if atom.__class__ is Database.AtomReference: atom = self.atoms[atom.number] atom.setPosition(position) def setIndex(self, atom, index): if atom.__class__ is Database.AtomReference: atom = self.atoms[atom.number] atom.setIndex(index) def getAtom(self, atom): if atom.__class__ is Database.AtomReference: atom = self.atoms[atom.number] return atom def getReference(self, atom): if atom.__class__ is Database.AtomReference: return atom return Database.AtomReference(self.atoms.index(atom)) def getAtomProperty(self, atom, property, levels = None): try: return atom.__dict__[property] except KeyError: try: return getattr(self, property)[self.getReference(atom)] except (AttributeError, KeyError): if levels is None: object = atom levels = [] while object != self: levels.append(object) object = object.parent if not levels: raise KeyError('Property ' + property + ' not defined for ', `atom`) return levels[-1].getAtomProperty(atom, property, levels[:-1]) def deleteUndefinedAtoms(self): delete = [a for a in self.atoms if a.position() is None] for a in delete: a.delete() def _deleteAtom(self, atom): self.atoms.remove(atom) self.is_modified = True self.type = None if self.parent is not None: self.parent._deleteAtom(atom) def distanceConstraintList(self): dc = self._distanceConstraintList() for o in self._subunits(): dc = dc + o._distanceConstraintList() return dc def _distanceConstraintList(self): try: return self.distance_constraints except AttributeError: return [] def numberOfDistanceConstraints(self): n = len(self._distanceConstraintList()) + \ sum(len(o._distanceConstraintList()) for o in self._subunits()) return n def setBondConstraints(self, universe=None): if universe is None: universe = self.universe() bond_database = universe.bondLengthDatabase() for o in self.bondedUnits(): o._setBondConstraints(universe, bond_database) def _setBondConstraints(self, universe, bond_database): self.distance_constraints = [] for bond in self.bonds: d = bond_database.bondLength(bond) if d is None: d = universe.distance(bond.a1, bond.a2) self.addDistanceConstraint(bond.a1, bond.a2, d) def addDistanceConstraint(self, atom1, atom2, distance): try: self.distance_constraints.append((atom1, atom2, distance)) except AttributeError: self.distance_constraints = [(atom1, atom2, distance)] def removeDistanceConstraints(self, universe=None): try: del self.distance_constraints except AttributeError: pass for o in self._subunits(): o.removeDistanceConstraints() def traverseBondTree(self, function = None): self.setBondAttributes() todo = [self.atoms[0]] done = {todo[0]: True} bonds = [] while todo: next_todo = [] for atom in todo: bonded = atom.bondedTo() for other in bonded: if not done.get(other, False): if function is None: bonds.append((atom, other)) else: bonds.append((function(atom), function(other))) next_todo.append(other) done[other] = True todo = next_todo self.clearBondAttributes() return bonds def _description(self, tag, index_map, toplevel): letter, kwargs = self._descriptionSpec() s = [letter, '(', `self.name`, ',['] s.extend([o._description(tag, index_map, 0) + ',' for o in self._subunits()]) s.extend([a._description(tag, index_map, 0) + ',' for a in self.atoms if not hasattr(a, tag)]) s.append(']') if toplevel: s.extend([',', `self._typeName()`]) if kwargs is not None: s.extend([',', kwargs]) constraints = self._distanceConstraintList() if constraints: s.append(',dc=[') if index_map is None: s.extend(['(%d,%d,%f),' % (c[0].index, c[1].index, c[2]) for c in constraints]) else: s.extend(['(%d,%d,%f),' % (index_map[c[0].index], index_map[c[1].index], c[2]) for c in constraints]) s.append(']') s.append(')') return ''.join(s) def _typeName(self): return self.type.name def _graphics(self, conf, distance_fn, model, module, options): glist = [] for bu in self.bondedUnits(): for a in bu.atomList(): glist.extend(a._graphics(conf, distance_fn, model, module, options)) if hasattr(bu, 'bonds'): for b in bu.bonds: glist.extend(b._graphics(conf, distance_fn, model, module, options)) return glist # # The classes for atoms, groups, molecules, and complexes. #
[docs]class Atom(ChemicalObject): """ Atom """ def __init__(self, atom_spec, _memo = None, **properties): """ :param atom_spec: a string (not case sensitive) specifying the chemical element :type atom_spec: str :keyword position: the position of the atom :type position: Scientific.Geometry.Vector :keyword name: a name given to the atom :type name: str """ Utility.uniqueID.registerObject(self) ChemicalObject.__init__(self, atom_spec, _memo) self._mass = self.type.average_mass self.array = None self.index = None if properties.has_key('position'): self.setPosition(properties['position']) del properties['position'] self.addProperties(properties) blueprintclass = Database.BlueprintAtom def __getstate__(self): state = copy.copy(self.__dict__) if self.array is not None: state['array'] = None state['pos'] = Vector(self.array[self.index,:]) return state def atomList(self): return [self] def atomIterator(self): yield self
[docs] def setPosition(self, position): """ Changes the position of the atom. :param position: the new position :type position: Scientific.Geometry.Vector """ if position is None: if self.array is None: try: del self.pos except AttributeError: pass else: self.array[self.index,0] = Utility.undefined self.array[self.index,1] = Utility.undefined self.array[self.index,2] = Utility.undefined else: if self.array is None: self.pos = position else: self.array[self.index,0] = position[0] self.array[self.index,1] = position[1] self.array[self.index,2] = position[2]
translateTo = setPosition
[docs] def position(self, conf = None): """ :returns: the position in configuration conf. If conf is 'None', use the current configuration. If the atom has not been assigned a position, the return value is None. :rtype: Scientific.Geometry.Vector """ if conf is None: if self.array is None: try: return self.pos except AttributeError: return None else: if N.logical_or.reduce( N.greater(self.array[self.index, :], Utility.undefined_limit)): return None else: return Vector(self.array[self.index,:]) else: return conf[self]
centerOfMass = position
[docs] def setMass(self, mass): """ Changes the mass of the atom. :param mass: the mass :type mass: float """ self._mass = mass universe = self.universe() if universe is not None: universe._changed(True)
def getAtom(self, atom): return self def translateBy(self, vector): if self.array is None: self.pos = self.pos + vector else: self.array[self.index,0] = self.array[self.index,0] + vector[0] self.array[self.index,1] = self.array[self.index,1] + vector[1] self.array[self.index,2] = self.array[self.index,2] + vector[2] def numberOfPoints(self): return 1 numberOfCartesianCoordinates = numberOfPoints def setIndex(self, index): if self.index is not None and self.index != index: raise ValueError('Wrong atom index') self.index = index def setArray(self, index): self.index = index self.array = None def getArray(self): return self.array def unsetArray(self): self.pos = self.position() self.array = None def setBondAttribute(self, atom): try: self.bonded_to__.append(atom) except AttributeError: self.bonded_to__ = [atom] def clearBondAttribute(self): try: del self.bonded_to__ except AttributeError: pass
[docs] def bondedTo(self): """ :returns: a list of all atoms to which a chemical bond exists. :rtype: list """ try: return self.bonded_to__ except AttributeError: if self.parent is None or not isChemicalObject(self.parent): return [] else: return self.parent.bondedTo(self)
def delete(self): if self.parent is not None: self.parent._deleteAtom(self) def getAtomProperty(self, atom, property, levels = None): if self != atom: raise ValueError("Wrong atom") return getattr(self, property) def _description(self, tag, index_map, toplevel): setattr(self, tag, None) if index_map is None: index = self.index else: index = index_map[self.index] if toplevel: return 'A(' + `self.name` + ',' + `index` + ',' + \ `self.symbol` + ')' else: return 'A(' + `self.name` + ',' + `index` + ')' def _graphics(self, conf, distance_fn, model, module, options): #PJC change: if model == 'ball_and_stick': color = self._atomColor(self, options) material = module.DiffuseMaterial(color) radius = options.get('ball_radius', 0.03) return [module.Sphere(self.position(), radius, material=material)] elif model == 'vdw' or model == 'vdw_and_stick': color = self._atomColor(self, options) material = module.DiffuseMaterial(color) try: radius = self.vdW_radius except: radius = options.get('ball_radius', 0.03) return [module.Sphere(self.position(), radius, material=material)] else: return [] if model != 'ball_and_stick': return [] color = self._atomColor(self, options) material = module.DiffuseMaterial(color) radius = options.get('ball_radius', 0.03) return [module.Sphere(self.position(), radius, material=material)] def writeXML(self, file, memo, toplevel=1): return ['<atom title="%s" elementType="%s"/>' % (self.name, self.type.symbol)] def getXMLAtomOrder(self): return [self]
[docs]class Group(CompositeChemicalObject, ChemicalObject): """ Group of bonded atoms Groups can contain atoms and other groups, and link them by chemical bonds. They are used to represent functional groups or any other part of a molecule that has a well-defined identity. Groups cannot be created in application programs, but only in database definitions for molecules or through a MoleculeFactory. """ def __init__(self, group_spec, _memo = None, **properties): """ :param group_spec: a string (not case sensitive) that specifies the group name in the chemical database :type group_spec: str :keyword position: the position of the center of mass of the group :type position: Scientific.Geometry.Vector :keyword name: a name given to the group :type name: str """ if group_spec is not None: # group_spec is None when called from MoleculeFactory ChemicalObject.__init__(self, group_spec, _memo) self.addProperties(properties) blueprintclass = Database.BlueprintGroup is_incomplete = True def bondedTo(self, atom): if self.parent is None or not isChemicalObject(self.parent): return [] else: return self.parent.bondedTo(atom) def setBondAttributes(self): pass def clearBondAttributes(self): pass def _subunits(self): return self.groups def _descriptionSpec(self): return "G", None
[docs]class Molecule(CompositeChemicalObject, ChemicalObject): """Molecule Molecules consist of atoms and groups linked by bonds. """ def __init__(self, molecule_spec, _memo = None, **properties): """ :param molecule_spec: a string (not case sensitive) that specifies the molecule name in the chemical database :type molecule_spec: str :keyword position: the position of the center of mass of the molecule :type position: Scientific.Geometry.Vector :keyword name: a name given to the molecule :type name: str :keyword configuration: the name of a configuration listed in the database definition of the molecule, which is used to initialize the atom positions. If no configuration is specified, the configuration named "default" will be used, if it exists. Otherwise the atom positions are undefined. :type configuration: str """ if molecule_spec is not None: # molecule_spec is None when called from MoleculeFactory ChemicalObject.__init__(self, molecule_spec, _memo) properties = copy.copy(properties) CompositeChemicalObject.__init__(self, properties) self.bonds = Bonds.BondList(self.bonds) blueprintclass = Database.BlueprintMolecule def bondedTo(self, atom): return self.bonds.bondedTo(atom) def setBondAttributes(self): self.bonds.setBondAttributes() def clearBondAttributes(self): for a in self.atoms: a.clearBondAttribute() def _subunits(self): return self.groups def _descriptionSpec(self): return "M", None def addGroup(self, group, bond_atom_pairs): for a1, a2 in bond_atom_pairs: o1 = a1.topLevelChemicalObject() o2 = a2.topLevelChemicalObject() if set([o1, o2]) != set([self, group]): raise ValueError("bond %s-%s outside object" % (str(a1), str(a2))) self.groups.append(group) self.atoms = self.atoms + group.atoms group.parent = self self.clearBondAttributes() for a1, a2 in bond_atom_pairs: self.bonds.append(Bonds.Bond((a1, a2))) for b in group.bonds: self.bonds.append(b) # construct positions of missing hydrogens
[docs] def findHydrogenPositions(self): """ Find reasonable positions for hydrogen atoms that have no position assigned. This method uses a heuristic approach based on standard geometry data. It was developed for proteins and DNA and may not give good results for other molecules. It raises an exception if presented with a topology it cannot handle. """ self.setBondAttributes() try: unknown = {} for a in self.atoms: if a.position() is None: if a.symbol != 'H': raise ValueError('position of ' + a.fullName() + \ ' is undefined') bonded = a.bondedTo()[0] unknown.setdefault(bonded, []).append(a) for a, list in unknown.items(): bonded = a.bondedTo() n = len(bonded) known = [b for b in bonded if b.position() is not None] nb = len(list) try: method = self._h_methods[a.symbol][n][nb] except KeyError: raise ValueError("Can't handle this yet: " + a.symbol + ' with ' + `n` + ' bonds (' + a.fullName() + ').') method(self, a, known, list) finally: self.clearBondAttributes() # default C-H bond length and X-C-H angle
_ch_bond = 1.09*Units.Ang _hch_angle = N.arccos(-1./3.)*Units.rad _nh_bond = 1.03*Units.Ang _hnh_angle = 120.*Units.deg _oh_bond = 0.95*Units.Ang _coh_angle = 114.9*Units.deg _sh_bond = 1.007*Units.Ang _csh_angle = 96.5*Units.deg def _C4oneH(self, atom, known, unknown): r = atom.position() n0 = (known[0].position()-r).normal() n1 = (known[1].position()-r).normal() n2 = (known[2].position()-r).normal() n3 = (n0 + n1 + n2).normal() unknown[0].setPosition(r-self._ch_bond*n3) def _C4twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() r2 = known[1].position() plane = Objects3D.Plane(r, r1, r2) axis = -((r1-r)+(r2-r)).normal() plane = plane.rotate(Objects3D.Line(r, axis), 90.*Units.deg) cone = Objects3D.Cone(r, axis, 0.5*self._hch_angle) sphere = Objects3D.Sphere(r, self._ch_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _C4threeH(self, atom, known, unknown): self._tetrahedralH(atom, known, unknown, self._ch_bond) def _C3oneH(self, atom, known, unknown): r = atom.position() n1 = (known[0].position()-r).normal() n2 = (known[1].position()-r).normal() n3 = -(n1 + n2).normal() unknown[0].setPosition(r+self._ch_bond*n3) def _C3twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) r2 = others[0].position() try: plane = Objects3D.Plane(r, r1, r2) except ZeroDivisionError: # We get here if all three points are colinear. # Add a small random displacement as a fix. from MMTK.Random import randomPointInSphere plane = Objects3D.Plane(r, r1, r2 + randomPointInSphere(0.001)) axis = (r-r1).normal() cone = Objects3D.Cone(r, axis, 0.5*self._hch_angle) sphere = Objects3D.Sphere(r, self._ch_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _C2oneH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() x = r + self._ch_bond * (r - r1).normal() unknown[0].setPosition(x) def _N2oneH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) r2 = others[0].position() try: plane = Objects3D.Plane(r, r1, r2) except ZeroDivisionError: # We get here when all three points are colinear. # Add a small random displacement as a fix. from MMTK.Random import randomPointInSphere plane = Objects3D.Plane(r, r1, r2 + randomPointInSphere(0.001)) axis = (r-r1).normal() cone = Objects3D.Cone(r, axis, 0.5*self._hch_angle) sphere = Objects3D.Sphere(r, self._nh_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) def _N3oneH(self, atom, known, unknown): r = atom.position() n1 = (known[0].position()-r).normal() n2 = (known[1].position()-r).normal() n3 = -(n1 + n2).normal() unknown[0].setPosition(r+self._nh_bond*n3) def _N3twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) r2 = others[0].position() plane = Objects3D.Plane(r, r1, r2) axis = (r-r1).normal() cone = Objects3D.Cone(r, axis, 0.5*self._hnh_angle) sphere = Objects3D.Sphere(r, self._nh_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _N4threeH(self, atom, known, unknown): self._tetrahedralH(atom, known, unknown, self._nh_bond) def _N4twoH(self, atom, known, unknown): r = atom.position() r1 = known[0].position() r2 = known[1].position() plane = Objects3D.Plane(r, r1, r2) axis = -((r1-r)+(r2-r)).normal() plane = plane.rotate(Objects3D.Line(r, axis), 90.*Units.deg) cone = Objects3D.Cone(r, axis, 0.5*self._hnh_angle) sphere = Objects3D.Sphere(r, self._nh_bond) circle = sphere.intersectWith(cone) points = circle.intersectWith(plane) unknown[0].setPosition(points[0]) unknown[1].setPosition(points[1]) def _N4oneH(self, atom, known, unknown): r = atom.position() n0 = (known[0].position()-r).normal() n1 = (known[1].position()-r).normal() n2 = (known[2].position()-r).normal() n3 = (n0 + n1 + n2).normal() unknown[0].setPosition(r-self._nh_bond*n3) def _O2(self, atom, known, unknown): others = known[0].bondedTo() for a in others: r = a.position() if a != atom and r is not None: break dihedral = 180.*Units.deg self._findPosition(unknown[0], atom.position(), known[0].position(), r, self._oh_bond, self._coh_angle, dihedral) def _S2(self, atom, known, unknown): c2 = filter(lambda a: a.symbol == 'C', known[0].bondedTo())[0] self._findPosition(unknown[0], atom.position(), known[0].position(), c2.position(), self._sh_bond, self._csh_angle, 180.*Units.deg) def _tetrahedralH(self, atom, known, unknown, bond): r = atom.position() n = (known[0].position()-r).normal() cone = Objects3D.Cone(r, n, N.arccos(-1./3.)) sphere = Objects3D.Sphere(r, bond) circle = sphere.intersectWith(cone) others = filter(lambda a: a.symbol != 'H', known[0].bondedTo()) others.remove(atom) other = others[0] ref = (Objects3D.Plane(circle.center, circle.normal) \ .projectionOf(other.position())-circle.center).normal() p0 = circle.center + ref*circle.radius p0 = Objects3D.rotatePoint(p0, Objects3D.Line(circle.center, circle.normal), 60.*Units.deg) p1 = Objects3D.rotatePoint(p0, Objects3D.Line(circle.center, circle.normal), 120.*Units.deg) p2 = Objects3D.rotatePoint(p1, Objects3D.Line(circle.center, circle.normal), 120.*Units.deg) unknown[0].setPosition(p0) unknown[1].setPosition(p1) unknown[2].setPosition(p2) def _findPosition(self, unknown, a1, a2, a3, bond, angle, dihedral): sphere = Objects3D.Sphere(a1, bond) cone = Objects3D.Cone(a1, a2-a1, angle) plane = Objects3D.Plane(a3, a2, a1) plane = plane.rotate(Objects3D.Line(a1, a2-a1), dihedral) points = sphere.intersectWith(cone).intersectWith(plane) for p in points: if (a1-a2).cross(p-a1)*(plane.normal) > 0: unknown.setPosition(p) break _h_methods = {'C': {4: {3: _C4threeH, 2: _C4twoH, 1: _C4oneH}, 3: {2: _C3twoH, 1: _C3oneH}, 2: {1: _C2oneH}}, 'N': {4: {3: _N4threeH, 2: _N4twoH, 1: _N4oneH}, 3: {2: _N3twoH, 1: _N3oneH}, 2: {1: _N2oneH}}, 'O': {2: {1: _O2}}, 'S': {2: {1: _S2}}, }
[docs]class ChainMolecule(Molecule): """ ChainMolecule ChainMolecules are generated by a MoleculeFactory from templates that have a sequence attribute. """ def __len__(self): return len(self.sequence) def __getitem__(self, item): return getattr(self, self.sequence[item])
class Crystal(CompositeChemicalObject, ChemicalObject): def __init__(self, blueprint, _memo = None, **properties): ChemicalObject.__init__(self, blueprint, _memo) properties = copy.copy(properties) CompositeChemicalObject.__init__(self, properties) self.bonds = Bonds.BondList(self.bonds) blueprintclass = Database.BlueprintCrystal def _subunits(self): return self.groups def _descriptionSpec(self): return "X", None
[docs]class Complex(CompositeChemicalObject, ChemicalObject): """ Complex A complex is an assembly of molecules that are not connected by chemical bonds. """ def __init__(self, complex_spec, _memo = None, **properties): """ :param complex_spec: a string (not case sensitive) that specifies the complex name in the chemical database :type complex_spec: str :keyword position: the position of the center of mass of the complex :type position: Scientific.Geometry.Vector :keyword name: a name given to the complex :type name: str :keyword configuration: the name of a configuration listed in the database definition of the complex, which is used to initialize the atom positions. If no configuration is specified, the configuration named "default" will be used, if it exists. Otherwise the atom positions are undefined. :type configuration: str """ ChemicalObject.__init__(self, complex_spec, _memo) properties = copy.copy(properties) CompositeChemicalObject.__init__(self, properties) blueprintclass = Database.BlueprintComplex def recreateAtomList(self): self.atoms = [] for m in self.molecules: self.atoms.extend(m.atoms) def bondedUnits(self): return self.molecules def setBondAttributes(self): for m in self.molecules: m.setBondAttributes() def clearBondAttributes(self): for m in self.molecules: m.clearBondAttributes() def _subunits(self): return self.molecules def _descriptionSpec(self): return "C", None def writeXML(self, file, memo, toplevel=1): if self.type is None: name = 'm' + `memo['counter']` memo['counter'] = memo['counter'] + 1 memo[id(self)] = name for molecule in self.molecules: molecule.writeXML(file, memo, 0) file.write('<molecule id="%s">\n' % name) for molecule in self.molecules: file.write(' <molecule ref="%s"/>\n' % memo[id(molecule)]) file.write('</molecule>\n') else: ChemicalObject.writeXML(self, file, memo, toplevel) if toplevel: return ['<molecule ref="%s"/>' % name] else: return None def getXMLAtomOrder(self): if self.type is None: atoms = [] for molecule in self.molecules: atoms.extend(molecule.getXMLAtomOrder()) return atoms else: return ChemicalObject.getXMLAtomOrder(self)
Database.registerInstanceClass(Atom.blueprintclass, Atom) Database.registerInstanceClass(Group.blueprintclass, Group) Database.registerInstanceClass(Molecule.blueprintclass, Molecule) Database.registerInstanceClass(Crystal.blueprintclass, Crystal) Database.registerInstanceClass(Complex.blueprintclass, Complex)
[docs]class AtomCluster(CompositeChemicalObject, ChemicalObject): """ An agglomeration of atoms An atom cluster acts like a molecule without any bonds or atom properties. It can be used to represent a group of atoms that are known to form a chemical unit but whose chemical properties are not sufficiently known to define a molecule. """ def __init__(self, atoms, **properties): """ :param atoms: a list of atoms in the cluster :type atoms: list :keyword position: the position of the center of mass of the cluster :type position: Scientific.Geometry.Vector :keyword name: a name given to the cluster :type name: str """ self.atoms = list(atoms) self.parent = None self.name = '' self.type = None for a in self.atoms: if a.parent is not None: raise ValueError(repr(a)+' is part of ' + repr(a.parent)) a.parent = self if a.name != '': setattr(self, a.name, a) properties = copy.copy(properties) CompositeChemicalObject.__init__(self, properties) self.bonds = Bonds.BondList([]) def bondedTo(self, atom): return [] def setBondAttributes(self): pass def clearBondAttributes(self): pass def _subunits(self): return [] def _description(self, tag, index_map, toplevel): s = 'AC(' + `self.name` + ',[' for a in self.atoms: s = s + a._description(tag, index_map, 1) + ',' s = s + ']' constraints = self._distanceConstraintList() if constraints: s = s + ',dc=[' if index_map is None: for c in constraints: s = s + '(%d,%d,%f),' % (c[0].index, c[1].index, c[2]) else: for c in constraints: s = s + '(%d,%d,%f),' % (index_map[c[0].index], index_map[c[1].index], c[2]) s = s + ']' return s + ')'
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Collections.html0000644000076600000240000045576112013143456021726 0ustar hinsenstaff00000000000000 MMTK.Collections — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Collections

# This module defines collections of chemical objects.
#
# Written by Konrad Hinsen
#

"""
Collections of chemical objects
"""

__docformat__ = 'restructuredtext'

from MMTK import Utility, Units, ParticleProperties, Visualization
from MMTK.Geometry import superpositionFit
from Scientific.Geometry import Vector, Tensor, Objects3D
from Scientific.Geometry import Transformation
from Scientific import N
import copy, itertools

#
# This class defines groups of atoms. It is used as a base class
# for anything containing atoms, including chemical objects, collections,
# universes etc., but it can't be used directly. All its subclasses
# must define a method atomList() that returns a list of all their atoms.
#
[docs]class GroupOfAtoms(object): """ Anything that consists of atoms A mix-in class that defines a large set of operations which are common to all objects that consist of atoms, i.e. any subset of a chemical system. Examples are atoms, molecules, collections, or universes. """
[docs] def numberOfAtoms(self): """ :returns: the number of atoms :rtype: int """ return len(self.atomList())
[docs] def numberOfPoints(self): """ :returns: the number of geometrical points that define the object. It is currently equal to the number of atoms, but could be different e.g. for quantum systems, in which each atom is described by a wave function or a path integral. :rtype: int """ return sum([a.numberOfPoints() for a in self.atomIterator()])
numberOfCartesianCoordinates = numberOfPoints
[docs] def numberOfFixedAtoms(self): """ :returns: the number of atoms that are fixed, i.e. that cannot move :rtype: int """ n = 0 for a in self.atomIterator(): try: if a.fixed: n = n + 1 except AttributeError: pass return n
[docs] def degreesOfFreedom(self): """ :returns: the number of mechanical degrees of freedom :rtype: int """ return 3*(self.numberOfAtoms()-self.numberOfFixedAtoms())
[docs] def atomCollection(self): """ :returns: a collection containing all atoms in the object """ return Collection(self.atomList())
[docs] def atomsWithDefinedPositions(self, conf = None): """ :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: a collection of all atoms that have a position in the given configuration """ return Collection([a for a in self.atomIterator() if Utility.isDefinedPosition(a.position(conf))])
[docs] def mass(self): """ :returns: the total mass :rtype: float """ return sum(a._mass for a in self.atomIterator())
[docs] def centerOfMass(self, conf = None): """ :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: the center of mass in the given configuration :rtype: Scientific.Geometry.Vector """ offset = None universe = self.universe() if universe is not None: offset = universe.contiguousObjectOffset([self], conf) m = 0. mr = Vector(0.,0.,0.) if offset is None: for a in self.atomIterator(): m += a._mass mr += a._mass * a.position(conf) else: for a in self.atomIterator(): m += a._mass mr += a._mass * (a.position(conf)+offset[a]) return mr/m
position = centerOfMass
[docs] def centerAndMomentOfInertia(self, conf = None): """ :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: the center of mass and the moment of inertia tensor in the given configuration """ from Scientific.Geometry import delta offset = None universe = self.universe() if universe is not None: offset = universe.contiguousObjectOffset([self], conf) m = 0. mr = Vector(0.,0.,0.) t = Tensor(3*[3*[0.]]) for a in self.atomIterator(): ma = a._mass if offset is None: r = a.position(conf) else: r = a.position(conf)+offset[a] m += ma mr += ma*r t += ma*r.dyadicProduct(r) cm = mr/m t -= m*cm.dyadicProduct(cm) t = t.trace()*delta - t return cm, t
[docs] def rotationalConstants(self, conf=None): """ :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: a sorted array of rotational constants A, B, C in internal units """ com, i = self.centerAndMomentOfInertia(conf) pmi = i.eigenvalues() return N.sort(Units.h / (8.*N.pi*N.pi*pmi))[::-1]
[docs] def boundingBox(self, conf = None): """ :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: two opposite corners of a bounding box around the object. The bounding box is the smallest rectangular bounding box with edges parallel to the coordinate axes. :rtype: tuple of two Scientific.Geometry.Vector """ atoms = self.atomList() min = atoms[0].position(conf).array max = min for a in atoms[1:]: r = a.position(conf).array min = N.minimum(min, r) max = N.maximum(max, r) return Vector(min), Vector(max)
[docs] def boundingSphere(self, conf = None): """ :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: a sphere that contains all atoms in the object. This is B{not} the minimal bounding sphere, just B{some} bounding sphere. :rtype: Scientific.Geometry.Objects3D.Sphere """ atoms = self.atomList() center = sum((a.position(conf) for a in atoms), Vector(0., 0., 0.)) / len(atoms) r = 0. for a in atoms: r = max(r, (a.position(conf)-center).length()) return Objects3D.Sphere(center, r)
[docs] def rmsDifference(self, conf1, conf2 = None): """ :param conf1: a configuration object :type conf1: :class:`~MMTK.ParticleProperties.Configuration` :param conf2: a configuration object, or None for the current configuration :type conf2: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: the RMS (root-mean-square) difference between the conformations of the object in two universe configurations, conf1 and conf2 :rtype: float """ universe = conf1.universe m = 0. rms = 0. for a in self.atomIterator(): ma = a._mass dr = universe.distanceVector(a.position(conf1), a.position(conf2)) m += ma rms += ma*dr*dr return N.sqrt(rms/m)
def findTransformationAsQuaternion(self, conf1, conf2 = None): universe = self.universe() if universe.is_periodic: raise ValueError("superposition in periodic universe " "is not defined") if conf1.universe != universe: raise ValueError("conformation is for a different universe") if conf2 is None: conf2 = conf1 conf1 = universe.configuration() else: if conf2.universe != universe: raise ValueError("conformation is for a different universe") weights = universe.masses() return superpositionFit([(weights[a], conf1[a], conf2[a]) for a in self.atomIterator()])
[docs] def findTransformation(self, conf1, conf2 = None): """ :param conf1: a configuration object :type conf1: :class:`~MMTK.ParticleProperties.Configuration` :param conf2: a configuration object, or None for the current configuration :type conf2: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :returns: the linear transformation that, when applied to the object in configuration conf1, minimizes the RMS distance to the conformation in conf2, and the minimal RMS distance. If conf2 is None, returns the transformation from the current configuration to conf1 and the associated RMS distance. """ q, cm1, cm2, rms = self.findTransformationAsQuaternion(conf1, conf2) return Transformation.Translation(cm2) * \ q.asRotation() * \ Transformation.Translation(-cm1), \ rms
[docs] def translateBy(self, vector): """ Translate the object by a displacement vector :param vector: the displacement vector :type vector: Scientific.Geometry.Vector """ for a in self.atomIterator(): a.translateBy(vector)
[docs] def translateTo(self, position): """ Translate the object such that its center of mass is at position :param position: the final position :type position: Scientific.Geometry.Vector """ self.translateBy(position-self.centerOfMass())
[docs] def normalizePosition(self): """ Translate the center of mass to the coordinate origin """ self.translateTo(Vector(0., 0., 0.))
[docs] def normalizeConfiguration(self, repr=None): """ Apply a linear transformation such that the center of mass of the object is translated to the coordinate origin and its principal axes of inertia become parallel to the three coordinate axes. :param repr: the specific representation for axis alignment: - Ir : x y z <--> b c a - IIr : x y z <--> c a b - IIIr : x y z <--> a b c - Il : x y z <--> c b a - IIl : x y z <--> a c b - IIIl : x y z <--> b a c """ transformation = self.normalizingTransformation(repr) self.applyTransformation(transformation)
[docs] def normalizingTransformation(self, repr=None): """ Calculate a linear transformation that shifts the center of mass of the object to the coordinate origin and makes its principal axes of inertia parallel to the three coordinate axes. :param repr: the specific representation for axis alignment: Ir : x y z <--> b c a IIr : x y z <--> c a b IIIr : x y z <--> a b c Il : x y z <--> c b a IIl : x y z <--> a c b IIIl : x y z <--> b a c :returns: the normalizing transformation :rtype: Scientific.Geometry.Transformation.RigidBodyTransformation """ from Scientific.LA import determinant cm, inertia = self.centerAndMomentOfInertia() ev, diag = inertia.diagonalization() if determinant(diag.array) < 0: diag.array[0] = -diag.array[0] if repr != None: seq = N.argsort(ev) if repr == 'Ir': seq = N.array([seq[1], seq[2], seq[0]]) elif repr == 'IIr': seq = N.array([seq[2], seq[0], seq[1]]) elif repr == 'Il': seq = N.seq[2::-1] elif repr == 'IIl': seq[1:3] = N.array([seq[2], seq[1]]) elif repr == 'IIIl': seq[0:2] = N.array([seq[1], seq[0]]) elif repr != 'IIIr': print 'unknown representation' diag.array = N.take(diag.array, seq) return Transformation.Rotation(diag)*Transformation.Translation(-cm)
[docs] def applyTransformation(self, t): """ Apply a transformation to the object :param t: the transformation to be applied :type t: Scientific.Geometry.Transformation """ for a in self.atomIterator(): a.setPosition(t(a.position()))
[docs] def displacementUnderTransformation(self, t): """ :param t: the transformation to be applied :type t: Scientific.Geometry.Transformation :returns: the displacement vectors for the atoms in the object that correspond to the transformation t. :rtype: :class:`~MMTK.ParticleProperties.ParticleVector` """ d = ParticleProperties.ParticleVector(self.universe()) for a in self.atomIterator(): r = a.position() d[a] = t(r)-r return d
[docs] def rotateAroundCenter(self, axis_direction, angle): """ Rotate the object around an axis that passes through its center of mass. :param axis_direction: the direction of the axis of rotation :type axis_direction: Scientific.Geometry.Vector :param angle: the rotation angle (in radians) :type angle: float """ cm = self.centerOfMass() t = Transformation.Translation(cm) * \ Transformation.Rotation(axis_direction, angle) * \ Transformation.Translation(-cm) self.applyTransformation(t)
[docs] def rotateAroundOrigin(self, axis_direction, angle): """ Rotate the object around an axis that passes through the coordinate origin. :param axis_direction: the direction of the axis of rotation :type axis_direction: Scientific.Geometry.Vector :param angle: the rotation angle (in radians) :type angle: float """ self.applyTransformation(Transformation.Rotation(axis_direction, angle))
[docs] def rotateAroundAxis(self, point1, point2, angle): """ Rotate the object arond an axis specified by two points :param point1: the first point :type point1: Scientific.Geometry.Vector :param point2: the second point :type point2: Scientific.Geometry.Vector :param angle: the rotation angle (in radians) :type angle: float """ tr1 = Transformation.Translation(-point1) tr2 = Transformation.Rotation(point2-point1, angle) tr3 = tr1.inverse() self.applyTransformation(tr3*tr2*tr1)
[docs] def writeToFile(self, filename, configuration = None, format = None): """ Write a representation of the object in a given configuration to a file. :param filename: the name of the file :type filename: str :param configuration: a configuration object, or None for the current configuration :type configuration: :class:`~MMTK.ParticleProperties.Configuration` or NoneType :param format: 'pdb' or 'vrml' (default: guess from filename) A subformat specification can be added, separated by a dot. Subformats of 'vrml' are 'wireframe' (default), 'ball_and_stick', 'highlight' (like 'wireframe', but with a small sphere for all atoms that have an attribute 'highlight' with a non-zero value), and 'charge' (wireframe plus small spheres for the atoms whose color indicates the charge on a red-to-green color scale) :type format: str """ from MMTK import ConfigIO universe = self.universe() if universe is not None: configuration = universe.contiguousObjectConfiguration( [self], configuration) file = ConfigIO.OutputFile(filename, format) file.write(self, configuration) file.close()
[docs] def view(self, configuration = None, format = 'pdb'): """ Start an external viewer for the object in the given configuration. :param configuration: the configuration to be visualized :type configuration: :class:`~MMTK.ParticleProperties.Configuration` :param format: 'pdb' (for running $PDBVIEWER) or 'vrml' (for running $VRMLVIEWER). An optional subformat specification can be added, see :class:`~MMTK.Collections.GroupOfAtoms.writeToFile` for the details. """ universe = self.universe() if universe is not None: configuration = universe.contiguousObjectConfiguration([self], configuration) Visualization.viewConfiguration(self, configuration, format)
[docs] def kineticEnergy(self, velocities = None): """ :param velocities: a set of velocities for all atoms, or None for the current velocities :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the kinetic energy :rtype: float """ if velocities is None: velocities = self.atomList()[0].universe().velocities() energy = 0. for a in self.atomIterator(): v = velocities[a] energy = energy + a._mass*(v*v) return 0.5*energy
[docs] def temperature(self, velocities = None): """ :param velocities: a set of velocities for all atoms, or None for the current velocities :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the temperature :rtype: float """ energy = self.kineticEnergy(velocities) return 2.*energy/(self.degreesOfFreedom()*Units.k_B)
[docs] def momentum(self, velocities = None): """ :param velocities: a set of velocities for all atoms, or None for the current velocities :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the momentum :rtype: Scientific.Geometry.Vector """ if velocities is None: velocities = self.atomList()[0].universe().velocities() return sum((a._mass*velocities[a] for a in self.atomIterator()), Vector(0., 0., 0.))
[docs] def angularMomentum(self, velocities = None, conf = None): """ :param velocities: a set of velocities for all atoms, or None for the current velocities :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` :returns: the angluar momentum :rtype: Scientific.Geometry.Vector """ if velocities is None: velocities = self.atomList()[0].universe().velocities() cm = self.centerOfMass(conf) return sum((a._mass*a.position(conf).cross(velocities[a]) for a in self.atomIterator()), Vector(0., 0., 0.))
[docs] def angularVelocity(self, velocities = None, conf = None): """ :param velocities: a set of velocities for all atoms, or None for the current velocities :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :param conf: a configuration object, or None for the current configuration :type conf: :class:`~MMTK.ParticleProperties.Configuration` :returns: the angluar velocity :rtype: Scientific.Geometry.Vector """ if velocities is None: velocities = self.atomList()[0].universe().velocities() cm, inertia = self.centerAndMomentOfInertia(conf) l = sum((a._mass*a.position(conf).cross(velocities[a]) for a in self.atomIterator()), Vector(0., 0., 0.)) return inertia.inverse()*l
[docs] def universe(self): """ :returns: the universe of which the object is part. For an object that is not part of a universe, the result is None :rtype: :class:`~MMTK.Universe.Universe` """ atoms = self.atomList() if not atoms: return None universe = atoms[0].universe() for a in atoms[1:]: if a.universe() is not universe: return None return universe
[docs] def charge(self): """ :returns: the total charge of the object. This is defined only for objects that are part of a universe with a force field that defines charges. :rtype: float """ return self.universe().forcefield().charge(self)
[docs] def dipole(self, reference = None): """ :returns: the total dipole moment of the object. This is defined only for objects that are part of a universe with a force field that defines charges. :rtype: Scientific.Geometry.Vector """ return self.universe().forcefield().dipole(self, reference)
[docs] def booleanMask(self): """ :returns: a ParticleScalar object that contains a value of 1 for each atom that is in the object and a value of 0 for all other atoms in the universe :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ universe = self.universe() if universe is None: raise ValueError("object not in a universe") array = N.zeros((universe.numberOfAtoms(),), N.Int) mask = ParticleProperties.ParticleScalar(universe, array) for a in self.atomIterator(): mask[a] = 1 return mask # # This class defines a general collection that can contain # chemical objects and other collections. #
[docs]class Collection(GroupOfAtoms, Visualization.Viewable): """ Collection of chemical objects Collections permit the grouping of arbitrary chemical objects (atoms, molecules, etc.) into one object for the purpose of analysis or manipulation. Collections permit length inquiry, item extraction by indexing, and iteration, like any Python sequence object. Two collections can be added to yield a collection that contains the combined elements. """ def __init__(self, *objects): """ :param objects: a chemical object or a sequence of chemical objects that define the initial content of the collection. """ self.objects = [] self.addObject(objects) is_collection = 1
[docs] def addObject(self, object): """ Add objects to the collection. :param object: the object(s) to be added. If it is another collection or a list, all of its elements are added """ from MMTK.ChemicalObjects import isChemicalObject if isChemicalObject(object): self.addChemicalObject(object) elif isCollection(object): self.addChemicalObjectList(object.objectList()) elif Utility.isSequenceObject(object): if object and isChemicalObject(object[0]): self.addChemicalObjectList(list(object)) else: for o in object: self.addObject(o) else: raise TypeError('Wrong object type in collection')
def addChemicalObject(self, object): self.objects.append(object) def addChemicalObjectList(self, list): self.objects.extend(list)
[docs] def removeObject(self, object): """ Remove an object or a list or collection of objects from the collection. The object(s) to be removed must be elements of the collection. :param object: the object to be removed, or a list or collection of objects whose elements are to be removed :raises ValueError: if the object is not an element of the collection """ from MMTK.ChemicalObjects import isChemicalObject if isChemicalObject(object): self.removeChemicalObject(object) elif isCollection(object) or Utility.isSequenceObject(object): for o in object: self.removeObject(o) else: raise ValueError('Object not in this collection')
def removeChemicalObject(self, object): try: self.objects.remove(object) except ValueError: raise ValueError('Object not in this collection')
[docs] def selectShell(self, point, r1, r2=0.): """ Select objects in a spherical shell around a central point. :param point: the center of the spherical shell :type point: Scientific.Geometry.Vector :param r1: inner or outer radius of the shell :type r1: float :param r2: inner or outer radius of the shell (default: 0.) :type r2: float :returns: a collection of all elements whose distance from point is between r1 and r2 :rtype: :class:`~MMTK.Collections.Collection` """ if r1 > r2: r1, r2 = r2, r1 universe = self.universe() in_shell = [] for o in self.objects: for a in o.atomIterator(): r = universe.distance(a.position(), point) if r >= r1 and r <= r2: in_shell.append(o) break return Collection(in_shell)
[docs] def selectBox(self, p1, p2): """ Select objects in a rectangular volume :param p1: one corner of the rectangular volume :type p1: Scientific.Geometry.Vector :param p2: the other corner of the rectangular volume :type p2: Scientific.Geometry.Vector :returns: a collection of all elements that lie within the rectangular volume :rtype: :class:`~MMTK.Collections.Collection` """ x1 = N.minimum(p1.array, p2.array) x2 = N.maximum(p1.array, p2.array) in_box = [] for o in self.objects: r = o.position().array if N.logical_and.reduce( \ N.logical_and(N.less_equal(x1, r), N.less(r, x2))): in_box.append(o) return Collection(in_box)
[docs] def objectList(self, type = None): """ Make a list of all objects in the collection that are instances of a specific type or of one of its subtypes. :param type: the type that serves as a filter. If None, all objects are returned :returns: the objects that match the given type :rtype: list """ if type is None: return self.objects else: return [o for o in self.objects if isinstance(o, type)]
[docs] def atomList(self): """ :returns: a list containing all atoms of all objects in the collection :rtype: list """ atoms = [] for o in self.objectList(): atoms.extend(o.atomList()) return atoms
def atomIterator(self): return itertools.chain(*(o.atomIterator() for o in self.objects))
[docs] def numberOfAtoms(self): """ :returns: the total number of atoms in the objects of the collection :rtype: int """ return sum(o.numberOfAtoms() for o in self.objectList())
[docs] def universe(self): """ :returns: the universe of which all objects in the collection are part. If no such universe exists, the return value is None :rtype: :class:`~MMTK.Universe.Universe` """ if not self.objects: return None universe = self.objects[0].universe() for o in self.objects[1:]: if o.universe() is not universe: return None return universe
def __len__(self): """ :returns: the number of objects in the collection :rtype: int """ return len(self.objects) def __getitem__(self, item): """ :param item: an index into the object list :type item: int :returns: the object with the given index """ return self.objects[item] def __iter__(self): return self.objects.__iter__() def __add__(self, other): return Collection(self.objectList(), other.objectList()) def __str__(self): return "Collection of %d objects" % len(self.objects)
[docs] def map(self, function): """ Apply a function to all objects in the collection and return the list of the results. If the results are chemical objects, a Collection object is returned instead of a list. :param function: the function to be applied :type function: callable :returns: the list or collection of the results """ from MMTK.ChemicalObjects import isChemicalObject list = [function(o) for o in self.objectList()] if list and isChemicalObject(list[0]): return Collection(list) else: return list
def bondedUnits(self): bu = [] for o in self.objects: bu = bu + o.bondedUnits() return bu def degreesOfFreedom(self): return GroupOfAtoms.degreesOfFreedom(self) \ - self.numberOfDistanceConstraints()
[docs] def distanceConstraintList(self): """ :returns: the list of distance constraints :rtype: list """ dc = [] for o in self.objects: dc.extend(o.distanceConstraintList()) return dc
[docs] def numberOfDistanceConstraints(self): """ :returns: the number of distance constraints """ return sum(o.numberOfDistanceConstraints() for o in self.objects)
[docs] def setBondConstraints(self, universe=None): """ Set distance constraints for all bonds """ if universe is None: universe = self.universe() for o in self.objects: o.setBondConstraints(universe)
[docs] def removeDistanceConstraints(self, universe=None): """ Remove all distance constraints """ if universe is None: universe = self.universe() for o in self.objects: o.removeDistanceConstraints(universe)
def _graphics(self, conf, distance_fn, model, module, options): gobs = [] for o in self.objects: gobs.extend(o._graphics(conf, distance_fn, model, module, options)) return gobs def __copy__(self): return self.__class__(copy.copy(self.objects)) # type check for collections
[docs]def isCollection(object): """ :param object: any Python object :returns: True if the object is a :class:`~MMTK.Collections.Collection` """ return hasattr(object, 'is_collection') # # This class defines a partitioned collection. Such collections # divide their objects into cubic boxes according to their positions. # It is then possible to find potential neighbours much more efficiently. #
[docs]class PartitionedCollection(Collection): """ Collection with cubic partitions A PartitionedCollection differs from a plain Collection by sorting its elements into small cubic cells. This makes adding objects slower, but geometrical operations like selectShell become much faster for a large number of objects. """ def __init__(self, partition_size, *objects): """ :param partition_size: the edge length of the cubic cells :param objects: a chemical object or a sequence of chemical objects that define the initial content of the collection. """ self.partition_size = 1.*partition_size self.undefined = [] self.partition = {} self.addObject(objects) def addChemicalObject(self, object): p = object.position() if p is None: self.undefined.append(object) else: index = self.partitionIndex(p) try: partition = self.partition[index] except KeyError: partition = [] self.partition[index] = partition partition.append(object) self.all = None def addChemicalObjectList(self, list): for object in list: self.addChemicalObject(object) def removeChemicalObject(self, object): p = object.position() if p is None: self.undefined.remove(object) else: index = self.partitionIndex(p) try: partition = self.partition[index] except KeyError: raise ValueError('Object not in this collection') try: partition.remove(object) except ValueError: raise ValueError('Object not in this collection') self.all = None def partitionIndex(self, x): return (int(N.floor(x[0]/self.partition_size)), int(N.floor(x[1]/self.partition_size)), int(N.floor(x[2]/self.partition_size))) def objectList(self): return sum(self.partition.values(), [self.undefined]) def __len__(self): return sum(len(p) for p in self.partition.values()) + \ len(self.undefined) def __getitem__(self, item): if self.all is None: self.all = self.objectList() if item >= len(self.all): self.all = None raise IndexError return self.all[item] def __copy__(self): return self.__class__(self.partition_size, copy.copy(self.objectList()))
[docs] def partitions(self): """ :returns: a list of cubic partitions. Each partition is specified by a tuple containing two vectors (describing the diagonally opposite corners) and the list of objects in the partition. :rtype: list """ list = [] for index, objects in self.partition.items(): min = Vector(index)*self.partition_size max = min + Vector(3*[self.partition_size]) list.append((min, max, objects)) return list
def selectCube(self, point, edge): x = int(round(point[0]/self.partition_size)) y = int(round(point[1]/self.partition_size)) z = int(round(point[2]/self.partition_size)) d = (Vector(x, y, z)*self.partition_size-point).length() n = int(N.ceil((edge + d)/(2.*self.partition_size))) objects = [] for nx in range(-n, n): for ny in range(-n, n): for nz in range(-n, n): try: objects.append(self.partition[(nx+x, ny+y, nz+z)]) except KeyError: pass return Collection(objects) def selectShell(self, point, min, max=0): if min > max: min, max = max, min objects = Collection() minsq = min**2 maxsq = max**2 for index in self.partition.keys(): d1 = self.partition_size*N.array(index) - point.array d2 = d1 + self.partition_size dmin = (d1 > 0.)*d1 - (d2 < 0.)*d2 dminsq = N.add.reduce(dmin**2) dmaxsq = N.add.reduce(N.maximum(d1**2, d2**2)) if dminsq >= minsq and dmaxsq <= maxsq: objects.addObject(self.partition[index]) elif dmaxsq >= minsq and dminsq <= maxsq: o = Collection(self.partition[index]).selectShell(point, min, max) objects.addObject(o) return objects
[docs] def pairsWithinCutoff(self, cutoff): """ :param cutoff: a cutoff for pair distances :returns: a list containing all pairs of objects in the collection whose center-of-mass distance is less than the cutoff :rtype: list """ pairs = [] positions = {} for index, objects in self.partition.items(): pos = map(lambda o: o.position(), objects) positions[index] = pos for o1, o2 in Utility.pairs(zip(objects, pos)): if (o2[1]-o1[1]).length() <= cutoff: pairs.append((o1[0], o2[0])) partition_cutoff = int(N.floor((cutoff/self.partition_size)**2)) ones = N.array([1,1,1]) zeros = N.array([0,0,0]) keys = self.partition.keys() for i in range(len(keys)): p1 = keys[i] for j in range(i+1, len(keys)): p2 = keys[j] d = N.maximum(abs(N.array(p2)-N.array(p1)) - ones, zeros) if N.add.reduce(d*d) <= partition_cutoff: for o1, pos1 in zip(self.partition[p1], positions[p1]): for o2, pos2 in zip(self.partition[p2], positions[p2]): if (pos2-pos1).length() <= cutoff: pairs.append((o1, o2)) return pairs # # A special form of partitioned collection that stores the atoms # of all objects that are added to it. #
[docs]class PartitionedAtomCollection(PartitionedCollection): """ Partitioned collection of atoms PartitionedAtomCollection objects behave like PartitionedCollection atoms, except that they store only atoms. When a composite chemical object is added, its atoms are stored instead. """ def __init__(*args): apply(PartitionedCollection.__init__, args) def addChemicalObject(self, object): for atom in object.atomIterator(): PartitionedCollection.addChemicalObject(self, atom) def removeChemicalObject(self, object): for atom in object.atomIterator(): PartitionedCollection.removeChemicalObject(self, atom)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ConfigIO.html0000644000076600000240000016656512013143454021104 0ustar hinsenstaff00000000000000 MMTK.ConfigIO — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ConfigIO

# This module deals with input and output of configurations.
#
# Written by Konrad Hinsen
#

"""
I/O of molecular configurations

Input: Z-Matrix and Cartesian
Output: VRML
PDB files are handled in :class:`~MMTK.PDB`.
"""

__docformat__ = 'restructuredtext'

from MMTK import PDB, Units, Utility
from Scientific.Geometry.Objects3D import Sphere, Cone, Plane, Line, \
                                          rotatePoint
from Scientific.Geometry import Vector
from Scientific.Visualization import VRML
from Scientific import N
import os

#
# This class represents a Z-Matrix. Z-Matrix data consists of a list
# with one element for each atom being defined. Each entry is a
# list containing the data defining the atom.
#
[docs]class ZMatrix(object): """ Z-Matrix specification of a molecule conformation ZMatrix objects can be used in chemical database entries to specify molecule conformations by internal coordinates. With the exception of the three first atoms, each atom is defined relative to three previously atoms by a distance, an angle, and a dihedral angle. """ def __init__(self, data): """ :param data: a list of atom definitions. Each atom definition (except for the first three ones) is a list containing seven elements: - the atom to be defined - a previously defined atom and the distance to it - another previously defined atom and the angle to it - a third previously defined atom and the dihedral angle to it The definition of the first atom contains only the first element, the second atom needs the first three elements, and the third atom is defined by the first five elements. """ self.data = data self.coordinates = {} _substitute = True def findPositions(self): # First atom at origin self.coordinates[self.data[0][0]] = Vector(0,0,0) # Second atom along x-axis self.coordinates[self.data[1][0]] = Vector(self.data[1][2],0,0) # Third atom in xy-plane try: pos1 = self.coordinates[self.data[2][1]] except KeyError: raise ValueError("atom %d has no defined position" % self.data[2][1].number) try: pos2 = self.coordinates[self.data[2][3]] except KeyError: raise ValueError("atom %d has no defined position" % self.data[2][3].number) sphere = Sphere(pos1, self.data[2][2]) cone = Cone(pos1, pos2-pos1, self.data[2][4]) plane = Plane(Vector(0,0,0), Vector(0,0,1)) points = sphere.intersectWith(cone).intersectWith(plane) self.coordinates[self.data[2][0]] = points[0] # All following atoms defined by distance + angle + dihedral for entry in self.data[3:]: try: pos1 = self.coordinates[entry[1]] except KeyError: raise ValueError("atom %d has no defined position" % entry[1].number) try: pos2 = self.coordinates[entry[3]] except KeyError: raise ValueError("atom %d has no defined position" % entry[3].number) try: pos3 = self.coordinates[entry[5]] except KeyError: raise ValueError("atom %d has no defined position" % entry[5].number) distance = entry[2] angle = entry[4] dihedral = entry[6] sphere = Sphere(pos1, distance) cone = Cone(pos1, pos2-pos1, angle) plane123 = Plane(pos3, pos2, pos1) points = sphere.intersectWith(cone).intersectWith(plane123) for p in points: if Plane(pos2, pos1, p).normal * plane123.normal > 0: break p = rotatePoint(p, Line(pos1, pos2-pos1), dihedral) self.coordinates[entry[0]] = p
[docs] def applyTo(self, object): """ Define the positions of the atoms in a chemical object by the internal coordinates of the Z-Matrix. :param object: the object to which the Z-Matrix is applied """ if not len(self.coordinates): self.findPositions() for entry in self.coordinates.items(): object.setPosition(entry[0], entry[1]) object.normalizePosition() # # This class represents a dictionary of Cartesian positions #
[docs]class Cartesian(object): """ Cartesian specification of a molecule conformation Cartesian objects can be used in chemical database entries to specify molecule conformations by Cartesian coordinates. """ def __init__(self, data): """ :param data: a dictionary mapping atoms to tuples of length three that define its Cartesian coordinates """ self.dict = data _substitute = True
[docs] def applyTo(self, object): """ Define the positions of the atoms in a chemical object by the stored coordinates. :param object: the object to which the coordinates are applied """ for a, r in self.dict.items(): object.setPosition(a, Vector(r[0], r[1], r[2])) # # VRML output #
class VRMLWireframeFile(VRML.VRMLFile): def __init__(self, filename, color_values = None): VRML.VRMLFile.__init__(self, filename, 'w') self.warning = 0 self.color_values = color_values if self.color_values is not None: lower = N.minimum.reduce(color_values.array) upper = N.maximum.reduce(color_values.array) self.color_scale = VRML.ColorScale((lower, upper)) def write(self, object, configuration = None, distance = None): from MMTK.ChemicalObjects import isChemicalObject from MMTK.Universe import InfiniteUniverse if distance is None: try: distance = object.universe().distanceVector except AttributeError: distance = InfiniteUniverse().distanceVector if not isChemicalObject(object): for o in object: self.write(o, configuration, distance) else: for bu in object.bondedUnits(): for a in bu.atomList(): self.writeAtom(a, configuration) if hasattr(bu, 'bonds'): for b in bu.bonds: self.writeBond(b, configuration, distance) def close(self): VRML.VRMLFile.close(self) if self.warning: Utility.warning('Some atoms are missing in the output file ' + \ 'because their positions are undefined.') self.warning = 0 def atomColor(self, atom): if self.color_values is None: return atom.color else: return self.color_scale(self.color_values[atom]) def writeAtom(self, atom, configuration): pass def writeBond(self, bond, configuration, distance): p1 = bond.a1.position(configuration) p2 = bond.a2.position(configuration) if p1 is not None and p2 is not None: bond_vector = 0.5*distance(bond.a1, bond.a2, configuration) cut = bond_vector != 0.5*(p2-p1) color1 = self.atomColor(bond.a1) color2 = self.atomColor(bond.a2) material1 = VRML.EmissiveMaterial(color1) material2 = VRML.EmissiveMaterial(color2) if color1 == color2 and not cut: c = VRML.Line(p1, p2, material = material1) c.writeToFile(self) else: c = VRML.Line(p1, p1+bond_vector, material = material1) c.writeToFile(self) c = VRML.Line(p2, p2-bond_vector, material = material2) c.writeToFile(self) class VRMLHighlight(VRMLWireframeFile): def writeAtom(self, atom, configuration): try: highlight = atom.highlight except AttributeError: highlight = 0 if highlight: p = atom.position(configuration) if p is None: self.warning = 1 else: s = VRML.Sphere(p, 0.1*Units.Ang, material = VRML.DiffuseMaterial(atom.color), reuse = 1) s.writeToFile(self) class VRMLBallAndStickFile(VRMLWireframeFile): def writeAtom(self, atom, configuration): p = atom.position(configuration) if p is None: self.warning = 1 else: color = self.atomColor(atom) s = VRML.Sphere(p, 0.1*Units.Ang, material = VRML.DiffuseMaterial(color), reuse = 1) s.writeToFile(self) def writeBond(self, bond, configuration, distance): p1 = bond.a1.position(configuration) p2 = bond.a2.position(configuration) if p1 is not None and p2 is not None: bond_vector = 0.5*distance(bond.a1, bond.a2, configuration) cut = bond_vector != 0.5*(p2-p1) color1 = self.atomColor(bond.a1) color2 = self.atomColor(bond.a2) material1 = VRML.EmissiveMaterial(color1) material2 = VRML.EmissiveMaterial(color2) if color1 == color2 and not cut: c = VRML.Cylinder(p1, p2, 0.03*Units.Ang, material = material1) c.writeToFile(self) else: c = VRML.Cylinder(p1, p1+bond_vector, 0.03*Units.Ang, material = material1) c.writeToFile(self) c = VRML.Cylinder(p2, p2-bond_vector, 0.03*Units.Ang, material = material2) c.writeToFile(self) class VRMLChargeFile(VRMLWireframeFile): color_scale = VRML.SymmetricColorScale(1.) def writeAtom(self, atom, configuration): p = atom.position(configuration) c = atom.charge() c = max(min(c, 1.), -1.) if p is None: self.warning = 1 else: s = VRML.Sphere(p, 0.1*Units.Ang, material = VRML.Material(diffuse_color = self.color_scale(c))) s.writeToFile(self) bond_material = VRML.DiffuseMaterial('black') def writeBond(self, bond, configuration, distance): p1 = bond.a1.position(configuration) p2 = bond.a2.position(configuration) if p1 is not None and p2 is not None: bond_vector = 0.5*distance(bond.a1, bond.a2, configuration) cut = bond_vector != 0.5*(p2-p1) if not cut: c = VRML.Line(p1, p2, material = self.bond_material) c.writeToFile(self) else: c = VRML.Line(p1, p1+bond_vector, material = self.bond_material) c.writeToFile(self) c = VRML.Line(p2, p2-bond_vector, material = self.bond_material) c.writeToFile(self) VRMLFile = VRMLWireframeFile # # Recognize some standard file types by their extensions # def fileFormatFromExtension(filename): filename, ext = os.path.splitext(filename) if ext in _file_compressions: filename, ext = os.path.splitext(filename) try: return _file_formats[ext] except KeyError: raise IOError('Unknown file format') _file_formats = {'.pdb': 'pdb', '.wrl': 'vrml'} _file_compressions = ['.gz', '.Z'] # # Output file for a specified format # def OutputFile(filename, format = None): if format is None: format = fileFormatFromExtension(filename) format = tuple(format.split('.')) try: return _file_types[format](filename) except KeyError: if len(format) == 1: return _file_types[format[0]](filename) else: _file_types[format[0]](filename, format[1]) _file_types = {'pdb': PDB.PDBOutputFile, ('vrml',): VRMLFile, ('vrml', 'wireframe'): VRMLWireframeFile, ('vrml', 'highlight'): VRMLHighlight, ('vrml', 'ball_and_stick'): VRMLBallAndStickFile, ('vrml', 'charge'): VRMLChargeFile}
MMTK-2.7.9/Doc/HTML/_modules/MMTK/DCD.html0000644000076600000240000005214412013143454020024 0ustar hinsenstaff00000000000000 MMTK.DCD — MMTK User Guide 2.7.7 documentation

Source code for MMTK.DCD

# This module implements a DCD reader/writer
#
# Written by Lutz Ehrlich
# Adapted to MMTK conventions by Konrad Hinsen


"""
Reading and writing of DCD trajectory files

The DCD format for trajectories is used by CHARMM, X-Plor,
and NAMD. It can be read by various visualization programs.

The DCD format is defined as a binary (unformatted) Fortran
format and is therefore platform-dependent.

"""

__docformat__ = 'restructuredtext'

import MMTK_DCD
from MMTK import PDB, Trajectory, Units
from Scientific import N


[docs]class DCDReader(Trajectory.TrajectoryGenerator): """ Reader for DCD trajectories (CHARMM/X-Plor/NAMD) A DCDReader reads a DCD trajectory and "plays back" the data as if it were generated directly by an integrator. The universe for which the DCD file is read must be perfectly compatible with the data in the file, including an identical internal atom numbering. This can be guaranteed only if the universe was created from a PDB file that is compatible with the DCD file without leaving out any part of the system. Reading is started by calling the reader object. The following data categories and variables are available for output: * category "time": time * category "configuration": configuration """ default_options = {} available_data = ['configuration', 'time'] restart_data = None def __init__(self, universe, **options): """ :param universe: the universe for which the information from the trajectory file is read :param options: keyword options :keyword dcd_file: the name of the DCD trajecory file to be read :keyword actions: a list of actions to be executed periodically (default is none) """ Trajectory.TrajectoryGenerator.__init__(self, universe, options) def __call__(self, **options): self.setCallOptions(options) configuration = self.universe.configuration() MMTK_DCD.readDCD(self.universe, configuration.array, self.getActions(), self.getOption('dcd_file'))
def writeDCD(vector_list, dcd_file_name, factor, atom_order=None, delta_t=0.1, conf_flag=1): universe = vector_list[0].universe natoms = universe.numberOfPoints() if atom_order is None: atom_order = N.arrayrange(natoms) else: atom_order = N.array(atom_order) i_start = 0 # always start at frame 0 n_savc = 1 # save every frame fd = MMTK_DCD.writeOpenDCD(dcd_file_name, natoms, len(vector_list), i_start, n_savc, delta_t) for vector in vector_list: if conf_flag: vector = universe.contiguousObjectConfiguration(None, vector) array = factor*vector.array x = N.take(array[:, 0], atom_order).astype(N.Float16) y = N.take(array[:, 1], atom_order).astype(N.Float16) z = N.take(array[:, 2], atom_order).astype(N.Float16) MMTK_DCD.writeDCDStep(fd, x, y, z) MMTK_DCD.writeCloseDCD(fd) def writePDB(universe, configuration, pdb_file_name): offset = None if universe is not None: configuration = universe.contiguousObjectConfiguration(None, configuration) pdb = PDB.PDBOutputFile(pdb_file_name, 'xplor') pdb.write(universe, configuration) sequence = pdb.atom_sequence pdb.close() return sequence
[docs]def writeDCDPDB(conf_list, dcd_file_name, pdb_file_name, delta_t=0.1): """ Write a sequence of configurations to a DCD file and generate a compatible PDB file. :param conf_list: the sequence of configurations :type conf_list: sequence of :class:`~MMTK.ParticleProperties.Configuration` :param dcd_file_name: the name of the DCD file :type dcd_file_name: str :param pdb_file_name: the name of the PDB file :type pdb_file_name: str :param delta_t: the time step between two configurations :type delta_t: float """ universe = conf_list[0].universe sequence = writePDB(universe, conf_list[0], pdb_file_name) indices = map(lambda a: a.index, sequence) writeDCD(conf_list, dcd_file_name, 1./Units.Ang, indices, delta_t, 1)
[docs]def writeVelocityDCDPDB(vel_list, dcd_file_name, pdb_file_name, delta_t=0.1): """ Write a sequence of velocity particle vectors to a DCD file and generate a compatible PDB file. :param vel_list: the sequence of velocity particle vectors :type vel_list: sequence of :class:`~MMTK.ParticleProperties.ParticleVector` :param dcd_file_name: the name of the DCD file :type dcd_file_name: str :param pdb_file_name: the name of the PDB file :type pdb_file_name: str :param delta_t: the time step between two velocity sets :type delta_t: float """ universe = vel_list[0].universe sequence = writePDB(universe, universe.configuration(), pdb_file_name) indices = map(lambda a: a.index, sequence) writeDCD(vel_list, dcd_file_name, 1./(Units.Ang/Units.akma_time), indices, delta_t, 0)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Deformation.html0000644000076600000240000015142612013143454021704 0ustar hinsenstaff00000000000000 MMTK.Deformation — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Deformation

# Deformation energy module
#
# Written by Konrad Hinsen
#

"""
Deformation energies in proteins

This module implements deformational energies for use in the analysis
of motions and conformational changes in macromolecules. A description
of the techniques can be found in the following articles:

  |  K. Hinsen
  |  Analysis of domain motions by approximate normal mode calculations
  |  Proteins 33 (1998): 417-429

  |  K. Hinsen, A. Thomas, M.J. Field
  |  Analysis of domain motions in large proteins
  |  Proteins 34 (1999): 369-382
"""

__docformat__ = 'restructuredtext'

try:
    from MMTK_forcefield import NonbondedList
    from MMTK_deformation import deformation, reduceDeformation, \
                                 reduceFiniteDeformation
except ImportError:
    pass
from MMTK import ParticleProperties
from Scientific import N

#
# Deformation energy evaluations
#
class DeformationEvaluationFunction(object):

    def __init__(self, universe, fc_length = 0.7, cutoff = 1.2,
                 factor = 46402., form = 'exponential'):
        self.universe = universe
        self.fc_length = fc_length
        self.cutoff = cutoff
        self.factor = factor

        nothing = N.zeros((0,2), N.Int)
        self.pairs = NonbondedList(nothing, nothing, nothing,
                                   universe._spec, cutoff)
        self.pairs.update(universe.configuration().array)
        self.normalize = 0
        try:
            self.version = self.forms.index(form)
        except ValueError:
            raise ValueError("unknown functional form")

    forms = ['exponential', 'calpha']

    def newConfiguration(self):
        self.pairs.update(self.universe.configuration().array)


[docs]class DeformationFunction(DeformationEvaluationFunction): """ Infinite-displacement deformation function The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. A DeformationFunction object must be called with a single parameter, which is a ParticleVector object containing the infinitesimal displacements of the atoms for which the deformation is to be evaluated. The return value is a ParticleScalar object containing the deformation value for each atom. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEvaluationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __call__(self, vector): conf = self.universe.configuration() r = ParticleProperties.ParticleScalar(self.universe) l = deformation(conf.array, vector.array, self.pairs, None, r.array, self.cutoff, self.fc_length, self.factor, self.normalize, 0, self.version) return r
[docs]class NormalizedDeformationFunction(DeformationFunction): """ Normalized infinite-displacement deformation function The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. The normalization is defined by equation 10 of reference 1 (see above). A NormalizedDeformationFunction object must be called with a single parameter, which is a ParticleVector object containing the infinitesimal displacements of the atoms for which the deformation is to be evaluated. The return value is a ParticleScalar object containing the deformation value for each atom. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __init__(self, *args, **kwargs): apply(DeformationFunction.__init__, (self, ) + args, kwargs) self.normalize = 1
[docs]class FiniteDeformationFunction(DeformationEvaluationFunction): """ Finite-displacement deformation function The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. A FiniteDeformationFunction object must be called with a single parameter, which is a Configuration or a ParticleVector object containing the alternate configuration of the universe for which the deformation is to be evaluated. The return value is a ParticleScalar object containing the deformation value for each atom. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEvaluationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __call__(self, vector): conf = self.universe.configuration() vector = vector-conf r = ParticleProperties.ParticleScalar(self.universe) l = deformation(conf.array, vector.array, self.pairs, None, r.array, self.cutoff, self.fc_length, self.factor, 0, 1, self.version) return r
[docs]class DeformationEnergyFunction(DeformationEvaluationFunction): """ Infinite-displacement deformation energy function The deformation energy is the sum of the deformation values over all atoms of a system. The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. A DeformationEnergyFunction is called with one or two parameters. The first parameter is a ParticleVector object containing the infinitesimal displacements of the atoms for which the deformation energy is to be evaluated. The optional second argument can be set to a non-zero value to request the gradients of the energy in addition to the energy itself. In that case there are two return values (energy and the gradients in a ParticleVector object), otherwise only the energy is returned. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEvaluationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __call__(self, vector, gradients = None): conf = self.universe.configuration() g = None if gradients is not None: if ParticleProperties.isParticleProperty(gradients): g = gradients elif isinstance(gradients, N.array_type): g = ParticleProperties.ParticleVector(self.universe, gradients) elif gradients: g = ParticleProperties.ParticleVector(self.universe) if g is None: g_array = None else: g_array = g.array l = deformation(conf.array, vector.array, self.pairs, g_array, None, self.cutoff, self.fc_length, self.factor, self.normalize, 0, self.version) if g is None: return l else: return l, g
[docs]class NormalizedDeformationEnergyFunction(DeformationEnergyFunction): """ Normalized infinite-displacement deformation energy function The normalized deformation energy is the sum of the normalized deformation values over all atoms of a system. The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. The normalization is defined by equation 10 of reference 1. A NormalizedDeformationEnergyFunction is called with one or two parameters. The first parameter is a ParticleVector object containing the infinitesimal displacements of the atoms for which the deformation energy is to be evaluated. The optional second argument can be set to a non-zero value to request the gradients of the energy in addition to the energy itself. In that case there are two return values (energy and the gradients in a ParticleVector object), otherwise only the energy is returned. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEnergyFunction.__init__(self, universe, fc_length, cutoff, factor, form) self.normalize = 1
[docs]class FiniteDeformationEnergyFunction(DeformationEvaluationFunction): """ Finite-displacement deformation energy function The deformation energy is the sum of the deformation values over all atoms of a system. The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. A FiniteDeformationEnergyFunction is called with one or two parameters. The first parameter is a ParticleVector object containing the alternate configuration of the universe for which the deformation energy is to be evaluated. The optional second argument can be set to a non-zero value to request the gradients of the energy in addition to the energy itself. In that case there are two return values (energy and the gradients in a ParticleVector object), otherwise only the energy is returned. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEvaluationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __call__(self, vector, gradients = None): conf = self.universe.configuration() g = None if gradients is not None: if ParticleProperties.isParticleProperty(gradients): g = gradients elif isinstance(gradients, N.array_type): g = ParticleProperties.ParticleVector(self.universe, gradients) elif gradients: g = ParticleProperties.ParticleVector(self.universe) if g is None: g_array = None else: g_array = g.array l = deformation(conf.array, vector.array, self.pairs, g_array, None, self.cutoff, self.fc_length, self.factor, 0, 1, self.version) if g is None: return l else: return l, g # # Deformation energy minimization #
[docs]class DeformationReducer(DeformationEvaluationFunction): """ Iterative reduction of the deformation energy The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. A DeformationReducer is called with two arguments. The first is a ParticleVector containing the initial infinitesimal displacements for all atoms. The second is an integer indicating the number of iterations. The result is a modification of the displacements by steepest-descent minimization of the deformation energy. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEvaluationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __call__(self, vector, niter): conf = self.universe.configuration() reduceDeformation(conf.array, vector.array, self.pairs, self.cutoff, self.fc_length, self.factor, niter, self.version)
[docs]class FiniteDeformationReducer(DeformationEvaluationFunction): """ Iterative reduction of the finite-displacement deformation energy The default values are appropriate for a |C_alpha| model of a protein with the global scaling described in the reference cited above. A FiniteDeformationReducer is called with two arguments. The first is a ParticleVector or Configuration containing the alternate configuration for which the deformation energy is evaluated. The second is the RMS distance that defines the termination condition. The return value a configuration that differs from the input configuration by approximately the specified RMS distance, and which is obtained by iterative steepest-descent minimization of the finite-displacement deformation energy. """ def __init__(self, universe, fc_length = 0.7, cutoff = 1.2, factor = 46402., form = 'exponential'): """ :param universe: the universe for which the deformation function is defined :type universe: :class:`~MMTK.Universe.Universe` :param fc_length: the range parameter r_0 in the pair interaction term :type fc_length: float :param cutoff: the cutoff used in the deformation calculation :type cutoff: float :param factor: a global scaling factor :type factor: float :param form: the functional form ('exponential' or 'calpha') :type form: str """ DeformationEvaluationFunction.__init__(self, universe, fc_length, cutoff, factor, form) def __call__(self, vector, rms_reduction): conf = self.universe.configuration() vector = vector-conf reduceFiniteDeformation(conf.array, vector.array, self.pairs, self.cutoff, self.fc_length, self.factor, rms_reduction, self.version) return conf+vector
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Dynamics.html0000644000076600000240000013561612013143455021210 0ustar hinsenstaff00000000000000 MMTK.Dynamics — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Dynamics

# This module implements MD integrators
#
# Written by Konrad Hinsen
#

"""
Molecular Dynamics integrators
"""

__docformat__ = 'restructuredtext'

from MMTK import Environment, Features, Trajectory, Units
import MMTK_dynamics
from Scientific import N

#
# Integrator base class
#
class Integrator(Trajectory.TrajectoryGenerator):

    def __init__(self, universe, options):
        Trajectory.TrajectoryGenerator.__init__(self, universe, options)

    default_options = {'first_step': 0, 'steps': 100, 'delta_t': 1.*Units.fs,
                       'background': False, 'threads': None,
                       'mpi_communicator': None, 'actions': []}

    available_data = ['configuration', 'velocities', 'gradients',
                      'energy', 'thermodynamic', 'time', 'auxiliary']

    restart_data = ['configuration', 'velocities', 'energy',
                    'thermodynamic', 'auxiliary']

    def __call__(self, options):
        raise AttributeError

#
# Velocity-Verlet integrator
#
[docs]class VelocityVerletIntegrator(Integrator): """ Velocity-Verlet molecular dynamics integrator The integrator can handle fixed atoms, distance constraints, a thermostat, and a barostat, as well as any combination. It is fully thread-safe. The integration is started by calling the integrator object. All the keyword options (see documnentation of __init__) can be specified either when creating the integrator or when calling it. The following data categories and variables are available for output: - category "time": time - category "configuration": configuration and box size (for periodic universes) - category "velocities": atomic velocities - category "gradients": energy gradients for each atom - category "energy": potential and kinetic energy, plus extended-system energy terms if a thermostat and/or barostat are used - category "thermodynamic": temperature, volume (if a barostat is used) and pressure - category "auxiliary": extended-system coordinates if a thermostat and/or barostat are used """ def __init__(self, universe, **options): """ :param universe: the universe on which the integrator acts :type universe: :class:`~MMTK.Universe.Universe` :keyword steps: the number of integration steps (default is 100) :type steps: int :keyword delta_t: the time step (default is 1 fs) :type delta_t: float :keyword actions: a list of actions to be executed periodically (default is none) :type actions: list :keyword threads: the number of threads to use in energy evaluation (default set by MMTK_ENERGY_THREADS) :type threads: int :keyword background: if True, the integration is executed as a separate thread (default: False) :type background: bool :keyword mpi_communicator: an MPI communicator object, or None, meaning no parallelization (default: None) :type mpi_communicator: Scientific.MPI.MPICommunicator """ Integrator.__init__(self, universe, options) self.features = [Features.FixedParticleFeature, Features.NoseThermostatFeature, Features.AndersenBarostatFeature, Features.DistanceConstraintsFeature] def __call__(self, **options): """ Run the integrator. The keyword options are the same as described under __init__. """ self.setCallOptions(options) used_features = Features.checkFeatures(self, self.universe) configuration = self.universe.configuration() velocities = self.universe.velocities() if velocities is None: raise ValueError("no velocities") masses = self.universe.masses() fixed = self.universe.getAtomBooleanArray('fixed') nt = self.getOption('threads') comm = self.getOption('mpi_communicator') evaluator = self.universe.energyEvaluator(threads=nt, mpi_communicator=comm) evaluator = evaluator.CEvaluator() constraints, const_distances_sq, c_blocks = \ _constraintArrays(self.universe) type = 'NVE' if Features.NoseThermostatFeature in used_features: type = 'NVT' thermostat = self.universe.environmentObjectList( Environment.NoseThermostat)[0] t_parameters = thermostat.parameters t_coordinates = thermostat.coordinates else: t_parameters = N.zeros((0,), N.Float) t_coordinates = N.zeros((2,), N.Float) if Features.AndersenBarostatFeature in used_features: if self.universe.cellVolume() is None: raise ValueError("Barostat requires finite volume universe") if type == 'NVE': type = 'NPH' else: type = 'NPT' barostat = self.universe.environmentObjectList( Environment.AndersenBarostat)[0] b_parameters = barostat.parameters b_coordinates = barostat.coordinates else: b_parameters = N.zeros((0,), N.Float) b_coordinates = N.zeros((1,), N.Float) args = (self.universe, configuration.array, velocities.array, masses.array, fixed.array, evaluator, constraints, const_distances_sq, c_blocks, t_parameters, t_coordinates, b_parameters, b_coordinates, self.getOption('delta_t'), self.getOption('first_step'), self.getOption('steps'), self.getActions(), type + ' dynamics trajectory with ' + self.optionString(['delta_t', 'steps'])) return self.run(MMTK_dynamics.integrateVV, args) # # Velocity scaling, removal of global translation/rotation #
[docs]class VelocityScaler(Trajectory.TrajectoryAction): """ Periodic velocity scaling action A VelocityScaler object is used in the action list of a VelocityVerletIntegrator. It rescales all atomic velocities by a common factor to make the temperature of the system equal to a predefined value. """ def __init__(self, temperature, window=0., first=0, last=None, skip=1): """ :param temperature: the temperature value to which the velocities should be scaled :type temperature: float :param window: the deviation from the ideal temperature that is tolerated in either direction before rescaling takes place :type window: float :param first: the number of the first step at which the action is executed :type first: int :param last: the number of the first step at which the action is no longer executed. A value of None indicates that the action should be executed indefinitely. :type last: int :param skip: the number of steps to skip between two applications of the action :type skip: int """ Trajectory.TrajectoryAction.__init__(self, first, last, skip) self.parameters = N.array([temperature, window], N.Float) self.Cfunction = MMTK_dynamics.scaleVelocities
[docs]class Heater(Trajectory.TrajectoryAction): """ Periodic heating action A Heater object us used in the action list of a VelocityVerletIntegrator. It scales the velocities to a temperature that increases over time. """ def __init__(self, temp1, temp2, gradient, first=0, last=None, skip=1): """ :param temp1: the temperature value to which the velocities should be scaled initially :type temp1: float :param temp2: the final temperature value to which the velocities should be scaled :type temp2: float :param gradient: the temperature gradient (in K/ps) :type gradient: float :param first: the number of the first step at which the action is executed :type first: int :param last: the number of the first step at which the action is no longer executed. A value of None indicates that the action should be executed indefinitely. :type last: int :param skip: the number of steps to skip between two applications of the action :type skip: int """ Trajectory.TrajectoryAction.__init__(self, first, last, skip) self.parameters = N.array([temp1, temp2, gradient], N.Float) self.Cfunction = MMTK_dynamics.heat
[docs]class BarostatReset(Trajectory.TrajectoryAction): """ Barostat reset action A BarostatReset object is used in the action list of a VelocityVerletIntegrator. It resets the barostat coordinate to zero. """ def __init__(self, first=0, last=None, skip=1): """ :param first: the number of the first step at which the action is executed :type first: int :param last: the number of the first step at which the action is no longer executed. A value of None indicates that the action should be executed indefinitely. :type last: int :param skip: the number of steps to skip between two applications of the action :type skip: int """ Trajectory.TrajectoryAction.__init__(self, first, last, skip) self.parameters = N.zeros((0,), N.Float) self.Cfunction = MMTK_dynamics.resetBarostat
[docs]class TranslationRemover(Trajectory.TrajectoryAction): """ Action that eliminates global translation A TranslationRemover object is used in the action list of a VelocityVerletIntegrator. It subtracts the total velocity of the system from each atomic velocity. """ def __init__(self, first=0, last=None, skip=1): """ :param first: the number of the first step at which the action is executed :type first: int :param last: the number of the first step at which the action is no longer executed. A value of None indicates that the action should be executed indefinitely. :type last: int :param skip: the number of steps to skip between two applications of the action :type skip: int """ Trajectory.TrajectoryAction.__init__(self, first, last, skip) self.parameters = None self.Cfunction = MMTK_dynamics.removeTranslation
[docs]class RotationRemover(Trajectory.TrajectoryAction): """ Action that eliminates global rotation A RotationRemover object is used in the action list of a VelocityVerletIntegrator. It adjusts the atomic velocities such that the total angular momentum is zero. """ def __init__(self, first=0, last=None, skip=1): """ :param first: the number of the first step at which the action is executed :type first: int :param last: the number of the first step at which the action is no longer executed. A value of None indicates that the action should be executed indefinitely. :type last: int :param skip: the number of steps to skip between two applications of the action :type skip: int """ Trajectory.TrajectoryAction.__init__(self, first, last, skip) self.parameters = None self.Cfunction = MMTK_dynamics.removeRotation # # Construct constraint arrays #
def _constraintArrays(universe): nc = universe.numberOfDistanceConstraints() constraints = N.zeros((nc, 2), N.Int) const_distances_sq = N.zeros((nc, ), N.Float) if nc > 0: i = 0 c_blocks = [0] for o in universe.objectList(): for c in o.distanceConstraintList(): constraints[i, 0] = c[0].index constraints[i, 1] = c[1].index const_distances_sq[i] = c[2]*c[2] i = i + 1 c_blocks.append(i) c_blocks = N.array(c_blocks) else: c_blocks = N.zeros((1,), N.Int) return constraints, const_distances_sq, c_blocks # # Enforce distance constraints for current configuration # def enforceConstraints(universe, configuration=None): constraints, const_distances_sq, c_blocks = _constraintArrays(universe) if len(constraints) == 0: return if configuration is None: configuration = universe.configuration() MMTK_dynamics.enforceConstraints(universe._spec, configuration.array, universe.masses().array, constraints, const_distances_sq, c_blocks) # # Project velocity vector onto the constraint surface # def projectVelocities(universe, velocities): constraints, const_distances_sq, c_blocks = _constraintArrays(universe) if len(constraints) == 0: return MMTK_dynamics.projectVelocities(universe._spec, universe.configuration().array, velocities.array, universe.masses().array, constraints, const_distances_sq, c_blocks)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Environment.html0000644000076600000240000003243512013143455021740 0ustar hinsenstaff00000000000000 MMTK.Environment — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Environment

# This module defines environment objects for universes.
#
# Written by Konrad Hinsen
#

"""
Environment objects

Environment objects are objects that define a simulation system without
being composed of atoms. Examples are thermostats, barostats, external
fields, etc.
"""

__docformat__ = 'restructuredtext'

from Scientific import N

#
# The environment object base class
#
class EnvironmentObject(object):

    is_environment_object = 1

    def checkCompatibilityWith(self, other):
        pass

    def description(self):
        return "o('Environment." + self.__class__.__name__ + \
               `tuple(self.parameters)` + "')"

# Type check

def isEnvironmentObject(object):
    return hasattr(object, 'is_environment_object')

#
# Nose thermostat class
#
[docs]class NoseThermostat(EnvironmentObject): """ Nose thermostat for Molecular Dynamics A thermostat object can be added to a universe and will then modify the integration algorithm to a simulation of an NVT ensemble. """ def __init__(self, temperature, relaxation_time = 0.2): """ :param temperature: the temperature set by the thermostat :type temperature: float :param relaxation_time: the relaxation time of the thermostat coordinate :type relaxation_time: float """ self.arguments = (temperature, relaxation_time) self.parameters = N.array([temperature, relaxation_time]) self.coordinates = N.array([0., 0.]) def setTemperature(self, temperature): self.parameters[0] = temperature def setRelaxationTime(self, t): self.parameters[1] = t def checkCompatibilityWith(self, other): if other.__class__ is NoseThermostat: raise ValueError("the universe already has a thermostat") # # Andersen barostat class #
[docs]class AndersenBarostat(EnvironmentObject): """ Andersen barostat for Molecular Dynamics A barostat object can be added to a universe and will then together with a thermostat object modify the integration algorithm to a simulation of an NPT ensemble. """ def __init__(self, pressure, relaxation_time = 1.5): """ :param pressure: the pressure set by the barostat :type pressure: float :param relaxation_time: the relaxation time of the barostat coordinate :type relaxation_time: float """ self.arguments = (pressure, relaxation_time) self.parameters = N.array([pressure, relaxation_time]) self.coordinates = N.array([0.]) def setPressure(self, pressure): self.parameters[0] = pressure def setRelaxationTime(self, t): self.parameters[1] = t def checkCompatibilityWith(self, other): if other.__class__ is AndersenBarostat: raise ValueError("the universe already has a barostat")
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Field.html0000644000076600000240000015645312013143454020465 0ustar hinsenstaff00000000000000 MMTK.Field — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Field

# This module defines scalar and vector fields in molecular systems
#
# Written by Konrad Hinsen
#

"""
Scalar and vector fields in molecular systems

This module defines field objects that are useful in the analysis
and visualization of collective motions in molecular systems. Atomic
quantities characterizing collective motions vary slowly in space, and
can be considered functions of position instead of values per atom.
Functions of position are called fields, and mathematical techniques
for the analysis of fields have proven useful in many branches of
physics. Fields can be described numerically by values on a
regular grid. In addition to permitting the application of vector
analysis methods to atomic quantities, the introduction of
fields is a valuable visualization aid, because information defined on
a coarse regular grid can be added to a picture of a molecular system
without overloading it.
"""

__docformat__ = 'restructuredtext'

from MMTK import Collections, ParticleProperties, Visualization
from Scientific.Visualization import Color
from Scientific.Geometry import Vector, TensorAnalysis
from Scientific import N


[docs]class AtomicField(object): """ A field whose values are determined by atomic quantities This is an abstract base class. To create field objects, use one of its subclasses. """ def __init__(self, system, grid_size, values): self.system = system if isinstance(grid_size, tuple): self.box, self.field, self.points, self.colors = grid_size else: if values.data_rank != 1: raise TypeError("data not a particle variable") self.box = Collections.PartitionedAtomCollection(grid_size, system) self._setField(values) def _setField(self, data): points = [] values = [] colors = [] default_color = Color.ColorByName('black') for min, max, atoms in self.box.partitions(): center = 0.5*(min+max) points.append(center.array) v = data.zero() total_weight = 0. color = default_color for a in atoms: d = a.position()-center weight = N.exp(-d*d/self.box.partition_size**2) v = v + weight*data[a] total_weight = total_weight + weight c = default_color try: c = a.color except AttributeError: pass if isinstance(c, basestring): c = Color.ColorByName(c) color = color + c values.append(v/total_weight) colors.append(color) min = N.minimum.reduce(points) max = N.maximum.reduce(points) axes = (N.arange(min[0], max[0]+1, self.box.partition_size), N.arange(min[1], max[1]+1, self.box.partition_size), N.arange(min[2], max[2]+1, self.box.partition_size)) array = N.zeros(tuple(map(len, axes)) + data.value_rank*(3,), N.Float) inside = N.zeros(tuple(map(len, axes)), N.Float) for p, v in zip(points, values): indices = N.floor((p-min)/self.box.partition_size+0.5) indices = tuple(indices.astype(N.Int)) array[indices] = v inside[indices] = 1. self.field = self.field_class(axes, array, data.zero()) inside = TensorAnalysis.ScalarField(axes, inside).gradient().length() self.points = [] self.colors = [] for i in range(len(points)): p = points[i] test = 0 try: test = apply(inside, tuple(p)) > 1.e-10 except ValueError: pass if test: self.points.append(p) self.colors.append(colors[i]) def __call__(self, point): return self.field(point)
[docs] def particleValues(self): """ :returns: the values of the field at the positions of the atoms :rtype: :class:`~MMTK.ParticleProperties.ParticleProperty` """ universe = self.system.universe() rank = self.field.rank if rank == 0: v = ParticleProperties.ParticleScalar(universe) elif rank == 1: v = ParticleProperties.ParticleVector(universe) else: raise ValueError("no appropriate return type") for a in self.system.atomList(): v[a] = self.field(a.position()) return v
[docs] def writeToFile(self, filename, scale = 1., length_scale=1., color = None): """ Writes a graphical representation of the field to a VRML file. :param filename: the name of the destination file :type filename: str :param scale: scale factor applied to all field values :type scale: float :param color: the color for all graphics objects :type color: Scientific.Visualization.Color """ from Scientific.Visualization import VRML2 objects = self.graphicsObjects(scale=scale, length_scale=length_scale, color=color, graphics_module=VRML2) VRML2.Scene(objects).writeToFile(filename)
[docs] def view(self, scale = 1., length_scale = 1., color = None): """ Shows a graphical representation of the field using a VRML viewer. :param scale: scale factor applied to all field values :type scale: float :param color: the color for all graphics objects :type color: Scientific.Visualization.Color """ from Scientific.Visualization import VRML2 objects = self.graphicsObjects(scale=scale, length_scale=length_scale, color=color, graphics_module=VRML2) VRML2.Scene(objects).view()
[docs]class AtomicScalarField(AtomicField, Visualization.Viewable): """ Scalar field defined by atomic quantities For visualization, scalar fields are represented by a small cube on each grid point whose color indicates the field's value on a symmetric red-to-green color scale defined by the range of the field values. Additional keyword options exist for graphics object generation: - scale=factor, to multiply all field values by a factor - range=(min, max), to eliminate graphics objects for values that are smaller than min or larger than max """ def __init__(self, system, grid_size, values): """ :param system: any subset of a molecular system :param grid_size: the spacing of a cubic grid on which the field values are defined. The value for a point is obtained by averaging the atomic quantities over all atoms in a cube centered on the point. :type grid_size: float :param values: the atomic values that define the field :type values: :class:`~MMTK.ParticleProperties.ParticleScalar` """ if values is not None and values.value_rank != 0: raise TypeError("data not a vector field") self.field_class = TensorAnalysis.ScalarField AtomicField.__init__(self, system, grid_size, values)
[docs] def gradient(self): """ :returns: the gradient of the field :rtype: :class:`~MMTK.Field.AtomicVectorField` """ field = self.field.gradient() return AtomicVectorField(self.system, (self.box, field, self.points, self.colors), None)
[docs] def laplacian(self): """ :returns: the laplacian of the field :rtype: :class:`~MMTK.Field.AtomicScalarField` """ field = self.field.laplacian() return AtomicScalarField(self.system, (self.box, field, self.points, self.colors), None)
def _graphics(self, conf, distance_fn, model, module, options): scale = options.get('scale', 1.) range = options.get('range', (None, None)) length_scale = options.get('length_scale', 1.) lower, upper = range size = self.box.partition_size/10. objects = [] color_scale = module.SymmetricColorScale(1.) for p, c in zip(self.points, self.colors): p = tuple(p) v = apply(self.field, p) if (lower is None or v > lower) and (upper is None or v < upper): p = apply(Vector, p) v = scale*v if v < -1. or v > 1.: m = module.DiffuseMaterial('black') else: m = module.Material(diffuse_color = color_scale(scale*v)) objects.append(module.Cube(length_scale*p, length_scale*size, material = m)) return objects
[docs]class AtomicVectorField(AtomicField, Visualization.Viewable): """ Vector field defined by atomic quantities For visualization, scalar fields are represented by a small arrow on each grid point. The arrow starts at the grid point and represents the vector value at that point. Additional keyword options exist for graphics object generation: - scale=factor, to multiply all field values by a factor - diameter=number, to define the diameter of the arrow objects (default: 1.) - range=(min, max), to eliminate graphics objects for values that are smaller than min or larger than max - color=string, to define the color of the arrows by a color name """ def __init__(self, system, grid_size, values): """ :param system: any subset of a molecular system :param grid_size: the spacing of a cubic grid on which the field values are defined. The value for a point is obtained by averaging the atomic quantities over all atoms in a cube centered on the point. :type grid_size: float :param values: the atomic values that define the field :type values: :class:`~MMTK.ParticleProperties.ParticleVector` """ if values is not None and values.value_rank != 1: raise TypeError("data not a vector field") self.field_class = TensorAnalysis.VectorField AtomicField.__init__(self, system, grid_size, values)
[docs] def length(self): """ :returns: a field of the length of the field vectors :rtype: :class:`~MMTK.Field.AtomicScalarField` """ field = self.field.length() return AtomicScalarField(self.system, (self.box, field, self.points, self.colors), None)
[docs] def divergence(self): """ :returns: the divergence of the field :rtype: :class:`~MMTK.Field.AtomicScalarField` """ field = self.field.divergence() return AtomicScalarField(self.system, (self.box, field, self.points, self.colors), None)
[docs] def curl(self): """ :returns: the curl of the field :rtype: :class:`~MMTK.Field.AtomicVectorField` """ field = self.field.curl() return AtomicVectorField(self.system, (self.box, field, self.points, self.colors), None)
[docs] def laplacian(self): """ :returns: the laplacian of the field :rtype: :class:`~MMTK.Field.AtomicVectorField` """ field = self.field.curl() return AtomicVectorField(self.system, (self.box, field, self.points, self.colors), None)
def _graphics(self, conf, distance_fn, model, module, options): scale = options.get('scale', 1.) diameter = options.get('diameter', 1.) color = options.get('color', None) range = options.get('range', (None, None)) length_scale = options.get('length_scale', 1.) lower, upper = range size = diameter*self.box.partition_size/50. if color is not None: color = module.ColorByName(color) objects = [] materials = {} for p, c in zip(self.points, self.colors): p = tuple(p) v = apply(self.field, p) lv = v.length() if (lower is None or lv > lower) and (upper is None or lv < upper): p = apply(Vector, p) if color is not None: c = color try: m = materials[c] except KeyError: m = module.Material(diffuse_color = c) materials[c] = m objects.append(module.Arrow(length_scale*p, length_scale*(p+scale*v), length_scale*size, material = m)) return objects
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/0000755000076600000240000000000012156626563020742 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/Amber.html0000644000076600000240000001024012013143455022635 0ustar hinsenstaff00000000000000 MMTK.ForceFields.Amber — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.Amber

# Amber forcefield initialization
#
# Written by Konrad Hinsen
#

"""
Amber force fields

"""

__docformat__ = 'restructuredtext'

from AmberForceField import Amber12SBForceField, Amber99ForceField, \
                            Amber94ForceField, Amber91ForceField, OPLSForceField
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/ANMFF.html0000644000076600000240000003105412013143455022444 0ustar hinsenstaff00000000000000 MMTK.ForceFields.ANMFF — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.ANMFF

# Anisotropic network force field
#
# Written by Konrad Hinsen
#

"""
Anisotropic Network Model
"""

__docformat__ = 'restructuredtext'

from MMTK.ForceFields.ForceField import ForceField, ForceFieldData
from MMTK import Utility
from MMTK_forcefield import NonbondedList, NonbondedListTerm
from MMTK_deformation import ANTerm
from Scientific import N

#
# The deformation force field class
#
[docs]class AnisotropicNetworkForceField(ForceField): """ Effective harmonic force field for an ANM protein model """ def __init__(self, cutoff = None, scale_factor = 1.): """ :param cutoff: the cutoff for pair interactions. Pair interactions in periodic systems are calculated using the minimum-image convention; the cutoff should therefore never be larger than half the smallest edge length of the elementary cell. :type cutoff: float :param scale_factor: a global scaling factor :type scale_factor: float """ ForceField.__init__(self, 'anisotropic_network') self.arguments = (cutoff,) self.cutoff = cutoff self.scale_factor = scale_factor def ready(self, global_data): return True def evaluatorTerms(self, universe, subset1, subset2, global_data): nothing = N.zeros((0, 2), N.Int) if subset1 is not None: set1 = set(a.index for a in subset1.atomList()) set2 = set(a.index for a in subset2.atomList()) excluded_pairs = set(Utility.orderedPairs(list(set1-set2))) \ | set(Utility.orderedPairs(list(set2-set1))) excluded_pairs = N.array(list(excluded_pairs)) atom_subset = list(set1 | set2) atom_subset.sort() atom_subset = N.array(atom_subset) else: atom_subset = N.array([], N.Int) excluded_pairs = nothing nbl = NonbondedList(excluded_pairs, nothing, atom_subset, universe._spec, self.cutoff) update = NonbondedListTerm(nbl) cutoff = self.cutoff if cutoff is None: cutoff = 0. ev = ANTerm(universe._spec, nbl, cutoff, self.scale_factor) return [update, ev]
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/BondFF.html0000644000076600000240000013373012013143454022716 0ustar hinsenstaff00000000000000 MMTK.ForceFields.BondFF — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.BondFF

# Detailed harmonic force field for proteins
#
# Written by Konrad Hinsen
#

"""
Simplified harmonic force field for normal mode calculations
"""

__docformat__ = 'restructuredtext'

from MMTK.ForceFields.NonBondedInteractions import NonBondedForceField
from MMTK.ForceFields.Amber.AmberForceField import AmberBondedForceField
from MMTK.ForceFields.MMForceField import MMAtomParameters
from MMTK.ForceFields.ForceField import ForceField, CompoundForceField
from MMTK import Utility
from MMTK_deformation import DeformationTerm
from Scientific.Geometry import Transformation
from Scientific import N

class BondForceField(AmberBondedForceField):

    def __init__(self, bonds=True, angles=True, dihedrals=True):
        AmberBondedForceField.__init__(self)
        self.arguments = (bonds, angles, dihedrals)
        self.bonded = self

    def addBondTerm(self, data, bond, object, global_data):
        if not self.arguments[0]:
            return
        a1 = bond.a1
        a2 = bond.a2
        i1 = a1.index
        i2 = a2.index
        global_data.add('excluded_pairs', Utility.normalizePair((i1, i2)))
        t1 = global_data.atom_type[a1]
        t2 = global_data.atom_type[a2]
        try:
            p = self.dataset.bondParameters(t1, t2)
        except KeyError:
            raise KeyError('No parameters for bond ' + `a1` +  '--' + `a2`)
        if p is not None:
            d = data.get('universe').distance(a1, a2)
            data.add('bonds', (i1, i2, d, p[1]))

    def addBondAngleTerm(self, data, angle, object, global_data):
        if not self.arguments[1]:
            return
        a1 = angle.a1
        a2 = angle.a2
        ca = angle.ca
        i1 = a1.index
        i2 = a2.index
        ic = ca.index
        global_data.add('excluded_pairs', Utility.normalizePair((i1, i2)))
        t1 = global_data.atom_type[a1]
        t2 = global_data.atom_type[a2]
        tc = global_data.atom_type[ca]
        try:
            p = self.dataset.bondAngleParameters(t1, tc, t2)
        except KeyError:
            raise KeyError('No parameters for angle ' + `a1` +
                            '--' + `ca` + '--' + `a2`)
        if p is not None:
            v1 = a1.position()-ca.position()
            v2 = a2.position()-ca.position()
            angle = v1.angle(v2)
            data.add('angles', (i1, ic, i2, angle) + p[1:])

    def addDihedralTerm(self, data, dihedral, object, global_data):
        if not self.arguments[2]:
            return
        a1 = dihedral.a1
        a2 = dihedral.a2
        a3 = dihedral.a3
        a4 = dihedral.a4
        i1 = a1.index
        i2 = a2.index
        i3 = a3.index
        i4 = a4.index
        global_data.add('1_4_pairs', Utility.normalizePair((i1, i4)))
        t1 = global_data.atom_type[a1]
        t2 = global_data.atom_type[a2]
        t3 = global_data.atom_type[a3]
        t4 = global_data.atom_type[a4]
        terms = self.dataset.dihedralParameters(t1, t2, t3, t4)
        if terms is not None:
            v1 = a1.position()-a2.position()
            v2 = a2.position()-a3.position()
            v3 = a4.position()-a3.position()
            a = v1.cross(v2).normal()
            b = v3.cross(v2).normal()
            cos = a*b
            sin = b.cross(a)*v2/v2.length()
            dihedral = Transformation.angleFromSineAndCosine(sin, cos)
            if dihedral > N.pi:
                dihedral -= 2.*N.pi
            for p in terms:
                if p[2] != 0.:
                    mult = p[0]
                    phase = N.fmod(N.pi-mult*dihedral, 2.*N.pi)
                    if phase < 0.:
                        phase += 2.*N.pi
                    data.add('dihedrals', (i1, i2, i3, i4,
                                           p[0], phase) + p[2:])

    def addImproperTerm(self, data, improper, object, global_data):
        if not self.arguments[2]:
            return
        a1 = improper.a1
        a2 = improper.a2
        a3 = improper.a3
        a4 = improper.a4
        i1 = a1.index
        i2 = a2.index
        i3 = a3.index
        i4 = a4.index
        t1 = global_data.atom_type[a1]
        t2 = global_data.atom_type[a2]
        t3 = global_data.atom_type[a3]
        t4 = global_data.atom_type[a4]
        terms = self.dataset.improperParameters(t1, t2, t3, t4)
        if terms is not None:
            atoms = [(t2,i2,a2), (t3,i3,a3), (t4,i4,a4)]
            atoms.sort(_order)
            i2, i3, i4 = tuple(map(lambda t: t[1], atoms))
            a2, a3, a4 = tuple(map(lambda t: t[2], atoms))
            v1 = a2.position()-a3.position()
            v2 = a3.position()-a1.position()
            v3 = a4.position()-a1.position()
            a = v1.cross(v2).normal()
            b = v3.cross(v2).normal()
            cos = a*b
            sin = b.cross(a)*v2/v2.length()
            dihedral = Transformation.angleFromSineAndCosine(sin, cos)
            if dihedral > N.pi:
                dihedral -= 2.*N.pi
            for p in terms:
                if p[2] != 0.:
                    mult = p[0]
                    phase = N.fmod(N.pi-mult*dihedral, 2.*N.pi)
                    if phase < 0.:
                        phase += 2.*N.pi
                    data.add('dihedrals', (i2, i3, i1, i4,
                                           p[0], phase) + p[2:])

def _order(a1, a2):
    ret = cmp(a1[0], a2[0])
    if ret == 0:
        ret = cmp(a1[1], a2[1])
    return ret

#
# Mid-range harmonic force field to complement bonded part
#
class MidrangeForceField(NonBondedForceField):

    def __init__(self, fc_length = 0.3, cutoff = 0.5, factor = 59908.8):
        NonBondedForceField.__init__(self, 'harmonic')
        self.arguments = (fc_length, cutoff, factor)
        self.fc_length = fc_length
        self.cutoff = cutoff
        self.factor = factor
        self.one_four_factor = 0.5

    def evaluatorParameters(self, universe, subset1, subset2, global_data):
        excluded_pairs, one_four_pairs, atom_subset = \
                        self.excludedPairs(subset1, subset2, global_data)
        if self.cutoff is None:
            cutoff = 0.
        else:
            cutoff = self.cutoff
        return {'deformation_term': {'cutoff': cutoff,
                                      'fc_length': self.fc_length,
                                      'scale_factor': self.factor,
                                      'one_four_factor': self.one_four_factor},
                 'nonbonded': {'excluded_pairs': excluded_pairs,
                               'one_four_pairs': one_four_pairs,
                               'atom_subset': atom_subset},
                }

    def evaluatorTerms(self, universe, subset1, subset2, global_data):
        param = self.evaluatorParameters(universe, subset1, subset2,
                                         global_data)['deformation_term']
        nblist, nblist_update = \
                self.nonbondedList(universe, subset1, subset2, global_data)
        ev = DeformationTerm(universe._spec, nblist, param['fc_length'],
                             param['cutoff'], param['scale_factor'],
                             param['one_four_factor'])
        return [nblist_update, ev]


[docs]class HarmonicForceField(MMAtomParameters, CompoundForceField): """ Simplified harmonic force field for normal mode calculations This force field is made up of the bonded terms from the Amber 94 force field with the equilibrium positions of all terms changed to the corresponding values in the input configuration, such that the input configuration becomes an energy minimum by construction. The nonbonded terms are replaced by a generic short-ranged deformation term. For a description of this force field, see: | Hinsen & Kneller, J. Chem. Phys. 24, 10766 (1999) For an application to DNA, see: | Viduna, Hinsen & Kneller, Phys. Rev. E 3, 3986 (2000) """ def __init__(self, fc_length = 0.45, cutoff = 0.6, factor = 400.): self.arguments = (fc_length, cutoff, factor) self.bonded = BondForceField(1, 1, 1) self.midrange = MidrangeForceField(fc_length, cutoff, factor) apply(CompoundForceField.__init__, (self, self.bonded, self.midrange)) description = ForceField.description
if __name__ == '__main__': from MMTK import * from MMTK.Proteins import Protein from MMTK.NormalModes import NormalModes world = InfiniteUniverse(HarmonicForceField(bonds=1,angles=1,dihedrals=1)) world.protein = Protein('bala1') print world.energy() modes = NormalModes(world) for m in modes: print m
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/CalphaFF.html0000644000076600000240000003202612013143454023220 0ustar hinsenstaff00000000000000 MMTK.ForceFields.CalphaFF — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.CalphaFF

# C-alpha force field
#
# Written by Konrad Hinsen
#

"""
C-alpha force field for protein normal mode analysis
(Elastic Network Model)
"""

__docformat__ = 'restructuredtext'

from MMTK.ForceFields.ForceField import ForceField, ForceFieldData
from MMTK import Utility
from MMTK_forcefield import NonbondedList, NonbondedListTerm
from MMTK_deformation import CalphaTerm
from Scientific import N

#
# The deformation force field class
#
[docs]class CalphaForceField(ForceField): """ Effective harmonic force field for a C-alpha protein model """ def __init__(self, cutoff = None, scale_factor = 1., version=1): """ :param cutoff: the cutoff for pair interactions, should be at least 2.5 nm. Pair interactions in periodic systems are calculated using the minimum-image convention; the cutoff should therefore never be larger than half the smallest edge length of the elementary cell. :type cutoff: float :param scale_factor: a global scaling factor :type scale_factor: float """ ForceField.__init__(self, 'calpha') self.arguments = (cutoff,) self.cutoff = cutoff self.scale_factor = scale_factor self.version = version def ready(self, global_data): return True def evaluatorTerms(self, universe, subset1, subset2, global_data): nothing = N.zeros((0, 2), N.Int) if subset1 is not None: set1 = set(a.index for a in subset1.atomList()) set2 = set(a.index for a in subset2.atomList()) excluded_pairs = set(Utility.orderedPairs(list(set1-set2))) \ | set(Utility.orderedPairs(list(set2-set1))) excluded_pairs = N.array(list(excluded_pairs)) atom_subset = list(set1 | set2) atom_subset.sort() atom_subset = N.array(atom_subset) else: atom_subset = N.array([], N.Int) excluded_pairs = nothing nbl = NonbondedList(excluded_pairs, nothing, atom_subset, universe._spec, self.cutoff) update = NonbondedListTerm(nbl) cutoff = self.cutoff if cutoff is None: cutoff = 0. ev = CalphaTerm(universe._spec, nbl, cutoff, self.scale_factor, self.version) return [update, ev]
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/DeformationFF.html0000644000076600000240000003425612013143454024306 0ustar hinsenstaff00000000000000 MMTK.ForceFields.DeformationFF — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.DeformationFF

# Deformation force field
#
# Written by Konrad Hinsen
#

"""
Deformation force field (elastic network model)
For proteins, CalphaForceField is usually a better choice.
"""

__docformat__ = 'restructuredtext'

from MMTK.ForceFields.ForceField import ForceField, ForceFieldData
from MMTK import Utility
from MMTK_forcefield import NonbondedList, NonbondedListTerm
from MMTK_deformation import DeformationTerm
from Scientific import N

#
# The deformation force field class
#
[docs]class DeformationForceField(ForceField): """ Deformation force field for protein normal mode calculations The pair interaction force constant has the form k(r)=factor*exp(-(r**2-0.01)/range**2). The default value for range is appropriate for a C-alpha model of a protein. The offset of 0.01 is a convenience for defining factor; with this definition, factor is the force constant for a distance of 0.1nm. """ def __init__(self, fc_length = 0.7, cutoff = 1.2, factor = 46402.): """ :param fc_length: a range parameter :type fc_length: float :param cutoff: the cutoff for pair interactions, should be at least 2.5 nm. Pair interactions in periodic systems are calculated using the minimum-image convention; the cutoff should therefore never be larger than half the smallest edge length of the elementary cell. :type cutoff: float :param factor: a global scaling factor :type factor: float """ self.arguments = (fc_length, cutoff, factor) ForceField.__init__(self, 'deformation') self.arguments = (fc_length, cutoff, factor) self.fc_length = fc_length self.cutoff = cutoff self.factor = factor def ready(self, global_data): return True def evaluatorTerms(self, universe, subset1, subset2, global_data): nothing = N.zeros((0, 2), N.Int) if subset1 is not None: set1 = set(a.index for a in subset1.atomList()) set2 = set(a.index for a in subset2.atomList()) excluded_pairs = set(Utility.orderedPairs(list(set1-set2))) \ | set(Utility.orderedPairs(list(set2-set1))) excluded_pairs = N.array(list(excluded_pairs)) atom_subset = list(set1 | set2) atom_subset.sort() atom_subset = N.array(atom_subset) else: atom_subset = N.array([], N.Int) excluded_pairs = nothing nbl = NonbondedList(excluded_pairs, nothing, atom_subset, universe._spec, self.cutoff) update = NonbondedListTerm(nbl) cutoff = self.cutoff if cutoff is None: cutoff = 0. ev = DeformationTerm(universe._spec, nbl, self.fc_length, self.cutoff, self.factor, 1.) return [update, ev]
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/ForceFieldTest.html0000644000076600000240000004466512013143455024473 0ustar hinsenstaff00000000000000 MMTK.ForceFields.ForceFieldTest — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.ForceFieldTest

# This module implements test functions.
#
# Written by Konrad Hinsen
#

"""
Force field consistency tests
"""

__docformat__ = 'restructuredtext'

from MMTK import Utility
from Scientific.Geometry import Vector, ex, ey, ez
from Scientific import N
import itertools

#
# Check consistency of energies and gradients.
#
[docs]def gradientTest(universe, atoms = None, delta = 0.0001): """ Test gradients by comparing to numerical derivatives of the energy. :param universe: the universe on which the test is performed :type universe: :class:`~MMTK.Universe.Universe` :param atoms: the atoms of the universe for which the gradient is tested (default: all atoms) :type atoms: list :param delta: the step size used in calculating the numerical derivatives :type delta: float """ e0, grad = universe.energyAndGradients() print 'Energy: ', e0 if atoms is None: atoms = universe.atomList() for a in atoms: print a print grad[a] num_grad = [] for v in [ex, ey, ez]: x = a.position() a.setPosition(x+delta*v) eplus = universe.energy() a.setPosition(x-delta*v) eminus = universe.energy() a.setPosition(x) num_grad.append(0.5*(eplus-eminus)/delta) print Vector(num_grad) # # Check consistency of gradients and force constants. #
[docs]def forceConstantTest(universe, atoms = None, delta = 0.0001): """ Test force constants by comparing to the numerical derivatives of the gradients. :param universe: the universe on which the test is performed :type universe: :class:`~MMTK.Universe.Universe` :param atoms: the atoms of the universe for which the gradient is tested (default: all atoms) :type atoms: list :param delta: the step size used in calculating the numerical derivatives :type delta: float """ e0, grad0, fc = universe.energyGradientsAndForceConstants() if atoms is None: atoms = universe.atomList() for a1, a2 in itertools.chain(itertools.izip(atoms, atoms), Utility.pairs(atoms)): print a1, a2 print fc[a1, a2] num_fc = [] for v in [ex, ey, ez]: x = a1.position() a1.setPosition(x+delta*v) e_plus, grad_plus = universe.energyAndGradients() a1.setPosition(x-delta*v) e_minus, grad_minus = universe.energyAndGradients() a1.setPosition(x) num_fc.append(0.5*(grad_plus[a2]-grad_minus[a2])/delta) print N.array(map(lambda a: a.array, num_fc)) # # Check consistency of gradients and virial #
[docs]def virialTest(universe): """ Test the virial by comparing to an explicit computation from positions and gradients. :param universe: the universe on which the test is performed :type universe: :class:`~MMTK.Universe.Universe` """ ev = universe.energyEvaluator() e, grad = ev(gradients = True) virial = ev.lastVirial() conf = universe.configuration() print virial, -(conf*grad).sumOverParticles()
if __name__ == '__main__': from MMTK import * from MMTK.ForceFields import Amber94ForceField delta = 0.001 world = InfiniteUniverse(Amber94ForceField()) m = Molecule('water') m.O.translateBy(Vector(0.,0.,0.01)) m.H1.translateBy(Vector(0.01,0.,0.)) atoms = None world.addObject(m) gradientTest(world, atoms, delta) forceConstantTest(world, atoms, delta) virialTest(world)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/LennardJonesFF.html0000644000076600000240000002441412013143455024415 0ustar hinsenstaff00000000000000 MMTK.ForceFields.LennardJonesFF — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.LennardJonesFF

# A Lennard-Jones force fields for simple liquids
#
# Written by Konrad Hinsen
#

"""
Lennard-Jones force field for simple liquids
"""

__docformat__ = 'restructuredtext'

from MMTK.ForceFields.NonBondedInteractions import LJForceField
from Scientific.Geometry import Vector
import copy

#
# The force field class
#
[docs]class LennardJonesForceField(LJForceField): """ Lennard-Jones force field The Lennard-Jones parameters are taken from the atom attributes LJ_radius and LJ_energy. The pair interaction energy has the form U(r)=4*LJ_energy*((LJ_radius/r)**12-(LJ_radius/r)**6). """ def __init__(self, cutoff = None): """ :param cutoff: a cutoff value or None, meaning no cutoff. Pair interactions in periodic systems are calculated using the minimum-image convention; the cutoff should therefore never be larger than half the smallest edge length of the elementary cell. :type cutoff: float """ self.arguments = (cutoff, ) LJForceField.__init__(self, 'LJ', cutoff) self.lj_14_factor = 1. def ready(self, global_data): return True def collectParameters(self, universe, global_data): if not hasattr(global_data, 'lj_parameters'): parameters = {} for o in universe: for a in o.atomList(): parameters[a.symbol] = (a.LJ_energy, a.LJ_radius, 0) global_data.lj_parameters = parameters def _atomType(self, object, atom, global_data): return atom.symbol def _ljParameters(self, type, global_data): return global_data.lj_parameters[type] def evaluatorParameters(self, universe, subset1, subset2, global_data): self.collectParameters(universe, global_data) return LJForceField.evaluatorParameters(self, universe, subset1, subset2, global_data)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/Restraints.html0000644000076600000240000014511012013143454023751 0ustar hinsenstaff00000000000000 MMTK.ForceFields.Restraints — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.Restraints

# Harmonic restraint terms that can be added to a force field.
#
# Written by Konrad Hinsen
#

"""
Harmonic restraint terms that can be added to any force field

Example::

 from MMTK import *
 from MMTK.ForceFields import Amber99ForceField
 from MMTK.ForceFields.Restraints import HarmonicDistanceRestraint
 
 universe = InfiniteUniverse()
 universe.protein = Protein('bala1')
 force_field = Amber99ForceField() + \
               HarmonicDistanceRestraint(universe.protein[0][1].peptide.N,
                                         universe.protein[0][1].peptide.O,
                                         0.5, 10.)
 universe.setForceField(force_field)
"""

__docformat__ = 'restructuredtext'

from MMTK.ChemicalObjects import isChemicalObject
from MMTK.ForceFields.ForceField import ForceField
from MMTK import Utility
from MMTK_forcefield import HarmonicDistanceTerm, HarmonicAngleTerm, \
                            CosineDihedralTerm
from MMTK_restraints import HarmonicCMTrapTerm, HarmonicCMDistanceTerm
from Scientific import N

[docs]class HarmonicDistanceRestraint(ForceField): """ Harmonic distance restraint between two atoms """ def __init__(self, obj1, obj2, distance, force_constant, nb_exclusion=False): """ :param obj1: the object defining center-of-mass 1 :type obj1: :class:`~MMTK.Collections.GroupOfAtoms` :param obj2: the object defining center-of-mass 2 :type obj2: :class:`~MMTK.Collections.GroupOfAtoms` :param distance: the distance between cm 1 and cm2 at which the restraint is zero :type distance: float :param force_constant: the force constant of the restraint term. The functional form of the restraint is force_constant*((cm1-cm2).length()-distance)**2, where cm1 and cm2 are the centrer-of-mass positions of the two objects. :type force_constant: float :param nb_exclussion: if True, non-bonded interactions between the restrained atoms are suppressed, as for a chemical bond :type nb_exclussion: bool """ if isinstance(obj1, int) and isinstance(obj2, int): # Older MMTK versions admitted only single atoms and # stored single indices. Support this mode for opening # trajectories made with those versions self.atom_indices_1 = [obj1] self.atom_indices_2 = [obj2] if isChemicalObject(obj1): obj1 = obj1.atomList() if isChemicalObject(obj2): obj2 = obj2.atomList() self.atom_indices_1 = self.getAtomParameterIndices(obj1) self.atom_indices_2 = self.getAtomParameterIndices(obj2) if nb_exclusion and (len(self.atom_indices_1) > 1 or len(self.atom_indices_2) > 1): raise ValueError("Non-bonded exclusion possible only " "between single-atom objects") self.arguments = (self.atom_indices_1, self.atom_indices_2, distance, force_constant, nb_exclusion) self.distance = distance self.force_constant = force_constant self.nb_exclusion = nb_exclusion ForceField.__init__(self, 'harmonic distance restraint') def declareDependencies(self, global_data): global_data.add('nb_exclusions', self.__class__) def evaluatorParameters(self, universe, subset1, subset2, global_data): if universe.is_periodic and \ (len(self.atom_indices_1) > 1 or len(self.atom_indices_1) > 1): raise ValueError("Center-of-mass restraints not implemented" " for periodic universes") ok = False for s1, s2 in [(subset1, subset2), (subset2, subset1)]: if s1 is None and s1 is None: ok = True break s1 = set(a.index for a in s1.atomIterator()) diff1 = set(self.atom_indices_1).difference(s1) s2 = set(a.index for a in s2.atomIterator()) diff2 = set(self.atom_indices_2).difference(s2) if not diff1 and not diff2: # Each object is in one of the subsets ok = True break if (diff1 and len(diff1) != len(self.atom_indices_1)) \ or (diff2 and len(diff2) != len(self.atom_indices_2)): # The subset contains some but not all of the # restrained atoms. raise ValueError("Restrained atoms partially " "in a subset") global_data.add('initialized', self.__class__) if not ok: # The objects are not in the subsets, so there is no # contribution to the total energy. return {'harmonic_distance_cm': []} if self.nb_exclusion: assert len(self.atom_indices_1) == 1 assert len(self.atom_indices_2) == 1 i1 = self.atom_indices_1[0] i2 = self.atom_indices_2[0] global_data.add('excluded_pairs', Utility.normalizePair((i1, i2))) if len(self.atom_indices_1) == 1 and len(self.atom_indices_2) == 1: # Keep the old format for the single-atom case for best # compatibility with older MMTK versions. return {'harmonic_distance_term': [(self.atom_indices_1[0], self.atom_indices_2[0], self.distance, self.force_constant)]} else: return {'harmonic_distance_cm': [(self.atom_indices_1, self.atom_indices_2, self.distance, self.force_constant)]} def evaluatorTerms(self, universe, subset1, subset2, global_data): params = self.evaluatorParameters(universe, subset1, subset2, global_data) if params.has_key('harmonic_distance_term'): params = params['harmonic_distance_term'] return [HarmonicDistanceTerm(universe._spec, N.array([p[:2]]), N.array([p[2:]]), self.name) for p in params] else: params = params['harmonic_distance_cm'] return [HarmonicCMDistanceTerm(universe, N.array(p[0]), N.array(p[1]), universe.masses().array, p[2], p[3]) for p in params] def description(self): return 'ForceFields.Restraints.' + self.__class__.__name__ + \ `self.arguments`
[docs]class HarmonicAngleRestraint(ForceField): """ Harmonic angle restraint between three atoms """ def __init__(self, atom1, atom2, atom3, angle, force_constant): """ :param atom1: first atom :type atom1: :class:`~MMTK.ChemicalObjects.Atom` :param atom2: second (central) atom :type atom2: :class:`~MMTK.ChemicalObjects.Atom` :param atom3: third atom :type atom3: :class:`~MMTK.ChemicalObjects.Atom` :param angle: the angle at which the restraint is zero :type angle: float :param force_constant: the force constant of the restraint term. The functional form of the restraint is force_constant*(phi-angle)**2, where phi is the angle atom1-atom2-atom3. """ self.index1, self.index2, self.index3 = \ self.getAtomParameterIndices((atom1, atom2, atom3)) self.arguments = (self.index1, self.index2, self.index3, angle, force_constant) self.angle = angle self.force_constant = force_constant ForceField.__init__(self, 'harmonic angle restraint') def evaluatorParameters(self, universe, subset1, subset2, global_data): return {'harmonic_angle_term': [(self.index1, self.index2, self.index3, self.angle, self.force_constant)]} def evaluatorTerms(self, universe, subset1, subset2, global_data): params = self.evaluatorParameters(universe, subset1, subset2, global_data)['harmonic_angle_term'] assert len(params) == 1 indices = N.array([params[0][:3]]) parameters = N.array([params[0][3:]]) return [HarmonicAngleTerm(universe._spec, indices, parameters, self.name)]
[docs]class HarmonicDihedralRestraint(ForceField): """ Harmonic dihedral angle restraint between four atoms """ def __init__(self, atom1, atom2, atom3, atom4, dihedral, force_constant): """ :param atom1: first atom :type atom1: :class:`~MMTK.ChemicalObjects.Atom` :param atom2: second (axis) atom :type atom2: :class:`~MMTK.ChemicalObjects.Atom` :param atom3: third (axis)atom :type atom3: :class:`~MMTK.ChemicalObjects.Atom` :param atom4: fourth atom :type atom4: :class:`~MMTK.ChemicalObjects.Atom` :param dihedral: the dihedral angle at which the restraint is zero :type dihedral: float :param force_constant: the force constant of the restraint term. The functional form of the restraint is force_constant*(phi-abs(dihedral))**2, where phi is the dihedral angle atom1-atom2-atom3-atom4. """ self.index1, self.index2, self.index3, self.index4 = \ self.getAtomParameterIndices((atom1, atom2, atom3, atom4)) self.dihedral = dihedral self.force_constant = force_constant self.arguments = (self.index1, self.index2, self.index3, self.index4, dihedral, force_constant) ForceField.__init__(self, 'harmonic dihedral restraint') def evaluatorParameters(self, universe, subset1, subset2, global_data): return {'cosine_dihedral_term': [(self.index1, self.index2, self.index3, self.index4, 0., self.dihedral, 0., self.force_constant)]} def evaluatorTerms(self, universe, subset1, subset2, global_data): params = self.evaluatorParameters(universe, subset1, subset2, global_data)['cosine_dihedral_term'] assert len(params) == 1 indices = N.array([params[0][:4]]) parameters = N.array([params[0][4:]]) return [CosineDihedralTerm(universe._spec, indices, parameters, self.name)]
[docs]class HarmonicTrapForceField(ForceField): """ Harmonic potential with respect to a fixed point in space """ def __init__(self, obj, center, force_constant): """ :param obj: the object on whose center of mass the force field acts :type obj: :class:`~MMTK.Collections.GroupOfAtoms` :param center: the point to which the atom is attached by the harmonic potential :type center: Scientific.Geometry.Vector :param force_constant: the force constant of the harmonic potential :type force_constant: float """ if isChemicalObject(obj): obj = obj.atomList() self.atom_indices = self.getAtomParameterIndices(obj) self.arguments = (self.atom_indices, center, force_constant) ForceField.__init__(self, 'harmonic_trap') self.center = center self.force_constant = force_constant def ready(self, global_data): return True def evaluatorParameters(self, universe, subset1, subset2, global_data): if universe.is_periodic and len(self.atom_indices) > 1: raise ValueError("Center-of-mass restraints not implemented" " for periodic universes") for subset in [subset1, subset2]: if subset is not None: subset = set(a.index for a in subset.atomIterator()) diff = set(self.atom_indices).difference(subset) if diff: if len(diff) == len(self.atom_indices): # The subset doesn't contain the restrained atoms. return {'harmonic_trap_cm': []} else: # The subset contains some but not all of the # restrained atoms. raise ValueError("Restrained atoms partially " "in a subset") return {'harmonic_trap_cm': [(self.atom_indices, self.center, self.force_constant)]} def evaluatorTerms(self, universe, subset1, subset2, global_data): params = self.evaluatorParameters(universe, subset1, subset2, global_data)['harmonic_trap_cm'] return [HarmonicCMTrapTerm(universe, N.array(p[0]), universe.masses().array, p[1], p[2]) for p in params]
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ForceFields/SPCEFF.html0000644000076600000240000003204712013143455022566 0ustar hinsenstaff00000000000000 MMTK.ForceFields.SPCEFF — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ForceFields.SPCEFF

# SPCE force field
#
# Written by Konrad Hinsen
#

"""
SPC/E force field for water simulations
"""

__docformat__ = 'restructuredtext'

from MMTK.ForceFields import MMForceField


class SPCEParameters(object):

    atom_type_property = 'spce_atom_type'
    charge_property = 'spce_charge'
    lennard_jones_1_4 = 1.
    electrostatic_1_4 = 1.

    def ljParameters(self, type):
        if type == 'O':
            # TIP3: 0.6363936, 0.315075240657
            return (0.650169580819, 0.31655578902, 0)
        elif type == 'H':
            return (0., 0., 0)
        else:
            raise ValueError('Unknown atom type ' + type)

    # Bond and angle parameters from:
    # O. Telemann, B. Jonsson, S. Engstrom
    # Mol. Phys. 60(1), 193-203 (1987)
    def bondParameters(self, at1, at2):
        if at1 == 'O' or at2 == 'O':
            return (0.1, 463700.)
        else:
            return (0.163298086184, 0.)
    
    def bondAngleParameters(self, at1, at2, at3):
        if at2 == 'O':
            return (1.91061193216, 383.)
        else:
            return (0.615490360716, 0.)
    
    def dihedralParameters(self, at1, at2, at3, at4):
        return [(1, 0., 0.)]
    
    def improperParameters(self, at1, at2, at3, at4):
        return [(1, 0., 0.)]


[docs]class SPCEForceField(MMForceField.MMForceField): """ Force field for water simulations with the SPC/E model """ def __init__(self, lj_options=None, es_options=None): """ :param lj_options: parameters for Lennard-Jones interactions; one of: * a number, specifying the cutoff * None, meaning the default method (no cutoff; inclusion of all pairs, using the minimum-image conventions for periodic universes) * a dictionary with an entry "method" which specifies the calculation method as either "direct" (all pair terms) or "cutoff", with the cutoff specified by the dictionary entry "cutoff". :param es_options: parameters for electrostatic interactions; one of: * a number, specifying the cutoff * None, meaning the default method (all pairs without cutoff for non-periodic system, Ewald summation for periodic systems) * a dictionary with an entry "method" which specifies the calculation method as either "direct" (all pair terms), "cutoff" (with the cutoff specified by the dictionary entry "cutoff"), "ewald" (Ewald summation, only for periodic universes), "screened" or "multipole" (fast-multipole method). """ MMForceField.MMForceField.__init__(self, 'SPCE', SPCEParameters(), lj_options, es_options) self.arguments = (lj_options, es_options)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/FourierBasis.html0000644000076600000240000006572112013143455022035 0ustar hinsenstaff00000000000000 MMTK.FourierBasis — MMTK User Guide 2.7.7 documentation

Source code for MMTK.FourierBasis

# Fourier basis for low-frequency normal mode calculations.
#
# Written by Konrad Hinsen
#

"""
Fourier basis for low-frequency normal mode calculations

This module provides a basis that is suitable for the
calculation of low-frequency normal modes. The basis is
derived from vector fields whose components are stationary
waves in a box surrounding the system. For a description,
see

  | K. Hinsen
  | Analysis of domain motions by approximate normal mode calculations
  | Proteins 33 (1998): 417-429
"""

from MMTK import ParticleProperties
from Scientific.Geometry import Vector
from Scientific import N

[docs]class FourierBasis(object): """ Collective-motion basis for low-frequency normal mode calculations A FourierBasis behaves like a sequence of :class:`~MMTK.ParticleProperties.ParticleVector` objects. The vectors are B{not} orthonormal, because orthonormalization is handled automatically by the normal mode class. """ def __init__(self, universe, cutoff): """ :param universe: the universe for which the basis will be used :type universe: :class:`~MMTK.Universe.Universe` :param cutoff: the wavelength cutoff. A smaller value yields a larger basis. :type cutoff: float """ p1, p2 = universe.boundingBox() p2 = p2 + Vector(cutoff, cutoff, cutoff) l = (p2-p1).array n_max = (0.5*l/cutoff+0.5).astype(N.Int) wave_numbers = [(nx, ny, nz) for nx in range(-n_max[0], n_max[0]+1) for ny in range(-n_max[1], n_max[1]+1) for nz in range(-n_max[2], n_max[2]+1) if (nx/l[0])**2 + (ny/l[1])**2 + (nz/l[2])**2 \ < 0.25/cutoff**2] atoms = universe.atomList() natoms = len(atoms) basis = N.zeros((3*len(wave_numbers)+3, natoms, 3), N.Float) cm = universe.centerOfMass() i = 0 for rotation in [Vector(1.,0.,0.), Vector(0.,1.,0.), Vector(0.,0.,1.)]: v = ParticleProperties.ParticleVector(universe, basis[i]) for a in atoms: v[a] = rotation.cross(a.position()-cm) i += i conf = universe.configuration().array-p1.array for n in wave_numbers: k = 2.*N.pi*N.array(n)/l w = self._w(conf[:, 0], k[0]) * self._w(conf[:, 1], k[1]) * \ self._w(conf[:, 2], k[2]) basis[i, :, 0] = w basis[i+1, :, 1] = w basis[i+2, :, 2] = w i += 3 self.array = basis self.universe = universe __safe_for_unpickling__ = True __had_initargs__ = True def _w(self, x, k): if k < 0: return N.sin(-k*x) else: return N.cos(k*x) def __len__(self): return self.array.shape[0] def __getitem__(self, item): return ParticleProperties.ParticleVector(self.universe, self.array[item]) # Estimate number of basis vectors for a given cutoff
[docs]def countBasisVectors(universe, cutoff): """ Estimate the number of basis vectors for a given universe and cutoff :param universe: the universe :type universe: :class:`~MMTK.Universe.Universe` :param cutoff: the wavelength cutoff. A smaller value yields a larger basis. :type cutoff: float :returns: the number of basis vectors in a FourierBasis :rtype: int """ p1, p2 = universe.boundingBox() p2 = p2 + Vector(cutoff, cutoff, cutoff) l = (p2-p1).array n_max = (0.5*l/cutoff+0.5).astype(N.Int) n_wave_numbers = 0 for nx in range(-n_max[0], n_max[0]+1): for ny in range(-n_max[1], n_max[1]+1): for nz in range(-n_max[2], n_max[2]+1): if (nx/l[0])**2 + (ny/l[1])**2 + (nz/l[2])**2 < 0.25/cutoff**2: n_wave_numbers += 1 return 3*n_wave_numbers+3 # Estimate cutoff for a given number of basis vectors
[docs]def estimateCutoff(universe, nmodes): """ Estimate the cutoff that yields a given number of basis vectors for a given universe. :param universe: the universe :type universe: :class:`~MMTK.Universe.Universe` :param nmodes: the number of basis vectors in a FourierBasis :type nmodes: int :returns: the wavelength cutoff and the precise number of basis vectors :rtype: tuple (float, int) """ natoms = universe.numberOfCartesianCoordinates() if nmodes > natoms: nmodes = 3*natoms cutoff = None else: p1, p2 = universe.boundingBox() cutoff_max = (p2-p1).length() cutoff = 0.5*cutoff_max nmodes_opt = nmodes nmodes = countBasisVectors(universe, cutoff) while nmodes > nmodes_opt: cutoff += 0.1 if cutoff > cutoff_max: cutoff = cutoff_max break nmodes = countBasisVectors(universe, cutoff) while nmodes < nmodes_opt: cutoff -= 0.1 if cutoff < 0.1: cutoff = 0.1 break nmodes = countBasisVectors(universe, cutoff) return cutoff, nmodes
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Geometry.html0000644000076600000240000034415412013143456021234 0ustar hinsenstaff00000000000000 MMTK.Geometry — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Geometry

# This module defines some geometrical objects in 3D-space.
#
# Written by Konrad Hinsen
#

"""
Elementary geometrical objects and operations

There are essentially two kinds of geometrical objects: shape objects
(spheres, planes, etc.), from which intersections can be calculated,
and lattice objects, which define a regular arrangements of points.
"""

__docformat__ = 'restructuredtext'

from Scientific.Geometry import Vector
from Scientific import N


# Error type
class GeomError(Exception):
    pass

# Small number
eps = 1.e-16

#
# The base class
#
[docs]class GeometricalObject3D(object): """ 3D shape object This is an abstract base class. To create 3D objects, use one of its subclasses. """
[docs] def intersectWith(self, other): """ :param other: another 3D object :returns: a 3D object that represents the intersection with other """ if self.__class__ > other.__class__: self, other = other, self try: f, switch = _intersectTable[(self.__class__, other.__class__)] if switch: return f(other, self) else: return f(self, other) except KeyError: raise GeomError("Can't calculate intersection of " + self.__class__.__name__ + " with " + other.__class__.__name__)
[docs] def volume(self): """ :returns: the volume of the object :rtype: float """ raise NotImplementedError
[docs] def hasPoint(self, point): """ :param point: a point in 3D space :type point: Scientific.Geometry.Vector :returns: True of the point lies on the surface of the object :rtype: bool """ return self.distanceFrom(point) < eps # subclasses that enclose a volume should override this method # a return value of None indicates "don't know", "can't compute", # or "not implemented (yet)".
[docs] def enclosesPoint(self, point): """ :param point: a point in 3D space :type point: Scientific.Geometry.Vector :returns: True of the point is inside the volume of the object :rtype: bool """ return None
_intersectTable = {} # # Boxes #
[docs]class Box(GeometricalObject3D): """ Rectangular box aligned with the coordinate axes """ def __init__(self, corner1, corner2): """ :param corner1: one corner of the box :type corner1: Scientific.Geometry.Vector :param corner2: the diagonally opposite corner :type corner2: Scientific.Geometry.Vector """ c1 = N.minimum(corner1.array, corner2.array) c2 = N.maximum(corner1.array, corner2.array) self.corners = c1, c2 def __repr__(self): return 'Box(' + `Vector(self.corners[0])` + ', ' \ + `Vector(self.corners[1])` + ')' __str__ = __repr__ def volume(self): c1, c2 = self.corners return N.multiply.reduce(c2-c1) def hasPoint(self, point): c1, c2 = self.corners min1 = N.minimum.reduce(N.fabs(point.array-c1)) min2 = N.minimum.reduce(N.fabs(point.array-c2)) return min1 < eps or min2 < eps def enclosesPoint(self, point): c1, c2 = self.corners out1 = N.logical_or.reduce(N.less(point.array-c1, 0)) out2 = N.logical_or.reduce(N.less_equal(c2-point.array, 0)) return not (out1 or out2) def cornerPoints(self): (c1x, c1y, c1z), (c2x, c2y, c2z) = self.corners return [Vector(c1x, c1y, c1z), Vector(c1x, c1y, c2z), Vector(c1x, c2y, c1z), Vector(c2x, c1y, c1z), Vector(c2x, c2y, c1z), Vector(c2x, c1y, c2z), Vector(c1x, c2y, c2z), Vector(c2x, c2y, c2z)] # # Spheres #
[docs]class Sphere(GeometricalObject3D): """ Sphere """ def __init__(self, center, radius): """ :param center: the center of the sphere :type center: Scientific.Geometry.Vector :param radius: the radius of the sphere :type radius: float """ self.center = center self.radius = radius def __repr__(self): return 'Sphere(' + `self.center` + ', ' + `self.radius` + ')' __str__ = __repr__ def volume(self): return (4.*N.pi/3.) * self.radius**3 def hasPoint(self, point): return N.fabs((point-self.center).length()-self.radius) < eps def enclosesPoint(self, point): return (point - self.center).length() < self.radius # # Cylinders #
[docs]class Cylinder(GeometricalObject3D): """ Cylinder """ def __init__(self, center1, center2, radius): """ :param center1: the center of the bottom circle :type center1: Scientific.Geometry.Vector :param center2: the center of the top circle :type center2: Scientific.Geometry.Vector :param radius: the radius of the cylinder :type radius: float """ self.center1 = center1 # center of base self.center2 = center2 # center of top self.radius = radius self.height = (center2-center1).length() def volume(self): return N.pi*self.radius*self.radius*self.height def __repr__(self): return 'Cylinder(' + `self.center1` + ', ' + `self.center2` + \ ', ' + `self.radius` + ')' __str__ = __repr__ def hasPoint(self, point): center_line = LineSegment(self.center1, self.center2) pt = center_line.projectionOf(point) if pt is None: return 0 return N.fabs((point - pt).length() - self.radius) < eps def enclosesPoint(self, point): center_line = LineSegment(self.center1, self.center2) pt = center_line.projectionOf(point) if pt is None: return 0 return (point - pt).length() < self.radius # # Planes #
[docs]class Plane(GeometricalObject3D): """ 2D plane in 3D space """ def __init__(self, *args): """ :param args: three points (of type Scientific.Geometry.Vector) that are not collinear, or a point in the plane and the normal vector of the plane """ if len(args) == 2: # point, normal self.normal = args[1].normal() self.distance_from_zero = self.normal*args[0] else: # three points v1 = args[1]-args[0] v2 = args[2]-args[1] self.normal = (v1.cross(v2)).normal() self.distance_from_zero = self.normal*args[1] def __repr__(self): return 'Plane(' + str(self.normal*self.distance_from_zero) + \ ', ' + str(self.normal) + ')' __str__ = __repr__ def distanceFrom(self, point): return abs(self.normal*point-self.distance_from_zero) def projectionOf(self, point): return point - (self.normal*point-self.distance_from_zero)*self.normal def rotate(self, axis, angle): point = rotatePoint(self.distance_from_zero*self.normal, axis, angle) normal = rotateDirection(self.normal, axis, angle) return Plane(point, normal) def volume(self): return 0. # # Infinite cones #
[docs]class Cone(GeometricalObject3D): """ Cone """ def __init__(self, center, axis, angle): """ :param center: the center (tip) of the cone :type center: Scientific.Geometry.Vector :param axis: the direction of the axis of rotational symmetry :type axis: Scientific.Geometry.Vector :param angle: the angle between any straight line on the cone surface and the axis of symmetry :type angle: float """ self.center = center self.axis = axis.normal() self.angle = angle def __repr__(self): return 'Cone(' + `self.center` + ', ' + `self.axis` + ',' + \ `self.angle` + ')' __str__ = __repr__ def volume(self): return None # # Circles #
[docs]class Circle(GeometricalObject3D): """ 2D circle in 3D space """ def __init__(self, center, normal, radius): """ :param center: the center of the circle :type center: Scientific.Geometry.Vector :param normal: the normal vector of the circle's plane :type normal: Scientific.Geometry.Vector :param radius: the radius of the circle :type radius: float """ self.center = center self.normal = normal self.radius = radius def planeOf(self): return Plane(self.center, self.normal) def __repr__(self): return 'Circle(' + `self.center` + ', ' + `self.normal` + \ ', ' + `self.radius` + ')' __str__ = __repr__ def volume(self): return 0. def distanceFrom(self, point): plane = self.planeOf() project_on_plane = plane.projectionOf(point) center_to_projection = project_on_plane - self.center if center_to_projection.length() < eps: return 0 closest_point = self.center + self.radius*center_to_projection.normal() return (point - closest_point).length() # # Lines #
[docs]class Line(GeometricalObject3D): """ Line """ def __init__(self, point, direction): """ :param point: any point on the line :type point: Scientific.Geometry.Vector :param direction: the direction of the line :type direction: Scientific.Geometry.Vector """ self.point = point self.direction = direction.normal()
[docs] def distanceFrom(self, point): """ :param point: a point in space :type point: Scientific.Geometry.Vector :returns: the smallest distance of the point from the line :rtype: float """ d = self.point-point d = d - (d*self.direction)*self.direction return d.length()
[docs] def projectionOf(self, point): """ :param point: a point in space :type point: Scientific.Geometry.Vector :returns: the orthogonal projection of the point onto the line :rtype: Scientific.Geometry.Vector """ d = self.point-point d = d - (d*self.direction)*self.direction return point+d
[docs] def perpendicularVector(self, plane): """ :param plane: a plane :type plane: Plane :returns: a vector in the plane perpendicular to the line :rtype: Scientific.Geometry.Vector """ return self.direction.cross(plane.normal)
def __repr__(self): return 'Line(' + `self.point` + ', ' + `self.direction` + ')' __str__ = __repr__ def volume(self): return 0.
class LineSegment(Line): def __init__(self, point1, point2): Line.__init__(self, point1, point2 - point1) self.point2 = point2 def __repr__(self): return 'LineSegment(' + `self.point` + ', ' + `self.point2` + ')' __str__ = __repr__ def distanceFrom(self, point): pt = self.projectionOf(point) if pt is not None: return (pt - point).length() d1 = (self.point - point).length() d2 = (self.point2 - point).length() return min(d1, d2) def projectionOf(self, point): d = self.point-point d = d - (d*self.direction)*self.direction pt = point+d if self.isWithin(pt): return pt return None def isWithin(point): v1 = point - self.point v2 = point - self.point2 if abs(v1 * v2) < eps: return 0 return not Same_Dir(v1, v2) # # Intersection calculations # def _addIntersectFunction(f, class1, class2): switch = class1 > class2 if switch: class1, class2 = class2, class1 _intersectTable[(class1, class2)] = (f, switch) # Box with box def _intersectBoxBox(box1, box2): c1 = N.maximum(box1.corners[0], box2.corners[0]) c2 = N.minimum(box1.corners[1], box2.corners[1]) if N.logical_or.reduce(N.greater_equal(c1, c2)): return None return Box(Vector(c1), Vector(c2)) _addIntersectFunction(_intersectBoxBox, Box, Box) # Sphere with sphere def _intersectSphereSphere(sphere1, sphere2): r1r2 = sphere2.center-sphere1.center d = r1r2.length() if d > sphere1.radius+sphere2.radius: return None if d+min(sphere1.radius, sphere2.radius) < \ max(sphere1.radius, sphere2.radius): return None x = 0.5*(d**2 + sphere1.radius**2 - sphere2.radius**2)/d h = N.sqrt(sphere1.radius**2-x**2) normal = r1r2.normal() return Circle(sphere1.center + x*normal, normal, h) _addIntersectFunction(_intersectSphereSphere, Sphere, Sphere) # Sphere with cone def _intersectSphereCone(sphere, cone): if sphere.center != cone.center: raise GeomError("Not yet implemented") from_center = sphere.radius*N.cos(cone.angle) radius = sphere.radius*N.sin(cone.angle) return Circle(cone.center+from_center*cone.axis, cone.axis, radius) _addIntersectFunction(_intersectSphereCone, Sphere, Cone) # Plane with plane def _intersectPlanePlane(plane1, plane2): if abs(abs(plane1.normal*plane2.normal)-1.) < eps: if abs(plane1.distance_from_zero-plane2.distance_from_zero) < eps: return plane1 else: return None else: direction = plane1.normal.cross(plane2.normal) point_in_1 = plane1.distance_from_zero*plane1.normal point_in_both = point_in_1 - (point_in_1*plane2.normal - plane2.distance_from_zero)*plane2.normal return Line(point_in_both, direction) _addIntersectFunction(_intersectPlanePlane, Plane, Plane) # Circle with plane def _intersectCirclePlane(circle, plane): if abs(abs(circle.normal*plane.normal)-1.) < eps: if plane.hasPoint(circle.center): return circle else: return None else: line = plane.intersectWith(Plane(circle.center, circle.normal)) x = line.distanceFrom(circle.center) if x > circle.radius: return None else: angle = N.arccos(x/circle.radius) along_line = N.sin(angle)*circle.radius normal = circle.normal.cross(line.direction) if line.distanceFrom(circle.center+normal) > x: normal = -normal return (circle.center+x*normal-along_line*line.direction, circle.center+x*normal+along_line*line.direction) _addIntersectFunction(_intersectCirclePlane, Circle, Plane) # # Rotation # def rotateDirection(vector, axis, angle): s = N.sin(angle) c = N.cos(angle) c1 = 1-c try: axis = axis.direction except AttributeError: pass return s*axis.cross(vector) + c1*(axis*vector)*axis + c*vector def rotatePoint(point, axis, angle): return axis.point + rotateDirection(point-axis.point, axis, angle) # # Lattices # # # Lattice base class #
[docs]class Lattice(object): """ General lattice Lattices are special sequence objects that contain vectors (points on the lattice) or objects that are constructed as functions of these vectors. Lattice objects behave like lists, i.e. they permit indexing, length inquiry, and iteration by 'for'-loops. Note that the lattices represented by these objects are finite, they have a finite (and fixed) number of repetitions along each lattice vector. This is an abstract base class. To create lattice objects, use one of its subclasses. """ def __init__(self, function): if function is not None: self.elements = map(function, self.elements) def __getitem__(self, item): return self.elements[item] def __setitem__(self, item, value): self.elements[item] = value def __len__(self): return len(self.elements) # # General rhombic lattice #
[docs]class RhombicLattice(Lattice): """ Rhombic lattice """ def __init__(self, elementary_cell, lattice_vectors, cells, function=None, base=None): """ :param elementary_cell: a list of points in the elementary cell :param lattice_vectors: a list of lattice vectors. Each lattice vector defines a lattice dimension (only values from one to three make sense) and indicates the displacement along this dimension from one cell to the next. :param cells: a list of integers, whose length must equal the number of dimensions. Each entry specifies how often a cell is repeated along this dimension. :param function: a function that is called for every lattice point with the vector describing the point as argument. The return value of this function is stored in the lattice object. If the function is 'None', the vector is directly stored in the lattice object. """ if len(lattice_vectors) != len(cells): raise TypeError('Inconsistent dimension specification') if base is None: base = Vector(0, 0, 0) self.dimension = len(lattice_vectors) self.elements = [] self.makeLattice(elementary_cell, lattice_vectors, cells, base) Lattice.__init__(self, function) def makeLattice(self, elementary_cell, lattice_vectors, cells, base): if len(cells) == 0: for p in elementary_cell: self.elements.append(p+base) else: for i in range(cells[0]): self.makeLattice(elementary_cell, lattice_vectors[1:], cells[1:], base+i*lattice_vectors[0]) # # Bravais lattice #
[docs]class BravaisLattice(RhombicLattice): """ Bravais lattice A Bravais lattice is a special case of a general rhombic lattice in which the elementary cell contains only one point. """ def __init__(self, lattice_vectors, cells, function=None, base=None): """ :param lattice_vectors: a list of lattice vectors. Each lattice vector defines a lattice dimension (only values from one to three make sense) and indicates the displacement along this dimension from one cell to the next. :param cells: a list of integers, whose length must equal the number of dimensions. Each entry specifies how often a cell is repeated along this dimension. :param function: a function that is called for every lattice point with the vector describing the point as argument. The return value of this function is stored in the lattice object. If the function is 'None', the vector is directly stored in the lattice object. """ cell = [Vector(0,0,0)] RhombicLattice.__init__(self, cell, lattice_vectors, cells, function, base) # # Simple cubic lattice #
[docs]class SCLattice(BravaisLattice): """ Simple Cubic lattice A Simple Cubic lattice is a special case of a Bravais lattice in which the elementary cell is a cube. """ def __init__(self, cellsize, cells, function=None, base=None): """ :param cellsize: the edge length of the elementary cell :type cellsize: float :param cells: a list of integers, whose length must equal the number of dimensions. Each entry specifies how often a cell is repeated along this dimension. :param function: a function that is called for every lattice point with the vector describing the point as argument. The return value of this function is stored in the lattice object. If the function is 'None', the vector is directly stored in the lattice object. """ lattice_vectors = (cellsize*Vector(1., 0., 0.), cellsize*Vector(0., 1., 0.), cellsize*Vector(0., 0., 1.)) if type(cells) != type(()): cells = 3*(cells,) BravaisLattice.__init__(self, lattice_vectors, cells, function, base) # # Body-centered cubic lattice #
[docs]class BCCLattice(RhombicLattice): """ Body-Centered Cubic lattice A Body-Centered Cubic lattice has two points per elementary cell. """ def __init__(self, cellsize, cells, function=None, base=None): """ :param cellsize: the edge length of the elementary cell :type cellsize: float :param cells: a list of integers, whose length must equal the number of dimensions. Each entry specifies how often a cell is repeated along this dimension. :param function: a function that is called for every lattice point with the vector describing the point as argument. The return value of this function is stored in the lattice object. If the function is 'None', the vector is directly stored in the lattice object. """ cell = [Vector(0,0,0), cellsize*Vector(0.5,0.5,0.5)] lattice_vectors = (cellsize*Vector(1., 0., 0.), cellsize*Vector(0., 1., 0.), cellsize*Vector(0., 0., 1.)) if type(cells) != type(()): cells = 3*(cells,) RhombicLattice.__init__(self, cell, lattice_vectors, cells, function, base) # # Face-centered cubic lattice #
[docs]class FCCLattice(RhombicLattice): """Face-Centered Cubic lattice A Face-Centered Cubic lattice has four points per elementary cell. """ def __init__(self, cellsize, cells, function=None, base=None): """ :param cellsize: the edge length of the elementary cell :type cellsize: float :param cells: a list of integers, whose length must equal the number of dimensions. Each entry specifies how often a cell is repeated along this dimension. :param function: a function that is called for every lattice point with the vector describing the point as argument. The return value of this function is stored in the lattice object. If the function is 'None', the vector is directly stored in the lattice object. """ cell = [Vector(0,0,0), cellsize*Vector( 0,0.5,0.5), cellsize*Vector(0.5, 0,0.5), cellsize*Vector(0.5,0.5, 0)] lattice_vectors = (cellsize*Vector(1., 0., 0.), cellsize*Vector(0., 1., 0.), cellsize*Vector(0., 0., 1.)) if type(cells) != type(()): cells = 3*(cells,) RhombicLattice.__init__(self, cell, lattice_vectors, cells, function, base) # # Optimal superposition of a molecule in two configurations #
[docs]def superpositionFit(confs): """ :param confs: the weight, reference position, and alternate position for each atom :type confs: sequence of (float, Vector, Vector) :returns: the quaternion representing the rotation, the center of mass in the alternate configuraton, the center of mass in the reference configuration, and the RMS distance after the optimal superposition """ w_sum = 0. wr_sum = N.zeros((3,), N.Float) for w, r_ref, r in confs: w_sum += w wr_sum += w*r_ref.array ref_cms = wr_sum/w_sum pos = N.zeros((3,), N.Float) possq = 0. cross = N.zeros((3, 3), N.Float) for w, r_ref, r in confs: w = w/w_sum r_ref = r_ref.array-ref_cms r = r.array pos = pos + w*r possq = possq + w*N.add.reduce(r*r) \ + w*N.add.reduce(r_ref*r_ref) cross = cross + w*r[:, N.NewAxis]*r_ref[N.NewAxis, :] k = N.zeros((4, 4), N.Float) k[0, 0] = -cross[0, 0]-cross[1, 1]-cross[2, 2] k[0, 1] = cross[1, 2]-cross[2, 1] k[0, 2] = cross[2, 0]-cross[0, 2] k[0, 3] = cross[0, 1]-cross[1, 0] k[1, 1] = -cross[0, 0]+cross[1, 1]+cross[2, 2] k[1, 2] = -cross[0, 1]-cross[1, 0] k[1, 3] = -cross[0, 2]-cross[2, 0] k[2, 2] = cross[0, 0]-cross[1, 1]+cross[2, 2] k[2, 3] = -cross[1, 2]-cross[2, 1] k[3, 3] = cross[0, 0]+cross[1, 1]-cross[2, 2] for i in range(1, 4): for j in range(i): k[i, j] = k[j, i] k = 2.*k for i in range(4): k[i, i] = k[i, i] + possq - N.add.reduce(pos*pos) from Scientific import LA e, v = LA.eigenvectors(k) i = N.argmin(e) v = v[i] if v[0] < 0: v = -v if e[i] <= 0.: rms = 0. else: rms = N.sqrt(e[i]) from Scientific.Geometry import Quaternion return Quaternion.Quaternion(v), Vector(ref_cms), \ Vector(pos), rms
MMTK-2.7.9/Doc/HTML/_modules/MMTK/InternalCoordinates.html0000644000076600000240000014202112013143455023374 0ustar hinsenstaff00000000000000 MMTK.InternalCoordinates — MMTK User Guide 2.7.7 documentation

Source code for MMTK.InternalCoordinates

# Manipulation of internal coordinates
#
# Written by Konrad Hinsen
#

"""
Manipulation of molecular configurations in terms of internal coordinates
"""

__docformat__ = 'restructuredtext'

import MMTK
from Scientific import N

#
# The abstract base class
#
class InternalCoordinate:

    def __init__(self, atoms):
        self.atoms = atoms
        self.molecule = None
        for m in atoms[0].topLevelChemicalObject().bondedUnits():
            if atoms[0] in m.atomList():
                self.molecule = m
                break
        if self.molecule is None:
            raise ValueError("inconsistent data structure")
        for a in atoms[1:]:
            if a not in self.molecule.atomList():
                raise ValueError("atoms not in the same molecule")
        self.universe = self.molecule.universe()
        if self.universe is None:
            self.universe = MMTK.InfiniteUniverse()

    def bondTest(self):
        for i in range(len(self.atoms)-1):
            if not self.atoms[i] in self.atoms[i+1].bondedTo():
                raise ValueError("no bond between %s and %s"
                                  % (self.atoms[i], self.atoms[i+1]))

    def findFragments(self, spec1, spec2, excluded_first_only=False):
        self.fragment1 = MMTK.Collection()
        self.fragment2 = MMTK.Collection()
        self.molecule.setBondAttributes()
        try:
            self.bondTest()
            for fragment, (start, excluded, error_check) \
                    in [(self.fragment1, spec1), (self.fragment2, spec2)]:
                atoms = set([start])
                first = True
                new_atoms = set([start])
                while new_atoms:
                    check = new_atoms
                    new_atoms = set()
                    for a in check:
                        for na in a.bondedTo():
                            if excluded_first_only:
                                if first:
                                    add = na not in excluded
                                else:
                                    add = True
                            else:
                                add = na not in excluded
                            if add and na not in atoms:
                                atoms.add(na)
                                new_atoms.add(na)
                    first = False
                if error_check in atoms:
                    raise ValueError("cyclic bond structure")
                for a in atoms:
                    fragment.addObject(a)
        finally:
            self.molecule.clearBondAttributes()

#
# Bond length
#
[docs]class BondLength(InternalCoordinate): """ Bond length coordinate A BondLength object permits calculating and modifying the length of a bond in a molecule, under the condition that the bond is not part of a circular bond structure (but it is not a problem to have a circular bond structure elsewhere in the molecule). Modifying the bond length moves the parts of the molecule on both sides of the bond along the bond direction in such a way that the center of mass of the molecule does not change. The initial construction of the BondLength object can be expensive (the bond structure of the molecule must be analyzed). It is therefore advisable to keep the object rather than recreate it frequently. Note that if you only want to calculate bond lengths (no modification), the method Universe.distance is simpler and faster. """ def __init__(self, atom1, atom2): """ :param atom1: the first atom that defines the bond :type atom1: :class:`~MMTK.ChemicalObjects.Atom` :param atom2: the second atom that defines the bond :type atom2: :class:`~MMTK.ChemicalObjects.Atom` """ InternalCoordinate.__init__(self, [atom1, atom2]) self.findFragments((atom1, set([atom2]), atom2), (atom2, set([atom1]), atom1), True)
[docs] def getValue(self, conf = None): """ :param conf: a configuration (defaults to the current configuration) :type conf: :class:`~MMTK.ParticleProperties.Configuration` :returns: the length of the bond in the configuration conf :rtype: float """ return self.universe.distance(self.atoms[0], self.atoms[1], conf)
[docs] def setValue(self, value): """ Sets the length of the bond :param value: the desired length of the bond :type value: float """ v = self.universe.distanceVector(self.atoms[0], self.atoms[1]) axis = v.normal() distance = value - v.length() m1 = self.fragment1.mass() m2 = self.fragment2.mass() d1 = -m2*distance/(m1+m2) d2 = m1*distance/(m1+m2) self.fragment1.translateBy(d1*axis) self.fragment2.translateBy(d2*axis) # # Bond angles #
[docs]class BondAngle(InternalCoordinate): """ Bond angle A BondAngle object permits calculating and modifying the angle between two bonds in a molecule, under the condition that the bonds are not part of a circular bond structure (but it is not a problem to have a circular bond structure elsewhere in the molecule). Modifying the bond angle rotates the parts of the molecule on both sides of the central atom around an axis passing through the central atom and perpendicular to the plane defined by the two bonds in such a way that there is no overall rotation of the molecule. The central atom and any other atoms bonded to it do not move. The initial construction of the BondAngle object can be expensive (the bond structure of the molecule must be analyzed). It is therefore advisable to keep the object rather than recreate it frequently. Note that if you only want to calculate bond angles (no modification), the method Universe.angle is simpler and faster. """ def __init__(self, atom1, atom2, atom3): """ :param atom1: the first atom that defines the angle :type atom1: :class:`~MMTK.ChemicalObjects.Atom` :param atom2: the second and central atom that defines the bond :type atom2: :class:`~MMTK.ChemicalObjects.Atom` :param atom3: the third atom that defines the bond :type atom3: :class:`~MMTK.ChemicalObjects.Atom` """ InternalCoordinate.__init__(self, [atom1, atom2, atom3]) excluded = set([atom2]) self.findFragments((atom1, excluded, atom3), (atom3, excluded, atom1))
[docs] def getValue(self, conf = None): """ :param conf: a configuration (defaults to the current configuration) :type conf: :class:`~MMTK.ParticleProperties.Configuration` :returns: the size of the angle in the configuration conf :rtype: float """ return self.universe.angle(self.atoms[0], self.atoms[1], self.atoms[2], conf)
[docs] def setValue(self, value): """ Sets the size of the angle :param value: the desired angle :type value: float """ from Scientific.Geometry import delta v1 = self.universe.distanceVector(self.atoms[1], self.atoms[0]) v2 = self.universe.distanceVector(self.atoms[1], self.atoms[2]) angle = v1.angle(v2) if N.fabs(angle - N.pi) < 1.e-4: raise ValueError("angle too close to pi") axis = v1.cross(v2).normal() d = angle-value cm1, th1 = self.fragment1.centerAndMomentOfInertia() r1 = self.universe.distanceVector(self.atoms[1], cm1) th1 -= self.fragment1.mass()*((r1*r1)*delta-r1.dyadicProduct(r1)) i1 = axis*(th1*axis) cm2, th2 = self.fragment2.centerAndMomentOfInertia() r2 = self.universe.distanceVector(self.atoms[1], cm2) th2 -= self.fragment2.mass()*((r2*r2)*delta-r2.dyadicProduct(r2)) i2 = axis*(th2*axis) d1 = i2*d/(i1+i2) d2 = d1-d self.fragment1.rotateAroundAxis(self.atoms[1].position(), self.atoms[1].position()+axis, d1) self.fragment2.rotateAroundAxis(self.atoms[1].position(), self.atoms[1].position()+axis, d2) # # Dihedral angles #
[docs]class DihedralAngle(InternalCoordinate): """ Dihedral angle A DihedralAngle object permits calculating and modifying the dihedral angle defined by three consecutive bonds in a molecule, under the condition that the central bond is not part of a circular bond structure (but it is not a problem to have a circular bond structure elsewhere in the molecule). Modifying the dihedral angle rotates the parts of the molecule on both sides of the central bond around this central bond in such a way that there is no overall rotation of the molecule. The initial construction of the DihedralAngle object can be expensive (the bond structure of the molecule must be analyzed). It is therefore advisable to keep the object rather than recreate it frequently. Note that if you only want to calculate bond angles (no modification), the method Universe.dihedral is simpler and faster. """ def __init__(self, atom1, atom2, atom3, atom4): """ :param atom1: the first atom that defines the dihedral :type atom1: :class:`~MMTK.ChemicalObjects.Atom` :param atom2: the second atom that defines the dihedral, must be on the central bond :type atom2: :class:`~MMTK.ChemicalObjects.Atom` :param atom3: the third atom that defines the dihedral, must be on the central bond :type atom3: :class:`~MMTK.ChemicalObjects.Atom` :param atom4: the fourth atom that defines the dihedral :type atom4: :class:`~MMTK.ChemicalObjects.Atom` """ InternalCoordinate.__init__(self, [atom1, atom2, atom3, atom4]) excluded = set([atom2, atom3]) self.findFragments((atom1, excluded, atom4), (atom4, excluded, atom1))
[docs] def getValue(self, conf = None): """ :param conf: a configuration (defaults to the current configuration) :type conf: :class:`~MMTK.ParticleProperties.Configuration` :returns: the size of the dihedral angle in the configuration conf :rtype: float """ return self.universe.dihedral(self.atoms[0], self.atoms[1], self.atoms[2], self.atoms[3], conf)
[docs] def setValue(self, value): """ Sets the size of the dihedral :param value: the desired dihedral angle :type value: float """ from Scientific.Geometry import delta angle = self.universe.dihedral(self.atoms[0], self.atoms[1], self.atoms[2], self.atoms[3]) v = self.universe.distanceVector(self.atoms[1], self.atoms[2]) axis = v.normal() d = N.fmod(angle-value, 2.*N.pi) cm1, th1 = self.fragment1.centerAndMomentOfInertia() r1 = self.universe.distanceVector(self.atoms[1], cm1) th1 -= self.fragment1.mass()*((r1*r1)*delta-r1.dyadicProduct(r1)) i1 = axis*(th1*axis) cm2, th2 = self.fragment2.centerAndMomentOfInertia() r2 = self.universe.distanceVector(self.atoms[2], cm2) th2 -= self.fragment2.mass()*((r2*r2)*delta-r2.dyadicProduct(r2)) i2 = axis*(th2*axis) d1 = i2*d/(i1+i2) d2 = d1-d self.fragment1.rotateAroundAxis(self.atoms[1].position(), self.atoms[1].position()+axis, d1) self.fragment2.rotateAroundAxis(self.atoms[2].position(), self.atoms[2].position()+axis, d2)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Minimization.html0000644000076600000240000005722712013143454022110 0ustar hinsenstaff00000000000000 MMTK.Minimization — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Minimization

# This module implements energy minimizers.
#
# Written by Konrad Hinsen
#

"""
Energy minimizers
"""

__docformat__ = 'restructuredtext'

from MMTK import Features, Trajectory, Units
from MMTK_minimization import conjugateGradient, steepestDescent

#
# Minimizer base class
#
class Minimizer(Trajectory.TrajectoryGenerator):

    def __init__(self, universe, options):
	Trajectory.TrajectoryGenerator.__init__(self, universe, options)

    default_options = {'steps': 100, 'step_size': 0.02*Units.Ang,
		       'convergence': 0.01*Units.kJ/(Units.mol*Units.nm),
                       'background': False, 'threads': None,
                       'mpi_communicator': None, 'actions': []}

    available_data = ['energy', 'configuration', 'gradients']

    restart_data = ['configuration', 'energy']

    def __call__(self, options):
	raise AttributeError

#
# Steepest descent minimizer
#
[docs]class SteepestDescentMinimizer(Minimizer): """ Steepest-descent minimizer The minimizer can handle fixed atoms, but no distance constraints. It is fully thread-safe. The minimization is started by calling the minimizer object. All the keyword options (see documnentation of __init__) can be specified either when creating the minimizer or when calling it. The following data categories and variables are available for output: - category "configuration": configuration and box size (for periodic universes) - category "gradients": energy gradients for each atom - category "energy": potential energy and norm of the potential energy gradient """ def __init__(self, universe, **options): """ :param universe: the universe on which the integrator acts :type universe: :class:`~MMTK.Universe.Universe` :keyword steps: the number of minimization steps (default is 100) :type steps: int :keyword step_size: the initial size of a minimization step (default is 0.002 nm) :type step_size: float :keyword convergence: the root-mean-square gradient length at which minimization stops (default is 0.01 kJ/mol/nm) :type convergence: float :keyword actions: a list of actions to be executed periodically (default is none) :type actions: list :keyword threads: the number of threads to use in energy evaluation (default set by MMTK_ENERGY_THREADS) :type threads: int :keyword background: if True, the integration is executed as a separate thread (default: False) :type background: bool :keyword mpi_communicator: an MPI communicator object, or None, meaning no parallelization (default: None) :type mpi_communicator: Scientific.MPI.MPICommunicator """ Minimizer.__init__(self, universe, options) self.features = [Features.FixedParticleFeature, Features.NoseThermostatFeature, Features.AndersenBarostatFeature] def __call__(self, **options): """ Run the minimizer. The keyword options are the same as described under __init__. """ self.setCallOptions(options) Features.checkFeatures(self, self.universe) configuration = self.universe.configuration() fixed = self.universe.getAtomBooleanArray('fixed') nt = self.getOption('threads') comm = self.getOption('mpi_communicator') evaluator = self.universe.energyEvaluator(threads=nt, mpi_communicator=comm) evaluator = evaluator.CEvaluator() args = (self.universe, configuration.array, fixed.array, evaluator, self.getOption('steps'), self.getOption('step_size'), self.getOption('convergence'), self.getActions(), 'Steepest descent minimization with ' + self.optionString(['convergence', 'step_size', 'steps'])) return self.run(steepestDescent, args) # # Conjugate gradient minimizer #
[docs]class ConjugateGradientMinimizer(Minimizer): """ Conjugate gradient minimizer The minimizer can handle fixed atoms, but no distance constraints. It is fully thread-safe. The minimization is started by calling the minimizer object. All the keyword options can be specified either when creating the minimizer or when calling it. The following data categories and variables are available for output: - category "configuration": configuration and box size (for periodic universes) - category "gradients": energy gradients for each atom - category "energy": potential energy and norm of the potential energy gradient """ def __init__(self, universe, **options): """ :param universe: the universe on which the integrator acts :type universe: :class:`~MMTK.Universe.Universe` :keyword steps: the number of minimization steps (default is 100) :type steps: int :keyword step_size: the initial size of a minimization step (default is 0.002 nm) :type step_size: float :keyword convergence: the root-mean-square gradient length at which minimization stops (default is 0.01 kJ/mol/nm) :type convergence: float :keyword actions: a list of actions to be executed periodically (default is none) :type actions: list :keyword threads: the number of threads to use in energy evaluation (default set by MMTK_ENERGY_THREADS) :type threads: int :keyword background: if True, the integration is executed as a separate thread (default: False) :type background: bool """ Minimizer.__init__(self, universe, options) self.features = [Features.FixedParticleFeature, Features.NoseThermostatFeature] def __call__(self, **options): """ Run the minimizer. The keyword options are the same as described under __init__. """ self.setCallOptions(options) Features.checkFeatures(self, self.universe) configuration = self.universe.configuration() fixed = self.universe.getAtomBooleanArray('fixed') nt = self.getOption('threads') evaluator = self.universe.energyEvaluator(threads=nt).CEvaluator() args =(self.universe, configuration.array, fixed.array, evaluator, self.getOption('steps'), self.getOption('step_size'), self.getOption('convergence'), self.getActions(), 'Conjugate gradient minimization with ' + self.optionString(['convergence', 'step_size', 'steps'])) return self.run(conjugateGradient, args)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/MolecularSurface.html0000644000076600000240000010616212013143455022667 0ustar hinsenstaff00000000000000 MMTK.MolecularSurface — MMTK User Guide 2.7.7 documentation

Source code for MMTK.MolecularSurface

#
# Copyright 2000 by Peter McCluskey (pcm@rahul.net).
# You may do anything you want with it, provided this notice is kept intact.
#

# This module is in part a replacement for the MolecularSurface MMTK module
# that uses the NSC code. It has a less restrictive license than the NSC code,
# and is mostly more flexible, but is probably slower and somewhat less
# accurate. It can run (slowly) without any of the C code. It has an
# additional routine surfacePointsAndGradients, which returns area, an
# "up" vector, and surface points for each atom.


# Modifications by Konrad Hinsen <hinsen@cnrs-orleans.fr>:
# - Replaced tabs by spaces
# - Added/adapted docstrings to MMTK conventions
# - Removed assignment of methods to class GroupOfAtoms
# - Use vectors in the return value of surfacePointsAndGradients
# - Replaced "math" module by "Numeric"
# - Renamed module _surface to MMTK_surface
# - Converted docstrings to epydoc
# - Changed first argument name from self to object

"""
Molecular surfaces and volumes.
"""

__docformat__ = 'restructuredtext'

from MMTK import surfm
from MMTK.Collections import Collection
from MMTK import Vector
from Scientific import N

[docs]def surfaceAndVolume(object, probe_radius = 0.): """ :param object: a chemical object :type object: :class:`~MMTK.Collections.GroupOfAtoms` :param probe_radius: the distance from the vdW-radii of the atoms at which the surface is computed :type probe_radius: float :returns: the molecular surface and volume of object :rtype: tuple """ atoms = object.atomList() smap = surfm.surface_atoms(atoms, probe_radius, ret_fmt = 2) tot_a = 0 tot_v = 0 for a in atoms: atom_data = smap[a] tot_a = tot_a + atom_data[0] tot_v = tot_v + atom_data[1] return (tot_a, tot_v)
[docs]def surfaceAtoms(object, probe_radius = 0.): """ :param object: a chemical object :type object: :class:`~MMTK.Collections.GroupOfAtoms` :param probe_radius: the distance from the vdW-radii of the atoms at which the surface is computed :type probe_radius: float :returns: a dictionary that maps the surface atoms to their exposed surface areas :rtype: dict """ atoms = object.atomList() smap = surfm.surface_atoms(atoms, probe_radius, ret_fmt = 1) surface_atoms = {} for a in atoms: area = smap[a] if area > 0.: # we have a surface atom surface_atoms[a] = area return surface_atoms
[docs]def surfacePointsAndGradients(object, probe_radius = 0., point_density = 258): """ :param object: a chemical object :type object: :class:`~MMTK.Collections.GroupOfAtoms` :param probe_radius: the distance from the vdW-radii of the atoms at which the surface is computed :type probe_radius: float :param point_density: the density of points that describe the surface :type point_density: int :returns: a dictionary that maps the surface atoms to a tuple containing three surface-related quantities: the exposed surface area, a list of points in the exposed surface, and a gradient vector pointing outward from the surface. :rtype: dict """ atoms = object.atomList() smap = surfm.surface_atoms(atoms, probe_radius, ret_fmt = 4, point_density = point_density) surface_data = {} for a in atoms: (area, volume, points1, grad) = smap[a] if area > 0.: # we have a surface atom surface_data[a] = (area, map(Vector, points1), Vector(grad)) return surface_data
class Contact(object): def __init__(self, a1, a2, dist = None): self.a1 = a1 self.a2 = a2 if dist is None: self.dist = (a1.position() - a2.position()).length() else: self.dist = dist def __getitem__(self, index): return (self.a1, self.a2)[index] def __cmp__(a, b): return cmp(a.dist, b.dist) def __hash__(self): return (self.a1, self.a2) def __repr__(self): return 'Contact(%s, %s)' % (self.a1, self.a2) __str__ = __repr__
[docs]def findContacts(object1, object2, contact_factor = 1.0, cutoff = 0.0): """ Identifies contacts between two molecules. A contact is defined as a pair of atoms whose distance is less than contact_factor*(r1+r2+cutoff), where r1 and r2 are the atomic van-der-Waals radii. :param object1: a chemical object :type object1: :class:`~MMTK.Collections.GroupOfAtoms` :param object2: a chemical object :type object2: :class:`~MMTK.Collections.GroupOfAtoms` :param contact_factor: a scale factor in the contact distance criterion :type contact_factor: float :param cutoff: a constant in the contact distance criterion :type cutoff: float :returns: a list of Contact objects that describe atomic contacts between object1 and object2. :rtype: list """ max_object1 = len(object1.atomList()) atoms = object1.atomList() + object2.atomList() tup = surfm.get_atom_data(atoms, 0.0) max_rad = tup[0] # max vdW_radius atom_data = tup[1] nbors = surfm.NeighborList(atoms, contact_factor*(2*max_rad+cutoff), atom_data) clist = [] done = {} for index1 in range(len(atoms)): for (index2, dist2) in nbors[index1]: if (index1 < max_object1) != (index2 < max_object1): if index1 < index2: a1 = atoms[index1] a2 = atoms[index2] else: a1 = atoms[index2] a2 = atoms[index1] dist = N.sqrt(dist2) if dist >= contact_factor*(a1.vdW_radius + a2.vdW_radius + cutoff): continue if not done.has_key((index1, index2)): clist.append(Contact(a1, a2, dist)) done[(index1, index2)] = 1 return clist
if __name__ == '__main__': from MMTK.PDB import PDBConfiguration from MMTK import Units import sys target_filename = sys.argv[2] pdb_conf1 = PDBConfiguration(target_filename) if sys.argv[1][:2] == '-f': chains = pdb_conf1.createNucleotideChains() molecule_names = [] if len(chains) >= 2: clist = findContacts(chains[0], chains[1]) else: molecule_names = [] for (key, mol) in pdb_conf1.molecules.items(): for o in mol: molecule_names.append(o.name) targets = pdb_conf1.createAll(molecule_names = molecule_names) if len(molecule_names) > 1: clist = findContacts(targets[0], targets[1]) else: atoms = targets.atomList() mid = len(atoms)/2 clist = findContacts(Collection(atoms[:mid]), Collection(atoms[mid:])) print len(clist), 'contacts' for c in clist[:8]: print '%-64s %6.2f' % (c, c.dist/Units.Ang) else: target = pdb_conf1.createAll() if sys.argv[1][:2] == '-v': (a, v) = target.surfaceAndVolume() print 'surface area %.2f volume %.2f' \ % (a/(Units.Ang**2), v/(Units.Ang**3)) elif sys.argv[1][:2] == '-a': smap = target.surfaceAtoms(probe_radius = 1.4*Units.Ang) print len(smap.keys()),'of',len(target.atomList()),'atoms on surface' elif sys.argv[1][:2] == '-p': smap = target.surfacePointsAndGradients(probe_radius = 1.4*Units.Ang) for (a, tup) in smap.items(): print '%-40.40s %6.2f %5d %5.1f %5.1f %5.1f' \ % (a.fullName(), tup[0]/(Units.Ang**2), len(tup[1]), tup[2][0], tup[2][1], tup[2][2])
MMTK-2.7.9/Doc/HTML/_modules/MMTK/MoleculeFactory.html0000644000076600000240000014215312013143454022527 0ustar hinsenstaff00000000000000 MMTK.MoleculeFactory — MMTK User Guide 2.7.7 documentation

Source code for MMTK.MoleculeFactory

# A molecule factory is used to create chemical objects
# in code, without a database definition.
#
# Written by Konrad Hinsen
#

"""
Molecule factory for creating chemical objects
"""

__docformat__ = 'restructuredtext'

from MMTK import Bonds, ChemicalObjects

#
# Molecule factories store AtomTemplate and GroupTemplate objects.
#

class AtomTemplate(object):
    
    def __init__(self, element):
        self.element = element

class GroupTemplate(object):
    
    def __init__(self, name):
        self.name = name
        self.children = []
        self.names = {}
        self.attributes = {}
        self.positions = {}
        self.bonds = []
        self.locked = False

    def addAtom(self, atom_name, element):
        """
        Add an atom.

        :param atom_name: the name of the atom
        :type atom_name: str
        :param element: the chemical element symbol
        :type element: str
        """
        if self.locked:
            raise ValueError("group is locked")
        atom = AtomTemplate(element)
        self.names[atom_name] = len(self.children)
        self.children.append(atom)

    def addSubgroup(self, subgroup_name, subgroup):
        """
        Add a subgroup.

        :param subgroup_name: the name of the subgroup within this group
        :type subgroup_name: str
        :param subgroup: the subgroup type
        :type subgroup: str
        """
        if self.locked:
            raise ValueError("group is locked")
        self.names[subgroup_name] = len(self.children)
        self.children.append(subgroup)

    def addBond(self, atom1, atom2):
        """
        Add a bond.

        :param atom1: the name of the first atom
        :type atom1: str
        :param atom2: the name of the second atom
        :type atom2: str
        """
        if self.locked:
            raise ValueError("group is locked")
        self.bonds.append((self.atomNameToPath(atom1),
                           self.atomNameToPath(atom2)))

    def setAttribute(self, name, value):
        if self.locked:
            raise ValueError("group is locked")
        self.attributes[name] = value

    def setPosition(self, name, vector):
        if self.locked:
            raise ValueError("group is locked")
        self.positions[name] = vector

    def getAtomReference(self, atom_name):
        path = self.atomNameToPath(atom_name)
        object = self
        for path_element in path[:-1]:
            object =  object.children[object.names[path_element]]
        atom_index = object.names[path[-1]]
        reference = 0
        for i in range(atom_index):
            if isinstance(object.children[i], AtomTemplate):
                reference += 1
        from MMTK.Database import AtomReference
        return AtomReference(reference)

    def atomNameToPath(self, atom_name):
        atom_name = atom_name.split('.')
        object = self
        try:
            for path_element in atom_name:
                object = object.children[object.names[path_element]]
            if not isinstance(object, AtomTemplate):
                raise ValueError("no atom " + atom)
        except KeyError:
            raise ValueError("no atom " + '.'.join(atom_name))
        return atom_name

    def writeXML(self, file, memo):
        if memo.get(self.name, False):
            return
        names = len(self.children)*[None]
        for name in self.names:
            names[self.names[name]] = name
        atoms = []
        subgroups = []
        for i in range(len(self.children)):
            object = self.children[i]
            name = names[i]
            if isinstance(object, GroupTemplate):
                object.writeXML(file, memo)
                subgroups.append((name, object))
            else:
                atoms.append((name, object))
        file.write('<molecule id="%s">\n' % self.name)
        for name, subgroup in subgroups:
            file.write('  <molecule ref="%s" title="%s"/>\n'
                       % (subgroup.name, name))
        if atoms:
            file.write('  <atomArray>\n')
            for name, atom in atoms:
                file.write('    <atom title="%s" elementType="%s"/>\n'
                           % (name, atom.element))
            file.write('  </atomArray>\n')
        if self.bonds:
            file.write('  <bondArray>\n')
            for a1, a2 in self.bonds:
                file.write('    <bond atomRefs2="%s %s"/>\n'
                           % (':'.join(a1), ':'.join(a2)))
            file.write('  </bondArray>\n')
        file.write('</molecule>\n')
        memo[self.name] = True

    def getXMLAtomOrder(self):
        atom_names = []
        names = len(self.children)*[None]
        for name in self.names:
            names[self.names[name]] = name
        for i in range(len(self.children)):
            oname = names[i]
            object = self.children[i]
            if isinstance(object, GroupTemplate):
                for name in object.getXMLAtomOrder():
                    atom_names.append(oname + ':' + name)
            else:
                atom_names.append(oname)
        return atom_names

#
# The MoleculeFactory class
#
[docs]class MoleculeFactory(object): ''' MoleculeFactory A MoleculeFactory serves to create molecules without reference to database definitions. Molecules and groups are defined in terms of atoms, groups, and bonds. Each MoleculeFactory constitutes an independent set of definitions. Definitions within a MoleculeFactory can refer to each other. Each MoleculeFactory stores a set of :class:`~MMTK.ChemicalObjects.Group` objects which are referred to by names. The typical operation sequence is to create a new group and then add atoms, bonds, and subgroups. It is also possible to define coordinates and arbitrary attributes (in particular for force fields). In the end, a finished object can be retrieved as a :class:`~MMTK.ChemicalObjects.Molecule` object. ''' def __init__(self): self.groups = {} self.locked_groups = {}
[docs] def createGroup(self, name): """ Create a new (initially empty) group object. """ if self.groups.has_key(name): raise ValueError("redefinition of group " + name) self.groups[name] = GroupTemplate(name)
[docs] def addSubgroup(self, group, subgroup_name, subgroup): """ Add a subgroup to a group :param group: the name of the group :type group: str :param subgroup_name: the name of the subgroup within the group :type subgroup_name: str :param subgroup: the subgroup type :type subgroup: str """ self.groups[group].addSubgroup(subgroup_name, self.groups[subgroup])
[docs] def addAtom(self, group, atom_name, element): """ Add an atom to a group :param group: the name of the group :type group: str :param atom_name: the name of the atom :type atom_name: str :param element: the chemical element symbol :type element: str """ self.groups[group].addAtom(atom_name, element)
[docs] def addBond(self, group, atom1, atom2): """ Add a bond to a group :param group: the name of the group :type group: str :param atom1: the name of the first atom :type atom1: str :param atom2: the name of the second atom :type atom2: str """ self.groups[group].addBond(atom1, atom2)
def setAttribute(self, group, name, value): self.groups[group].setAttribute(name, value) def setPosition(self, group, atom, vector): self.groups[group].setPosition(atom, vector) def getAtomReference(self, group, atom): return self.groups[group].getAtomReference(atom)
[docs] def retrieveMolecule(self, group): """ :param group: the name of the group to be used as a template :type group: str :returns: a molecule defined by the contents of the group :rtype: :class:`~MMTK.ChemicalObjects.Molecule` """ group = self.groups[group] return self.makeChemicalObjects(group, True)
def makeChemicalObjects(self, template, top_level): self.groups[template.name].locked = True if top_level: if template.attributes.has_key('sequence'): object = ChemicalObjects.ChainMolecule(None) else: object = ChemicalObjects.Molecule(None) else: object = ChemicalObjects.Group(None) object.atoms = [] object.bonds = Bonds.BondList([]) object.groups = [] object.type = self.groups[template.name] object.parent = None child_objects = [] for child in template.children: if isinstance(child, GroupTemplate): group = self.makeChemicalObjects(child, False) object.groups.append(group) object.atoms.extend(group.atoms) object.bonds.extend(group.bonds) group.parent = object child_objects.append(group) else: atom = ChemicalObjects.Atom(child.element) object.atoms.append(atom) atom.parent = object child_objects.append(atom) for name, index in template.names.items(): setattr(object, name, child_objects[index]) child_objects[index].name = name for name, value in template.attributes.items(): path = name.split('.') setattr(self.namePath(object, path[:-1]), path[-1], value) for atom1, atom2 in template.bonds: atom1 = self.namePath(object, atom1) atom2 = self.namePath(object, atom2) object.bonds.append(Bonds.Bond((atom1, atom2))) for name, vector in template.positions.items(): path = name.split('.') self.namePath(object, path).setPosition(vector) return object def namePath(self, object, path): for item in path: object = getattr(object, item) return object def writeXML(self, file): file.write('<?xml version="1.0" encoding="ISO-8859-1" ' + 'standalone="yes"?>\n\n') file.write('<templates>\n\n') memo = {} for group in self.groups: self.groups[group].writeXML(file, memo) file.write('\n</templates>\n')
MMTK-2.7.9/Doc/HTML/_modules/MMTK/NormalModes/0000755000076600000240000000000012156626563020775 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_modules/MMTK/NormalModes/BrownianModes.html0000644000076600000240000024226212013143454024423 0ustar hinsenstaff00000000000000 MMTK.NormalModes.BrownianModes — MMTK User Guide 2.7.7 documentation

Source code for MMTK.NormalModes.BrownianModes

# Brownian normal mode calculations.
#
# Written by Konrad Hinsen
#

"""
Brownian normal modes
"""

__docformat__ = 'restructuredtext'

from MMTK import Features, Units, ParticleProperties, Random
from Scientific.Functions.Interpolation import InterpolatingFunction
from Scientific import N

from MMTK.NormalModes import Core

#
# Class for a single mode
#
[docs]class BrownianMode(Core.Mode): """ Single Brownian normal mode Mode objects are created by indexing a :class:`~MMTK.NormalModes.BrownianModes.BrownianModes` object. They contain the atomic displacements corresponding to a single mode. In addition, the inverse of the relaxation time corresponding to the mode is stored in the attribute "inv_relaxation_time". Note: the Brownian mode vectors are not friction weighted and therefore not orthogonal to each other. """ def __init__(self, universe, n, inv_relaxation_time, mode): self.inv_relaxation_time = inv_relaxation_time Core.Mode.__init__(self, universe, n, mode) def __str__(self): return 'Mode ' + `self.number` + \ ' with inverse relaxation time ' \ + `self.inv_relaxation_time` __repr__ = __str__ # # Class for a full set of normal modes #
[docs]class BrownianModes(Core.NormalModes): """ Brownian modes describe the independent relaxation motions of a harmonic system with friction. They are obtained by diagonalizing the friction-weighted force constant matrix. In order to obtain physically reasonable normal modes, the configuration of the universe must correspond to a local minimum of the potential energy. Individual modes (see :class:`~MMTK.NormalModes.BrownianModes.BrownianMode`) can be extracted by indexing with an integer. Looping over the modes is possible as well. Brownian modes describe the independent relaxation motions of a harmonic system with friction. They are obtained by diagonalizing the friction-weighted force constant matrix. In order to obtain physically reasonable normal modes, the configuration of the universe must correspond to a local minimum of the potential energy. A BrownianModes object behaves like a sequence of modes. Individual modes (see :class:`~MMTK.NormalModes.BrownianMode`) can be extracted by indexing with an integer. Looping over the modes is possible as well. """ features = [] def __init__(self, universe = None, friction = None, temperature = 300.*Units.K, subspace = None, delta = None, sparse = False): """ :param universe: the system for which the normal modes are calculated; it must have a force field which provides the second derivatives of the potential energy :type universe: :class:`~MMTK.Universe.Universe` :param friction: the friction coefficient for each particle. Note: The friction coefficients are not mass-weighted, i.e. they have the dimension of an inverse time. :type friction: :class:`~MMTK.ParticleProperties.ParticleScalar` :param temperature: the temperature for which the amplitudes of the atomic displacement vectors are calculated. A value of None can be specified to have no scaling at all. In that case the mass-weighted norm of each normal mode is one. :type temperature: float :param subspace: the basis for the subspace in which the normal modes are calculated (or, more precisely, a set of vectors spanning the subspace; it does not have to be orthogonal). This can either be a sequence of :class:`~MMTK.ParticleProperties.ParticleVector` objects or a tuple of two such sequences. In the second case, the subspace is defined by the space spanned by the second set of vectors projected on the complement of the space spanned by the first set of vectors. The first set thus defines directions that are excluded from the subspace. The default value of None indicates a standard normal mode calculation in the 3N-dimensional configuration space. :param delta: the rms step length for numerical differentiation. The default value of None indicates analytical differentiation. Numerical differentiation is available only when a subspace basis is used as well. Instead of calculating the full force constant matrix and then multiplying with the subspace basis, the subspace force constant matrix is obtained by numerical differentiation of the energy gradients along the basis vectors of the subspace. If the basis is much smaller than the full configuration space, this approach needs much less memory. :type delta: float :param sparse: a flag that indicates if a sparse representation of the force constant matrix is to be used. This is of interest when there are no long-range interactions and a subspace of smaller size then 3N is specified. In that case, the calculation will use much less memory with a sparse representation. :type sparse: bool """ if universe == None: return Features.checkFeatures(self, universe) Core.NormalModes.__init__(self, universe, subspace, delta, sparse, ['array', 'inv_relaxation_times']) self.friction = friction self.temperature = temperature self.weights = N.sqrt(friction.array) self.weights = self.weights[:, N.NewAxis] self._forceConstantMatrix() ev = self._diagonalize() self.inv_relaxation_times = ev self.sort_index = N.argsort(self.inv_relaxation_times) self.array.shape = (self.nmodes, self.natoms, 3) self.cleanup() def __getitem__(self, item): index = self.sort_index[item] return BrownianMode(self.universe, item, self.inv_relaxation_times[index], self.array[index]/self.weights)
[docs] def rawMode(self, item): """ :param item: the index of a normal mode :type item: int :returns: the unscaled mode vector :rtype: :class:`~MMTK.NormalModes.BrownianModes.BrownianMode` """ index = self.sort_index[item] return BrownianMode(self.universe, item, self.inv_relaxation_times[index], self.array[index])
def fluctuations(self, first_mode=6): f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode*mode)/mode.inv_relaxation_time f = Units.k_B*self.temperature*f/self.friction return f
[docs] def meanSquareDisplacement(self, subset=None, weights=None, time_range = (0., None, None), first_mode = 6): """ :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: atomic masses) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the averaged mean-square displacement of the atoms in subset as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/(total*self.friction) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) rt = mode.inv_relaxation_time d = (weights*(mode*mode)).sumOverParticles() N.add(msd, d*(1.-N.exp(-rt*time))/rt, msd) N.multiply(msd, 2.*Units.k_B*self.temperature, msd) return InterpolatingFunction((time,), msd)
[docs] def staticStructureFactor(self, q_range = (1., 15.), subset=None, weights=None, random_vectors=15, first_mode = 6): """ :param q_range: the range of angular wavenumber values :type q_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: coherent scattering lengths) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Static Structure Factor as a function of angular wavenumber :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_coherent') mask = subset.booleanMask() weights = N.repeat(weights.array, mask.array) weights = weights/N.sqrt(N.add.reduce(weights*weights)) friction = N.repeat(self.friction.array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (q_range+(None,))[:3] if step is None: step = (last-first)/50. q = N.arange(first, last, step) kT = Units.k_B*self.temperature natoms = subset.numberOfAtoms() sq = 0. random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: sab = N.zeros((natoms, natoms), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) sab = sab + (d[N.NewAxis,:]-d[:,N.NewAxis])**2/irt sab = sab[N.NewAxis,:,:]*q[:, N.NewAxis, N.NewAxis]**2 phase = N.exp(-1.j*q[:, N.NewAxis] * N.dot(r, v.array)[N.NewAxis, :]) \ * weights[N.NewAxis, :] temp = N.sum(phase[:, :, N.NewAxis]*N.exp(-0.5*kT*sab), 1) temp = N.sum(N.conjugate(phase)*temp, 1) sq = sq + temp.real return InterpolatingFunction((q,), sq/len(random_vectors))
[docs] def coherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, weights=None, random_vectors=15, first_mode = 6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: coherent scattering lengths) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Coherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_coherent') mask = subset.booleanMask() weights = N.repeat(weights.array, mask.array) weights = weights/N.sqrt(N.add.reduce(weights*weights)) friction = N.repeat(self.friction.array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.rawMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B*self.temperature fcoh = N.zeros((len(time),), N.Complex) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j*q*N.dot(r, v.array)) for ai in range(natoms): fbt = N.zeros((natoms, len(time)), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = N.exp(-irt*time)/irt N.add(fbt, d[ai] * d[:, N.NewAxis] * ft[N.NewAxis, :], fbt) N.add(fbt, (-0.5/irt) * (d[ai]**2 + d[:, N.NewAxis]**2), fbt) N.add(fcoh, weights[ai]*phase[ai] * N.dot(weights*N.conjugate(phase), N.exp(kT*fbt)), fcoh) return InterpolatingFunction((time,), fcoh.real/len(random_vectors))
[docs] def incoherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, random_vectors=15, first_mode = 6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Incoherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe mask = subset.booleanMask() weights_inc = self.universe.getParticleScalar('b_incoherent') weights_inc = N.repeat(weights_inc.array**2, mask.array) weights_inc = weights_inc/N.add.reduce(weights_inc) friction = N.repeat(self.friction.array, mask.array) mass = N.repeat(self.universe.masses().array, mask.array) r = N.repeat(self.universe.configuration().array, mask.array) first, last, step = (time_range + (None, None))[:3] if last is None: last = 3./self.weighedMode(first_mode).inv_relaxation_time if step is None: step = (last-first)/300. time = N.arange(first, last, step) natoms = subset.numberOfAtoms() kT = Units.k_B*self.temperature finc = N.zeros((len(time),), N.Float) eisf = 0. random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: phase = N.exp(-1.j*q*N.dot(r, v.array)) faat = N.zeros((natoms, len(time)), N.Float) eisf_sum = N.zeros((natoms,), N.Float) for i in range(first_mode, self.nmodes): irt = self.rawMode(i).inv_relaxation_time d = q * N.repeat((self.rawMode(i)*v).array, mask.array) \ / N.sqrt(friction) ft = (N.exp(-irt*time)-1.)/irt N.add(faat, d[:, N.NewAxis]**2 * ft[N.NewAxis, :], faat) N.add(eisf_sum, -d**2/irt, eisf_sum) N.add(finc, N.sum(weights_inc[:, N.NewAxis] * N.exp(kT*faat), 0), finc) eisf = eisf + N.sum(weights_inc*N.exp(kT*eisf_sum)) return InterpolatingFunction((time,), finc/len(random_vectors))
[docs] def EISF(self, q_range = (0., 15.), subset=None, weights = None, random_vectors = 15, first_mode = 6): """ :param q_range: the range of angular wavenumber values :type q_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: incoherent scattering lengths) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Elastic Incoherent Structure Factor (EISF) as a function of angular wavenumber :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_incoherent') weights = weights*weights weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/total first, last, step = (q_range+(None,))[:3] if step is None: step = (last-first)/50. q = N.arange(first, last, step) f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f = f + (1./mode.inv_relaxation_time)*mode.dyadicProduct(mode) f = Units.k_B*self.temperature*f/self.friction eisf = N.zeros(q.shape, N.Float) random_vectors = Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): exp = N.exp(-v*(f[a]*v)) N.add(eisf, weights[a]*exp**(q*q), eisf) return InterpolatingFunction((q,), eisf/len(random_vectors))
MMTK-2.7.9/Doc/HTML/_modules/MMTK/NormalModes/EnergeticModes.html0000644000076600000240000006047012013143455024551 0ustar hinsenstaff00000000000000 MMTK.NormalModes.EnergeticModes — MMTK User Guide 2.7.7 documentation

Source code for MMTK.NormalModes.EnergeticModes

# Energetic normal mode calculations.
#
# Written by Konrad Hinsen
#

"""
Energetic normal modes
"""

__docformat__ = 'restructuredtext'

from MMTK import Features, Units, ParticleProperties
from MMTK.NormalModes import Core
from Scientific import N

#
# Class for a single mode
#
[docs]class EnergeticMode(Core.Mode): """ Single energetic normal mode Mode objects are created by indexing a :class:`MMTK.NormalModes.EnergeticModes.EnergeticModes` object. They contain the atomic displacements corresponding to a single mode. In addition, the force constant corresponding to the mode is stored in the attribute "force_constant". """ def __init__(self, universe, n, force_constant, mode): self.force_constant = force_constant Core.Mode.__init__(self, universe, n, mode) def __str__(self): return 'Mode ' + `self.number` + ' with force constant ' + \ `self.force_constant` __repr__ = __str__ # # Class for a full set of normal modes #
[docs]class EnergeticModes(Core.NormalModes): """ Energetic modes describe the principal axes of an harmonic approximation to the potential energy surface of a system. They are obtained by diagonalizing the force constant matrix without prior mass-weighting. In order to obtain physically reasonable normal modes, the configuration of the universe must correspond to a local minimum of the potential energy. Individual modes (see class :class:`~MMTK.NormalModes.EnergeticModes.EnergeticMode`) can be extracted by indexing with an integer. Looping over the modes is possible as well. """ features = [] def __init__(self, universe=None, temperature = 300*Units.K, subspace = None, delta = None, sparse = False): """ :param universe: the system for which the normal modes are calculated; it must have a force field which provides the second derivatives of the potential energy :type universe: :class:`~MMTK.Universe.Universe` :param temperature: the temperature for which the amplitudes of the atomic displacement vectors are calculated. A value of None can be specified to have no scaling at all. In that case the mass-weighted norm of each normal mode is one. :type temperature: float :param subspace: the basis for the subspace in which the normal modes are calculated (or, more precisely, a set of vectors spanning the subspace; it does not have to be orthogonal). This can either be a sequence of :class:`~MMTK.ParticleProperties.ParticleVector` objects or a tuple of two such sequences. In the second case, the subspace is defined by the space spanned by the second set of vectors projected on the complement of the space spanned by the first set of vectors. The first set thus defines directions that are excluded from the subspace. The default value of None indicates a standard normal mode calculation in the 3N-dimensional configuration space. :param delta: the rms step length for numerical differentiation. The default value of None indicates analytical differentiation. Numerical differentiation is available only when a subspace basis is used as well. Instead of calculating the full force constant matrix and then multiplying with the subspace basis, the subspace force constant matrix is obtained by numerical differentiation of the energy gradients along the basis vectors of the subspace. If the basis is much smaller than the full configuration space, this approach needs much less memory. :type delta: float :param sparse: a flag that indicates if a sparse representation of the force constant matrix is to be used. This is of interest when there are no long-range interactions and a subspace of smaller size then 3N is specified. In that case, the calculation will use much less memory with a sparse representation. :type sparse: bool """ if universe == None: return Features.checkFeatures(self, universe) Core.NormalModes.__init__(self, universe, subspace, delta, sparse, ['array', 'force_constants']) self.temperature = temperature self.weights = N.ones((1, 1), N.Float) self._forceConstantMatrix() ev = self._diagonalize() self.force_constants = ev self.sort_index = N.argsort(self.force_constants) self.array.shape = (self.nmodes, self.natoms, 3) self.cleanup() def __getitem__(self, item): index = self.sort_index[item] f = self.force_constants[index] #!! if self.temperature is None or item < 6: amplitude = 1. else: amplitude = N.sqrt(2.*self.temperature*Units.k_B / self.force_constants[index]) return EnergeticMode(self.universe, item, self.force_constants[index], amplitude*self.array[index])
[docs] def rawMode(self, item): """ :param item: the index of a normal mode :type item: int :returns: the unscaled mode vector :rtype: :class:`~MMTK.NormalModes.EnergeticModes.EnergeticMode` """ index = self.sort_index[item] f = self.force_constants[index] return EnergeticMode(self.universe, item, self.force_constants[index], self.array[index])
def fluctuations(self, first_mode=6): f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode*mode)/mode.force_constant if self.temperature is not None: f.array *= Units.k_B*self.temperature return f def anisotropicFluctuations(self, first_mode=6): f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) array = mode.array f.array += (array[:, :, N.NewAxis]*array[:, N.NewAxis, :]) \ / mode.force_constant if self.temperature is not None: f.array *= Units.k_B*self.temperature return f
MMTK-2.7.9/Doc/HTML/_modules/MMTK/NormalModes/VibrationalModes.html0000644000076600000240000017412712013143455025123 0ustar hinsenstaff00000000000000 MMTK.NormalModes.VibrationalModes — MMTK User Guide 2.7.7 documentation

Source code for MMTK.NormalModes.VibrationalModes

# Vibrational normal mode calculations.
#
# Written by Konrad Hinsen
#

"""
Vibrational normal modes
"""

__docformat__ = 'restructuredtext'

from MMTK import Features, Units, ParticleProperties
from Scientific import N

from MMTK.NormalModes import Core

#
# Class for a single mode
#
[docs]class VibrationalMode(Core.Mode): """ Single vibrational normal mode Mode objects are created by indexing a :class:`~MMTK.NormalModes.VibrationalModes.VibrationalModes` object. They contain the atomic displacements corresponding to a single mode. In addition, the frequency corresponding to the mode is stored in the attribute "frequency". Note: the normal mode vectors are *not* mass weighted, and therefore not orthogonal to each other. """ def __init__(self, universe, n, frequency, mode): self.frequency = frequency Core.Mode.__init__(self, universe, n, mode) def __str__(self): return 'Mode ' + `self.number` + ' with frequency ' + `self.frequency` __repr__ = __str__ def effectiveMassAndForceConstant(self): m = self.massWeightedDotProduct(self)/self.dotProduct(self) k = m*(2.*N.pi*self.frequency)**2 return m, k # # Class for a full set of normal modes #
[docs]class VibrationalModes(Core.NormalModes): """ Vibrational modes describe the independent vibrational motions of a harmonic system. They are obtained by diagonalizing the mass-weighted force constant matrix. In order to obtain physically reasonable normal modes, the configuration of the universe must correspond to a local minimum of the potential energy. Individual modes (see :class:`~MMTK.NormalModes.VibrationalModes.VibrationalMode`) can be extracted by indexing with an integer. Looping over the modes is possible as well. """ features = [] def __init__(self, universe=None, temperature = 300.*Units.K, subspace = None, delta = None, sparse = False): """ :param universe: the system for which the normal modes are calculated; it must have a force field which provides the second derivatives of the potential energy :type universe: :class:`~MMTK.Universe.Universe` :param temperature: the temperature for which the amplitudes of the atomic displacement vectors are calculated. A value of None can be specified to have no scaling at all. In that case the mass-weighted norm of each normal mode is one. :type temperature: float :param subspace: the basis for the subspace in which the normal modes are calculated (or, more precisely, a set of vectors spanning the subspace; it does not have to be orthogonal). This can either be a sequence of :class:`~MMTK.ParticleProperties.ParticleVector` objects or a tuple of two such sequences. In the second case, the subspace is defined by the space spanned by the second set of vectors projected on the complement of the space spanned by the first set of vectors. The first set thus defines directions that are excluded from the subspace. The default value of None indicates a standard normal mode calculation in the 3N-dimensional configuration space. :param delta: the rms step length for numerical differentiation. The default value of None indicates analytical differentiation. Numerical differentiation is available only when a subspace basis is used as well. Instead of calculating the full force constant matrix and then multiplying with the subspace basis, the subspace force constant matrix is obtained by numerical differentiation of the energy gradients along the basis vectors of the subspace. If the basis is much smaller than the full configuration space, this approach needs much less memory. :type delta: float :param sparse: a flag that indicates if a sparse representation of the force constant matrix is to be used. This is of interest when there are no long-range interactions and a subspace of smaller size then 3N is specified. In that case, the calculation will use much less memory with a sparse representation. :type sparse: bool """ if universe == None: return Features.checkFeatures(self, universe) Core.NormalModes.__init__(self, universe, subspace, delta, sparse, ['array', 'imaginary', 'frequencies', 'omega_sq']) self.temperature = temperature self.weights = N.sqrt(self.universe.masses().array) self.weights = self.weights[:, N.NewAxis] self._forceConstantMatrix() ev = self._diagonalize() self.imaginary = N.less(ev, 0.) self.omega_sq = ev self.frequencies = N.sqrt(N.fabs(ev)) / (2.*N.pi) self.sort_index = N.argsort(self.frequencies) self.array.shape = (self.nmodes, self.natoms, 3) self.cleanup() def __setstate__(self, state): # This fixes unpickled objects from pre-2.5 versions. # Their modes were stored in un-weighted coordinates. if not state.has_key('weights'): weights = N.sqrt(state['universe'].masses().array)[:, N.NewAxis] state['weights'] = weights state['array'] *= weights[N.NewAxis, :, :] if state['temperature'] is not None: factor = N.sqrt(2.*state['temperature']*Units.k_B/Units.amu) / \ (2.*N.pi) freq = state['frequencies'] for i in range(state['nmodes']): index = state['sort_index'][i] if index >= 6: state['array'][index] *= freq[index]/factor self.__dict__.update(state) def __getitem__(self, item): index = self.sort_index[item] f = self.frequencies[index] if self.imaginary[index]: f = f*1.j # !! if self.temperature is None or item < 6: amplitude = 1. else: amplitude = N.sqrt(2.*self.temperature*Units.k_B/Units.amu) / \ (2.*N.pi*f) return VibrationalMode(self.universe, item, f, amplitude*self.array[index]/self.weights)
[docs] def rawMode(self, item): """ :param item: the index of a normal mode :type item: int :returns: the unscaled mode vector :rtype: :class:`~MMTK.NormalModes.VibrationalModes.VibrationalMode` """ index = self.sort_index[item] f = self.frequencies[index] if self.imaginary[index]: f = f*1.j return VibrationalMode(self.universe, item, f, self.array[index])
def fluctuations(self, first_mode=6): f = ParticleProperties.ParticleScalar(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) f += (mode*mode)/mode.frequency**2 f.array /= self.weights[:, 0]**2 s = 1./(2.*N.pi)**2 if self.temperature is not None: s *= Units.k_B*self.temperature f.array *= s return f def anisotropicFluctuations(self, first_mode=6): f = ParticleProperties.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self.rawMode(i) array = mode.array f.array += (array[:, :, N.NewAxis]*array[:, N.NewAxis, :]) \ / mode.frequency**2 s = (2.*N.pi)**2 if self.temperature is not None: s /= Units.k_B*self.temperature f.array /= s*self.universe.masses().array[:, N.NewAxis, N.NewAxis] return f
[docs] def meanSquareDisplacement(self, subset=None, weights=None, time_range = (0., None, None), tau=None, first_mode = 6): """ :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: atomic masses) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param tau: the relaxation time of an exponential damping factor (default: no damping) :type tau: float :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the averaged mean-square displacement of the atoms in subset as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.masses() weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/total first, last, step = (time_range + (None, None))[:3] if last is None: last = 20./self[first_mode].frequency if step is None: step = (last-first)/300. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time/tau)**2) msd = N.zeros(time.shape, N.Float) for i in range(first_mode, self.nmodes): mode = self[i] omega = 2.*N.pi*mode.frequency d = (weights*(mode*mode)).sumOverParticles() N.add(msd, d*(1.-damping*N.cos(omega*time)), msd) return InterpolatingFunction((time,), msd)
[docs] def EISF(self, q_range = (0., 15.), subset=None, weights = None, random_vectors = 15, first_mode = 6): """ :param q_range: the range of angular wavenumber values :type q_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: incoherent scattering lengths) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Elastic Incoherent Structure Factor (EISF) as a function of angular wavenumber :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_incoherent') weights = weights*weights weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/total first, last, step = (q_range+(None,))[:3] if step is None: step = (last-first)/50. q = N.arange(first, last, step) f = MMTK.ParticleTensor(self.universe) for i in range(first_mode, self.nmodes): mode = self[i] f = f + mode.dyadicProduct(mode) eisf = N.zeros(q.shape, N.Float) for i in range(random_vectors): v = MMTK.Random.randomDirection() for a in subset.atomList(): exp = N.exp(-v*(f[a]*v)) N.add(eisf, weights[a]*exp**(q*q), eisf) return InterpolatingFunction((q,), eisf/random_vectors)
[docs] def incoherentScatteringFunction(self, q, time_range = (0., None, None), subset=None, weights=None, tau=None, random_vectors=15, first_mode = 6): """ :param q: the angular wavenumber :type q: float :param time_range: the time values at which the mean-square displacement is evaluated, specified as a range tuple (first, last, step). The defaults are first=0, last= 20 times the longest vibration perdiod, and step defined such that 300 points are used in total. :type time_range: tuple :param subset: the subset of the universe used in the calculation (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param weights: the weight to be given to each atom in the average (default: incoherent scattering lengths) :type weights: :class:`~MMTK.ParticleProperties.ParticleScalar` :param tau: the relaxation time of an exponential damping factor (default: no damping) :type tau: float :param random_vectors: the number of random direction vectors used in the orientational average :type random_vectors: int :param first_mode: the first mode to be taken into account for the fluctuation calculation. The default value of 6 is right for molecules in vacuum. :type first_mode: int :returns: the Incoherent Scattering Function as a function of time :rtype: Scientific.Functions.Interpolation.InterpolatingFunction """ if self.temperature is None: raise ValueError("no temperature available") if subset is None: subset = self.universe if weights is None: weights = self.universe.getParticleScalar('b_incoherent') weights = weights*weights weights = weights*subset.booleanMask() total = weights.sumOverParticles() weights = weights/total first, last, step = (time_range + (None, None))[:3] if last is None: last = 20./self[first_mode].frequency if step is None: step = (last-first)/400. time = N.arange(first, last, step) if tau is None: damping = 1. else: damping = N.exp(-(time/tau)**2) finc = N.zeros(time.shape, N.Float) random_vectors = MMTK.Random.randomDirections(random_vectors) for v in random_vectors: for a in subset.atomList(): msd = 0. for i in range(first_mode, self.nmodes): mode = self[i] d = (v*mode[a])**2 omega = 2.*N.pi*mode.frequency msd = msd+d*(1.-damping*N.cos(omega*time)) N.add(finc, weights[a]*N.exp(-q*q*msd), finc) return InterpolatingFunction((time,), finc/len(random_vectors))
MMTK-2.7.9/Doc/HTML/_modules/MMTK/NucleicAcids.html0000644000076600000240000012210712013143454021755 0ustar hinsenstaff00000000000000 MMTK.NucleicAcids — MMTK User Guide 2.7.7 documentation

Source code for MMTK.NucleicAcids

# This module implements classes for nucleotide chains.
#
# Written by Konrad Hinsen
#

"""
Nucleic acid chains
"""

__docformat__ = 'restructuredtext'

from MMTK import Biopolymers, Bonds, ChemicalObjects, Collections, \
                 ConfigIO, Database, Universe, Utility
from Scientific.Geometry import Vector

from MMTK.Biopolymers import defineNucleicAcidResidue

#
# Nucleotides are special groups
#
[docs]class Nucleotide(Biopolymers.Residue): """ Nucleic acid residue Nucleotides are a special kind of group. Like any other group, they are defined in the chemical database. Each residue has two or three subgroups ('sugar' and 'base', plus 'phosphate' except for 5'-terminal residues) and is usually connected to other residues to form a nucleotide chain. The database contains three variants of each residue (5'-terminal, 3'-terminal, non-terminal). """ def __init__(self, name = None, model = 'all'): """ :param name: the name of the nucleotide in the chemical database. This is the full name of the residue plus the suffix "_5ter" or "_3ter" for the terminal variants. :type name: str :param model: one of "all" (all-atom), "none" (no hydrogens), "polar" (united-atom with only polar hydrogens), "polar_charmm" (like "polar", but defining polar hydrogens like in the CHARMM force field). Currently the database has definitions only for "all". :type model: str """ if name is not None: blueprint = _residueBlueprint(name, model) ChemicalObjects.Group.__init__(self, blueprint) self.model = model self._init()
[docs] def backbone(self): """ :returns: the sugar and phosphate groups :rtype: :class:`~MMTK.ChemicalObjects.Group` """ bb = self.sugar if hasattr(self, 'phosphate'): bb = Collections.Collection([bb, self.phosphate]) return bb
[docs] def bases(self): """ :returns: the base group :rtype: :class:`~MMTK.ChemicalObjects.Group` """ return self.base
def _residueBlueprint(name, model): try: blueprint = _residue_blueprints[(name, model)] except KeyError: if model == 'polar': name = name + '_uni' elif model == 'polar_charmm': name = name + '_uni2' elif model == 'none': name = name + '_noh' blueprint = Database.BlueprintGroup(name) _residue_blueprints[(name, model)] = blueprint return blueprint _residue_blueprints = {} # # Nucleotide chains are molecules with added features. #
[docs]class NucleotideChain(Biopolymers.ResidueChain): """ Nucleotide chain Nucleotide chains consist of nucleotides that are linked together. They are a special kind of molecule, i.e. all molecule operations are available. Nucleotide chains act as sequences of residues. If n is a NucleotideChain object, then * len(n) yields the number of nucleotides * n[i] yields nucleotide number i * n[i:j] yields the subchain from nucleotide number i up to but excluding nucleotide number j """ def __init__(self, sequence, **properties): """ :param sequence: the nucleotide sequence. This can be a string containing the one-letter codes, or a list of two-letter codes (a "d" or "r" for the type of sugar, and the one-letter base code), or a :class:`~MMTK.PDB.PDBNucleotideChain` object. If a PDBNucleotideChain object is supplied, the atomic positions it contains are assigned to the atoms of the newly generated nucleotide chain, otherwise the positions of all atoms are undefined. :keyword model: one of "all" (all-atom), "no_hydrogens" or "none" (no hydrogens), "polar_hydrogens" or "polar" (united-atom with only polar hydrogens), "polar_charmm" (like "polar", but defining polar hydrogens like in the CHARMM force field), "polar_opls" (like "polar", but defining polar hydrogens like in the latest OPLS force field). Default is "all". Currently the database contains definitions only for "all". :type model: str :keyword terminus_5: if True, the first residue is constructed using the 5'-terminal variant, if False the non-terminal version is used. Default is True. :type terminus_5: bool :keyword terminus_3: if True, the last residue is constructed using the 3'-terminal variant, if False the non-terminal version is used. Default is True. :type terminus_3: bool :keyword circular: if True, a bond is constructed between the first and the last residue. Default is False. :type circular: bool :keyword name: a name for the chain (a string) :type name: str """ if sequence is not None: hydrogens = self.binaryProperty(properties, 'hydrogens', 'all') if hydrogens == 1: hydrogens = 'all' elif hydrogens == 0: hydrogens = 'none' term5 = self.binaryProperty(properties, 'terminus_5', True) term3 = self.binaryProperty(properties, 'terminus_3', True) circular = self.binaryProperty(properties, 'circular', False) try: model = properties['model'].lower() except KeyError: model = hydrogens self.version_spec = {'hydrogens': hydrogens, 'terminus_5': term5, 'terminus_3': term3, 'model': model, 'circular': circular} if isinstance(sequence[0], basestring): conf = None else: conf = sequence sequence = [r.name for r in sequence] sequence = [Biopolymers._fullName(r) for r in sequence] if term5: if sequence[0].find('5ter') == -1: sequence[0] += '_5ter' if term3: if sequence[-1].find('3ter') == -1: sequence[-1] += '_3ter' self.groups = [] n = 0 for residue in sequence: n += 1 r = Nucleotide(residue, model) r.name = r.symbol + '_' + `n` r.sequence_number = n r.parent = self self.groups.append(r) self._setupChain(circular, properties, conf) is_nucleotide_chain = True def __getslice__(self, first, last): return NucleotideSubChain(self, self.groups[first:last])
[docs] def backbone(self): """ :returns: the sugar and phosphate groups of all nucleotides :rtype: :class:`~MMTK.Collections.Collection` """ bb = Collections.Collection([]) for residue in self.groups: try: bb.addObject(residue.phosphate) except AttributeError: pass bb.addObject(residue.sugar) return bb
[docs] def bases(self): """ :returns: the base groups of all nucleotides :rtype: :class:`~MMTK.Collections.Collection` """ return Collections.Collection([r.base for r in self.groups])
def _descriptionSpec(self): kwargs = ','.join([name + '=' + `self.version_spec[name]` for name in sorted(self.version_spec.keys())]) return "N", kwargs def _typeName(self): return ''.join([s.ljust(3) for s in self.sequence()]) def _graphics(self, conf, distance_fn, model, module, options): if model != 'backbone': return ChemicalObjects.Molecule._graphics(self, conf, distance_fn, model, module, options) color = options.get('color', 'black') material = module.EmissiveMaterial(color) objects = [] for i in range(1, len(self.groups)-1): a1 = self.groups[i].phosphate.P a2 = self.groups[i+1].phosphate.P p1 = a1.position(conf) p2 = a2.position(conf) if p1 is not None and p2 is not None: bond_vector = 0.5*distance_fn(a1, a2, conf) cut = bond_vector != 0.5*(p2-p1) if not cut: objects.append(module.Line(p1, p2, material = material)) else: objects.append(module.Line(p1, p1+bond_vector, material = material)) objects.append(module.Line(p2, p2-bond_vector, material = material)) return objects # # Subchains are created by slicing chains or extracting a chain from # a group of connected chains. #
[docs]class NucleotideSubChain(NucleotideChain): """ A contiguous part of a nucleotide chain NucleotideSubChain objects are the result of slicing operations on NucleotideChain objects. They cannot be created directly. NucleotideSubChain objects permit all operations of NucleotideChain objects, but cannot be added to a universe. """ def __init__(self, chain, groups, name = ''): self.groups = groups self.atoms = [] self.bonds = [] for g in self.groups: self.atoms = self.atoms + g.atoms self.bonds = self.bonds + g.bonds for i in range(len(self.groups)-1): self.bonds.append(Bonds.Bond((self.groups[i].sugar.O_3, self.groups[i+1].phosphate.P))) self.bonds = Bonds.BondList(self.bonds) self.name = name self.parent = chain.parent self.type = None self.configurations = {} self.part_of = chain is_incomplete = True def __repr__(self): if self.name == '': return 'SubChain of ' + repr(self.part_of) else: return ChemicalObjects.Molecule.__repr__(self) __str__ = __repr__ # # Type check functions #
[docs]def isNucleotideChain(x): "Returns True if x is a NucleotideChain." return hasattr(x, 'is_nucleotide_chain')
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ParticleProperties.html0000644000076600000240000027017012013143455023254 0ustar hinsenstaff00000000000000 MMTK.ParticleProperties — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ParticleProperties

# This module implements classes that represent the atomic properties in a
# simulation, i.e. configurations, force vectors, etc.
#
# Written by Konrad Hinsen
#

"""
Quantities defined for each particle in a universe
"""

__docformat__ = 'restructuredtext'

from MMTK import Utility
from Scientific.Geometry import Vector, isVector, Tensor, isTensor
from Scientific.indexing import index_expression
from Scientific import N
import copy

#
# Base class for all properties defined for a universe.
#
[docs]class ParticleProperty(object): """ Property defined for each particle This is an abstract base class; for creating instances, use one of its subclasses. ParticleProperty objects store properties that are defined per particle, such as mass, position, velocity, etc. The value corresponding to a particular atom can be retrieved or changed by indexing with the atom object. """ def __init__(self, universe, data_rank, value_rank): universe.configuration() self.universe = universe self.version = universe._version self.n = universe.numberOfPoints() self.data_rank = data_rank self.value_rank = value_rank __safe_for_unpickling__ = True __had_initargs__ = True def __len__(self): return self.n def _checkCompatibility(self, other, allow_scalar=False): if isParticleProperty(other): if other.data_rank != self.data_rank: raise TypeError('Incompatible types') if self.universe != other.universe: raise ValueError('Variables are for different universes') if self.version != other.version: raise ValueError("Universe version numbers do not agree") if self.value_rank == other.value_rank: return self.return_class, other.array elif allow_scalar and (self.value_rank==0 or other.value_rank==0): if self.value_rank == 0: return other.return_class, other.array else: return self.return_class, other.array else: raise ValueError("Ranks do not match") elif isVector(other) or isTensor(other): if len(other.array.shape) > self.value_rank: raise TypeError('Incompatible types') return self.return_class, other.array else: return self.return_class, other
[docs] def zero(self): """ :returns: an object of the element type (scalar, vector, etc.) with the value 0. :rtype: element type """ pass
[docs] def sumOverParticles(self): """ :returns: the sum of the values for all particles. :rtype: element type """ pass
def _arithmetic(self, other, op, allow_scalar=False): a1 = self.array return_class, a2 = self._checkCompatibility(other, allow_scalar) if type(a2) != N.ArrayType: a2 = N.array([a2]) if len(a1.shape) != len(a2.shape): if len(a1.shape) == 1: a1 = a1[index_expression[...] + (len(a2.shape)-1)*index_expression[N.NewAxis]] else: a2 = a2[index_expression[...] + (len(a1.shape)-1)*index_expression[N.NewAxis]] return return_class(self.universe, op(a1, a2)) def __add__(self, other): return self._arithmetic(other, N.add) __radd__ = __add__ def __sub__(self, other): return self._arithmetic(other, N.subtract) def __rsub__(self, other): return self._arithmetic(other, lambda a, b: N.subtract(b, a)) def __mul__(self, other): return self._arithmetic(other, N.multiply, True) __rmul__ = __mul__ def __div__(self, other): return self._arithmetic(other, N.divide, True) def __rdiv__(self, other): return self._arithmetic(other, lambda a, b: N.divide(b, a), True) def __neg__(self): return self.return_class(self.universe, -self.array) def __copy__(self, memo = None): return self.__class__(self.universe, copy.copy(self.array)) __deepcopy__ = __copy__
[docs] def assign(self, other): """ Copy all values from another compatible ParticleProperty object. :param other: the data source """ self._checkCompatibility(other) self.array[:] = other.array[:]
[docs] def scaleBy(self, factor): """ Multiply all values by a factor :param factor: the scale factor :type factor: float """ self.array[:] = self.array[:]*factor
[docs] def selectAtoms(self, condition): """ Return a collection containing all atoms a for which condition(property[a]) is True. :param condition: a test function :type condition: callable """ from MMTK.Collections import Collection return Collection([a for a in self.universe.atomList() if condition(self[a])])
ParticleProperty.return_class = ParticleProperty # # One scalar per particle. #
[docs]class ParticleScalar(ParticleProperty): """ Scalar property defined for each particle ParticleScalar objects can be added to each other and multiplied with scalars. """ def __init__(self, universe, data_array=None): """ :param universe: the universe for which the values are defined :type universe: :class:`~MMTK.Universe.Universe` :param data_array: the data array containing the values for each particle. If None, a new array containing zeros is created and used. Otherwise, the array myst be of shape (N,), where N is the number of particles in the universe. :type data_array: Scientific.N.array_type """ ParticleProperty.__init__(self, universe, 1, 0) if data_array is None: self.array = N.zeros((self.n,), N.Float) else: self.array = data_array if data_array.shape[0] != self.n: raise ValueError('Data incompatible with universe') def __getitem__(self, item): if not isinstance(item, int): item = item.index return self.array[item] def __setitem__(self, item, value): if not isinstance(item, int): item = item.index self.array[item] = value def zero(self): return 0.
[docs] def maximum(self): """ :returns: the highest value in the data array particle :rtype: float """ return N.maximum.reduce(self.array)
[docs] def minimum(self): """ :returns: the smallest value in the data array particle :rtype: float """ return N.minimum.reduce(self.array)
def sumOverParticles(self): return N.add.reduce(self.array)
[docs] def applyFunction(self, function): """ :param function: a function that is applied to each data value :returns: a new ParticleScalar object containing the function results """ return ParticleScalar(self.universe, function(self.array))
ParticleScalar.return_class = ParticleScalar # # One vector per particle. #
[docs]class ParticleVector(ParticleProperty): """ Vector property defined for each particle ParticleVector objects can be added to each other and multiplied with scalars or :class:`~MMTK.ParticleProperties.ParticleScalar` objects; all of these operations result in another ParticleVector object. Multiplication with a vector or another ParticleVector object yields a :class:`~MMTK.ParticleProperties.ParticleScalar` object containing the dot products for each particle. Multiplications that treat ParticleVectors as vectors in a 3N-dimensional space are implemented as methods. """ def __init__(self, universe, data_array=None): """ :param universe: the universe for which the values are defined :type universe: :class:`~MMTK.Universe.Universe` :param data_array: the data array containing the values for each particle. If None, a new array containing zeros is created and used. Otherwise, the array myst be of shape (N,3), where N is the number of particles in the universe. :type data_array: Scientific.N.array_type """ ParticleProperty.__init__(self, universe, 1, 1) if data_array is None: self.array = N.zeros((self.n, 3), N.Float) else: self.array = data_array if data_array.shape[0] != self.n: raise ValueError('Data incompatible with universe') def __getitem__(self, item): if not isinstance(item, int): item = item.index return Vector(self.array[item]) def __setitem__(self, item, value): if not isinstance(item, int): item = item.index self.array[item] = value.array def __mul__(self, other): if isParticleProperty(other): if self.universe != other.universe: raise ValueError('Variables are for different universes') if other.value_rank == 0: return ParticleVector(self.universe, self.array*other.array[:,N.NewAxis]) elif other.value_rank == 1: return ParticleScalar(self.universe, N.add.reduce(self.array * \ other.array, -1)) else: raise TypeError('not yet implemented') elif isVector(other): return ParticleScalar(self.universe, N.add.reduce( self.array*other.array[N.NewAxis,:], -1)) elif isTensor(other): raise TypeError('not yet implemented') else: return ParticleVector(self.universe, self.array*other) __rmul__ = __mul__ _product_with_vector = __mul__ def zero(self): return Vector(0., 0., 0.)
[docs] def length(self): """ :returns: the length (norm) of the vector for each particle :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ return ParticleScalar(self.universe, N.sqrt(N.add.reduce(self.array**2, -1)))
def sumOverParticles(self): return Vector(N.add.reduce(self.array, 0))
[docs] def norm(self): """ :returns: the norm of the ParticleVector seen as a 3N-dimensional vector :rtype: float """ return N.sqrt(N.add.reduce(N.ravel(self.array**2)))
totalNorm = norm def scaledToNorm(self, norm): f = norm/self.norm() return ParticleVector(self.universe, f*self.array)
[docs] def dotProduct(self, other): """ :param other: another ParticleVector :type other: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the dot product with other, treating both operands as 3N-dimensional vectors. """ if self.universe != other.universe: raise ValueError('Variables are for different universes') return N.add.reduce(N.ravel(self.array * other.array))
[docs] def massWeightedNorm(self): """ :returns: the mass-weighted norm of the ParticleVector seen as a 3N-dimensional vector :rtype: float """ m = self.universe.masses().array return N.sqrt(N.sum(N.ravel(m[:, N.NewAxis] * self.array**2)) / N.sum(m))
def scaledToMassWeightedNorm(self, norm): f = norm/self.massWeightedNorm() return ParticleVector(self.universe, f*self.array)
[docs] def massWeightedDotProduct(self, other): """ :param other: another ParticleVector :type other: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the mass-weighted dot product with other treating both operands as 3N-dimensional vectors :rtype: float """ if self.universe != other.universe: raise ValueError('Variables are for different universes') m = self.universe.masses().array return N.add.reduce(N.ravel(self.array * other.array * \ m[:, N.NewAxis]))
[docs] def dyadicProduct(self, other): """ :param other: another ParticleVector :type other: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the dyadic product with other :rtype: :class:`~MMTK.ParticleProperties.ParticleTensor` """ if self.universe != other.universe: raise ValueError('Variables are for different universes') return ParticleTensor(self.universe, self.array[:, :, N.NewAxis] * \ other.array[:, N.NewAxis, :])
ParticleVector.return_class = ParticleVector # # Configuration variables: ParticleVector plus universe parameters #
[docs]class Configuration(ParticleVector): """ Configuration of a universe Configuration instances represent a configuration of a universe, consisting of positions for all atoms (like in a ParticleVector) plus the geometry of the universe itself, e.g. the cell shape for periodic universes. """ def __init__(self, universe, data_array=None, cell = None): """ :param universe: the universe for which the values are defined :type universe: :class:`~MMTK.Universe.Universe` :param data_array: the data array containing the values for each particle. If None, a new array containing zeros is created and used. Otherwise, the array myst be of shape (N,3), where N is the number of particles in the universe. :type data_array: Scientific.N.array_type :param cell: the cell parameters of the universe, i.e. the return value of universe.cellParameters() """ ParticleVector.__init__(self, universe, data_array) if cell is None: self.cell_parameters = universe.cellParameters() else: self.cell_parameters = cell def __add__(self, other): value = ParticleVector.__add__(self, other) return Configuration(self.universe, value.array, self.cell_parameters) def __sub__(self, other): value = ParticleVector.__sub__(self, other) return Configuration(self.universe, value.array, self.cell_parameters) def __copy__(self, memo = None): return self.__class__(self.universe, copy.copy(self.array), copy.copy(self.cell_parameters)) __deepcopy__ = __copy__ def hasValidPositions(self): return N.logical_and.reduce(N.ravel(N.less(self.array, Utility.undefined_limit))) def convertToBoxCoordinates(self): array = self.universe._realToBoxPointArray(self.array, self.cell_parameters) self.array[:] = array def convertFromBoxCoordinates(self): array = self.universe._boxToRealPointArray(self.array, self.cell_parameters) self.array[:] = array # # One tensor per particle. #
[docs]class ParticleTensor(ParticleProperty): """ Rank-2 tensor property defined for each particle ParticleTensor objects can be added to each other and multiplied with scalars or :class:`~MMTK.ParticleProperties.ParticleScalar` objects; all of these operations result in another ParticleTensor object. """ def __init__(self, universe, data_array=None): """ :param universe: the universe for which the values are defined :type universe: :class:`~MMTK.Universe.Universe` :param data_array: the data array containing the values for each particle. If None, a new array containing zeros is created and used. Otherwise, the array myst be of shape (N,3,3), where N is the number of particles in the universe. :type data_array: Scientific.N.array_type """ ParticleProperty.__init__(self, universe, 1, 2) if data_array is None: self.array = N.zeros((self.n, 3, 3), N.Float) else: self.array = data_array if data_array.shape[0] != self.n: raise ValueError('Data incompatible with universe') def __getitem__(self, item): if not isinstance(item, int): item = item.index return Tensor(self.array[item]) def __setitem__(self, item, value): if not isinstance(item, int): item = item.index self.array[item] = value.array def __mul__(self, other): if isParticleProperty(other): if self.universe != other.universe: raise ValueError('Variables are for different universes') if other.value_rank == 0: return ParticleTensor(self.universe, self.array*other.array[:, N.NewAxis, N.NewAxis]) else: raise TypeError('not yet implemented') elif isVector(other): raise TypeError('not yet implemented') elif isTensor(other): raise TypeError('not yet implemented') else: return ParticleTensor(self.universe, self.array*other) __rmul__ = __mul__ def zero(self): return Tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) def sumOverParticles(self): return Tensor(N.add.reduce(self.array, 0)) def trace(self): return ParticleScalar(self.universe, self.array[:, 0, 0] + self.array[:, 1, 1] + self.array[:, 2, 2])
ParticleTensor.return_class = ParticleTensor # # One tensor per pair, symmetric. # class SymmetricPairTensor(ParticleProperty): def __init__(self, universe, data_array=None): """ :param universe: the universe for which the values are defined :type universe: :class:`~MMTK.Universe.Universe` :param data_array: the data array containing the values for each particle. If None, a new array containing zeros is created and used. Otherwise, the array myst be of shape (N,3,N,3), where N is the number of particles in the universe. :type data_array: Scientific.N.array_type """ ParticleProperty.__init__(self, universe, 2, 2) if data_array is None: self.array = N.zeros((self.n,3, self.n,3), N.Float) else: self.array = data_array if data_array.shape[0] != self.n or \ data_array.shape[2] != self.n: raise ValueError('Data incompatible with universe') self.symmetrized = False def __getitem__(self, item): i1, i2 = item if not isinstance(i1, int): i1 = i1.index if not isinstance(i2, int): i2 = i2.index if i1 > i2: i1, i2 = i2, i1 return Tensor(N.transpose(self.array[i1,:,i2,:])) else: return Tensor(self.array[i1,:,i2,:]) def __setitem__(self, item, value): i1, i2 = item if not isinstance(i1, int): i1 = i1.index if not isinstance(i2, int): i2 = i2.index if i1 > i2: i1, i2 = i2, i1 self.array[i1,:,i2,:] = value.transpose().array else: self.array[i1,:,i2,:] = value.array def zero(self): return Tensor(3*[[0., 0., 0.]]) def symmetrize(self): if not self.symmetrized: a = self.array n = a.shape[0] a.shape = (3*n, 3*n) nn = 3*n for i in range(nn): for j in range(i+1, nn): a[j,i] = a[i,j] a.shape = (n, 3, n, 3) self.symmetrized = True def __mul__(self, other): self.symmetrize() if isParticleProperty(other): if self.universe != other.universe: raise ValueError('Variables are for different universes') if other.value_rank == 1: n = self.array.shape[0] sa = N.reshape(self.array, (n, 3, 3*n)) oa = N.reshape(other.array, (3*n, )) return ParticleVector(self.universe, N.dot(sa, oa)) else: raise TypeError('not yet implemented') SymmetricPairTensor.return_class = SymmetricPairTensor # # Type check function #
[docs]def isParticleProperty(object): """ :param object: any object :returns: True if object is a :class:`~MMTK.ParticleProperties.ParticleProperty` :rtype: bool """ return isinstance(object, ParticleProperty)
[docs]def isConfiguration(object): """ :param object: any object :returns: True if object is a :class:`~MMTK.ParticleProperties.Configuration` :rtype: bool """ return isinstance(object, Configuration)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/PDB.html0000644000076600000240000033202112013143454020032 0ustar hinsenstaff00000000000000 MMTK.PDB — MMTK User Guide 2.7.7 documentation

Source code for MMTK.PDB

# This module deals with input and output of configurations in PDB format.
#
# Written by Konrad Hinsen
#

"""
PDB files

This module provides classes that represent molecules in a PDB file.
They permit various manipulations and the creation of MMTK objects.
Note that the classes defined in this module are specializations
of classed defined in Scientific.IO.PDB; the methods defined in
that module are also available.
"""

__docformat__ = 'restructuredtext'

from MMTK import ChemicalObjects, Collections, Database, Units, \
                 Universe, Utility
from Scientific.Geometry import Vector
import Scientific.IO.PDB
import copy, string

#
# The chain classes from Scientific.IO.PDB are extended by methods
# that construct MMTK objects and set configurations.
#
class PDBChain(object):

    def applyTo(self, chain, map = 'pdbmap', alt = 'pdb_alternative',
                atom_map = None):
        if len(chain) != len(self):
            raise ValueError("chain lengths do not match")
        for i in range(len(chain)):
            residue = chain[i]
            pdbmap = getattr(residue, map)
            try: altmap = getattr(residue, alt)
            except AttributeError: altmap = {}
            setResidueConfiguration(residue, self[i], pdbmap[0], altmap,
                                    atom_map)
    
[docs]class PDBPeptideChain(Scientific.IO.PDB.PeptideChain, PDBChain): """ Peptide chain in a PDB file See the description of Scientific.IO.PDB.PeptideChain for the constructor and additional methods. In MMTK, PDBPeptideChain objects are usually obtained from a PDBConfiguration object via its attribute peptide_chains (see the documentation of Scientific.IO.PDB.Structure). """
[docs] def createPeptideChain(self, model = 'all', n_terminus=None, c_terminus=None): """ :returns: a :class:`~MMTK.Proteins.PeptideChain` object corresponding to the peptide chain in the PDB file. The parameter model has the same meaning as for the PeptideChain constructor. :rtype: :class:`~MMTK.Proteins.PeptideChain` """ self.identifyProtonation() from MMTK import Proteins properties = {'model': model} if self.segment_id != '': properties['name'] = self.segment_id elif self.chain_id != '': properties['name'] = self.chain_id if c_terminus is None: properties['c_terminus'] = self.isTerminated() else: properties['c_terminus'] = c_terminus if n_terminus is not None: properties['n_terminus'] = n_terminus chain = apply(Proteins.PeptideChain, (self,), properties) if model != 'no_hydrogens': chain.findHydrogenPositions() return chain
def identifyProtonation(self): for residue in self.residues: if residue.name == 'HIS': count_hd = 0 count_he = 0 for atom in residue: if 'HD' in atom.name: count_hd += 1 if 'HE' in atom.name: count_he += 1 if count_hd + count_he == 0: # default for crystallographic structures without hydrogens residue.name = 'HIE' elif count_he == 2: if count_hd == 2: residue.name = 'HIP' else: residue.name = 'HIE' else: residue.name = 'HID' elif residue.name == 'GLU': for atom in residue: if 'HE' in atom.name: residue.name = 'GLP' break elif residue.name == 'ASP': for atom in residue: if 'HD' in atom.name: residue.name = 'APP' break elif residue.name == 'LYS': count_hz = 0 for atom in residue: if 'HZ' in atom.name: count_hz += 1 if count_hz > 0 and count_hz < 3: # for count_hz == 0 (most probably a crystallographic # structure), keep LYS which is the most frequent one. residue.name = 'LYP'
[docs]class PDBNucleotideChain(Scientific.IO.PDB.NucleotideChain, PDBChain): """ Nucleotide chain in a PDB file See the description of Scientific.IO.PDB.NucleotideChain for the constructor and additional methods. In MMTK, PDBNucleotideChain objects are usually obtained from a PDBConfiguration object via its attribute nucleotide_chains (see the documentation of Scientific.IO.PDB.Structure). """
[docs] def createNucleotideChain(self, model='all'): """ :returns: a :class:`~MMTK.NucleicAcids.NucleotideChain` object corresponding to the nucleotide chain in the PDB file. The parameter model has the same meaning as for the NucleotideChain constructor. :rtype: :class:`~MMTK.NucleicAcids.NucleotideChain` """ from MMTK import NucleicAcids properties = {'model': model} if self.segment_id != '': properties['name'] = self.segment_id elif self.chain_id != '': properties['name'] = self.chain_id if self[0].hasPhosphate(): properties['terminus_5'] = 0 chain = apply(NucleicAcids.NucleotideChain, (self,), properties) if model != 'no_hydrogens': chain.findHydrogenPositions() return chain
[docs]class PDBMolecule(Scientific.IO.PDB.Molecule): """ Molecule in a PDB file See the description of Scientific.IO.PDB.Molecule for the constructor and additional methods. In MMTK, PDBMolecule objects are usually obtained from a PDBConfiguration object via its attribute molecules (see the documentation of Scientific.IO.PDB.Structure). A molecule is by definition any residue in a PDB file that is not an amino acid or nucleotide residue. """ def applyTo(self, molecule, map = 'pdbmap', alt = 'pdb_alternative', atom_map = None): pdbmap = getattr(molecule, map) try: altmap = getattr(molecule, alt) except AttributeError: altmap = {} setResidueConfiguration(molecule, self, pdbmap[0], altmap, atom_map)
[docs] def createMolecule(self, name=None): """ :returns: a :class:`~MMTK.ChemicalObjects.Molecule` object corresponding to the molecule in the PDB file. The parameter name specifies the molecule name as defined in the chemical database. It can be left out for known molecules (currently only water). :rtype: :class:`~MMTK.ChemicalObjects.Molecule` """ if name is None: name = molecule_names[self.name] m = ChemicalObjects.Molecule(name) setConfiguration(m, [self]) return m # # The structure class from Scientific.IO.PDB is extended by methods # that construct MMTK objects and set configurations. #
[docs]class PDBConfiguration(Scientific.IO.PDB.Structure): """ Everything in a PDB file A PDBConfiguration object represents the full contents of a PDB file. It can be used to create MMTK objects for all or part of the molecules, or to change the configuration of an existing system. """ def __init__(self, file_or_filename, model = 0, alternate_code = 'A'): """ :param file_or_filename: the name of a PDB file, or a file object :param model: the number of the model to be used from a multiple model file :type model: int :param alternate_code: the alternate code to be used for atoms that have multiple positions :type alternate_code: str """ if isinstance(file_or_filename, basestring): file_or_filename = Database.PDBPath(file_or_filename) Scientific.IO.PDB.Structure.__init__(self, file_or_filename, model, alternate_code) self._numberAtoms() self._convertUnits() peptide_chain_constructor = PDBPeptideChain nucleotide_chain_constructor = PDBNucleotideChain molecule_constructor = PDBMolecule def _numberAtoms(self): n = 1 for residue in self.residues: for atom in residue: atom.number = n n += 1 def _convertUnits(self): for residue in self.residues: for atom in residue: atom.position = atom.position*Units.Ang try: b = atom.properties['temperature_factor'] atom.properties['temperature_factor'] = b*Units.Ang**2 except KeyError: pass try: u = atom.properties['u'] atom.properties['u'] = u*Units.Ang**2 except KeyError: pass # All these attributes exist only if ScientificPython >= 2.7.5 is used. # The Scaling transformation was introduced with the same version, # so if it exists, the rest should work as well. try: from Scientific.Geometry.Transformation import Scaling except ImportError: return for attribute in ['a', 'b', 'c']: value = getattr(self, attribute) if value is not None: setattr(self, attribute, value*Units.Ang) for attribute in ['alpha', 'beta', 'gamma']: value = getattr(self, attribute) if value is not None: setattr(self, attribute, value*Units.deg) if self.to_fractional is not None: self.to_fractional = self.to_fractional*Scaling(1./Units.Ang) v1 = self.to_fractional(Vector(1., 0., 0.)) v2 = self.to_fractional(Vector(0., 1., 0.)) v3 = self.to_fractional(Vector(0., 0., 1.)) self.reciprocal_basis = (Vector(v1[0], v2[0], v3[0]), Vector(v1[1], v2[1], v3[1]), Vector(v1[2], v2[2], v3[2])) else: self.reciprocal_basis = None if self.from_fractional is not None: self.from_fractional = Scaling(Units.Ang)*self.from_fractional self.basis = (self.from_fractional(Vector(1., 0., 0.)), self.from_fractional(Vector(0., 1., 0.)), self.from_fractional(Vector(0., 0., 1.))) else: self.basis = None for i in range(len(self.ncs_transformations)): tr = self.ncs_transformations[i] tr_new = Scaling(Units.Ang)*tr*Scaling(1./Units.Ang) tr_new.given = tr.given tr_new.serial = tr.serial self.ncs_transformations[i] = tr_new for i in range(len(self.cs_transformations)): tr = self.cs_transformations[i] tr_new = Scaling(Units.Ang)*tr*Scaling(1./Units.Ang) self.cs_transformations[i] = tr_new
[docs] def createUnitCellUniverse(self): """ Constructs an empty universe (OrthrhombicPeriodicUniverse or ParallelepipedicPeriodicUniverse) representing the unit cell of the crystal. If the PDB file does not define a unit cell at all, an InfiniteUniverse is returned. :returns: a universe :rtype: :class:`~MMTK.Universe.Universe` """ if self.from_fractional is None: return Universe.InfiniteUniverse() e1 = self.from_fractional(Vector(1., 0., 0.)) e2 = self.from_fractional(Vector(0., 1., 0.)) e3 = self.from_fractional(Vector(0., 0., 1.)) if abs(e1.normal()*Vector(1., 0., 0.)-1.) < 1.e-15 \ and abs(e2.normal()*Vector(0., 1., 0.)-1.) < 1.e-15 \ and abs(e3.normal()*Vector(0., 0., 1.)-1.) < 1.e-15: return \ Universe.OrthorhombicPeriodicUniverse((e1.length(), e2.length(), e3.length())) return Universe.ParallelepipedicPeriodicUniverse((e1, e2, e3))
[docs] def createPeptideChains(self, model='all'): """ :returns: a list of :class:`~MMTK.Proteins.PeptideChain` objects, one for each peptide chain in the PDB file. The parameter model has the same meaning as for the PeptideChain constructor. :rtype: list """ return [chain.createPeptideChain(model) for chain in self.peptide_chains]
[docs] def createNucleotideChains(self, model='all'): """ :returns: a list of :class:`~MMTK.NucleicAcids.NucleotideChain` objects, one for each nucleotide chain in the PDB file. The parameter model has the same meaning as for the NucleotideChain constructor. :rtype: list """ return [chain.createNucleotideChain(model) for chain in self.nucleotide_chains]
[docs] def createMolecules(self, names = None, permit_undefined=True): """ :param names: If a list of molecule names (as defined in the chemical database) and/or PDB residue names, only molecules mentioned in this list will be constructed. If a dictionary, it is used to map PDB residue names to molecule names. With the default (None), only water molecules are built. :type names: list :param permit_undefined: If False, an exception is raised when a PDB residue is encountered for which no molecule name is supplied in names. If True, an AtomCluster object is constructed for each unknown molecule. :returns: a collection of :class:`~MMTK.ChemicalObjects.Molecule` objects, one for each molecule in the PDB file. Each PDB residue not describing an amino acid or nucleotide residue is considered a molecule. :rtype: :class:`~MMTK.Collections.Collection` """ collection = Collections.Collection() mol_dicts = [molecule_names] if type(names) == type({}): mol_dicts.append(names) names = None for name in self.molecules.keys(): full_name = None for dict in mol_dicts: full_name = dict.get(name, None) if names is None or name in names or full_name in names: if full_name is None and not permit_undefined: raise ValueError("no definition for molecule " + name) for molecule in self.molecules[name]: if full_name: m = ChemicalObjects.Molecule(full_name) setConfiguration(m, [molecule]) else: pdbdict = {} atoms = [] i = 0 for atom in molecule: aname = atom.name while aname[0] in string.digits: aname = aname[1:] + aname[0] try: element = atom['element'].strip() a = ChemicalObjects.Atom(element, name = aname) except KeyError: try: a = ChemicalObjects.Atom(aname[:2].strip(), name = aname) except IOError: a = ChemicalObjects.Atom(aname[:1], name = aname) a.setPosition(atom.position) atoms.append(a) pdbdict[atom.name] = Database.AtomReference(i) i += 1 m = ChemicalObjects.AtomCluster(atoms, name = name) if len(pdbdict) == len(molecule): # pdbmap is correct only if the AtomCluster has # unique atom names m.pdbmap = [(name, pdbdict)] setConfiguration(m, [molecule]) collection.addObject(m) return collection
def createGroups(self, mapping): groups = [] for name in self.molecules.keys(): full_name = mapping.get(name, None) if full_name is not None: for molecule in self.molecules[name]: g = ChemicalObjects.Group(full_name) setConfiguration(g, [molecule], toplevel=0) groups.append(g) return groups
[docs] def createAll(self, molecule_names = None, permit_undefined=True): """ :returns: a collection containing all objects contained in the PDB file, i.e. the combination of the objects returned by :func:`~MMTK.PDB.PDBConfiguration.createPeptideChains`, :func:`~MMTK.PDB.PDBConfiguration.createNucleotideChains`, and :func:`~MMTK.PDB.PDBConfiguration.createMolecules`. The parameters have the same meaning as for :func:`~MMTK.PDB.PDBConfiguration.createMolecules`. :rtype: :class:`~MMTK.Collectionc.Collection` """ collection = Collections.Collection() peptide_chains = self.createPeptideChains() if peptide_chains: import Proteins collection.addObject(Proteins.Protein(peptide_chains)) nucleotide_chains = self.createNucleotideChains() collection.addObject(nucleotide_chains) molecules = self.createMolecules(molecule_names, permit_undefined) collection.addObject(molecules) return collection
[docs] def asuToUnitCell(self, asu_contents, compact=True): """ :param asu_contents: the molecules in the asymmetric unit, usually obtained from :func:`~MMTK.PDB.PDBConfiguration.createAll()`. :param compact: if True, all molecules images are shifted such that their centers of mass lie inside the unit cell. :type compact: bool :returns: a collection containing all molecules in the unit cell, obtained by copying and moving the molecules from the asymmetric unit according to the crystallographic symmetry operations. :rtype: :class:`~MMTK.Collections.Collection` """ unit_cell_contents = Collections.Collection() for symop in self.cs_transformations: transformation = symop.asLinearTransformation() rotation = transformation.tensor translation = transformation.vector image = copy.deepcopy(asu_contents) for atom in image.atomList(): atom.setPosition(symop(atom.position())) if compact: cm = image.centerOfMass() cm_fr = self.to_fractional(cm) cm_fr = Vector(cm_fr[0] % 1., cm_fr[1] % 1., cm_fr[2] % 1.) \ - Vector(0.5, 0.5, 0.5) cm = self.from_fractional(cm_fr) image.translateTo(cm) unit_cell_contents.addObject(image) return unit_cell_contents
[docs] def applyTo(self, object, atom_map=None): """ Sets the configuration of object from the coordinates in the PDB file. The object must be compatible with the PDB file, i.e. contain the same subobjects and in the same order. This is usually only guaranteed if the object was created by the method :func:`~MMTK.PDB.PDBConfiguration.createAll` from a PDB file with the same layout. :param object: a chemical object or collection of chemical objects """ setConfiguration(object, self.residues, atom_map=atom_map) # # An alternative name for compatibility in Database files. #
PDBFile = PDBConfiguration # # Set atom coordinates from PDB configuration. # def setResidueConfiguration(object, pdb_residue, pdbmap, altmap, atom_map = None): defined = 0 for atom in pdb_residue: name = atom.name try: name = altmap[name] except KeyError: pass try: pdbname = pdbmap[1][name] except KeyError: pdbname = None if not object.isSubsetModel(): raise IOError('Atom '+atom.name+' of PDB residue ' + pdb_residue.name+' not found in residue ' + pdbmap[0] + ' of object ' + object.fullName()) if pdbname: object.setPosition(pdbname, atom.position) try: object.setIndex(pdbname, atom.number-1) except ValueError: pass if atom_map is not None: atom_map[object.getAtom(pdbname)] = atom defined += 1 return defined def setConfiguration(object, pdb_residues, map = 'pdbmap', alt = 'pdb_alternative', atom_map = None, toplevel = True): defined = 0 if hasattr(object, 'is_protein'): i = 0 for chain in object: l = len(chain) defined += setConfiguration(chain, pdb_residues[i:i+l], map, alt, atom_map, False) i = i + l elif hasattr(object, 'is_chain'): for i in range(len(object)): defined += setConfiguration(object[i], pdb_residues[i:i+1], map, alt, atom_map, False) elif hasattr(object, map): pdbmap = getattr(object, map) try: altmap = getattr(object, alt) except AttributeError: altmap = {} nres = len(pdb_residues) if len(pdbmap) != nres: raise IOError('PDB configuration does not match object ' + object.fullName()) for i in range(nres): defined += setResidueConfiguration(object, pdb_residues[i], pdbmap[i], altmap, atom_map) elif Collections.isCollection(object): nres = len(pdb_residues) if len(object) != nres: raise IOError('PDB configuration does not match object ' + object.fullName()) for i in range(nres): defined += setConfiguration(object[i], [pdb_residues[i]], map, alt, atom_map, False) else: try: name = object.fullName() except AttributeError: try: name = object.name except AttributeError: name = '???' raise IOError('PDB configuration does not match object ' + name) if toplevel and defined < object.numberOfAtoms(): name = '[unnamed object]' try: name = object.fullName() except: pass if name: name = ' in ' + name Utility.warning(`object.numberOfAtoms()-defined` + ' atom(s)' + name + ' were not assigned (new) positions.') return defined # # Create objects from a PDB configuration. # molecule_names = {'HOH': 'water', 'TIP': 'water', 'TIP3': 'water', 'WAT': 'water', 'HEM': 'heme'} def defineMolecule(code, name): if molecule_names.has_key(code): raise ValueError("PDB code " + code + " already used") molecule_names[code] = name # # This object represents a PDB file for output. #
[docs]class PDBOutputFile(object): """ PDB file for output """ def __init__(self, filename, subformat= None): """ :param filename: the name of the PDB file that is created :type filename: str :param subformat: a variant of the PDB format; these subformats are defined in module Scientific.IO.PDB. The default is the standard PDB format. :type subformat: str """ self.file = Scientific.IO.PDB.PDBFile(filename, 'w', subformat) self.warning = False self.atom_sequence = [] self.model_number = None
[docs] def nextModel(self): """ Start a new model """ if self.model_number is None: self.model_number = 1 else: self.file.writeLine('ENDMDL', '') self.model_number = self.model_number + 1 self.file.writeLine('MODEL', {'serial_number': self.model_number})
[docs] def write(self, object, configuration = None, tag = None): """ Write an object to the file :param object: the object to be written :type object: :class:`~MMTK.Collections.GroupOfAtoms` :param configuration: the configuration from which the coordinates are taken (default: current configuration) :type configuration: :class:`~MMTK.ParticleProperties.Configuration` """ if not ChemicalObjects.isChemicalObject(object): for o in object: self.write(o, configuration) else: toplevel = tag is None if toplevel: tag = Utility.uniqueAttribute() if hasattr(object, 'pdbmap'): for residue in object.pdbmap: self.file.nextResidue(residue[0], ) sorted_atoms = residue[1].items() sorted_atoms.sort(lambda x, y: cmp(x[1].number, y[1].number)) for atom_name, atom in sorted_atoms: atom = object.getAtom(atom) p = atom.position(configuration) if Utility.isDefinedPosition(p): try: occ = atom.occupancy except AttributeError: occ = 0. try: temp = atom.temperature_factor except AttributeError: temp = 0. self.file.writeAtom(atom_name, p/Units.Ang, occ, temp, atom.type.symbol) self.atom_sequence.append(atom) else: self.warning = True setattr(atom, tag, None) else: if hasattr(object, 'is_protein'): for chain in object: self.write(chain, configuration, tag) elif hasattr(object, 'is_chain'): self.file.nextChain(None, object.name) for residue in object: self.write(residue, configuration, tag) self.file.terminateChain() elif hasattr(object, 'molecules'): for m in object.molecules: self.write(m, configuration, tag) elif hasattr(object, 'groups'): for g in object.groups: self.write(g, configuration, tag) if toplevel: for a in object.atomList(): if not hasattr(a, tag): self.write(a, configuration, tag) delattr(a, tag)
[docs] def close(self): """ Closes the file. Must be called in order to prevent data loss. """ if self.model_number is not None: self.file.writeLine('ENDMDL', '') self.file.close() if self.warning: Utility.warning('Some atoms are missing in the output file ' + \ 'because their positions are undefined.') self.warning = False
MMTK-2.7.9/Doc/HTML/_modules/MMTK/PDBMoleculeFactory.html0000644000076600000240000015456712013143455023072 0ustar hinsenstaff00000000000000 MMTK.PDBMoleculeFactory — MMTK User Guide 2.7.7 documentation

Source code for MMTK.PDBMoleculeFactory

# This module constructs molecules and universes corresponding exactly
# to the contents of a PDB file, using residue and atom names as
# given in the file.
#
# Written by Konrad Hinsen
#

"""
Molecule factory defined by a PDB file

This module permits the construction of molecular objects that correspond
exactly to the contents of a PDB file. It is used for working with
experimental data. Note that most force fields cannot be applied to the
systems generated in this way because the molecule factory does not know
any force field parameters.
"""

__docformat__ = 'restructuredtext'

import MMTK
from MMTK.MoleculeFactory import MoleculeFactory
from Scientific.Geometry import Vector, delta
from Scientific import N
import copy

[docs]class PDBMoleculeFactory(MoleculeFactory): """ A PDBMoleculeFactory generates molecules and universes from the contents of a PDBConfiguration. Nothing is added or left out (except as defined by the optional residue and atom filters), and the atom and residue names are exactly those of the original PDB file. """ def __init__(self, pdb_conf, residue_filter=None, atom_filter=None): """ :param pdb_conf: a PDBConfiguration :type pdb_conf: :class:`~MMTK.PDB.PDBConfiguration` :param residue_filter: a function taking a residue object (as defined in Scientific.IO.PDB) and returning True if that residue is to be kept in the molecule factory :type residue_filter: callable :param atom_filter: a function taking a residue object and an atom object (as defined in Scientific.IO.PDB) and returning True if that atom is to be kept in the molecule factory :type atom_filter: callable """ MoleculeFactory.__init__(self) if residue_filter is None and atom_filter is None: self.pdb_conf = pdb_conf else: self.pdb_conf = copy.deepcopy(pdb_conf) if atom_filter is not None: for residue in self.pdb_conf.residues: delete = [atom for atom in residue if not atom_filter(residue, atom)] for atom in delete: residue.deleteAtom(atom) if residue_filter is not None: delete = [residue for residue in self.pdb_conf.residues if len(residue) == 0 or not residue_filter(residue)] for residue in delete: self.pdb_conf.deleteResidue(residue) self.peptide_chains = [] self.nucleotide_chains = [] self.molecules = {} self.makeAll()
[docs] def retrieveMolecules(self): """ Constructs Molecule objects corresponding to the contents of the PDBConfiguration. Each peptide or nucleotide chain becomes one molecule. For residues that are neither amino acids nor nucleic acids, each residue becomes one molecule. Chain molecules (peptide and nucleotide chains) can be iterated over to retrieve the individual residues. The residues can also be accessed as attributes whose names are the three-letter residue name (upper case) followed by an underscore and the residue number from the PDB file (e.g. 'GLY_34'). Each object that corresponds to a PDB residue (i.e. each residue in a chain and each non-chain molecule) has the attributes 'residue_name' and 'residue_number'. Each atom has the attributes 'serial_number', 'occupancy' and 'temperature_factor'. Atoms for which a ANISOU record exists also have an attribute 'u' whose value is a tensor object. :returns: a list of Molecule objects :rtype: list """ objects = [self.retrieveMolecule(o) for o in self.peptide_chains + self.nucleotide_chains] for mollist in self.molecules.values(): objects.extend([self.retrieveMolecule(residue) for residue in mollist]) return objects
[docs] def retrieveUniverse(self): """ Constructs an empty universe (OrthrhombicPeriodicUniverse or ParallelepipedicPeriodicUniverse) representing the unit cell of the crystal. If the PDB file does not define a unit cell at all, an InfiniteUniverse is returned. :returns: a universe :rtype: :class:`~MMTK.Universe.Universe` """ return self.pdb_conf.createUnitCellUniverse()
[docs] def retrieveAsymmetricUnit(self): """ Constructs a universe (OrthrhombicPeriodicUniverse or ParallelepipedicPeriodicUniverse) representing the unit cell of the crystal and adds the molecules representing the asymmetric unit. :returns: a universe :rtype: :class:`~MMTK.Universe.Universe` """ universe = self.retrieveUniverse() universe.addObject(self.retrieveMolecules()) return universe
[docs] def retrieveUnitCell(self, compact=True): """ Constructs a universe (OrthrhombicPeriodicUniverse or ParallelepipedicPeriodicUniverse) representing the unit cell of the crystal and adds all the molecules it contains, i.e. the molecules of the asymmetric unit and its images obtained by applying the crystallographic symmetry operations. :param compact: if True, the images are shifted such that their centers of mass lie inside the unit cell. :type compact: bool :returns: a universe :rtype: :class:`~MMTK.Universe.Universe` """ if not self.pdb_conf.cs_transformations: return self.retrieveAsymmetricUnit() universe = self.retrieveUniverse() asu_count = 0 for symop in self.pdb_conf.cs_transformations: transformation = symop.asLinearTransformation() rotation = transformation.tensor translation = transformation.vector is_asu = translation.length() < 1.e-8 and \ N.maximum.reduce(N.ravel(N.fabs((rotation -delta).array))) < 1.e-8 if is_asu: asu_count += 1 asu = MMTK.Collection(self.retrieveMolecules()) for atom in asu.atomList(): atom.setPosition(symop(atom.position())) if hasattr(atom, 'u'): atom.u = rotation.dot(atom.u.dot(rotation.transpose())) atom.u = atom.u.symmetricalPart() atom.in_asu = is_asu if compact: cm = asu.centerOfMass() cm_fr = self.pdb_conf.to_fractional(cm) cm_fr = Vector(cm_fr[0] % 1., cm_fr[1] % 1., cm_fr[2] % 1.) \ - Vector(0.5, 0.5, 0.5) cm = self.pdb_conf.from_fractional(cm_fr) asu.translateTo(cm) universe.addObject(asu) assert asu_count == 1 return universe
def makeAll(self): cystines = [] for chain in self.pdb_conf.peptide_chains: chain_id = chain.chain_id if not chain_id: chain_id = 'PeptideChain'+str(len(self.peptide_chains)+1) for residue in chain: if residue.name == 'CYS' and residue.atoms.has_key('SG'): cystines.append((chain_id, residue, residue.atoms['SG'])) self.makeChain(chain, chain_id) self.peptide_chains.append(chain_id) for i in range(len(cystines)): for j in range(i+1, len(cystines)): d = (cystines[i][2].position-cystines[j][2].position).length() if d < 0.25: if cystines[i][0] is cystines[j][0]: cys1 = cystines[i][1] cys2 = cystines[j][1] cys1 = cys1.name + '_' + str(cys1.number) cys2 = cys2.name + '_' + str(cys2.number) self.addBond(cystines[i][0], cys1+'.SG', cys2+'.SG') else: raise NotImplemented('Inter-chain disulfide bridges' ' not yet implemented') for chain in self.pdb_conf.nucleotide_chains: chain_id = chain.chain_id if not chain_id: chain_id = 'NucleotideChain'+str(len(self.nucleotide_chains)+1) self.makeChain(chain, chain_id) self.nucleotide_chains.append(chain_id) for molname, mollist in self.pdb_conf.molecules.items(): self.molecules[molname] = [] for residue in mollist: base_resname = residue.name + '_' + str(residue.number) suffix = 1 resname = base_resname done = False while not done: try: self.makeResidue(residue, resname) done = True except ValueError, e: if str(e).split()[0] == "redefinition": resname = base_resname + "_" + str(suffix) suffix += 1 else: raise self.molecules[molname].append(resname) def makeChain(self, chain, chain_id): groups = [] for residue in chain: resname = chain_id + '_' + residue.name + '_' + str(residue.number) groups.append(resname) self.makeResidue(residue, resname) self.createGroup(chain_id) local_resnames = [] for resname in groups: local_resname = '_'.join(resname.split('_')[1:]) self.addSubgroup(chain_id, local_resname, resname) local_resnames.append(local_resname) self.setAttribute(chain_id, 'sequence', local_resnames) for i in range(1, len(chain)): if chain[i-1].number == chain[i].number-1: for atom1 in chain[i-1]: for atom2 in chain[i]: if self.assumeBond(atom1.properties['element'], atom1.position, atom2.properties['element'], atom2.position): self.addBond(chain_id, local_resnames[i-1]+'.'+atom1.name, local_resnames[i]+'.'+atom2.name) def makeResidue(self, residue, group_name): self.createGroup(group_name) self.setAttribute(group_name, 'residue_name', residue.name) self.setAttribute(group_name, 'residue_number', residue.number) atoms = [] for atom in residue: atoms.append((atom.name, atom.properties['element'], atom.position)) self.addAtom(group_name, atom.name, atom.properties['element']) self.setPosition(group_name, atom.name, atom.position) self.setAttribute(group_name, atom.name+'.temperature_factor', atom.properties['temperature_factor']) self.setAttribute(group_name, atom.name+'.occupancy', atom.properties['occupancy']) self.setAttribute(group_name, atom.name+'.serial_number', atom.properties['serial_number']) if atom.properties.has_key('u'): self.setAttribute(group_name, atom.name+'.u', atom.properties['u']) for i in range(len(atoms)): atom_i, element_i, pos_i = atoms[i] for j in range(i+1, len(atoms)): atom_j, element_j, pos_j = atoms[j] if self.assumeBond(element_i, pos_i, element_j, pos_j): self.addBond(group_name, atom_i, atom_j) def assumeBond(self, element1, pos1, element2, pos2): if element1 > element2: element1, element2 = element2, element1 try: d = self.bond_lengths[(element1, element2)] return (pos1-pos2).length() < d except KeyError: return False bond_lengths = {('C', 'C'): 0.16, ('C', 'H'): 0.115, ('C', 'N'): 0.215, ('C', 'O'): 0.16, ('C', 'P'): 0.2, ('C', 'S'): 0.2, ('H', 'H'): 0.08, ('H', 'N'): 0.115, ('H', 'O'): 0.115, ('H', 'P'): 0.15, ('H', 'S'): 0.14, ('N', 'N'): 0.155, ('N', 'O'): 0.15, ('O', 'O'): 0.16, ('O', 'P'): 0.17, ('O', 'S'): 0.16, ('P', 'S'): 0.2, ('S', 'S'): 0.25, }
MMTK-2.7.9/Doc/HTML/_modules/MMTK/ProteinFriction.html0000644000076600000240000002220012013143455022537 0ustar hinsenstaff00000000000000 MMTK.ProteinFriction — MMTK User Guide 2.7.7 documentation

Source code for MMTK.ProteinFriction

# Friction constants for protein C-alpha models
#
# Written by Konrad Hinsen
#

"""
A friction constant model for |C_alpha| models of proteins
"""

__docformat__ = 'restructuredtext'

import MMTK.ParticleProperties
from Scientific import N

[docs]def calphaFrictionConstants(protein, set=2): """ :param protein: a |C_alpha| model protein :type protein: :class:`~MMTK.Proteins.Protein` :param set: the number of a friction constant set (1, 2, 3, or 4) :return: the estimated friction constants for the atoms in the protein :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ radius = 1.5 atoms = protein.atomCollection() f = MMTK.ParticleProperties.ParticleScalar(protein.universe()) for chain in protein: for residue in chain: a = residue.peptide.C_alpha m = atoms.selectShell(a.position(), radius).mass() d = 3.*m/(4.*N.pi*radius**3) if set == 1: # linear fit to initial slope f[a] = max(1000., 121.2*d-8600) elif set == 2: # exponential fit 400 steps f[a] = max(1000., 68.2*d-5160) elif set == 3: # exponential fit 200 steps f[a] = max(1000., 38.2*d-2160) elif set == 4: # expansion fit 50 steps f[a] = max(1000., 20.4*d-500.) return f
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Proteins.html0000644000076600000240000044036412013143455021243 0ustar hinsenstaff00000000000000 MMTK.Proteins — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Proteins

# This module implements classes for peptide chains and proteins.
#
# Written by Konrad Hinsen
#

"""
Peptide chains and proteins
"""

__docformat__ = 'restructuredtext'

from MMTK import Biopolymers, Bonds, ChemicalObjects, Collections, \
                 ConfigIO, Database, Units, Universe, Utility
from Scientific.Geometry import Vector

from MMTK.Biopolymers import defineAminoAcidResidue

#
# Residues are special groups
#
[docs]class Residue(Biopolymers.Residue): """ Amino acid residue Amino acid residues are a special kind of group. They are defined in the chemical database. Each residue has two subgroups ('peptide' and 'sidechain') and is usually connected to other residues to form a peptide chain. The database contains three variants of each residue (N-terminal, C-terminal, non-terminal) and various models (all-atom, united-atom, |C_alpha|). """ def __init__(self, name = None, model = 'all'): """ :param name: the name of the residue in the chemical database. This is the full name of the residue plus the suffix "_nt" or "_ct" for the terminal variants. :type name: str :param model: one of "all" (all-atom), "none" (no hydrogens), "polar" (united-atom with only polar hydrogens), "polar_charmm" (like "polar", but defining polar hydrogens like in the CHARMM force field), "polar_opls" (like "polar", but defining polar hydrogens like in the latest OPLS force field), "calpha" (only the |C_alpha| atom). :type model: str """ if name is not None: blueprint = _residueBlueprint(name, model) ChemicalObjects.Group.__init__(self, blueprint) self.model = model self._init() def _init(self): Biopolymers.Residue._init(self) # create peptide attribute for calpha model if self.model == 'calpha': self.peptide = self def isNTerminus(self): return hasattr(self.peptide, 'H_3') def isCTerminus(self): return hasattr(self.peptide, 'O_2') def _makeCystine(self): if self.model == 'calpha': return self if self.symbol.lower() != 'cys': raise ValueError(`self` + " is not cysteine.") new_residue = 'cystine_ss' if self.isNTerminus(): new_residue = new_residue + '_nt' elif self.isCTerminus(): new_residue = new_residue + '_ct' new_residue = Residue(new_residue, self.model) for g in ['peptide', 'sidechain']: g_old = getattr(self, g) g_new = getattr(new_residue, g) for a in getattr(g_new, 'atoms'): set_method = getattr(getattr(g_new, a.name), 'setPosition') set_method(getattr(getattr(g_old, a.name), 'position')()) return new_residue def isSubsetModel(self): return self.model == 'calpha'
[docs] def backbone(self): """ :returns: the peptide group :rtype: :class:`~MMTK.ChemicalObjects.Group` """ return self.peptide
[docs] def sidechains(self): """ :returns: the sidechain group :rtype: :class:`~MMTK.ChemicalObjects.Group` """ return self.sidechain
[docs] def phiPsi(self, conf = None): """ :returns: the values of the backbone dihedral angles phi and psi. :rtype: tuple (float, float) """ universe = self.universe() if universe is None: universe = Universe.InfiniteUniverse() C = None for a in self.peptide.N.bondedTo(): if a.parent.parent != self: C = a break if C is None: phi = None else: phi = universe.dihedral(self.peptide.C, self.peptide.C_alpha, self.peptide.N, C, conf) N = None for a in self.peptide.C.bondedTo(): if a.parent.parent != self: N = a break if N is None: psi = None else: psi = universe.dihedral(N, self.peptide.C, self.peptide.C_alpha, self.peptide.N, conf) return phi, psi
[docs] def phiAngle(self): """ :returns: an object representing the phi angle and allowing to modify it :rtype: MMTK.InternalCoordinates.DihedralAngle """ from MMTK.InternalCoordinates import DihedralAngle C = None for a in self.peptide.N.bondedTo(): if a.parent.parent != self: C = a break if C is None: raise ValueError("residue is N-terminus") return DihedralAngle(self.peptide.C, self.peptide.C_alpha, self.peptide.N, C)
[docs] def psiAngle(self): """ :returns: an object representing the psi angle and allowing to modify it :rtype: MMTK.InternalCoordinates.DihedralAngle """ from MMTK.InternalCoordinates import DihedralAngle N = None for a in self.peptide.C.bondedTo(): if a.parent.parent != self: N = a break if N is None: raise ValueError("residue is C-terminus") return DihedralAngle(N, self.peptide.C, self.peptide.C_alpha, self.peptide.N)
[docs] def chiAngle(self): """ :returns: an object representing the chi angle and allowing to modify it :rtype: MMTK.InternalCoordinates.DihedralAngle """ from MMTK.InternalCoordinates import DihedralAngle try: C_beta = self.sidechain.C_beta except AttributeError: raise ValueError("no C_beta in sidechain") X = None for atom_name in ['C_gamma', 'C_gamma_1', 'S_gamma', 'O_gamma', 'O_gamma_1', 'H_beta_1']: try: X = getattr(self.sidechain, atom_name) break except AttributeError: pass if X is None: raise ValueError("no sidechain reference atom found") return DihedralAngle(self.peptide.N, self.peptide.C_alpha, C_beta, X)
def _residueBlueprint(name, model): try: blueprint = _residue_blueprints[(name, model)] except KeyError: if model == 'polar': name = name + '_uni' elif model == 'polar_charmm': name = name + '_uni2' elif model == 'polar_oldopls': name = name + '_uni3' elif model == 'none': name = name + '_noh' elif model == 'calpha': name = name + '_calpha' blueprint = Database.BlueprintGroup(name) _residue_blueprints[(name, model)] = blueprint return blueprint _residue_blueprints = {} # # Peptide chains are molecules with added features. #
[docs]class PeptideChain(Biopolymers.ResidueChain): """ Peptide chain Peptide chains consist of amino acid residues that are linked by peptide bonds. They are a special kind of molecule, i.e. all molecule operations are available. Peptide chains act as sequences of residues. If p is a PeptideChain object, then * len(p) yields the number of residues * p[i] yields residue number i * p[i:j] yields the subchain from residue number i up to but excluding residue number j :param sequence: the amino acid sequence. This can be a string containing the one-letter codes, or a list of three-letter codes, or a :class:`~MMTK.PDB.PDBPeptideChain` object. If a PDBPeptideChain object is supplied, the atomic positions it contains are assigned to the atoms of the newly generated peptide chain, otherwise the positions of all atoms are undefined. :keyword model: one of "all" (all-atom), "no_hydrogens" or "none" (no hydrogens), "polar_hydrogens" or "polar" (united-atom with only polar hydrogens), "polar_charmm" (like "polar", but defining polar hydrogens like in the CHARMM force field), "polar_opls" (like "polar", but defining polar hydrogens like in the latest OPLS force field), "calpha" (only the |C_alpha| atom of each residue). Default is "all". :type model: str :keyword n_terminus: if True, the first residue is constructed using the N-terminal variant, if False the non-terminal version is used. Default is True. :type n_terminus: bool :keyword c_terminus: if True, the last residue is constructed using the C-terminal variant, if False the non-terminal version is used. Default is True. :type c_terminus: bool :keyword circular: if True, a peptide bond is constructed between the first and the last residues. Default is False. :type circular: bool :keyword name: a name for the chain (a string) :type name: str """ def __init__(self, sequence, **properties): if sequence is not None: model = 'all' if properties.has_key('model'): model = properties['model'].lower() elif properties.has_key('hydrogens'): model = properties['hydrogens'] if model == 1: model = 'all' elif model == 0: model = 'none' else: model = model.lower() if model == 'no_hydrogens': model = 'none' elif model == 'polar_hydrogens': model = 'polar' n_term = self.binaryProperty(properties, 'n_terminus', True) c_term = self.binaryProperty(properties, 'c_terminus', True) circular = self.binaryProperty(properties, 'circular', False) self.version_spec = {'n_terminus': n_term, 'c_terminus': c_term, 'model': model, 'circular': circular} if type(sequence[0]) == type(''): conf = None numbers = range(len(sequence)) else: conf = sequence sequence = conf.sequence() numbers = [r.number for r in conf] sequence = map(Biopolymers._fullName, sequence) if model != 'calpha': if n_term: sequence[0] = sequence[0] + '_nt' if c_term: sequence[-1] = sequence[-1] + '_ct' self.groups = [] n = 0 for residue, number in zip(sequence, numbers): n = n + 1 r = Residue(residue, model) r.name = r.symbol + str(number) r.sequence_number = n r.parent = self self.groups.append(r) self._setupChain(circular, properties, conf) is_peptide_chain = True def __getslice__(self, first, last): return SubChain(self, self.groups[first:last])
[docs] def sequence(self): """ :returns: the primary sequence as a list of three-letter residue codes. :rtype: list """ return [r.symbol for r in self.groups]
[docs] def backbone(self): """ :returns: the peptide groups of all residues :rtype: :class:`~MMTK.Collections.Collection` """ backbone = Collections.Collection() for r in self.groups: try: backbone.addObject(r.peptide) except AttributeError: pass return backbone
[docs] def sidechains(self): """ :returns: the sidechain groups of all residues :rtype: :class:`~MMTK.Collections.Collection` """ sidechains = Collections.Collection() for r in self.groups: try: sidechains.addObject(r.sidechain) except AttributeError: pass return sidechains
[docs] def phiPsi(self, conf = None): """ :returns: a list of the (phi, psi) backbone angles for each residue :rtype: list of tuple of float """ universe = self.universe() if universe is None: universe = Universe.InfiniteUniverse() angles = [] for i in range(len(self)): r = self[i] if i == 0: phi = None else: phi = universe.dihedral(r.peptide.C, r.peptide.C_alpha, r.peptide.N, self[i-1].peptide.C, conf) if i == len(self)-1: psi = None else: psi = universe.dihedral(self[i+1].peptide.N, r.peptide.C, r.peptide.C_alpha, r.peptide.N, conf) angles.append((phi, psi)) return angles
[docs] def replaceResidue(self, r_old, r_new): """ :param r_old: the residue to be replaced (must be part of the chain) :type r_old: Residue :param r_new: the residue that replaces r_old :type r_new: Residue """ n = self.groups.index(r_old) for a in r_old.atoms: self.atoms.remove(a) obsolete_bonds = [] for b in self.bonds: if b.a1 in r_old.atoms or b.a2 in r_old.atoms: obsolete_bonds.append(b) for b in obsolete_bonds: self.bonds.remove(b) r_old.parent = None self.atoms.extend(r_new.atoms) self.bonds.extend(r_new.bonds) r_new.sequence_number = n+1 r_new.name = r_new.symbol+`n+1` r_new.parent = self self.groups[n] = r_new if n > 0: peptide_old = self.bonds.bondsOf(r_old.peptide.N) if peptide_old: self.bonds.remove(peptide_old[0]) if not (self.groups[n-1].isCTerminus() or self.groups[n].isNTerminus()): # ConnectedChain objects can have N/C-terminal # residues inside the (virtual) chain, so the # test is necessary. self.bonds.append(Bonds.Bond((self.groups[n-1].peptide.C, self.groups[n].peptide.N))) if n < len(self.groups)-1: peptide_old = self.bonds.bondsOf(r_old.peptide.C) if peptide_old: self.bonds.remove(peptide_old[0]) if not (self.groups[n].isCTerminus() or self.groups[n+1].isNTerminus()): self.bonds.append(Bonds.Bond((self.groups[n].peptide.C, self.groups[n+1].peptide.N))) if isinstance(self.parent, ChemicalObjects.Complex): self.parent.recreateAtomList() universe = self.universe() if universe is not None: universe._changed(True) # add sulfur bridges between cysteine residues
def _addSSBridges(self, bonds): for b in bonds: cys1 = b[0] if cys1.symbol.lower() == 'cyx': cys_ss1 = cys1 else: cys_ss1 = cys1._makeCystine() self.replaceResidue(cys1, cys_ss1) cys2 = b[1] if cys2.symbol.lower() == 'cyx': cys_ss2 = cys2 else: cys_ss2 = cys2._makeCystine() self.replaceResidue(cys2, cys_ss2) self.bonds.append(Bonds.Bond((cys_ss1.sidechain.S_gamma, cys_ss2.sidechain.S_gamma))) def _descriptionSpec(self): kwargs = ','.join([name + '=' + `self.version_spec[name]` for name in sorted(self.version_spec.keys())]) return "S", kwargs def _typeName(self): return ''.join(self.sequence()) def _graphics(self, conf, distance_fn, model, module, options): if model != 'backbone': return ChemicalObjects.Molecule._graphics(self, conf, distance_fn, model, module, options) color = options.get('color', 'black') material = module.EmissiveMaterial(color) objects = [] for i in range(len(self.groups)-1): a1 = self.groups[i].peptide.C_alpha a2 = self.groups[i+1].peptide.C_alpha p1 = a1.position(conf) p2 = a2.position(conf) if p1 is not None and p2 is not None: bond_vector = 0.5*distance_fn(a1, a2, conf) cut = bond_vector != 0.5*(p2-p1) if not cut: objects.append(module.Line(p1, p2, material = material)) else: objects.append(module.Line(p1, p1+bond_vector, material = material)) objects.append(module.Line(p2, p2-bond_vector, material = material)) return objects # # Subchains are created by slicing chains or extracting a chain from # a group of connected chains. #
[docs]class SubChain(PeptideChain): """ A contiguous part of a peptide chain SubChain objects are the result of slicing operations on PeptideChain objects. They cannot be created directly. SubChain objects permit all operations of PeptideChain objects, but cannot be added to a universe. """ def __init__(self, chain=None, groups=None, name = ''): if chain is not None: self.groups = groups self.atoms = [] self.bonds = [] for g in self.groups: self.atoms.extend(g.atoms) self.bonds.extend(g.bonds) for i in range(len(self.groups)-1): link1 = self.groups[i].chain_links[1] link2 = self.groups[i+1].chain_links[0] self.bonds.append(Bonds.Bond((link1, link2))) self.bonds = Bonds.BondList(self.bonds) self.name = name self.model = chain.model self.parent = chain.parent self.type = None self.configurations = {} self.part_of = chain is_incomplete = True def __repr__(self): if self.name == '': return 'SubChain of ' + repr(self.part_of) else: return ChemicalObjects.Molecule.__repr__(self) __str__ = __repr__ def replaceResidue(self, r_old, r_new): for a in r_old.atoms: self.atoms.remove(a) obsolete_bonds = [] for b in self.bonds: if b.a1 in r_old.atoms or b.a2 in r_old.atoms: obsolete_bonds.append(b) for b in obsolete_bonds: self.bonds.remove(b) n = self.groups.index(r_old) if n > 0: for b in self.bonds.bondsOf(r_old.peptide.N): self.bonds.remove(b) if n < len(self.groups)-1: for b in self.bonds.bondsOf(r_old.peptide.C): self.bonds.remove(b) PeptideChain.replaceResidue(self.part_of, r_old, r_new) self.groups[n] = r_new self.atoms.extend(r_new.atoms) self.bonds.extend(r_new.bonds) if n > 0: self.bonds.append(Bonds.Bond((self.groups[n-1].peptide.C, self.groups[n].peptide.N))) if n < len(self.groups)-1: self.bonds.append(Bonds.Bond((self.groups[n].peptide.C, self.groups[n+1].peptide.N))) def _distanceConstraintList(self): atoms = self.atomList() return [(a1, a2, d) for a1, a2, d in self.part_of._distanceConstraintList() if a1 in atoms and a2 in atoms] def addDistanceConstraint(self, atom1, atom2, distance): chain = self while True: try: chain = chain.part_of except AttributeError: break try: chain.distance_constraints.append((atom1, atom2, distance)) except AttributeError: chain.distance_constraints = [(atom1, atom2, distance)] def removeDistanceConstraints(self, universe=None): raise NotImplementedError # # Connected chains are collections of peptide chains connected by s-s bridges. #
[docs]class ConnectedChains(PeptideChain): """ Peptide chains connected by disulfide bridges A group of peptide chains connected by disulfide bridges must be considered a single molecule due to the presence of chemical bonds. Such a molecule is represented by a ConnectedChains object. These objects are created automatically when a Protein object is assembled. They are normally not used directly by application programs. When a chain with disulfide bridges to other chains is extracted from a Protein object, the return value is a SubChain object that indirectly refers to a ConnectedChains object. """ def __init__(self, chains=None): if chains is not None: self.chains = [] self.groups = [] self.atoms = [] self.bonds = Bonds.BondList([]) self.chain_names = [] self.model = chains[0].model version_spec = chains[0].version_spec for c in chains: if c.version_spec['model'] != version_spec['model']: raise ValueError("mixing chains of different model: " + c.version_spec['model'] + "/" + version_spec['model']) ng = len(self.groups) self.chains.append((c.name, ng, ng+len(c.groups), c.version_spec)) self.groups.extend(c.groups) self.atoms.extend(c.atoms) self.bonds.extend(c.bonds) try: name = c.name except AttributeError: name = '' self.chain_names.append(name) for g in self.groups: g.parent = self self.name = '' self.parent = None self.type = None self.configurations = {} is_connected_chains = True def _finalize(self): for i in range(len(self.chains)): c = self.chains[i] sub_chain = SubChain(self, self.groups[c[1]:c[2]], c[0]) sub_chain.version_spec = c[3] for g in sub_chain.groups: g.parent = sub_chain self.chains[i] = sub_chain def __len__(self): return len(self.chains) def __getitem__(self, item): return self.chains[item] def __getslice__(self, first, last): raise TypeError("Can't slice connected chains") def _graphics(self, conf, distance_fn, model, module, options): if model != 'backbone': return ChemicalObjects.Molecule._graphics(self, conf, distance_fn, model, module, options) objects = [] for chain in self: objects = objects + chain._graphics(conf, distance_fn, model, module, options) return objects # # Proteins are complexes of peptide chains, connected peptide chains, # and possibly other things. #
[docs]class Protein(ChemicalObjects.Complex): """ Protein A Protein object is a special kind of :class:`~MMTK.ChemicalObjects.Complex` object which is made up of peptide chains and possibly ligands. If the atoms in the peptide chains that make up a protein have defined positions, sulfur bridges within chains and between chains will be constructed automatically during protein generation based on a distance criterion between cystein sidechains. Proteins act as sequences of chains. If p is a Protein object, then * len(p) yields the number of chains * p[i] yields chain number i """ def __init__(self, *items, **properties): """ :param items: either a sequence of peptide chain objects, or a string, which is interpreted as the name of a database definition for a protein. If that definition does not exist, the string is taken to be the name of a PDB file, from which all peptide chains are constructed and assembled into a protein. :keyword model: one of "all" (all-atom), "no_hydrogens" or "none" (no hydrogens),"polar_hydrogens" or "polar" (united-atom with only polar hydrogens), "polar_charmm" (like "polar", but defining polar hydrogens like in the CHARMM force field), "polar_opls" (like "polar", but defining polar hydrogens like in the latest OPLS force field), "calpha" (only the |C_alpha| atom of each residue). Default is "all". :type model: str :keyword position: the center-of-mass position of the protein :type position: Scientific.Geometry.Vector :keyword name: a name for the protein :type name: str """ if items == (None,): return self.name = '' if len(items) == 1 and type(items[0]) == type(''): try: filename = Database.databasePath(items[0], 'Proteins') found = 1 except IOError: found = 0 if found: blueprint = Database.BlueprintProtein(items[0]) items = blueprint.chains for attr, value in vars(blueprint).items(): if attr not in ['type', 'chains']: setattr(self, attr, value) else: import PDB conf = PDB.PDBConfiguration(items[0]) model = properties.get('model', 'all') items = conf.createPeptideChains(model) molecules = [] for i in items: if ChemicalObjects.isChemicalObject(i): molecules.append(i) else: molecules = molecules + list(i) for m, i in zip(molecules, range(len(molecules))): m._numbers = [i] if not m.name: m.name = 'chain'+`i` ss = self._findSSBridges(molecules) new_mol = {} for m in molecules: new_mol[m] = ([m],[]) for bond in ss: m1 = new_mol[bond[0].topLevelChemicalObject()] m2 = new_mol[bond[1].topLevelChemicalObject()] if m1 == m2: m1[1].append(bond) else: combined = (m1[0] + m2[0], m1[1] + m2[1] + [bond]) for m in combined[0]: new_mol[m] = combined self.molecules = [] while new_mol: m = new_mol.values()[0] for i in m[0]: del new_mol[i] bonds = m[1] if len(m[0]) == 1: m = m[0][0] m._addSSBridges(bonds) else: numbers = sum((i._numbers for i in m[0]), []) m = ConnectedChains(m[0]) m._numbers = numbers m._addSSBridges(bonds) m._finalize() for c in m: c.parent = self m.parent = self self.molecules.append(m) self.atoms = [] self.chains = [] for m in self.molecules: self.atoms.extend(m.atoms) if hasattr(m, 'is_connected_chains'): for c, name, i in zip(range(len(m)), m.chain_names, m._numbers): self.chains.append((m, c, name, i)) else: try: name = m.name except AttributeError: name = '' self.chains.append((m, None, name, m._numbers[0])) self.chains.sort(lambda c1, c2: cmp(c1[3], c2[3])) self.chains = map(lambda c: c[:3], self.chains) self.parent = None self.type = None self.configurations = {} try: self.name = properties['name'] del properties['name'] except KeyError: pass if properties.has_key('position'): self.translateTo(properties['position']) del properties['position'] self.addProperties(properties) undefined = 0 for a in self.atoms: if a.position() is None: undefined += 1 if undefined > 0 and undefined != len(self.atoms): Utility.warning('Some atoms in a protein ' + 'have undefined positions.') is_protein = True def __len__(self): return len(self.chains) def __getitem__(self, item): if isinstance(item, int): m, c, name = self.chains[item] else: for m, c, name in self.chains: if name == item: break if name != item: raise ValueError('No chain with name ' + item) if c is None: return m else: return m[c]
[docs] def residuesOfType(self, *types): """ :param types: a sequence of residue codes (one- or three-letter) :type types: sequence of str :returns: all residues whose type (one- or three-letter code) is contained in types :rtype: :class:`~MMTK.Collections.Collection` """ rlist = Collections.Collection([]) for m in self.molecules: if isPeptideChain(m): rlist = rlist + apply(m.residuesOfType, types) return rlist
[docs] def backbone(self): """ :returns: the peptide groups of all residues in all chains :rtype: :class:`~MMTK.Collections.Collection` """ rlist = Collections.Collection([]) for m in self.molecules: if isPeptideChain(m): rlist = rlist + m.backbone() return rlist
[docs] def sidechains(self): """ :returns: the sidechain groups of all residues in all chains :rtype: :class:`~MMTK.Collections.Collection` """ rlist = Collections.Collection([]) for m in self.molecules: if isPeptideChain(m): rlist = rlist + m.sidechains() return rlist
[docs] def residues(self): """ :returns: all residues in all chains :rtype: :class:`~MMTK.Collections.Collection` """ rlist = Collections.Collection([]) for m in self.molecules: if isPeptideChain(m): rlist = rlist + m.residues() return rlist
[docs] def phiPsi(self, conf = None): """ :returns: a list of the (phi, psi) backbone angles for all residue in all chains :rtype: list of list of tuple of float """ return [chain.phiPsi(conf) for chain in self]
_ss_bond_max = 0.25*Units.nm def _findSSBridges(self, molecules): molecules = filter(lambda m: hasattr(m, 'is_peptide_chain'), molecules) cys = Collections.Collection([]) for m in molecules: if m.version_spec['model'] != 'calpha': cys = cys + m.residuesOfType('cys') + m.residuesOfType('cyx') s = cys.map(lambda r: r.sidechain.S_gamma) ns = len(s) ss = [] for i in xrange(ns-1): for j in xrange(i+1,ns): r1 = s[i].position() r2 = s[j].position() if r1 and r2 and (r1-r2).length() < self._ss_bond_max: ss.append((cys[i], cys[j])) return ss def _subunits(self): return list(self) def _description(self, tag, index_map, toplevel): if not toplevel: raise ValueError return 'l(' + `self.__class__.__name__` + ',' + `self.name` + ',[' + \ ','.join(o._description(tag, index_map, True) for o in self) + \ '])' def _graphics(self, conf, distance_fn, model, module, options): if model != 'backbone': return ChemicalObjects.Complex._graphics(self, conf, distance_fn, model, module, options) objects = [] for chain in self: objects.extend(chain._graphics(conf, distance_fn, model, module, options)) return objects # # Type check functions #
[docs]def isPeptideChain(x): """ :param x: any object :returns: True if x is a peptide chain :rtype: bool """ return hasattr(x, 'is_peptide_chain')
[docs]def isProtein(x): """ :param x: any object :returns: True if x is a protein :rtype: bool """ return hasattr(x, 'is_protein')
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Random.html0000644000076600000240000007147412013143456020663 0ustar hinsenstaff00000000000000 MMTK.Random — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Random

# Functions for finding random points and orientations.
#
# Written by: Konrad Hinsen
# 

"""
Random quantities for use in molecular simulations
"""

__docformat__ = 'restructuredtext'

from Scientific.Geometry import Vector
from Scientific.Geometry.Transformation import Rotation
from Scientific import N
from MMTK import ParticleProperties, Units

try:
    numeric = N.package
except AttributeError:
    numeric = "Numeric"

RNG = None
try:
    if numeric == "Numeric":
        import RNG
    elif numeric == "NumPy":
        import numpy.oldnumeric.rng as RNG
except ImportError:
    pass


if RNG is None:

    if numeric == "Numeric":
        from RandomArray import uniform, seed
    elif numeric == "NumPy":
        from numpy.oldnumeric.random_array import uniform, seed
    random = __import__('random')
    seed(1, 1)
    random.seed(1)

    def initializeRandomNumbersFromTime():
        random.seed()
        seed(0, 0)

    def gaussian(mean, std, shape=None):
        if shape is None:
            x = random.normalvariate(0., 1.)
        else:
            x = N.zeros(shape, N.Float)
            xflat = N.ravel(x)
            for i in range(len(xflat)):
                xflat[i] = random.normalvariate(0., 1.)
        return mean + std*x

else:

    _uniform_generator = \
                    RNG.CreateGenerator(-1, RNG.UniformDistribution(0., 1.))
    _gaussian_generator = \
                    RNG.CreateGenerator(-1, RNG.NormalDistribution(0., 1.))

    def initializeRandomNumbersFromTime():
        global _uniform_generator, _gaussian_generator
        _uniform_generator = \
                       RNG.CreateGenerator(0, RNG.UniformDistribution(0., 1.))
        _gaussian_generator = \
                       RNG.CreateGenerator(0, RNG.NormalDistribution(0., 1.))

    def uniform(x1, x2, shape=None):
        if shape is None:
            x = _uniform_generator.ranf()
        else:
            n = N.multiply.reduce(shape)
            x = _uniform_generator.sample(n)
            x.shape = shape
        return x1+(x2-x1)*x

    def gaussian(mean, std, shape=None):
        if shape is None:
            x = _gaussian_generator.ranf()
        else:
            n = N.multiply.reduce(shape)
            x = _gaussian_generator.sample(n)
            x.shape = shape
        return mean+std*x

del numeric

#
# Random point in a rectangular box centered around the origin
#
[docs]def randomPointInBox(a, b = None, c = None): """ :param a: the edge length of a box along the x axis :type a: float :param b: the edge length of a box along the y axis (default: a) :type b: float :param c: the edge length of a box along the z axis (default: a) :type c: float :returns: a vector drawn from a uniform distribution within a rectangular box with edge lengths a, b, c. :rtype: Scientific.Geometry.Vector """ if b is None: b = a if c is None: c = a x = uniform(-0.5*a, 0.5*a) y = uniform(-0.5*b, 0.5*b) z = uniform(-0.5*c, 0.5*c) return Vector(x, y, z) # # Random point in a sphere around the origin. #
[docs]def randomPointInSphere(r): """ :param r: the radius of a sphere :type r: float :returns: a vector drawn from a uniform distribution within a sphere of radius r. :rtype: Scientific.Geometry.Vector """ rsq = r*r while 1: x = N.array([uniform(-r, r), uniform(-r, r), uniform(-r, r)]) if N.dot(x, x) < rsq: break return Vector(x) # # Random direction (unit vector). #
[docs]def randomDirection(): """ :returns: a vector drawn from a uniform distribution on the surface of a unit sphere. :rtype: Scientific.Geometry.Vector """ r = randomPointInSphere(1.) return r.normal()
[docs]def randomDirections(n): """ :param n: the number of direction vectors :returns: a list of n vectors drawn from a uniform distribution on the surface of a unit sphere. If n is negative, returns a deterministic list of not more than -n vectors of unit length (useful for testing purposes). :rtype: list """ if n < 0: vs = [Vector(1., 0., 0.), Vector(0., -1., 0.), Vector(0., 0., 1.), Vector(-1., 1., 0.).normal(), Vector(-1., 0., 1.).normal(), Vector(0., 1., -1.).normal(), Vector(1., -1., 1.).normal()] return vs[:-n] else: return [randomDirection() for i in range(n)] # # Random rotation. #
[docs]def randomRotation(max_angle = N.pi): """ :param max_angle: the upper limit for the rotation angle :type max_angle: float :returns: a random rotation with a uniform axis distribution and angles drawn from a uniform distribution between -max_angle and max_angle. :rtype: Scientific.Geometry.Transformations.Rotation """ return Rotation(randomDirection(), uniform(-max_angle, max_angle)) # # Random velocity (gaussian) #
[docs]def randomVelocity(temperature, mass): """ :param temperature: the temperature defining a Maxwell distribution :type temperature: float :param mass: the mass of a particle :type mass: float :returns: a random velocity vector for a particle of a given mass at a given temperature :rtype: Scientific.Geometry.Vector """ sigma = N.sqrt((temperature*Units.k_B)/(mass*Units.amu)) return Vector(gaussian(0., sigma, (3,))) # # Random ParticleVector (gaussian) #
[docs]def randomParticleVector(universe, width): """ :param universe: a universe :type universe: :class:`~MMTK.Universe.Universe` :param width: the width (standard deviation) of a Gaussian distribution :type width: float :returns: a set of vectors drawn from a Gaussian distribution with a given width centered around zero. :rtype: :class:`~MMTK.ParticleProperties.ParticleVector` """ data = gaussian(0., 0.577350269189*width, (universe.numberOfPoints(), 3)) return ParticleProperties.ParticleVector(universe, data)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Solvation.html0000644000076600000240000006471112013143456021415 0ustar hinsenstaff00000000000000 MMTK.Solvation — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Solvation

# This module contains code for solvation.
#
# Written by Konrad Hinsen
#

"""
Solvation of solute molecules
"""

__docformat__ = 'restructuredtext'

from MMTK import ChemicalObjects, Units, Universe
from MMTK.MolecularSurface import surfaceAndVolume
from MMTK.Minimization import SteepestDescentMinimizer
from MMTK.Dynamics import VelocityVerletIntegrator, VelocityScaler, \
                          TranslationRemover, RotationRemover
from MMTK.Trajectory import Trajectory, TrajectoryOutput, SnapshotGenerator, \
                            StandardLogOutput
import copy

#
# Calculate the number of solvent molecules
#
[docs]def numberOfSolventMolecules(universe, solvent, density): """ :param universe: a finite universe :type universe: :class:`~MMTK.Universe.Universe` :param solvent: a molecule, or the name of a molecule in the database :type solvent: :class:`~MMTK.ChemicalObjects.Molecule` or str :param density: the density of the solvent (amu/nm**3) :type density: float :returns: the number of solvent molecules that must be added to the universe, in addition to whatever it already contains, to obtain the given solvent density. :rtype: int """ if isinstance(solvent, basestring): solvent = ChemicalObjects.Molecule(solvent) cell_volume = universe.cellVolume() if cell_volume is None: raise TypeError("universe volume is undefined") solute_volume = 0. for o in universe._objects: solute_volume = solute_volume + surfaceAndVolume(o)[1] return int(round(density*(cell_volume-solute_volume)/solvent.mass())) # # Add solvent to a universe containing solute molecules. #
[docs]def addSolvent(universe, solvent, density, scale_factor=4.): """ Scales up the universe and adds as many solvent molecules as are necessary to obtain the specified solvent density, taking account of the solute molecules that are already present in the universe. The molecules are placed at random positions in the scaled-up universe, but without overlaps between any two molecules. :param universe: a finite universe :type universe: :class:`~MMTK.Universe.Universe` :param solvent: a molecule, or the name of a molecule in the database :type solvent: :class:`~MMTK.ChemicalObjects.Molecule` or str :param density: the density of the solvent (amu/nm**3) :type density: float :param scale_factor: the factor by which the initial universe is expanded before adding the solvent molecules :type scale_factor: float """ # Calculate number of solvent molecules and universe size if isinstance(solvent, basestring): solvent = ChemicalObjects.Molecule(solvent) cell_volume = universe.cellVolume() if cell_volume is None: raise TypeError("universe volume is undefined") solute = copy.copy(universe._objects) solute_volume = 0. excluded_regions = [] for o in solute: solute_volume = solute_volume + surfaceAndVolume(o)[1] excluded_regions.append(o.boundingSphere()) n_solvent = int(round(density*(cell_volume-solute_volume)/solvent.mass())) solvent_volume = n_solvent*solvent.mass()/density cell_volume = solvent_volume + solute_volume universe.translateBy(-solute.position()) universe.scaleSize((cell_volume/universe.cellVolume())**(1./3.)) # Scale up the universe and add solvent molecules at random positions universe.scaleSize(scale_factor) universe.scale_factor = scale_factor for i in range(n_solvent): m = copy.copy(solvent) m.translateTo(universe.randomPoint()) while True: s = m.boundingSphere() collision = False for region in excluded_regions: if (s.center-region.center).length() < s.radius+region.radius: collision = True break if not collision: break m.translateTo(universe.randomPoint()) universe.addObject(m) excluded_regions.append(s) # # Shrink the universe to its final size #
[docs]def shrinkUniverse(universe, temperature=300.*Units.K, trajectory=None, scale_factor=0.95): """ Shrinks the universe, which must have been scaled up by :class:`~MMTK.Solvation.addSolvent`, back to its original size. The compression is performed in small steps, in between which some energy minimization and molecular dynamics steps are executed. The molecular dynamics is run at the given temperature, and an optional trajectory can be specified in which intermediate configurations are stored. :param universe: a finite universe :type universe: :class:`~MMTK.Universe.Universe` :param temperature: the temperature at which the Molecular Dynamics steps are run :type temperature: float :param trajectory: the trajectory in which the progress of the shrinking procedure is stored, or a filename :type trajectory: :class:`~MMTK.Trajectory.Trajectory` or str :param scale_factor: the factor by which the universe is scaled at each reduction step :type scale_factor: float """ # Set velocities and initialize trajectory output universe.initializeVelocitiesToTemperature(temperature) if trajectory is not None: if isinstance(trajectory, basestring): trajectory = Trajectory(universe, trajectory, "w", "solvation protocol") close_trajectory = True else: close_trajectory = False actions = [TrajectoryOutput(trajectory, ["configuration"], 0, None, 1)] snapshot = SnapshotGenerator(universe, actions=actions) snapshot() # Do some minimization and equilibration minimizer = SteepestDescentMinimizer(universe, step_size = 0.05*Units.Ang) actions = [VelocityScaler(temperature, 0.01*temperature, 0, None, 1), TranslationRemover(0, None, 20)] integrator = VelocityVerletIntegrator(universe, delta_t = 0.5*Units.fs, actions = actions) for i in range(5): minimizer(steps = 40) integrator(steps = 200) # Scale down the system in small steps i = 0 while universe.scale_factor > 1.: if trajectory is not None and i % 1 == 0: snapshot() i = i + 1 step_factor = max(scale_factor, 1./universe.scale_factor) for object in universe: object.translateTo(step_factor*object.position()) universe.scaleSize(step_factor) universe.scale_factor = universe.scale_factor*step_factor for i in range(3): minimizer(steps = 10) integrator(steps = 50) del universe.scale_factor if trajectory is not None: snapshot() if close_trajectory: trajectory.close()
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Subspace.html0000644000076600000240000017243412013143456021206 0ustar hinsenstaff00000000000000 MMTK.Subspace — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Subspace

# This module implements subspaces for motion analysis etc.
#
# Written by Konrad Hinsen
#

"""
Linear subspaces for infinitesimal motions

This module implements subspaces for infinitesimal (or finite
small-amplitude) atomic motions. They can be used in normal mode
calculations or for analyzing complex motions. For an explanation, see:

  |  K. Hinsen and G.R. Kneller
  |  Projection methods for the analysis of complex motions in macromolecules
  |  Mol. Sim. 23:275-292 (2000)
"""

__docformat__ = 'restructuredtext'

from MMTK import Utility, ParticleProperties
from Scientific.Geometry import Vector, ex, ey, ez
from Scientific import N
import copy

#
# Import LAPACK routines
#
try:
    array_package = N.package
except AttributeError:
    array_package = "Numeric"

dgesdd = None
try:
    if array_package == "Numeric":
        from lapack_lite import dgesdd, LapackError
    else:
        from numpy.linalg.lapack_lite import dgesdd, LapackError
except ImportError: pass
if dgesdd is None:
    try:
        # PyLAPACK
        from lapack_dge import dgesdd
    except ImportError: pass
if dgesdd:
    n = 1
    array = N.zeros((n, n), N.Float)
    sv = N.zeros((n,), N.Float)
    u = N.zeros((n, n), N.Float)
    vt = N.zeros((n, n), N.Float)
    work = N.zeros((1,), N.Float)
    int_types = [N.Int, N.Int8, N.Int16, N.Int32]
    try:
        int_types.append(N.Int64)
        int_types.append(N.Int128)
    except AttributeError:
        pass
    for int_type in int_types:
        iwork = N.zeros((1,), int_type)
        try:
            dgesdd('S', n, n, array, n, sv, u, n, vt, n, work, -1, iwork, 0)
            break
        except LapackError:
            pass
    del n, array, sv, u, vt, work, iwork, int_types

del array_package

#
# A set of particle vectors that define a subspace
#
class ParticleVectorSet(object):

    def __init__(self, universe, data):
        self.universe = universe
        if type(data) == N.arraytype:
            self.n = data.shape[0]
            self.array = data
        else:
            self.n = data
            self.array = N.zeros((self.n, universe.numberOfAtoms(), 3),
                                 N.Float)

    def __len__(self):
        return self.n

    def __getitem__(self, i):
        if i >= self.n:
            raise IndexError
        return ParticleProperties.ParticleVector(self.universe, self.array[i])


[docs]class Subspace(object): """ Subspace of infinitesimal atomic motions """ def __init__(self, universe, vectors): """ :param universe: the universe for which the subspace is created :type universe: :class:`~MMTK.Universe.Universe` :param vectors: a list of :class:`~MMTK.ParticleProperties.ParticleVector` objects that define the subspace. They need not be orthogonal or linearly independent. :type vectors: list """ self.universe = universe self.vectors = vectors self._basis = None def __add__(self, other): return Subspace(self.vectors+other.vectors) def __len__(self): return len(self.vectors) def __getitem__(self, item): return self.vectors[item]
[docs] def getBasis(self): """ Construct a basis for the subspace by orthonormalization of the input vectors using Singular Value Decomposition. The basis consists of a sequence of :class:`~MMTK.ParticleProperties.ParticleVector` objects that are orthonormal in configuration space. :returns: the basis """ if self._basis is None: basis = N.array([v.array for v in self.vectors], N.Float) shape = basis.shape nvectors = shape[0] natoms = shape[1] basis.shape = (nvectors, 3*natoms) sv = N.zeros((min(nvectors, 3*natoms),), N.Float) min_n_m = min(3*natoms, nvectors) vt = N.zeros((nvectors, min_n_m), N.Float) work = N.zeros((1,), N.Float) iwork = N.zeros((8*min_n_m,), int_type) if 3*natoms >= nvectors: result = dgesdd('O', 3*natoms, nvectors, basis, 3*natoms, sv, basis, 3*natoms, vt, min_n_m, work, -1, iwork, 0) work = N.zeros((int(work[0]),), N.Float) result = dgesdd('O', 3*natoms, nvectors, basis, 3*natoms, sv, basis, 3*natoms, vt, min_n_m, work, work.shape[0], iwork, 0) u = basis else: u = N.zeros((min_n_m, 3*natoms), N.Float) result = dgesdd('S', 3*natoms, nvectors, basis, 3*natoms, sv, u, 3*natoms, vt, min_n_m, work, -1, iwork, 0) work = N.zeros((int(work[0]),), N.Float) result = dgesdd('S', 3*natoms, nvectors, basis, 3*natoms, sv, u, 3*natoms, vt, min_n_m, work, work.shape[0], iwork, 0) if result['info'] != 0: raise ValueError('Lapack SVD: ' + `result['info']`) svmax = N.maximum.reduce(sv) nvectors = N.add.reduce(N.greater(sv, 1.e-10*svmax)) u = u[:nvectors] u.shape = (nvectors, natoms, 3) self._basis = ParticleVectorSet(self.universe, u) return self._basis
[docs] def projectionOf(self, vector): """ :param vector: a particle vector :type vector: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the projection of the vector onto the subspace. """ vector = vector.array basis = self.getBasis().array p = N.zeros(vector.shape, N.Float) for bv in basis: N.add(N.add.reduce(N.ravel(bv*vector))*bv, p, p) return ParticleProperties.ParticleVector(self.universe, p)
[docs] def projectionComplementOf(self, vector): """ :param vector: a particle vector :type vector: :class:`~MMTK.ParticleProperties.ParticleVector` :returns: the projection of the vector onto the orthogonal complement of the subspace. """ return vector - self.projectionOf(vector)
[docs] def complement(self): """ :returns: the orthogonal complement subspace :rtype: :class:`~MMTK.Subspace.Subspace` """ basis = [] for i in range(self.universe.numberOfAtoms()): for e in [ex, ey, ez]: p = ParticleProperties.ParticleVector(self.universe) p[i] = e basis.append(self.projectionComplementOf(p)) return Subspace(self.universe, basis)
[docs]class LinkedRigidBodyMotionSubspace(Subspace): """ Subspace for the motion of linked rigid bodies This class describes the same subspace as RigidBodyMotionSubspace, and is used by the latter. For collections of several chains of linked rigid bodies, RigidBodyMotionSubspace is more efficient. """ def __init__(self, universe, rigid_bodies): """ :param universe: the universe for which the subspace is created :type universe: :class:`~MMTK.Universe.Universe` :param rigid_bodies: a list or set of rigid bodies with some common atoms """ ex_ey_ez = [Vector(1.,0.,0.), Vector(0.,1.,0.), Vector(0.,0.,1.)] # Constructs # 1) a list of vectors describing the rigid-body motions of each # rigid body as if it were independent. # 2) a list of pair-distance constraint vectors for all pairs of # atoms inside a rigid body. # The LRB subspace is constructed from the projections of the # first set of vectors onto the orthogonal complement of the # subspace generated by the second set of vectors. vectors = [] c_vectors = [] for rb in rigid_bodies: atoms = rb.atomList() for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in atoms: v[a] = d vectors.append(v) if len(atoms) > 1: center = rb.centerOfMass() iv = len(vectors)-3 for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in atoms: v[a] = d.cross(a.position()-center) for vt in vectors[iv:]: v -= v.dotProduct(vt)*vt if v.dotProduct(v) > 0.: vectors.append(v) for a1, a2 in Utility.pairs(atoms): distance = universe.distanceVector(a1.position(), a2.position()) v = ParticleProperties.ParticleVector(universe) v[a1] = distance v[a2] = -distance c_vectors.append(v) if c_vectors: constraints = Subspace(universe, c_vectors) vectors = [constraints.projectionComplementOf(v) for v in vectors] Subspace.__init__(self, universe, vectors)
[docs]class RigidMotionSubspace(Subspace): """ Subspace of rigid-body motions A rigid-body motion subspace is the subspace which contains the rigid-body motions of any number of chemical objects. """ def __init__(self, universe, objects): """ :param universe: the universe for which the subspace is created :type universe: :class:`~MMTK.Universe.Universe` :param objects: a sequence of objects whose rigid-body motion is included in the subspace """ if not Utility.isSequenceObject(objects): objects = [objects] else: objects = copy.copy(objects) # Identify connected sets of linked rigid bodies and remove # them from the plain rigid body list. atom_map = {} for o in objects: for a in o.atomIterator(): am = atom_map.get(a, []) am.append(o) atom_map[a] = am rb_map = {} for rbs in atom_map.values(): if len(rbs) > 1: for rb in rbs: rb_map[rb] = rb_map.get(rb, frozenset()) \ .union(frozenset(rbs)) for rb in rb_map.keys(): objects.remove(rb) while True: changed = False for rbs in rb_map.values(): for rb in rbs: s = rb_map[rb] rb_map[rb] = s.union(rbs) if s != rb_map[rb]: changed = True if not changed: break lrbs = frozenset(rb_map.values()) # Generate the subspace vectors for the isolated rigid bodies. ex_ey_ez = [Vector(1.,0.,0.), Vector(0.,1.,0.), Vector(0.,0.,1.)] vectors = [] for o in objects: rb_atoms = o.atomList() for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in rb_atoms: v[a] = d vectors.append(v/N.sqrt(len(rb_atoms))) if len(rb_atoms) > 1: center = o.centerOfMass() iv = len(vectors)-3 for d in ex_ey_ez: v = ParticleProperties.ParticleVector(universe) for a in rb_atoms: v[a] = d.cross(a.position()-center) for vt in vectors[iv:]: v -= v.dotProduct(vt)*vt norm_sq = N.sqrt(v.dotProduct(v)) if norm_sq > 0.: vectors.append(v/norm_sq) # Generate the subspace vectors for the linked rigid bodies. for lrb in lrbs: lrb_ss = LinkedRigidBodyMotionSubspace(universe, lrb) for v in lrb_ss.getBasis(): vectors.append(v) Subspace.__init__(self, universe, vectors) # The vector set is already orthonormal by construction, # so we can skip the lengthy SVD procedure. self._basis = ParticleVectorSet(universe, len(vectors)) for i in range(len(vectors)): self._basis.array[i] = vectors[i].array
[docs]class PairDistanceSubspace(Subspace): """ Subspace of pair-distance motions A pair-distance motion subspace is the subspace which contains the relative motions of any number of atom pairs along their distance vector, e.g. bond elongation between two bonded atoms. """ def __init__(self, universe, atom_pairs): """ :param universe: the universe for which the subspace is created :type universe: :class:`~MMTK.Universe.Universe` :param atom_pairs: a sequence of atom pairs whose distance-vector motion is included in the subspace """ vectors = [] for atom1, atom2 in atom_pairs: v = ParticleProperties.ParticleVector(universe) distance = atom1.position()-atom2.position() v[atom1] = distance v[atom2] = -distance vectors.append(v) Subspace.__init__(self, universe, vectors)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Trajectory.html0000644000076600000240000067241312013143454021567 0ustar hinsenstaff00000000000000 MMTK.Trajectory — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Trajectory

# This module implements trajetories and trajectory generators.
#
# Written by Konrad Hinsen
#

"""
Trajectory files and their contents
"""

__docformat__ = 'restructuredtext'

from MMTK import Collections, Units, Universe, Utility, \
                 ParticleProperties, Visualization
from Scientific.Geometry import Vector
from Scientific import N
import copy, os, sys

# Report error if the netCDF module is not available.
try:
    from Scientific.IO import NetCDF
except ImportError:
    raise Utility.MMTKError("Trajectories are not available " +
                             "because the netCDF module is missing.")
#
# Trajectory class
#
[docs]class Trajectory(object): # # Trajectory cache # # This cache is maintained for better efficiency in parallel # processing. The cache contains all trajectories currently open # for reading. When a trajectory object is unpickled, a trajectory # from the cache is reused if possible. This means that """ Trajectory file The data in a trajectory file can be accessed by step or by variable. If t is a Trajectory object, then: * len(t) is the number of steps * t[i] is the data for step i, in the form of a dictionary that maps variable names to data * t[i:j] and t[i:j:n] return a :class:`~MMTK.Trajectory.SubTrajectory` object that refers to a subset of the total number of steps (no data is copied) * t.variable returns the value of the named variable at all time steps. If the variable is a simple scalar, it is read completely and returned as an array. If the variable contains data for each atom, a :class:`~MMTK.Trajectory.TrajectoryVariable` object is returned from which data at specific steps can be obtained by further indexing operations. The routines that generate trajectories decide what variables are used and what they contain. The most frequently used variable is "configuration", which stores the positions of all atoms. Other common variables are "time", "velocities", "temperature", "pressure", and various energy terms whose name end with "_energy". """ def __init__(self, object, filename, mode = 'r', comment = None, double_precision = False, cycle = 0, block_size = 1): """ :param object: the object whose data is stored in the trajectory file. This can be 'None' when opening a file for reading; in that case, a universe object is constructed from the description stored in the trajectory file. This universe object can be accessed via the attribute 'universe' of the trajectory object. :type object: :class:`~MMTK.ChemicalObjects.ChemicalObject` :param filename: the name of the trajectory file :type filename: str :param mode: one of "r" (read-only), "w" (create new file for writing), or "a" (append to existing file or create if the file does not exist) :type mode: str :param comment: optional comment that is stored in the file; allowed only with mode="r" :type comment: str :param double_precision: if True, data in the file is stored using double precision; default is single precision. Note that all I/O via trajectory objects is double precision; conversion from and to single precision file variables is handled automatically. :type double_precision: bool :param cycle: if non-zero, a trajectory is created for a fixed number of steps equal to the value of cycle, and these steps are used cyclically. This is meant for restart trajectories. :type cycle: int :param block_size: an optimization parameter that influences the file structure and the I/O performance for very large files. A block size of 1 is optimal for sequential access to configurations etc., whereas a block size equal to the number of steps is optimal for reading coordinates or scalar variables along the time axis. The default value is 1. Note that older MMTK releases always used a block size of 1 and cannot handle trajectories with different block sizes. :type block_size: int """ filename = os.path.expanduser(filename) self.filename = filename self.mode = mode if object is None and mode == 'r': file = NetCDF.NetCDFFile(filename, 'r') description = file.variables['description'][:].tostring() try: self.block_size = file.dimensions['minor_step_number'] except KeyError: self.block_size = 1 conf = None cell = None if self.block_size == 1: try: conf_var = file.variables['configuration'] conf = conf_var[0, :, :] except KeyError: pass try: cell = file.variables['box_size'][0, :] except KeyError: pass else: try: conf_var = file.variables['configuration'] conf = conf_var[0, :, :, 0] except KeyError: pass try: cell = file.variables['box_size'][0, :, 0] except KeyError: pass file.close() import Skeleton local = {} skeleton = eval(description, vars(Skeleton), local) universe = skeleton.make({}, conf) universe.setCellParameters(cell) object = universe initialize = 1 else: universe = object.universe() if universe is None: raise ValueError("objects not in the same universe") description = None initialize = 0 universe.configuration() if object is universe: index_map = None inverse_map = None else: if mode == 'r': raise ValueError("can't read trajectory for a non-universe") index_map = N.array([a.index for a in object.atomList()]) inverse_map = universe.numberOfPoints()*[None] for i in range(len(index_map)): inverse_map[index_map[i]] = i toplevel = set() for o in Collections.Collection(object): toplevel.add(o.topLevelChemicalObject()) object = Collections.Collection(list(toplevel)) if description is None: description = universe.description(object, inverse_map) import MMTK_trajectory self.trajectory = MMTK_trajectory.Trajectory(universe, description, index_map, filename, mode + 's', double_precision, cycle, block_size) self.universe = universe self.index_map = index_map try: self.block_size = \ self.trajectory.file.dimensions['minor_step_number'] except KeyError: self.block_size = 1 if comment is not None: if mode == 'r': raise IOError('cannot add comment in read-only mode') self.trajectory.file.comment = comment if initialize and conf is not None: self.universe.setFromTrajectory(self) self.particle_trajectory_reader = ParticleTrajectoryReader(self) def __getstate__(self): if self.mode != 'r': raise ValueError("Cannot copy or pickle write-mode trajectories") return self.filename def __setstate__(self, state): self.__init__(None, state)
[docs] def flush(self): """ Make sure that all data that has been written to the trajectory is also written to the file. """ self.trajectory.flush()
[docs] def close(self): """ Close the trajectory file. Must be called after writing to ensure that all buffered data is written to the file. No data access is possible after closing a file. """ self.trajectory.close()
def __len__(self): return self.trajectory.nsteps def __getitem__(self, item): if not isinstance(item, int): return SubTrajectory(self, N.arange(len(self)))[item] if item < 0: item += len(self) if item >= len(self): raise IndexError data = {} for name, var in self.trajectory.file.variables.items(): if 'step_number' not in var.dimensions: continue if 'atom_number' in var.dimensions: if 'xyz' in var.dimensions: array = ParticleProperties.ParticleVector(self.universe, self.trajectory.readParticleVector(name, item)) else: array = ParticleProperties.ParticleScalar(self.universe, self.trajectory.readParticleScalar(name, item)) else: bs = self.block_size if bs == 1: array = var[item] else: if len(var.shape) == 2: array = var[item/bs, item%bs] else: array = var[item/bs, ..., item%bs] data[name] = 0.+array if data.has_key('configuration'): box = data.get('box_size', None) if box is not None: box = box.astype(N.Float) conf = data['configuration'] data['configuration'] = \ ParticleProperties.Configuration(conf.universe, conf.array, box) return data def __getslice__(self, first, last): return self[(slice(first, last),)] def __getattr__(self, name): try: var = self.trajectory.file.variables[name] except KeyError: raise AttributeError("no variable named " + name) if 'atom_number' in var.dimensions: return TrajectoryVariable(self.universe, self, name) elif 'box_size_length' in var.dimensions: if 'minor_step_number' in var.dimensions: bs = N.transpose(var[:], [0, 2, 1]) bs = N.reshape(bs, (bs.shape[0]*bs.shape[1], bs.shape[2])) return bs[:len(self)] else: return var[:] else: return N.ravel(N.array(var))[:len(self)] def defaultStep(self): try: step = int(self.trajectory.file.last_step[0]) except AttributeError: step = 0 return step
[docs] def readParticleTrajectory(self, atom, first=0, last=None, skip=1, variable = "configuration"): """ Read trajectory information for a single atom but for multiple time steps. :param atom: the atom whose trajectory is requested :type atom: :class:`~MMTK.ChemicalObjects.Atom` :param first: the number of the first step to be read :type first: int :param last: the number of the first step not to be read. A value of None indicates that the whole trajectory should be read. :type last: int :param skip: the number of steps to skip between two steps read :type skip: int :param variable: the name of the trajectory variable to be read. If the variable is "configuration", the resulting trajectory is made continuous by eliminating all jumps caused by periodic boundary conditions. The pseudo-variable "box_coordinates" can be read to obtain the values of the variable "configuration" scaled to box coordinates. For non-periodic universes there is no difference between box coordinates and real coordinates. :type variable: str :returns: the trajectory for a single atom :rtype: :class:`~MMTK.Trajectory.ParticleTrajectory` """ return ParticleTrajectory(self, atom, first, last, skip, variable)
[docs] def readRigidBodyTrajectory(self, object, first=0, last=None, skip=1, reference = None): """ Read the positions for an object at multiple time steps and extract the rigid-body motion (center-of-mass position plus orientation as a quaternion) by an optimal-transformation fit. :param object: the object whose rigid-body trajectory is requested :type object: :class:`~MMTK.Collections.GroupOfAtoms` :param first: the number of the first step to be read :type first: int :param last: the number of the first step not to be read. A value of None indicates that the whole trajectory should be read. :type last: int :param skip: the number of steps to skip between two steps read :type skip: int :param reference: the reference configuration for the fit :type reference: :class:`~MMTK.ParticleProperties.Configuration` :returns: the trajectory for a single rigid body :rtype: :class:`~MMTK.Trajectory.RigidBodyTrajectory` """ return RigidBodyTrajectory(self, object, first, last, skip, reference)
[docs] def variables(self): """ :returns: a list of the names of all variables that are stored in the trajectory :rtype: list of str """ vars = copy.copy(self.trajectory.file.variables.keys()) vars.remove('step') try: vars.remove('description') except ValueError: pass return vars
[docs] def view(self, first=0, last=None, skip=1, object = None): """ Show an animation of the trajectory using an external visualization program. :param first: the number of the first step in the animation :type first: int :param last: the number of the first step not to include in the animation. A value of None indicates that the whole trajectory should be used. :type last: int :param skip: the number of steps to skip between two steps read :type skip: int :param object: the object to be animated, which must be in the universe stored in the trajectory. None stands for the whole universe. :type object: :class:`~MMTK.Collections.GroupOfAtoms` """ Visualization.viewTrajectory(self, first, last, skip, object)
def _boxTransformation(self, pt_in, pt_out, to_box=0): from MMTK_trajectory import boxTransformation try: box_size = self.trajectory.recently_read_box_size except AttributeError: return boxTransformation(self.universe._spec, pt_in, pt_out, box_size, to_box)
[docs]class SubTrajectory(object): """ Reference to a subset of a trajectory A SubTrajectory object is created by slicing a Trajectory object or another SubTrajectory object. It provides all the operations defined on Trajectory objects. """ def __init__(self, trajectory, indices): self.trajectory = trajectory self.indices = indices self.universe = trajectory.universe def __len__(self): return len(self.indices) def __getitem__(self, item): if isinstance(item, int): return self.trajectory[int(self.indices[item])] else: return SubTrajectory(self.trajectory, self.indices[item]) def __getslice__(self, first, last): return self[(slice(first, last),)] def __getattr__(self, name): return SubVariable(getattr(self.trajectory, name), self.indices) def readParticleTrajectory(self, atom, first=0, last=None, skip=1, variable = "configuration"): if last is None: last = len(self.indices) indices = self.indices[first:last:skip] first = indices[0] last = indices[-1]+1 if len(self.indices) > 1: skip = self.indices[1]-self.indices[0] else: skip = 1 return self.trajectory.readParticleTrajectory(atom, first, last, skip, variable) def readRigidBodyTrajectory(self, object, first=0, last=None, skip=1, reference = None): if last is None: last = len(self.indices) indices = self.indices[first:last:skip] first = indices[0] last = indices[-1]+1 if len(self.indices) > 1: skip = self.indices[1]-self.indices[0] else: skip = 1 return RigidBodyTrajectory(self.trajectory, object, first, last, skip, reference) def variables(self): return self.trajectory.variables() def view(self, first=0, last=None, step=1, subset = None): Visualization.viewTrajectory(self, first, last, step, subset) def close(self): del self.trajectory def _boxTransformation(self, pt_in, pt_out, to_box=0): Trajectory._boxTransformation(self.trajectory, pt_in, pt_out, to_box) # # Trajectory variables #
[docs]class TrajectoryVariable(object): """ Variable in a trajectory A TrajectoryVariable object is created by extracting a variable from a Trajectory object if that variable contains data for each atom and is thus potentially large. No data is read from the trajectory file when a TrajectoryVariable object is created; the read operation takes place when the TrajectoryVariable is indexed with a specific step number. If t is a TrajectoryVariable object, then: * len(t) is the number of steps * t[i] is the data for step i, in the form of a ParticleScalar, a ParticleVector, or a Configuration object, depending on the variable * t[i:j] and t[i:j:n] return a SubVariable object that refers to a subset of the total number of steps """ def __init__(self, universe, trajectory, name): self.universe = universe self.trajectory = trajectory self.name = name self.var = self.trajectory.trajectory.file.variables[self.name] if self.name == 'configuration': try: self.box_size = \ self.trajectory.trajectory.file.variables['box_size'] except KeyError: self.box_size = None def __len__(self): return len(self.trajectory) def __getitem__(self, item): if not isinstance(item, int): return SubVariable(self, N.arange(len(self)))[item] item = int(item) # gets rid of numpy.intXX objects if item < 0: item = item + len(self.trajectory) if item >= len(self.trajectory): raise IndexError if self.name == 'configuration': if self.box_size is None: box = None elif len(self.box_size.shape) == 3: bs = self.trajectory.block_size box = self.box_size[item/bs, :, item%bs].astype(N.Float) else: box = self.box_size[item].astype(N.Float) array = ParticleProperties.Configuration(self.universe, self.trajectory.trajectory.readParticleVector(self.name, item), box) elif 'xyz' in self.var.dimensions: array = ParticleProperties.ParticleVector(self.universe, self.trajectory.trajectory.readParticleVector(self.name, item)) else: array = ParticleProperties.ParticleScalar(self.universe, self.trajectory.trajectory.readParticleScalar(self.name, item)) return array def __getslice__(self, first, last): return self[(slice(first, last),)] def average(self): sum = self[0] for value in self[1:]: sum = sum + value return sum/len(self)
[docs]class SubVariable(TrajectoryVariable): """ Reference to a subset of a :class:`~MMTK.Trajectory.TrajectoryVariable` A SubVariable object is created by slicing a TrajectoryVariable object or another SubVariable object. It provides all the operations defined on TrajectoryVariable objects. """ def __init__(self, variable, indices): self.variable = variable self.indices = indices def __len__(self): return len(self.indices) def __getitem__(self, item): if isinstance(item, int): return self.variable[self.indices[item]] else: return SubVariable(self.variable, self.indices[item]) def __getslice__(self, first, last): return self[(slice(first, last),)] # # Trajectory consisting of multiple files #
[docs]class TrajectorySet(object): """ Trajectory file set A TrajectorySet permits to treat a sequence of trajectory files like a single trajectory for reading data. It behaves exactly like a :class:`~MMTK.Trajectory.Trajectory` object. The trajectory files must all contain data for the same system. The variables stored in the individual files need not be the same, but only variables common to all files can be accessed. Note: depending on how the sequence of trajectories was constructed, the first configuration of each trajectory might be the same as the last one in the preceding trajectory. To avoid counting it twice, specify (filename, 1, None, 1) for all but the first trajectory in the set. """ def __init__(self, object, filenames): """ :param object: the object whose data is stored in the trajectory files. This can be (and usually is) None; in that case, a universe object is constructed from the description stored in the first trajectory file. This universe object can be accessed via the attribute universe of the trajectory set object. :param filenames: a list of trajectory file names or (filename, first_step, last_step, increment) tuples. """ first = filenames[0] if isinstance(first, tuple): first = Trajectory(object, first[0])[first[1]:first[2]:first[3]] else: first = Trajectory(object, first) self.universe = first.universe self.trajectories = [first] self.nsteps = [0, len(first)] self.cell_parameters = [] for file in filenames[1:]: if isinstance(file, tuple): t = Trajectory(self.universe, file[0])[file[1]:file[2]:file[3]] else: t = Trajectory(self.universe, file) self.trajectories.append(t) self.nsteps.append(self.nsteps[-1]+len(t)) try: self.cell_parameters.append(t[0]['box_size']) except KeyError: pass vars = {} for t in self.trajectories: for v in t.variables(): vars[v] = vars.get(v, 0) + 1 self.vars = [] for v, count in vars.items(): if count == len(self.trajectories): self.vars.append(v) def close(self): for t in self.trajectories: t.close() def __len__(self): return self.nsteps[-1] def __getitem__(self, item): if not isinstance(item, int): return SubTrajectory(self, N.arange(len(self)))[item] if item >= len(self): raise IndexError tindex = N.add.reduce(N.greater_equal(item, self.nsteps))-1 return self.trajectories[tindex][item-self.nsteps[tindex]] def __getslice__(self, first, last): return self[(slice(first, last),)] def __getattr__(self, name): if name not in self.vars+['step']: raise AttributeError("no variable named " + name) var = self.trajectories[0].trajectory.file.variables[name] if 'atom_number' in var.dimensions: return TrajectorySetVariable(self.universe, self, name) else: data = [] for t in self.trajectories: var = t.trajectory.file.variables[name] data.append(N.ravel(N.array(var))[:len(t)]) return N.concatenate(data) def readParticleTrajectory(self, atom, first=0, last=None, skip=1, variable = "configuration"): total = None self.steps_read = [] for i in range(len(self.trajectories)): if self.nsteps[i+1] <= first: self.steps_read.append(0) continue if last is not None and self.nsteps[i] >= last: break n = max(0, (self.nsteps[i]-first+skip-1)/skip) start = first+skip*n-self.nsteps[i] n = (self.nsteps[i+1]-first+skip-1)/skip stop = first+skip*n if last is not None: stop = min(stop, last) stop = stop-self.nsteps[i] if start >= 0 and start < self.nsteps[i+1]-self.nsteps[i]: t = self.trajectories[i] pt = t.readParticleTrajectory(atom, start, stop, skip, variable) self.steps_read.append((stop-start)/skip) if total is None: total = pt else: if variable == "configuration" \ and self.cell_parameters[0] is not None: jump = pt.array[0]-total.array[-1] mult = -(jump/self.cell_parameters[i-1]).astype('i') if len(N.nonzero(mult)) > 0: t._boxTransformation(pt.array, pt.array, 1) N.add(pt.array, mult[N.NewAxis, : ], pt.array) t._boxTransformation(pt.array, pt.array, 0) jump = pt.array[0] - total.array[-1] mask = N.less(jump, -0.5*self.cell_parameters[i-1])- \ N.greater(jump, 0.5*self.cell_parameters[i-1]) if len(N.nonzero(mask)) > 0: t._boxTransformation(pt.array, pt.array, 1) N.add(pt.array, mask[N.NewAxis, :], pt.array) t._boxTransformation(pt.array, pt.array, 0) elif variable == "box_coordinates" \ and self.cell_parameters[0] is not None: jump = pt.array[0]-total.array[-1] mult = -jump.astype('i') if len(N.nonzero(mult)) > 0: N.add(pt.array, mult[N.NewAxis, : ], pt.array) jump = pt.array[0] - total.array[-1] mask = N.less(jump, -0.5)- \ N.greater(jump, 0.5) if len(N.nonzero(mask)) > 0: N.add(pt.array, mask[N.NewAxis, :], pt.array) total.array = N.concatenate((total.array, pt.array)) else: self.steps_read.append(0) return total def readRigidBodyTrajectory(self, object, first=0, last=None, skip=1, reference = None): return RigidBodyTrajectory(self, object, first, last, skip, reference) def _boxTransformation(self, pt_in, pt_out, to_box=0): n = 0 for i in range(len(self.steps_read)): t = self.trajectories[i] steps = self.steps_read[i] if steps > 0: t._boxTransformation(pt_in[n:n+steps], pt_out[n:n+steps], to_box) n = n + steps def variables(self): return self.vars def view(self, first=0, last=None, step=1, object = None): Visualization.viewTrajectory(self, first, last, step, object)
[docs]class TrajectorySetVariable(TrajectoryVariable): """ Variable in a trajectory set A TrajectorySetVariable object is created by extracting a variable from a TrajectorySet object if that variable contains data for each atom and is thus potentially large. It behaves exactly like a TrajectoryVariable object. """ def __init__(self, universe, trajectory_set, name): self.universe = universe self.trajectory_set = trajectory_set self.name = name def __len__(self): return len(self.trajectory_set) def __getitem__(self, item): if not isinstance(item, int): return SubVariable(self, N.arange(len(self)))[item] if item >= len(self.trajectory_set): raise IndexError tindex = N.add.reduce(N.greater_equal(item, self.trajectory_set.nsteps))-1 step = item-self.trajectory_set.nsteps[tindex] t = self.trajectory_set.trajectories[tindex] return getattr(t, self.name)[step] # # Cache for atom trajectories #
class ParticleTrajectoryReader(object): def __init__(self, trajectory): self.trajectory = trajectory self.natoms = self.trajectory.universe.numberOfAtoms() self._trajectory = trajectory.trajectory self.cache = {} self.cache_lifetime = 2 def __call__(self, atom, variable, first, last, skip, correct, box): if isinstance(atom, int): index = atom else: index = atom.index if atom.universe() is not self.trajectory.universe: raise ValueError("objects not in the same universe") key = (index, variable, first, last, skip, correct, box) data, count = self.cache.get(key, (None, 0)) if data is not None: self.cache[key] = (data, self.cache_lifetime) return data delete = [] for k, value in self.cache.items(): data, count = value count -= 1 if count == 0: delete.append(k) else: self.cache[k] = (data, count) for k in delete: del self.cache[k] cache_size = min(10, max(1, 100000/max(1, len(self.trajectory)))) natoms = min(cache_size, self.natoms-index) data = self._trajectory.readParticleTrajectories(index, natoms, variable, first, last, skip, correct, box) for i in range(natoms): key = (index+i, variable, first, last, skip, correct, box) self.cache[key] = (data[i], self.cache_lifetime) return data[0] # # Single-atom trajectory #
[docs]class ParticleTrajectory(object): """ Trajectory data for a single particle A ParticleTrajectory object is created by calling the method :func:`~MMTK.Trajectory.Trajectory.readParticleTrajectory` on a :class:`~MMTK.Trajectory.Trajectory` object. If pt is a ParticleTrajectory object, then * len(pt) is the number of steps stored in it * pt[i] is the value at step i (a vector) """ def __init__(self, trajectory, atom, first=0, last=None, skip=1, variable = "configuration"): if last is None: last = len(trajectory) if variable == "box_coordinates": variable = "configuration" box = 1 else: box = 0 reader = trajectory.particle_trajectory_reader self.array = reader(atom, variable, first, last, skip, variable == "configuration", box) def __len__(self): return self.array.shape[0] def __getitem__(self, index): return Vector(self.array[index])
[docs] def translateBy(self, vector): """ Adds a vector to the values at all steps. This does B{not} change the data in the trajectory file. :param vector: the vector to be added :type vector: Scientific.Geometry.Vector """ N.add(self.array, vector.array[N.NewAxis, :], self.array) # # Rigid-body trajectory #
[docs]class RigidBodyTrajectory(object): """ Rigid-body trajectory data A RigidBodyTrajectory object is created by calling the method :func:`~MMTK.Trajectory.Trajectory.readRigidBodyTrajectory` on a :class:`~MMTK.Trajectory.Trajectory` object. If rbt is a RigidBodyTrajectory object, then * len(rbt) is the number of steps stored in it * rbt[i] is the value at step i (a vector for the center of mass and a quaternion for the orientation) """ def __init__(self, trajectory, object, first=0, last=None, skip=1, reference = None): self.trajectory = trajectory universe = trajectory.universe if last is None: last = len(trajectory) first_conf = trajectory.configuration[first] offset = universe.contiguousObjectOffset([object], first_conf, True) if reference is None: reference = first_conf reference = universe.contiguousObjectConfiguration([object], reference) steps = (last-first+skip-1)/skip mass = object.mass() ref_cms = object.centerOfMass(reference) atoms = object.atomList() possq = N.zeros((steps,), N.Float) cross = N.zeros((steps, 3, 3), N.Float) rcms = N.zeros((steps, 3), N.Float) # cms of the CONTIGUOUS object made of CONTINUOUS atom trajectories for a in atoms: r = trajectory.readParticleTrajectory(a, first, last, skip, "box_coordinates").array w = a._mass/mass N.add(rcms, w*r, rcms) if offset is not None: N.add(rcms, w*offset[a].array, rcms) # relative coords of the CONTIGUOUS reference r_ref = N.zeros((len(atoms), 3), N.Float) for a in range(len(atoms)): r_ref[a] = atoms[a].position(reference).array - ref_cms.array # main loop: storing data needed to fill M matrix for a in range(len(atoms)): r = trajectory.readParticleTrajectory(atoms[a], first, last, skip, "box_coordinates").array r = r - rcms # (a-b)**2 != a**2 - b**2 if offset is not None: N.add(r, offset[atoms[a]].array,r) trajectory._boxTransformation(r, r) w = atoms[a]._mass/mass N.add(possq, w*N.add.reduce(r*r, -1), possq) N.add(possq, w*N.add.reduce(r_ref[a]*r_ref[a],-1), possq) N.add(cross, w*r[:,:,N.NewAxis]*r_ref[N.NewAxis, a,:],cross) self.trajectory._boxTransformation(rcms, rcms) # filling matrix M (formula no 40) k = N.zeros((steps, 4, 4), N.Float) k[:, 0, 0] = -cross[:, 0, 0]-cross[:, 1, 1]-cross[:, 2, 2] k[:, 0, 1] = cross[:, 1, 2]-cross[:, 2, 1] k[:, 0, 2] = cross[:, 2, 0]-cross[:, 0, 2] k[:, 0, 3] = cross[:, 0, 1]-cross[:, 1, 0] k[:, 1, 1] = -cross[:, 0, 0]+cross[:, 1, 1]+cross[:, 2, 2] k[:, 1, 2] = -cross[:, 0, 1]-cross[:, 1, 0] k[:, 1, 3] = -cross[:, 0, 2]-cross[:, 2, 0] k[:, 2, 2] = cross[:, 0, 0]-cross[:, 1, 1]+cross[:, 2, 2] k[:, 2, 3] = -cross[:, 1, 2]-cross[:, 2, 1] k[:, 3, 3] = cross[:, 0, 0]+cross[:, 1, 1]-cross[:, 2, 2] del cross for i in range(1, 4): for j in range(i): k[:, i, j] = k[:, j, i] N.multiply(k, 2., k) for i in range(4): N.add(k[:,i,i], possq, k[:,i,i]) del possq quaternions = N.zeros((steps, 4), N.Float) fit = N.zeros((steps,), N.Float) from Scientific.LA import eigenvectors for i in range(steps): e, v = eigenvectors(k[i]) j = N.argmin(e) if e[j] < 0.: fit[i] = 0. else: fit[i] = N.sqrt(e[j]) if v[j,0] < 0.: quaternions[i] = -v[j] # eliminate jumps else: quaternions[i] = v[j] self.fit = fit self.cms = rcms self.quaternions = quaternions def __len__(self): return self.cms.shape[0] def __getitem__(self, index): from Scientific.Geometry.Quaternion import Quaternion return Vector(self.cms[index]), Quaternion(self.quaternions[index]) # # Type check for trajectory objects #
[docs]def isTrajectory(object): """ :param object: any Python object :returns: True if object is a trajectory """ import MMTK_trajectory return isinstance(object, (Trajectory, MMTK_trajectory.trajectory_type)) # # Base class for all objects that generate trajectories #
[docs]class TrajectoryGenerator(object): """ Trajectory generator base class This base class implements the common aspects of everything that generates trajectories: integrators, minimizers, etc. """ def __init__(self, universe, options): self.universe = universe self.options = options def setCallOptions(self, options): self.call_options = options def getActions(self): try: self.actions = self.getOption('actions') except ValueError: self.actions = [] try: if self.getOption('background'): import MMTK_state_accessor self.state_accessor = MMTK_state_accessor.StateAccessor() self.actions.append(self.state_accessor) except ValueError: pass try: steps = self.getOption('steps') except ValueError: steps = None return map(lambda a, t=self, s=steps: a.getSpecificationList(t, s), self.actions) def cleanupActions(self): for a in self.actions: a.cleanup() def getOption(self, option): try: value = self.call_options[option] except KeyError: try: value = self.options[option] except KeyError: try: value = self.default_options[option] except KeyError: raise ValueError('undefined option: ' + option) return value def optionString(self, options): s = '' for o in options: s = s + o + '=' + `self.getOption(o)` + ', ' return s[:-2] def run(self, function, args): if self.getOption('background'): import ThreadManager return ThreadManager.TrajectoryGeneratorThread(self.universe, function, args, self.state_accessor) else: apply(function, args) # # Trajectory action base class #
[docs]class TrajectoryAction(object): """ Trajectory action base class Subclasses of this base class implement the actions that can be inserted into trajectory generation at regular intervals. """ def __init__(self, first, last, skip): self.first = first self.last = last self.skip = skip spec_type = 'function' def _getSpecificationList(self, trajectory_generator, steps): first = self.first last = self.last if first < 0: first = first + steps if last is None: import MMTK_trajectory last = MMTK_trajectory.maxint elif last < 0: last = last + steps+1 return (self.spec_type, first, last, self.skip) def getSpecificationList(self, trajectory_generator, steps): return self._getSpecificationList(trajectory_generator, steps) \ + (self.Cfunction, self.parameters) def cleanup(self): pass
[docs]class TrajectoryOutput(TrajectoryAction): """ Trajectory output action A TrajectoryOutput object can be used in the action list of any trajectory-generating operation. It writes any of the available data to a trajectory file. It is possible to use several TrajectoryOutput objects at the same time in order to produce multiple trajectories from a single run. """ def __init__(self, trajectory, data = None, first=0, last=None, skip=1): """ :param trajectory: a trajectory object or a string, which is interpreted as the name of a file that is opened as a trajectory in append mode :param data: a list of data categories. All variables provided by the trajectory generator that fall in any of the listed categories are written to the trajectory file. See the descriptions of the trajectory generators for a list of variables and categories. By default (data = None) the categories "configuration", "energy", "thermodynamic", and "time" are written. :param first: the number of the first step at which the action is run :type first: int :param last: the number of the step at which the action is suspended. A value of None indicates that the action should be applied indefinitely. :type last: int :param skip: the number of steps to skip between two action runs :type skip: int """ TrajectoryAction.__init__(self, first, last, skip) self.destination = trajectory self.categories = data self.must_be_closed = None spec_type = 'trajectory' def getSpecificationList(self, trajectory_generator, steps): if type(self.destination) == type(''): destination = self._setupDestination(self.destination, trajectory_generator.universe) else: destination = self.destination if self.categories is None: categories = self._defaultCategories(trajectory_generator) else: if self.categories == 'all' or self.categories == ['all']: categories = trajectory_generator.available_data else: categories = self.categories for item in categories: if item not in trajectory_generator.available_data: raise ValueError('data item %s is not available' % item) return self._getSpecificationList(trajectory_generator, steps) \ + (destination, categories) def _setupDestination(self, destination, universe): self.must_be_closed = Trajectory(universe, destination, 'a') return self.must_be_closed def cleanup(self): if self.must_be_closed is not None: self.must_be_closed.close() def _defaultCategories(self, trajectory_generator): available = trajectory_generator.available_data return tuple(filter(lambda x, a=available: x in a, self.default_data)) default_data = ['configuration', 'energy', 'thermodynamic', 'time']
[docs]class RestartTrajectoryOutput(TrajectoryOutput): """ Restart trajectory output action A RestartTrajectoryOutput object is used in the action list of any trajectory-generating operation. It writes those variables to a trajectory that the trajectory generator declares as necessary for restarting. """ def __init__(self, trajectory, skip=100, length=3): """ :param trajectory: a trajectory object or a string, which is interpreted as the name of a file that is opened as a trajectory in append mode with a cycle length of length and double-precision variables :param skip: the number of steps between two write operations to the restart trajectory :type skip: int :param length: the number of steps stored in the restart trajectory; used only if trajectory is a string """ TrajectoryAction.__init__(self, 0, None, skip) self.destination = trajectory self.categories = None self.length = length def _setupDestination(self, destination, universe): self.must_be_closed = Trajectory(universe, destination, 'a', 'Restart trajectory', 1, self.length) return self.must_be_closed def _defaultCategories(self, trajectory_generator): if trajectory_generator.restart_data is None: raise ValueError("Trajectory generator does not permit restart") return trajectory_generator.restart_data
[docs]class LogOutput(TrajectoryOutput): """ Protocol file output action A LogOutput object can be used in the action list of any trajectory-generating operation. It writes any of the available data to a text file. """ def __init__(self, file, data = None, first=0, last=None, skip=1): """ :param file: a file object or a string, which is interpreted as the name of a file that is opened in write mode :param data: a list of data categories. All variables provided by the trajectory generator that fall in any of the listed categories are written to the trajectory file. See the descriptions of the trajectory generators for a list of variables and categories. By default (data = None) the categories "configuration", "energy", "thermodynamic", and "time" are written. :param first: the number of the first step at which the action is run :type first: int :param last: the number of the step at which the action is suspended. A value of None indicates that the action should be applied indefinitely. :type last: int :param skip: the number of steps to skip between two action runs :type skip: int """ TrajectoryOutput.__init__(self, file, data, first, last, skip) def _setupDestination(self, destination, universe): self.must_be_closed = open(destination, 'w') return self.must_be_closed spec_type = 'print' default_data = ['energy', 'time']
[docs]class StandardLogOutput(LogOutput): """ Standard protocol output action A StandardLogOutput object can be used in the action list of any trajectory-generating operation. It is a specialization of LogOutput to the most common case and writes data in the categories "time" and "energy" to the standard output stream. :param skip: the number of steps to skip between two action runs :type skip: int """ def __init__(self, skip=50): LogOutput.__init__(self, sys.stdout, None, 0, None, skip) # # Snapshot generator #
[docs]class SnapshotGenerator(TrajectoryGenerator): """ Trajectory generator for single steps A SnapshotGenerator is used for manual assembly of trajectory files. At each call it writes one step to the trajectory, using the current state of the universe (configuration, velocities, etc.) and data provided explicitly with the call. Each call to the SnapshotGenerator object produces one step. All the keyword options can be specified either when creating the generator or when calling it. """ def __init__(self, universe, **options): """ :param universe: the universe on which the generator acts :keyword data: a dictionary that supplies values for variables that are not part of the universe state (e.g. potential energy) :keyword actions: a list of actions to be executed periodically (default is none) """ TrajectoryGenerator.__init__(self, universe, options) self.available_data = [] try: e, g = self.universe.energyAndGradients() except: pass else: self.available_data.append('energy') self.available_data.append('gradients') try: self.universe.configuration() self.available_data.append('configuration') except: pass if self.universe.cellVolume() is not None: self.available_data.append('thermodynamic') if self.universe.velocities() is not None: self.available_data.append('velocities') self.available_data.append('energy') self.available_data.append('thermodynamic') default_options = {'steps': 0, 'actions': []} def __call__(self, **options): self.setCallOptions(options) from MMTK_trajectory import snapshot data = copy.copy(options.get('data', {})) energy_terms = 0 for name in data.keys(): if name == 'time' and 'time' not in self.available_data: self.available_data.append('time') if name[-7:] == '_energy': energy_terms = energy_terms + 1 if 'energy' not in self.available_data: self.available_data.append('energy') if (name == 'temperature' or name == 'pressure') \ and 'thermodynamic' not in self.available_data: self.available_data.append('thermodynamic') if name == 'gradients' and 'gradients' not in self.available_data: self.available_data.append('gradients') actions = self.getActions() for action in actions: categories = action[-1] for c in categories: if c == 'energy' and not data.has_key('kinetic_energy'): v = self.universe.velocities() if v is not None: m = self.universe.masses() e = (v*v*m*0.5).sumOverParticles() data['kinetic_energy'] = e df = self.universe.degreesOfFreedom() data['temperature'] = 2.*e/df/Units.k_B/Units.K if c == 'configuration': if data.has_key('configuration'): data['configuration'] = data['configuration'].array else: data['configuration'] = \ self.universe.configuration().array if c == 'velocities': if data.has_key('velocities'): data['velocities'] = data['velocities'].array else: data['velocities'] = self.universe.velocities().array if c == 'gradients': if data.has_key('gradients'): data['gradients'] = data['gradients'].array p = self.universe.cellParameters() if p is not None: data['box_size'] = p volume = self.universe.cellVolume() if volume is not None: data['volume'] = volume try: m = self.universe.masses() data['masses'] = m.array except: pass snapshot(self.universe, data, actions, energy_terms) # # Trajectory reader (not yet functional...) #
if False: class TrajectoryReader(TrajectoryGenerator): def __init__(self, trajectory, options): TrajectoryGenerator.__init__(self, trajectory.universe, options) self.input = trajectory self.available_data = trajectory.variables() default_options = {'trajectory': None, 'log': None, 'options': []} def __call__(self, **options): self.setCallOptions(options) from MMTK_trajectory import readTrajectory readTrajectory(self.universe, self.input.trajectory, [self.getOption('trajectory'), self.getOption('log')] + self.getOption('options')) # # Print information about trajectory file #
[docs]def trajectoryInfo(filename): """ :param filename: the name of a trajectory file :type filename: str :returns: a string with summarial information about the trajectory """ from Scientific.IO import NetCDF file = NetCDF.NetCDFFile(filename, 'r') nsteps = file.variables['step'].shape[0] if 'minor_step_number' in file.dimensions.keys(): nsteps = nsteps*file.variables['step'].shape[1] s = 'Information about trajectory file ' + filename + ':\n' try: s += file.comment + '\n' except AttributeError: pass s += `file.dimensions['atom_number']` + ' atoms\n' s += `nsteps` + ' steps\n' s += file.history file.close() return s
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Universe.html0000644000076600000240000074256612013143455021251 0ustar hinsenstaff00000000000000 MMTK.Universe — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Universe

# This module implements the various types of universes
# (infinite, periodic etc.). A universe defines the
# geometry of space, the force field, and external interactions
# (boundary conditions, external fields, etc.)
#
# Written by Konrad Hinsen
#

"""
Universes
"""

__docformat__ = 'restructuredtext'

from MMTK import Bonds, ChemicalObjects, Collections, Environment, \
                 Random, Utility, ParticleProperties, Visualization
from Scientific.Geometry import Transformation
from Scientific.Geometry import Vector, isVector
from Scientific import N
import copy

try:
    import threading
    if not hasattr(threading, 'Thread'):
        threading = None
except ImportError:
    threading = None

#
# The base class for all universes.
#
[docs]class Universe(Collections.GroupOfAtoms, Visualization.Viewable): """ Universe A universe represents a complete model of a chemical system, i.e. the molecules, their environment (topology, boundary conditions, thermostats, etc.), and optionally a force field. The class Universe is an abstract base class that defines properties common to all kinds of universes. To create universe objects, use one of its subclasses. In addition to the methods listed below, universe objects support the following operations (u is any universe object, o is any chemical object): * len(u) yields the number of chemical objects in the universe * u[i] returns object number i * u.name = o adds o to the universe and also makes it accessible as an attribute * del u.name removes the object that was assigned to u.name from the universe """ def __init__(self, forcefield, properties): self._forcefield = forcefield self._evaluator = {} self.name = '' if properties.has_key('name'): self.name = properties['name'] del properties['name'] self._objects = Collections.Collection() self._environment = [] self._configuration = None self._masses = None self._atom_properties = {} self._atoms = None self._bond_database = None self._bond_pairs = None self._version = 0 self._np = None is_universe = True is_periodic = False is_orthogonal = False def __getstate__(self): state = copy.copy(self.__dict__) state['_evaluator'] = {} state['_configuration'] = None del state['_masses'] del state['_bond_database'] del state['_bond_pairs'] del state['_np'] del state['_spec'] return state def __setstate__(self, state): state['_np'] = None state['_atoms'] = None state['_bond_database'] = None state['_bond_pairs'] = None self.__dict__['_environment'] = [] if state.has_key('atom_properties'): self.__dict__['_atom_properties'] = state['atom_properties'] del state['atom_properties'] for attr, value in state.items(): self.__dict__[attr] = value self._evaluator = {} self._masses = None self._createSpec() def __len__(self): return len(self._objects) def __getitem__(self, item): return self._objects[item] def __setattr__(self, attr, value): if attr[0] != '_' and self.__dict__.has_key(attr): try: self.removeObject(self.__dict__[attr]) except ValueError: pass self.__dict__[attr] = value if attr[0] != '_' and (ChemicalObjects.isChemicalObject(value) or Environment.isEnvironmentObject(value)): self.addObject(value) def __delattr__(self, attr): try: self.removeObject(self.__dict__[attr]) except ValueError: pass del self.__dict__[attr] def __repr__(self): return self.__class__.__name__ + ' ' + self.name + ' containing ' + \ `len(self._objects)` + ' objects.' __str__ = __repr__ def __copy__(self): return copy.deepcopy(self)
[docs] def objectList(self, klass = None): """ :param klass: an optional class argument :type klass: class :returns: a list of all chemical objects in the universe. If klass is given, only objects that are instances of klass are returned. :rtype: list """ return self._objects.objectList(klass)
[docs] def environmentObjectList(self, klass = None): """ :param klass: an optional class argument :type klass: class :returns: a list of all environment objects in the universe. If klass is given, only objects that are instances of klass are returned. :rtype: list """ if klass is None: return self._environment else: return filter(lambda o, k=klass: o.__class__ is k, self._environment)
[docs] def atomList(self): """ :returns: a list of all atoms in the universe :rtype: list """ if self._atoms is None: self._atoms = self._objects.atomList() return self._atoms
def atomIterator(self): return self._objects.atomIterator() def bondedUnits(self): return self._objects.bondedUnits()
[docs] def universe(self): """ :returns: the universe itself """ return self
[docs] def addObject(self, object, steal = False): """ Adds object to the universe. If object is a Collection, all elements of the Collection are added to the universe. :param object: the object (chemical or environment) to be added :param steal: if True, permit stealing the object from another universe, otherwise the object must not yet be attached to any universe. :type steal: bool """ if ChemicalObjects.isChemicalObject(object): if (not steal) and object.parent is not None: if isUniverse(object.parent): raise ValueError(`object` + ' is already in another universe') else: raise ValueError(`object` + ' is part of another object') object.parent = self self._objects.addObject(object) self._changed(True) elif Environment.isEnvironmentObject(object): for o in self._environment: o.checkCompatibilityWith(object) self._environment.append(object) self._changed(False) elif Collections.isCollection(object) \ or Utility.isSequenceObject(object): for o in object: self.addObject(o, steal) else: raise TypeError(repr(object) + ' cannot be added to a universe')
[docs] def removeObject(self, object): """ Removes object from the universe. If object is a Collection, each of its elements is removed. The object to be removed must be in the universe. :param object: the object (chemical or environment) to be removed """ if ChemicalObjects.isChemicalObject(object): if object.parent != self: raise ValueError(`object` + ' is not in this universe.') object.parent = None self._objects.removeObject(object) self._changed(True) elif Collections.isCollection(object) \ or (Utility.isSequenceObject(object) # Strings are nasty because their elements are strings # as well. This creates infinite recursion without # this special-case handling. and not isinstance(object, basestring)): for o in object: self.removeObject(o) elif Environment.isEnvironmentObject(object): self._environment.remove(object) self._changed(False) else: raise ValueError(`object` + ' is not in this universe.')
[docs] def selectShell(self, point, r1, r2=0.): """ :param point: a point in space :type point: Scientific.Geometry.Vector :param r1: one of the radii of a spherical shell :type r1: float :param r2: the other of the two radii of a spherical shell :type r2: float :returns: a Collection of all objects in the universe whose distance from point lies between r1 and r2. """ return self._objects.selectShell(point, r1, r2)
[docs] def selectBox(self, p1, p2): """ :param p1: one corner of a box in space :type p1: Scientific.Geometry.Vector :param p2: the other corner of a box in space :type p2: Scientific.Geometry.Vector :returns: a Collection of all objects in the universe that lie within the box whose diagonally opposite corners are given by p1 and p2. """ return self._objects.selectBox(p1, p2)
def _changed(self, system_size_changed): self._evaluator = {} self._bond_database = None self._version += 1 if system_size_changed: if self._configuration is not None: for a in self.atomList(): a.unsetArray() self._configuration = None self._masses = None self._atom_properties = {} self._atoms = None self._np = None self._bond_pairs = None else: if self._configuration is not None: self._configuration.version = self._version if self._masses is not None: self._masses.version = self._version
[docs] def acquireReadStateLock(self): """ Acquire the universe read state lock. Any application that uses threading must acquire this lock prior to accessing the current state of the universe, in particular its configuration (particle positions). This guarantees the consistency of the data; while any thread holds the read state lock, no other thread can obtain the write state lock that permits modifying the state. The read state lock should be released as soon as possible. The read state lock can be acquired only if no thread holds the write state lock. If the read state lock cannot be acquired immediately, the thread will be blocked until it becomes available. Any number of threads can acquire the read state lock simultaneously. """ return self._spec.stateLock(1)
[docs] def acquireWriteStateLock(self): """ Acquire the universe write state lock. Any application that uses threading must acquire this lock prior to modifying the current state of the universe, in particular its configuration (particle positions). This guarantees the consistency of the data; while any thread holds the write state lock, no other thread can obtain the read state lock that permits accessing the state. The write state lock should be released as soon as possible. The write state lock can be acquired only if no other thread holds either the read state lock or the write state lock. If the write state lock cannot be acquired immediately, the thread will be blocked until it becomes available. """ return self._spec.stateLock(-1)
[docs] def releaseReadStateLock(self, write=False): """ Release the universe read state lock. """ return self._spec.stateLock(2)
[docs] def releaseWriteStateLock(self, write=False): """ Release the universe write state lock. """ return self._spec.stateLock(-2)
[docs] def acquireConfigurationChangeLock(self, waitflag=True): """ Acquire the configuration change lock. This lock should be acquired before starting an algorithm that changes the configuration continuously, e.g. minimization or molecular dynamics algorithms. This guarantees the proper order of execution when several such operations are started in succession. For example, when a minimization should be followed by a dynamics run, the use of this flag permits both operations to be started as background tasks which will be executed one after the other, permitting other threads to run in parallel. The configuration change lock should not be confused with the universe state lock. The former guarantees the proper sequence of long-running algorithms, whereas the latter guarantees the consistency of the data. A dynamics algorithm, for example, keeps the configuration change lock from the beginning to the end, but acquires the universe state lock only immediately before modifying configuration and velocities, and releases it immediately afterwards. :param waitflag: if true, the method waits until the lock becomes available; this is the most common mode. If false, the method returns immediately even if another thread holds the lock. :type waitflag: bool :returns: a flag indicating if the lock was successfully acquired (1) or not (0). :rtype: int """ if waitflag: return self._spec.configurationChangeLock(1) else: return self._spec.configurationChangeLock(0)
[docs] def releaseConfigurationChangeLock(self): """ Releases the configuration change lock. """ self._spec.configurationChangeLock(2)
[docs] def setForceField(self, forcefield): """ :param forcefield: the new forcefield for this universe :type forcefield: :class:`~MMTK.ForceFields.ForceField.ForceField` """ self._forcefield = forcefield self._evaluator = {} self._bond_database = None
def position(self, object, conf): if ChemicalObjects.isChemicalObject(object): return object.position(conf) elif isVector(object): return object else: return Vector(object) def numberOfAtoms(self): return self._objects.numberOfAtoms() def numberOfPoints(self): if self._np is None: self._np = Collections.GroupOfAtoms.numberOfPoints(self) return self._np numberOfCartesianCoordinates = numberOfPoints
[docs] def configuration(self): """ :returns: the configuration object describing the current configuration of the universe. Note that this is not a copy of the current state, but a reference: the positions in the configuration object will change when coordinate changes are applied to the universe in whatever way. :rtype: :class:`~MMTK.ParticleProperties.Configuration` """ if self._configuration is None: np = self.numberOfAtoms() coordinates = N.zeros((np, 3), N.Float) index_map = {} redef = [] for a in self.atomList(): if a.index is None or a.index >= np: redef.append(a) else: if index_map.get(a.index, None) is None: index_map[a.index] = a else: redef.append(a) free_indices = [i for i in xrange(np) if index_map.get(i, None) is None] assert len(free_indices) == len(redef) for a, i in zip(redef, free_indices): a.index = i # At this point a.index runs from 0 to np-1 in the universe. for a in self.atomList(): if a.array is None: try: coordinates[a.index, :] = a.pos.array del a.pos except AttributeError: coordinates[a.index, :] = Utility.undefined else: coordinates[a.index, :] = a.array[a.index, :] a.array = coordinates # Define configuration object. self._configuration = 1 # a hack to prevent endless recursion self._configuration = \ ParticleProperties.Configuration(self, coordinates) return self._configuration
[docs] def copyConfiguration(self): """ This operation is thread-safe; it won't return inconsistent data even when another thread is modifying the configuration. :returns: a copy of the current configuration :rtype: :class:`~MMTK.ParticleProperties.Configuration` """ self.acquireReadStateLock() try: conf = copy.copy(self.configuration()) finally: self.releaseReadStateLock() return conf
def atomNames(self): self.configuration() names = self.numberOfAtoms()*[None] for a in self.atomList(): names[a.index] = a.fullName() return names
[docs] def setConfiguration(self, configuration, block=True): """ Update the current configuration of the universe by copying the given input configuration. :param configuration: the new configuration :type configuration: :class:`~MMTK.ParticleProperties.Configuration` :param block: if True, the operation blocks other threads from accessing the configuration before the update is completed. If False, it is assumed that the caller takes care of locking. :type block: bool """ if not ParticleProperties.isConfiguration(configuration): raise TypeError('not a universe configuration') conf = self.configuration() if block: self.acquireWriteStateLock() try: conf.assign(configuration) self.setCellParameters(configuration.cell_parameters) finally: if block: self.releaseWriteStateLock()
[docs] def addToConfiguration(self, displacement, block=True): """ Update the current configuration of the universe by adding the given displacement vector. :param displacement: the displacement vector for each atom :type displacement: :class:`~MMTK.ParticleProperties.ParticleVector` :param block: if True, the operation blocks other threads from accessing the configuration before the update is completed. If False, it is assumed that the caller takes care of locking. :type block: bool """ conf = self.configuration() if block: self.acquireWriteStateLock() try: conf.assign(conf+displacement) finally: if block: self.releaseWriteStateLock()
[docs] def getParticleScalar(self, name, datatype = N.Float): """ :param name: the name of an atom attribute :type name: str :param datatype: the datatype of the array allocated to hold the data :returns: the values of the attribute 'name' for each atom in the universe. :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ conf = self.configuration() array = N.zeros((len(conf),), datatype) for a in self.atomList(): array[a.index] = getattr(a, name) return ParticleProperties.ParticleScalar(self, array)
getAtomScalarArray = getParticleScalar
[docs] def getParticleBoolean(self, name): """ :param name: the name of an atom attribute :type name: str :returns: the values of the boolean attribute 'name' for each atom in the universe, or False for atoms that do not have the attribute. :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ conf = self.configuration() array = N.zeros((len(conf),), N.Int) for a in self.atomList(): try: array[a.index] = getattr(a, name) except AttributeError: pass return ParticleProperties.ParticleScalar(self, array)
getAtomBooleanArray = getParticleBoolean
[docs] def masses(self): """ :returns: the masses of all atoms in the universe :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ if self._masses is None: self._masses = self.getParticleScalar('_mass') return self._masses
[docs] def charges(self): """ Return the atomic charges defined by the universe's force field. :returns: the charges of all atoms in the universe :rtype: :class:`~MMTK.ParticleProperties.ParticleScalar` """ ff = self._forcefield if ff is None: raise ValueError("no force field defined") return ff.charges(self)
[docs] def velocities(self): """ :returns: the current velocities of all atoms, or None if no velocities are defined. Note that this is not a copy of the current state but a reference to it; its data will change whenever any changes are made to the current velocities. :rtype: :class:`~MMTK.ParticleProperties.ParticleVector` """ try: return self._atom_properties['velocity'] except KeyError: return None
[docs] def setVelocities(self, velocities, block=True): """ Update the current velocities of the universe by copying the given input velocities. :param velocities: the new velocities, or None to remove the velocity definition from the universe :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :param block: if True, the operation blocks other threads from accessing the configuration before the update is completed. If False, it is assumed that the caller takes care of locking. :type block: bool """ if velocities is None: try: del self._atom_properties['velocity'] except KeyError: pass else: try: v = self._atom_properties['velocity'] except KeyError: v = ParticleProperties.ParticleVector(self) self._atom_properties['velocity'] = v if block: self.acquireWriteStateLock() try: v.assign(velocities) finally: if block: self.releaseWriteStateLock()
[docs] def initializeVelocitiesToTemperature(self, temperature): """ Generate random velocities for all atoms from a Boltzmann distribution. :param temperature: the reference temperature for the Boltzmann distribution :type temperature: float """ self.configuration() masses = self.masses() if self._atom_properties.has_key('velocity'): del self._atom_properties['velocity'] fixed = self.getParticleBoolean('fixed') np = self.numberOfPoints() velocities = N.zeros((np, 3), N.Float) for i in xrange(np): m = masses[i] if m > 0. and not fixed[i]: velocities[i] = Random.randomVelocity(temperature, m).array self._atom_properties['velocity'] = \ ParticleProperties.ParticleVector(self, velocities) self.adjustVelocitiesToConstraints()
[docs] def scaleVelocitiesToTemperature(self, temperature, block=True): """ Scale all velocities by a common factor in order to obtain the specified temperature. :param temperature: the reference temperature :type temperature: float :param block: if True, the operation blocks other threads from accessing the configuration before the update is completed. If False, it is assumed that the caller takes care of locking. :type block: bool """ velocities = self.velocities() factor = N.sqrt(temperature/self.temperature()) if block: self.acquireWriteStateLock() try: velocities.scaleBy(factor) finally: if block: self.releaseWriteStateLock()
def degreesOfFreedom(self): return GroupOfAtoms.degreesOfFreedom(self) \ - self.numberOfDistanceConstraints()
[docs] def distanceConstraintList(self): """ :returns: the list of distance constraints :rtype: list """ return self._objects.distanceConstraintList()
[docs] def numberOfDistanceConstraints(self): """ :returns: the number of distance constraints :rtype: int """ return self._objects.numberOfDistanceConstraints()
[docs] def setBondConstraints(self): """ Sets distance constraints for all bonds. """ self.configuration() self._objects.setBondConstraints(self) self.enforceConstraints()
[docs] def removeDistanceConstraints(self): """ Removes all distance constraints. """ self._objects.removeDistanceConstraints(self)
[docs] def enforceConstraints(self, configuration=None, velocities=None): """ Enforces the previously defined distance constraints by modifying the configuration and velocities. :param configuration: the configuration in which the constraints are enforced (None for current configuration) :type configuration: :class:`~MMTK.ParticleProperties.Configuration` :param velocities: the velocities in which the constraints are enforced (None for current velocities) :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` """ from MMTK import Dynamics Dynamics.enforceConstraints(self, configuration) self.adjustVelocitiesToConstraints(velocities)
[docs] def adjustVelocitiesToConstraints(self, velocities=None, block=True): """ Modifies the velocities to be compatible with the distance constraints, i.e. projects out the velocity components along the constrained distances. :param velocities: the velocities in which the constraints are enforced (None for current velocities) :type velocities: :class:`~MMTK.ParticleProperties.ParticleVector` :param block: if True, the operation blocks other threads from accessing the configuration before the update is completed. If False, it is assumed that the caller takes care of locking. :type block: bool """ from MMTK import Dynamics if velocities is None: velocities = self.velocities() if velocities is not None: if block: self.acquireWriteStateLock() try: Dynamics.projectVelocities(self, velocities) finally: if block: self.releaseWriteStateLock()
def bondLengthDatabase(self): if self._bond_database is None: self._bond_database = None if self._bond_database is None: ff = self._forcefield try: self._bond_database = ff.bondLengthDatabase(self) except AttributeError: pass if self._bond_database is None: self._bond_database = Bonds.DummyBondLengthDatabase(self) return self._bond_database
[docs] def forcefield(self): """ :returns: the force field :rtype: :class:`~MMTK.ForceFields.ForceField.ForceField` """ return self._forcefield
def energyEvaluatorParameters(self, subset1 = None, subset2 = None): self.configuration() from MMTK.ForceFields import ForceField ffdata = ForceField.ForceFieldData() return self._forcefield.evaluatorParameters(self, subset1, subset2, ffdata) def energyEvaluator(self, subset1 = None, subset2 = None, threads=None, mpi_communicator=None): if self._forcefield is None: raise ValueError("no force field defined") try: eval = self._evaluator[(subset1, subset2, threads)] except KeyError: from MMTK.ForceFields import ForceField eval = ForceField.EnergyEvaluator(self, self._forcefield, subset1, subset2, threads, mpi_communicator) self._evaluator[(subset1, subset2, threads)] = eval return eval
[docs] def energy(self, subset1 = None, subset2 = None, small_change=False): """ :param subset1: a subset of a universe, or None :type subset1: :class:`~MMTK.ChemicalObjects.ChemicalObject` :param subset2: a subset of a universe, or None :type subset2: :class:`~MMTK.ChemicalObjects.ChemicalObject` :param small_change: if True, algorithms optimized for small configurational changes relative to the last evaluation may be used. :type small_change: bool :returns: the potential energy of interaction between the atoms in subset1 and the atoms in subset2. If subset2 is None, the interactions within subset1 are calculated. It both subsets are None, the potential energy of the whole universe is returned. :rtype: float """ eval = self.energyEvaluator(subset1, subset2) return eval(0, 0, small_change)
[docs] def energyAndGradients(self, subset1 = None, subset2 = None, small_change=False): """ :returns: the energy and the energy gradients :rtype: (float, :class:`~MMTK.ParticleProperties.ParticleVector`) """ eval = self.energyEvaluator(subset1, subset2) return eval(1, 0, small_change)
[docs] def energyAndForceConstants(self, subset1 = None, subset2 = None, small_change=False): """ :returns: the energy and the force constants :rtype: (float, :class:`~MMTK.ParticleProperties.SymmetricPairTensor`) """ eval = self.energyEvaluator(subset1, subset2) e, g, fc = eval(0, 1, small_change) return e, fc
[docs] def energyGradientsAndForceConstants(self, subset1 = None, subset2 = None, small_change=False): """ :returns: the energy, its gradients, and the force constants :rtype: (float, :class:`~MMTK.ParticleProperties.ParticleVector`, :class:`~MMTK.ParticleProperties.SymmetricPairTensor`) """ eval = self.energyEvaluator(subset1, subset2) return eval(1, 1, small_change)
[docs] def energyTerms(self, subset1 = None, subset2 = None, small_change=False): """ :returns: a dictionary containing the energy values for each energy term separately. The energy terms are defined by the force field. :rtype: dict """ eval = self.energyEvaluator(subset1, subset2) eval(0, 0, small_change) return eval.lastEnergyTerms()
[docs] def configurationDifference(self, conf1, conf2): """ :param conf1: a configuration :type conf1: :class:`~MMTK.ParticleProperties.Configuration` :param conf2: a configuration :type conf2: :class:`~MMTK.ParticleProperties.Configuration` :returns: the difference vector between the two configurations for each atom, taking into account the universe topology (e.g. minimum-image convention). :rtype: :class:`~MMTK.ParticleProperties.ParticleVector` """ d = conf2-conf1 cell = conf1.cell_parameters if cell is not None: self._spec.foldCoordinatesIntoBox(d.array) return d
[docs] def distanceVector(self, p1, p2, conf=None): """ :param p1: a vector or a chemical object whose position is taken :param p2: a vector or a chemical object whose position is taken :param conf: a configuration (None for the current configuration) :returns: the distance vector between p1 and p2 (i.e. the vector from p1 to p2) in the configuration conf, taking into account the universe's topology. """ p1 = self.position(p1, conf) p2 = self.position(p2, conf) if conf is None: return Vector(self._spec.distanceVector(p1.array, p2.array)) else: cell = self._fixCellParameters(conf.cell_parameters) if cell is None: return Vector(self._spec.distanceVector(p1.array, p2.array)) else: return Vector(self._spec.distanceVector(p1.array, p2.array, cell))
[docs] def distance(self, p1, p2, conf = None): """ :param p1: a vector or a chemical object whose position is taken :param p2: a vector or a chemical object whose position is taken :param conf: a configuration (None for the current configuration) :returns: the distance between p1 and p2, i.e. the length of the distance vector :rtype: float """ return self.distanceVector(p1, p2, conf).length()
[docs] def angle(self, p1, p2, p3, conf = None): """ :param p1: a vector or a chemical object whose position is taken :param p2: a vector or a chemical object whose position is taken :param p3: a vector or a chemical object whose position is taken :param conf: a configuration (None for the current configuration) :returns: the angle between the distance vectors p1-p2 and p3-p2 :rtype: float """ v1 = self.distanceVector(p2, p1, conf) v2 = self.distanceVector(p2, p3, conf) return v1.angle(v2)
[docs] def dihedral(self, p1, p2, p3, p4, conf = None): """ :param p1: a vector or a chemical object whose position is taken :param p2: a vector or a chemical object whose position is taken :param p3: a vector or a chemical object whose position is taken :param p4: a vector or a chemical object whose position is taken :param conf: a configuration (None for the current configuration) :returns: the dihedral angle between the plane containing the distance vectors p1-p2 and p3-p2 and the plane containing the distance vectors p2-p3 and p4-p3 :rtype: float """ v1 = self.distanceVector(p2, p1, conf) v2 = self.distanceVector(p3, p2, conf) v3 = self.distanceVector(p3, p4, conf) a = v1.cross(v2).normal() b = v3.cross(v2).normal() cos = a*b sin = b.cross(a)*v2/v2.length() return Transformation.angleFromSineAndCosine(sin, cos)
def _deleteAtom(self, atom): pass
[docs] def basisVectors(self): """ :returns: the basis vectors of the elementary cell of a periodic universe, or None for a non-periodic universe :rtype: NoneType or list """ return None
[docs] def reciprocalBasisVectors(self): """ :returns: the reciprocal basis vectors of the elementary cell of a periodic universe, or None for a non-periodic universe :rtype: NoneType or list """ return None
def cellParameters(self): return None def setCellParameters(self, parameters): if parameters is not None: raise ValueError('incompatible cell parameters') def _fixCellParameters(self, cell_parameters): return cell_parameters
[docs] def cellVolume(self): """ :returns: the volume of the elementary cell of a periodic universe, None for a non-periodic universe :rtype: NoneType or float """ return None
[docs] def largestDistance(self): """ :returns: the largest possible distance between any two points that can be represented independent of orientation, i.e. the radius of the largest sphere that fits into the simulation cell. Returns None if no such upper limit exists. :rtype: NoneType or float """ return None
[docs] def contiguousObjectOffset(self, objects = None, conf = None, box_coordinates = False): """ :param objects: a list of chemical objects, or None for all objects in the universe :type objects: list :param conf: a configuration (None for the current configuration) :param box_coordinates: use box coordinates rather than real ones :type box_coordinates: bool :returns: a set of displacement vectors relative to the conf which, when added to the configuration, create a configuration in which none of the objects is split across the edge of the elementary cell. For nonperiodic universes the return value is None. :rtype: :class:`~MMTK.ParticleProperties.ParticleVector` """ return None
[docs] def contiguousObjectConfiguration(self, objects = None, conf = None): """ :param objects: a list of chemical objects, or None for all objects in the universe :type objects: list :param conf: a configuration (None for the current configuration) :returns: configuration conf (default: current configuration) corrected by the contiguous object offsets for that configuration. :rtype: :class:`~MMTK.ParticleProperties.Configuration` """ if conf is None: conf = self.configuration() offset = self.contiguousObjectOffset(objects, conf) if offset is not None: return conf + offset else: return copy.copy(conf)
[docs] def realToBoxCoordinates(self, vector): """ Box coordinates are defined only for periodic universes; their components have values between -0.5 and 0.5; these extreme values correspond to the walls of the simulation box. :param vector: a point in the universe :returns: the box coordinate equivalent of vector, or the original vector if no box coordinate system exists :rtype: Scientific.Geometry.Vector """ return vector
[docs] def boxToRealCoordinates(self, vector): """ :param vector: a point in the universe expressed in box coordinates :returns: the real-space equivalent of vector :rtype: Scientific.Geometry.Vector """ return vector
def _realToBoxPointArray(self, array, parameters=None): return array def _boxToRealPointArray(self, array, parameters=None): return array
[docs] def cartesianToFractional(self, vector): """ Fractional coordinates are defined only for periodic universes; their components have values between 0. and 1. :param vector: a point in the universe :type vector: Scientific.Geometry.Vector :returns: the fractional coordinate equivalent of vector :rtype: Scientific.N.array_type """ raise ValueError("Universe is not periodic")
def cartesianToFractionalMatrix(self): raise ValueError("Universe is not periodic")
[docs] def fractionalToCartesian(self, array): """ Fractional coordinates are defined only for periodic universes; their components have values between 0. and 1. :param array: an array of fractional coordinates :type array: Scientific.N.array_type :returns: the real-space equivalent of vector :rtype: Scientific.Geometry.Vector """ raise ValueError("Universe is not periodic")
def fractionalToCartesianMatrix(self): raise ValueError("Universe is not periodic") def foldCoordinatesIntoBox(self): return
[docs] def randomPoint(self): """ :returns: a random point from a uniform distribution within the universe. This operation is defined only for finite-volume universes, e.g. periodic universes. :rtype: Scientific.Geometry.Vector """ raise TypeError("undefined operation")
[docs] def map(self, function): """ Apply a function to all objects in the universe and return the list of the results. If the results are chemical objects, a Collection object is returned instead of a list. :param function: the function to be applied :type function: callable :returns: the list or collection of the results """ return self._objects.map(function)
def description(self, objects = None, index_map = None): if objects is None: objects = self attributes = {} for attr in dir(self): if attr[0] != '_': object = getattr(self, attr) if ChemicalObjects.isChemicalObject(object) \ or Environment.isEnvironmentObject(object): attributes[object] = attr items = [] for o in objects.objectList(): attr = attributes.get(o, None) if attr is not None: items.append(repr(attr)) items.append(o.description(index_map)) for o in self._environment: attr = attributes.get(o, None) if attr is not None: items.append(repr(attr)) items.append(o.description()) try: classname = self.classname_for_trajectories except AttributeError: classname = self.__class__.__name__ s = 'c(%s,[%s])' % \ (`classname + self._descriptionArguments()`, ','.join(items)) return s def _graphics(self, conf, distance_fn, model, module, options): return self._objects._graphics(conf, distance_fn, model, module, options)
[docs] def setFromTrajectory(self, trajectory, step = None): """ Set the state of the universe to the one stored in a trajectory. This operation is thread-safe; it blocks other threads that want to access the configuration or velocities while the data is being updated. :param trajectory: a trajectory object for this universe :type trajectory: :class:`~MMTK.Trajectory.Trajectory` :param step: a step number, or None for the default step (0 for a standard trajectory, the last written step for a restart trajectory) :type step: int """ if step is None: step = trajectory.defaultStep() self.acquireWriteStateLock() try: self.setConfiguration(trajectory.configuration[step], False) vel = self.velocities() try: vel_tr = trajectory.velocities[step] except AttributeError: if vel is not None: Utility.warning("velocities were not modified because " + "the trajectory does not contain " + "velocity data.") return if vel is None: self._atom_properties['velocity'] = vel_tr else: vel.assign(vel_tr) finally: self.releaseWriteStateLock() # # More efficient reimplementations of methods in Collections.GroupOfAtoms #
def numberOfFixedAtoms(self): return self.getParticleBoolean('fixed').sumOverParticles() def degreesOfFreedom(self): return 3*(self.numberOfAtoms()-self.numberOfFixedAtoms()) \ - self.numberOfDistanceConstraints() def mass(self): return self.masses().sumOverParticles() def centerOfMass(self, conf = None): m = self.masses() if conf is None: conf = self.configuration() return (m*conf).sumOverParticles()/m.sumOverParticles() def kineticEnergy(self, velocities = None): if velocities is None: velocities = self.velocities() return 0.5*velocities.massWeightedDotProduct(velocities) def momentum(self, velocities = None): if velocities is None: velocities = self.velocities() return (self.masses()*velocities).sumOverParticles() def translateBy(self, vector): conf = self.configuration().array N.add(conf, vector.array[N.NewAxis, :], conf) def applyTransformation(self, t): conf = self.configuration().array rot = t.rotation().tensor.array conf[:] = N.dot(conf, N.transpose(rot)) N.add(conf, t.translation().vector.array[N.NewAxis, :], conf) def writeXML(self, file): file.write('<?xml version="1.0" encoding="ISO-8859-1" ' + 'standalone="yes"?>\n\n') file.write('<molecularsystem>\n\n') file.write('<templates>\n\n') memo = {'counter': 1} instances = [] atoms = [] for object in self._objects.objectList(): instances = instances + object.writeXML(file, memo, 1) atoms = atoms + object.getXMLAtomOrder() file.write('\n</templates>\n\n') file.write('<universe %s>\n' % self.XMLSpec()) for instance in instances: file.write(' ') file.write(instance) file.write('\n') conf = self.configuration() if conf.hasValidPositions(): file.write(' <configuration>\n') file.write(' <atomArray units="units:nm"\n') file.write(' x3="') for atom in atoms: file.write(str(conf[atom][0])) file.write(' ') file.write('"\n') file.write(' y3="') for atom in atoms: file.write(str(conf[atom][1])) file.write(' ') file.write('"\n') file.write(' z3="') for atom in atoms: file.write(str(conf[atom][2])) file.write(' ') file.write('"\n') file.write(' />\n') file.write(' </configuration>\n') file.write('</universe>\n\n') file.write('</molecularsystem>\n') # # Infinite universes #
[docs]class InfiniteUniverse(Universe): """ Infinite (unbounded and nonperiodic) universe. """ def __init__(self, forcefield=None, **properties): """ :param forcefield: a force field, or None for no force field :type forcefield: :class:`~MMTK.ForceFields.ForceField.ForceField` """ Universe.__init__(self, forcefield, properties) self._createSpec() def CdistanceFunction(self): from MMTK_universe import infinite_universe_distance_function return infinite_universe_distance_function, N.array([0.]) def CcorrectionFunction(self): from MMTK_universe import infinite_universe_correction_function return infinite_universe_correction_function, N.array([0.]) def CvolumeFunction(self): from MMTK_universe import infinite_universe_volume_function return infinite_universe_volume_function, N.array([0.]) def CboxTransformationFunction(self): return None, N.array([0.]) def _createSpec(self): from MMTK_universe import InfiniteUniverseSpec self._spec = InfiniteUniverseSpec() def _descriptionArguments(self): if self._forcefield is None: return '()' else: return '(%s)' % self._forcefield.description() def XMLSpec(self): return 'topology="infinite"' # # 3D periodic universe base class #
class Periodic3DUniverse(Universe): is_periodic = True def setVolume(self, volume): """ Multiplies all edge lengths by the same factor such that the cell volume becomes equal to the specified value. :param volume: the desired volume :type volume: float """ factor = (volume/self.cellVolume())**(1./3.) self.scaleSize(factor) def foldCoordinatesIntoBox(self): self._spec.foldCoordinatesIntoBox(self.configuration().array) def basisVectors(self): return [self.boxToRealCoordinates(Vector(1., 0., 0.)), self.boxToRealCoordinates(Vector(0., 1., 0.)), self.boxToRealCoordinates(Vector(0., 0., 1.))] def cartesianToFractional(self, vector): r1, r2, r3 = self.reciprocalBasisVectors() return N.array([r1*vector, r2*vector, r3*vector]) def cartesianToFractionalMatrix(self): return N.array(self.reciprocalBasisVectors()) def fractionalToCartesian(self, array): e1, e2, e3 = self.basisVectors() return array[0]*e1 + array[1]*e2 + array[2]*e3 def fractionalToCartesianMatrix(self): return N.transpose(self.basisVectors()) def randomPoint(self): return self.boxToRealCoordinates(Random.randomPointInBox(1., 1., 1.)) def contiguousObjectOffset(self, objects = None, conf = None, box_coordinates = 0): from MMTK_universe import contiguous_object_offset if objects is None or objects == self or objects == [self]: default = True objects = self._objects.objectList() pairs = self._bond_pairs else: default = False pairs = None if conf is None: conf = self.configuration() cell = self._fixCellParameters(conf.cell_parameters) offset = ParticleProperties.ParticleVector(self) if pairs is None: pairs = [] for o in objects: new_object = True if ChemicalObjects.isChemicalObject(o): units = o.bondedUnits() elif Collections.isCollection(o) or isUniverse(o): units = set([u for element in o for u in element.topLevelChemicalObject() .bondedUnits()]) else: raise ValueError(str(o) + " not a chemical object") for bu in units: atoms = [a.index for a in bu.atomsWithDefinedPositions()] mpairs = bu.traverseBondTree(lambda a: a.index) mpairs = [(a1, a2) for (a1, a2) in mpairs if a1 in atoms and a2 in atoms] if len(mpairs) == 0: mpairs = Utility.pairs(atoms) new_object = False pairs.extend(mpairs) pairs = N.array(pairs) if default: self._bond_pairs = pairs if cell is None: contiguous_object_offset(self._spec, pairs, conf.array, offset.array, box_coordinates) else: contiguous_object_offset(self._spec, pairs, conf.array, offset.array, box_coordinates, cell) return offset def _graphics(self, conf, distance_fn, model, module, options): objects = self._objects._graphics(conf, distance_fn, model, module, options) v1, v2, v3 = self.basisVectors() p = -0.5*(v1+v2+v3) color = options.get('color', 'white') material = module.EmissiveMaterial(color) objects.append(module.Line(p, p+v1, material=material)) objects.append(module.Line(p, p+v2, material=material)) objects.append(module.Line(p+v1, p+v1+v2, material=material)) objects.append(module.Line(p+v2, p+v1+v2, material=material)) objects.append(module.Line(p, p+v3, material=material)) objects.append(module.Line(p+v1, p+v1+v3, material=material)) objects.append(module.Line(p+v2, p+v2+v3, material=material)) objects.append(module.Line(p+v1+v2, p+v1+v2+v3, material=material)) objects.append(module.Line(p+v3, p+v1+v3, material=material)) objects.append(module.Line(p+v3, p+v2+v3, material=material)) objects.append(module.Line(p+v1+v3, p+v1+v2+v3, material=material)) objects.append(module.Line(p+v2+v3, p+v1+v2+v3, material=material)) return objects # # Orthorhombic universe with periodic boundary conditions #
[docs]class OrthorhombicPeriodicUniverse(Periodic3DUniverse): """ Periodic universe with orthorhombic elementary cell. """ def __init__(self, size = None, forcefield = None, **properties): """ :param size: a sequence of length three specifying the edge lengths along the x, y, and z directions :param forcefield: a force field, or None for no force field :type forcefield: :class:`~MMTK.ForceFields.ForceField.ForceField` """ Universe.__init__(self, forcefield, properties) self.data = N.zeros((3,), N.Float) if size is not None: self.setSize(size) self._createSpec() is_orthogonal = True def __setstate__(self, state): Universe.__setstate__(self, state) if len(self.data.shape) == 2: self.data = self.data[0] def setSize(self, size): self.data[:] = size
[docs] def scaleSize(self, factor): """ Multiplies all edge lengths by a factor. :param factor: the scale factor :type factor: float """ self.data[:] = factor*self.data self._spec.foldCoordinatesIntoBox(self.configuration().array)
def setCellParameters(self, parameters): if parameters is not None: self.data[:] = parameters def realToBoxCoordinates(self, vector): x, y, z = vector return Vector(x/self.data[0], y/self.data[1], z/self.data[2]) def boxToRealCoordinates(self, vector): x, y, z = vector return Vector(x*self.data[0], y*self.data[1], z*self.data[2]) def _realToBoxPointArray(self, array, parameters=None): if parameters is None: parameters = self.data if parameters.shape == (3,): parameters = parameters[N.NewAxis, :] return array/parameters def _boxToRealPointArray(self, array, parameters=None): if parameters is None: parameters = self.data if parameters.shape == (3,): parameters = parameters[N.NewAxis, :] return array*parameters def CdistanceFunction(self): from MMTK_universe import orthorhombic_universe_distance_function return orthorhombic_universe_distance_function, self.data def CcorrectionFunction(self): from MMTK_universe import orthorhombic_universe_correction_function return orthorhombic_universe_correction_function, self.data def CvolumeFunction(self): from MMTK_universe import orthorhombic_universe_volume_function return orthorhombic_universe_volume_function, self.data def CboxTransformationFunction(self): from MMTK_universe import orthorhombic_universe_box_transformation return orthorhombic_universe_box_transformation, self.data def cellParameters(self): return self.data def reciprocalBasisVectors(self): return [Vector(1., 0., 0.)/self.data[0], Vector(0., 1., 0.)/self.data[1], Vector(0., 0., 1.)/self.data[2]] def cellVolume(self): return N.multiply.reduce(self.data) def largestDistance(self): return 0.5*N.minimum.reduce(self.data) def _createSpec(self): from MMTK_universe import OrthorhombicPeriodicUniverseSpec self._spec = OrthorhombicPeriodicUniverseSpec(self.data) def _descriptionArguments(self): if self._forcefield is None: return '((0.,0.,0.),)' else: return '((0.,0.,0.),%s)' % self._forcefield.description() def XMLSpec(self): return 'topology="periodic3d" ' + \ 'cellshape="orthorhombic" ' + \ ('cellsize="%f %f %f" ' % tuple(self.data)) + \ 'units="units:nm"' # # Cubic universe with periodic boundary conditions #
[docs]class CubicPeriodicUniverse(OrthorhombicPeriodicUniverse): """ Periodic universe with cubic elementary cell. """
[docs] def setSize(self, size): """ Set the edge length to a given value. :param size: the new size :type size: float """ OrthorhombicPeriodicUniverse.setSize(self, 3*(size,))
def _descriptionArguments(self): if self._forcefield is None: return '(0.)' else: return '(0.,%s)' % self._forcefield.description() # # Parallelepipedic universe with periodic boundary conditions #
[docs]class ParallelepipedicPeriodicUniverse(Periodic3DUniverse): """ Periodic universe with parallelepipedic elementary cell. """ def __init__(self, shape = None, forcefield = None, **properties): """ :param shape: the basis vectors :type shape: sequence of Scientific.Geometry.Vector :param forcefield: a force field, or None for no force field :type forcefield: :class:`~MMTK.ForceFields.ForceField.ForceField` """ Universe.__init__(self, forcefield, properties) self.data = N.zeros((19,), N.Float) if shape is not None: self.setShape(shape) self._createSpec() is_periodic = True def setShape(self, shape): self.data[:9] = N.ravel(N.transpose([list(s) for s in shape])) from MMTK_universe import parallelepiped_invert parallelepiped_invert(self.data)
[docs] def scaleSize(self, factor): """ Multiplies all edge lengths by a factor. :param factor: the scale factor :type factor: float """ self.data[:9] = factor*self.data[:9] from MMTK_universe import parallelepiped_invert parallelepiped_invert(self.data) self._spec.foldCoordinatesIntoBox(self.configuration().array)
def setCellParameters(self, parameters): if parameters is not None: self.data[:9] = parameters from MMTK_universe import parallelepiped_invert parallelepiped_invert(self.data) def _fixCellParameters(self, cell_parameters): full_parameters = 0.*self.data full_parameters[:9] = cell_parameters from MMTK_universe import parallelepiped_invert parallelepiped_invert(full_parameters) return full_parameters def realToBoxCoordinates(self, vector): x, y, z = vector return Vector(self.data[0+9]*x + self.data[1+9]*y + self.data[2+9]*z, self.data[3+9]*x + self.data[4+9]*y + self.data[5+9]*z, self.data[6+9]*x + self.data[7+9]*y + self.data[8+9]*z) def boxToRealCoordinates(self, vector): x, y, z = vector return Vector(self.data[0]*x + self.data[1]*y + self.data[2]*z, self.data[3]*x + self.data[4]*y + self.data[5]*z, self.data[6]*x + self.data[7]*y + self.data[8]*z) def _realToBoxPointArray(self, array, parameters=None): if parameters is None: matrix = N.reshape(self.data[9:18], (1, 3, 3)) else: parameters = N.concatenate([parameters, N.zeros((10,), N.Float)]) from MMTK_universe import parallelepiped_invert parallelepiped_invert(parameters) matrix = N.reshape(parameters[9:18], (1, 3, 3)) return N.add.reduce(matrix*array[:, N.NewAxis, :], axis=-1) def _boxToRealPointArray(self, array, parameters=None): if parameters is None: parameters = self.data[:9] matrix = N.reshape(parameters, (1, 3, 3)) return N.add.reduce(matrix*array[:, N.NewAxis, :], axis=-1) def CdistanceFunction(self): from MMTK_universe import parallelepipedic_universe_distance_function return parallelepipedic_universe_distance_function, self.data def CcorrectionFunction(self): from MMTK_universe import parallelepipedic_universe_correction_function return parallelepipedic_universe_correction_function, self.data def CvolumeFunction(self): from MMTK_universe import parallelepipedic_universe_volume_function return parallelepipedic_universe_volume_function, self.data def CboxTransformationFunction(self): from MMTK_universe import parallelepipedic_universe_box_transformation return parallelepipedic_universe_box_transformation, self.data def cellParameters(self): return self.data[:9] def reciprocalBasisVectors(self): return [Vector(self.data[9:12]), Vector(self.data[12:15]), Vector(self.data[15:18])] def cellVolume(self): return abs(self.data[18]) def largestDistance(self): return min([0.5/v.length() for v in self.reciprocalBasisVectors()]) def _createSpec(self): from MMTK_universe import ParallelepipedicPeriodicUniverseSpec self._spec = ParallelepipedicPeriodicUniverseSpec(self.data) def _descriptionArguments(self): if self._forcefield is None: return '((Vector(0.,0.,0.),Vector(0.,0.,0.),Vector(0.,0.,0.)))' else: return '((Vector(0.,0.,0.),Vector(0.,0.,0.),Vector(0.,0.,0.)),%s)'\ % self._forcefield.description() def XMLSpec(self): return 'topology="periodic3d" ' + \ 'cellshape="parallelepipedic" ' + \ ('cellshape="%f %f %f %f %f %f %f %f %f" ' % tuple(self.data[:9])) + \ 'units="units:nm"' # # Recognition functions #
[docs]def isUniverse(object): """ :param object: any Python object :returns: True if object is a universe. """ return isinstance(object, Universe)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/Visualization.html0000644000076600000240000025462012013143455022277 0ustar hinsenstaff00000000000000 MMTK.Visualization — MMTK User Guide 2.7.7 documentation

Source code for MMTK.Visualization

# This module contains interfaces to external visualization programs
# and a visualization base class
#
# Written by Konrad Hinsen
#

"""
Visualization of chemical objects, including animation

This module provides visualization of chemical objects and animated
visualization of normal modes and sequences of configurations, including
trajectories. Visualization depends on external visualization programs.
On Unix systems, these programs are defined by environment variables.
Under Windows NT, the system definitions for files with extension
"pdb" and "wrl" are used.

A viewer for PDB files can be defined by the environment variable
'PDBVIEWER'. For showing a PDB file, MMTK will execute a command
consisting of the value of this variable followed by a space
and the name of the PDB file.

A viewer for VRML files can be defined by the environment variable
'VRMLVIEWER'. For showing a VRML file, MMTK will execute a command
consisting of the value of this variable followed by a space
and the name of the VRML file.

Since there is no standard for launching viewers for animation,
MMTK supports only two programs: VMD and XMol. MMTK detects
these programs by inspecting the value of the environment variable
'PDBVIEWER'. This value must be the file name of the executable,
and must give "vmd" or "xmol" after stripping off an optional
directory specification.
"""

__docformat__ = 'restructuredtext'

from MMTK import Units, Utility
from Scientific import N
import subprocess, sys, tempfile, os

#
# If you want temporary files in a non-standard directory, make
# its name the value of this variable:
#
tempdir = None

#
# Identify OS
#
running_on_windows = sys.platform == 'win32'
running_on_macosx = sys.platform.startswith('darwin')
running_on_linux = sys.platform.startswith('linux')

#
# Get visualization program names
#
viewer = {}
try:
    pdbviewer = os.environ['PDBVIEWER']
    prog = os.path.split(pdbviewer)[1].lower().split('.')[0]
    viewer['pdb'] = (prog, pdbviewer)
except KeyError: pass
try:
    vrmlviewer = os.environ['VRMLVIEWER']
    prog = os.path.split(vrmlviewer)[1].lower().split('.')[0]
    viewer['vrml'] = (prog, vrmlviewer)
except KeyError: pass


[docs]def definePDBViewer(progname, exec_path): """ Define the program used to view PDB files. :param progname: the canonical name of the PDB viewer. If it is a known one (one of "vmd", "xmol", "imol"), special features such as animation may be available. :type progname: str :param exec_path: the path to the executable program :type exec_path: str """ viewer['pdb'] = (progname.lower(), exec_path)
[docs]def defineVRMLiewer(progname, exec_path): """ Define the program used to view VRML files. :param progname: the canonical name of the VRML viewer :type progname: str :param exec_path: the path to the executable program :type exec_path: str """ viewer['vrml'] = (progname.lower(), exec_path) # # Visualization base class. Defines methods for general visualization # tasks. #
[docs]class Viewable(object): """ Any viewable chemical object This is a mix-in class that defines a general visualization method for all viewable objects, i.e. chemical objects (atoms, molecules, etc.), collections, and universes. """
[docs] def graphicsObjects(self, **options): """ :keyword configuration: the configuration in which the objects are drawn (default: the current configuration) :type configuration: :class:`~MMTK.ParticleProperties.Configuration` :keyword model: the graphical representation to be used (one of "wireframe", "tube", "ball_and_stick", "vdw" and "vdw_and_stick"). The vdw models use balls with the radii taken from the atom objects. Default is "wireframe". :type model: str :keyword ball_radius: the radius of the balls representing the atoms in a ball_and_stick model, default: 0.03 This is also used in vdw and vdw_and_stick when an atom does not supply a radius. :type ball_radius: float :keyword stick_radius: the radius of the sticks representing the bonds in a ball_and_stick, vdw_and_stick or tube model. Default: 0.02 for the tube model, 0.01 for the ball_and_stick and vdw_and_stick models :type stick_radius: float :keyword graphics_module: the module in which the elementary graphics objects are defined (default: Scientific.Visualization.VRML) :type graphics_module: module :keyword color_values: a color value for each atom which defines the color via the color scale object specified by the option color_scale. If no value is given, the atoms' colors are taken from the attribute 'color' of each atom object (default values for each chemical element are provided in the chemical database). :type color_values: :class:`~MMTK.ParticleProperties.ParticleScalar` :keyword color_scale: an object that returns a color object (as defined in the module Scientific.Visualization.Color) when called with a number argument. Suitable objects are defined by Scientific.Visualization.Color.ColorScale and Scientific.Visualization.Color.SymmetricColorScale. The object is used only when the option color_values is specified as well. The default is a blue-to-red color scale that covers the range of the values given in color_values. :type color_scale: callable :keyword color: a color name predefined in the module Scientific.Visualization.Color. The corresponding color is applied to all graphics objects that are returned. :returns: a list of graphics objects that represent the object for which the method is called. :rtype: list """ conf = options.get('configuration', None) model = options.get('model', 'wireframe') if model == 'tube': model = 'ball_and_stick' radius = options.get('stick_radius', 0.02) options['stick_radius'] = radius options['ball_radius'] = radius try: module = options['graphics_module'] except KeyError: from Scientific.Visualization import VRML module = VRML color = options.get('color', None) if color is None: color_values = options.get('color_values', None) if color_values is not None: lower = N.minimum.reduce(color_values.array) upper = N.maximum.reduce(color_values.array) options['color_scale'] = module.ColorScale((lower, upper)) try: distance_fn = self.universe().distanceVector except AttributeError: from MMTK import Universe distance_fn = Universe.InfiniteUniverse().distanceVector return self._graphics(conf, distance_fn, model, module, options)
def _atomColor(self, atom, options): color = options.get('color', None) if color is not None: return color color_values = options.get('color_values', None) if color_values is None: return atom.color else: scale = options['color_scale'] return scale(color_values[atom]) # # View anything viewable. #
[docs]def view(object, *parameters): "Equivalent to object.view(parameters)." object.view(*parameters) # # Display an object or a collection of objects using an external # viewing program. #
def genericViewConfiguration(object, configuration = None, format = 'pdb', label = None): format = format.lower() viewer_format = format.split('.')[0] tempfile.tempdir = tempdir filename = tempfile.mktemp() tempfile.tempdir = None if viewer_format == 'pdb': filename = filename + '.pdb' elif viewer_format == 'vrml': filename = filename + '.wrl' if running_on_windows: object.writeToFile(filename, configuration, format) try: os.startfile(filename) except win32api.error, error_number: #Looking for error 31, SE_ERR_NOASSOC, in particular file_type = os.path.splitext(filename)[1] if error_number[0]==31: print ('There is no program associated with .%s files,' + \ ' please install a suitable viewer') % file_type else: print 'Unexpected error attempting to open .%s file' % file_type print sys.exc_value elif viewer.has_key(viewer_format): # On Unix-like systems, give priority to a user-specified viewer. object.writeToFile(filename, configuration, format) if os.fork() == 0: pipe = os.popen(viewer[format][1] + ' ' + filename + \ ' 1> /dev/null 2>&1', 'w') pipe.close() os.unlink(filename) os._exit(0) elif running_on_macosx: object.writeToFile(filename, configuration, format) subprocess.call(["/usr/bin/open", filename]) elif running_on_linux: object.writeToFile(filename, configuration, format) subprocess.call(["xdg-open", filename]) else: Utility.warning('No viewer for %s defined.' % viewer_format) return def viewConfiguration(*args, **kwargs): pdbviewer, exec_path = viewer.get('pdb', (None, None)) function = {'vmd': viewConfigurationVMD, 'xmol': viewConfigurationXMol, 'imol': viewConfigurationIMol} \ .get(pdbviewer,genericViewConfiguration) function(*args, **kwargs) # # Normal mode and trajectory animation # def viewSequence(object, conf_list, periodic = False, label = None): """ Launches an animation using an external viewer. :param object: the object for which the animation is displayed. :type object: :class:`~MMTK.Collections.GroupOfAtoms` :param conf_list: a sequence of configurations that define the animation :type conf_list: sequence :param periodic: if True, turn animation into a loop :param label: an optional text string that some interfaces use to pass a description of the object to the visualization system. :type label: str """ pdbviewer, exec_path = viewer.get('pdb', (None, None)) function = {'vmd': viewSequenceVMD, 'xmol': viewSequenceXMol, 'imol': viewSequenceIMol, None: None}[pdbviewer] if function is None: Utility.warning('No viewer with animation feature defined.') else: function(object, conf_list, periodic, label)
[docs]def viewTrajectory(trajectory, first=0, last=None, skip=1, subset = None, label = None): """ Launches an animation based on a trajectory using an external viewer. :param trajectory: the trajectory :type trajectory: :class:`~MMTK.Trajectory.Trajectory` :param first: the first trajectory step to be used :type first: int :param last: the first trajectory step NOT to be used :type last: int :param skip: the distance between two consecutive steps shown :type skip: int :param subset: the subset of the universe that is shown (default: the whole universe) :type subset: :class:`~MMTK.Collections.GroupOfAtoms` :param label: an optional text string that some interfaces use to pass a description of the object to the visualization system. :type label: str """ if type(trajectory) == type(''): from MMTK.Trajectory import Trajectory trajectory = Trajectory(None, trajectory, 'r') if last is None: last = len(trajectory) elif last < 0: last = len(trajectory) + last universe = trajectory.universe if subset is None: subset = universe viewSequence(subset, trajectory.configuration[first:last:skip], label)
def viewMode(mode, factor=1., subset=None, label=None): universe = mode.universe if subset is None: subset = universe conf = universe.configuration() viewSequence(subset, [conf, conf+factor*mode, conf, conf-factor*mode], 1, label) # # XMol support # # # Animation with XMol. # viewConfigurationXMol = viewConfiguration def viewSequenceXMol(object, conf_list, periodic = 0, label = None): tempfile.tempdir = tempdir file_list = [] for conf in conf_list: file = tempfile.mktemp() file_list.append(file) object.writeToFile(file, conf, 'pdb') bigfile = tempfile.mktemp() tempfile.tempdir = None subprocess.call(['cat'] + file_list + ['>', bigfile]) for file in file_list: os.unlink(file) if os.fork() == 0: pipe = os.popen('xmol -readFormat pdb ' + bigfile + \ ' 1> /dev/null 2>&1', 'w') pipe.close() os.unlink(bigfile) os._exit(0) # # VMD support # # # View configuration # def isCalpha(object): from MMTK.Proteins import isProtein, isPeptideChain from MMTK.Universe import isUniverse from MMTK.Collections import isCollection if isProtein(object): chain_list = list(object) elif isPeptideChain(object): chain_list = [object] elif isUniverse(object) or isCollection(object): chain_list = [] for element in object: if isProtein(element): chain_list = chain_list + list(element) elif isPeptideChain(element): chain_list.append(element) else: return False else: return False for chain in chain_list: try: if chain.model != 'calpha': return False except AttributeError: return False return True def viewConfigurationVMD(object, configuration = None, format = 'pdb', label = None): from MMTK import Universe format = format.lower() if format != 'pdb': return genericViewConfiguration(object, configuration, format) tempfile.tempdir = tempdir filename = tempfile.mktemp() filename_tcl = filename.replace('\\', '\\\\') script = tempfile.mktemp() script_tcl = script.replace('\\', '\\\\') tempfile.tempdir = None object.writeToFile(filename, configuration, format) file = open(script, 'w') file.write('mol load pdb ' + filename_tcl + '\n') if isCalpha(object): file.write('mol modstyle 0 all trace\n') file.write('color Name 1 white\n') file.write('color Name 2 white\n') file.write('color Name 3 white\n') if Universe.isUniverse(object): # add a box around periodic universes basis = object.basisVectors() if basis is not None: v1, v2, v3 = basis p = -0.5*(v1+v2+v3) for p1, p2 in [(p, p+v1), (p, p+v2), (p+v1, p+v1+v2), (p+v2, p+v1+v2), (p, p+v3), (p+v1, p+v1+v3), (p+v2, p+v2+v3), (p+v1+v2, p+v1+v2+v3), (p+v3, p+v1+v3), (p+v3, p+v2+v3), (p+v1+v3, p+v1+v2+v3), (p+v2+v3, p+v1+v2+v3)]: file.write('graphics 0 line {%f %f %f} {%f %f %f}\n' % (tuple(p1/Units.Ang) + tuple(p2/Units.Ang))) file.write('file delete ' + filename_tcl + '\n') if sys.platform != 'win32': # Under Windows, it seems to be impossible to delete # the script file while it is still in use. For the moment # we just don't delete it at all. file.write('file delete ' + script_tcl + '\n') file.close() subprocess.Popen([viewer['pdb'][1], '-nt', '-e', script]) # # Animate sequence # def viewSequenceVMD(object, conf_list, periodic = 0, label=None): tempfile.tempdir = tempdir script = tempfile.mktemp() script_tcl = script.replace('\\', '\\\\') np = object.numberOfPoints() universe = object.universe() if np == universe.numberOfPoints() \ and len(conf_list) > 2: from MMTK import DCD pdbfile = tempfile.mktemp() pdbfile_tcl = pdbfile.replace('\\', '\\\\') dcdfile = tempfile.mktemp() dcdfile_tcl = dcdfile.replace('\\', '\\\\') tempfile.tempdir = None sequence = DCD.writePDB(universe, conf_list[0], pdbfile) indices = map(lambda a: a.index, sequence) DCD.writeDCD(conf_list[1:], dcdfile, 1./Units.Ang, indices) file = open(script, 'w') file.write('mol load pdb ' + pdbfile_tcl + '\n') if isCalpha(object): file.write('mol modstyle 0 all trace\n') file.write('animate read dcd ' + dcdfile_tcl + '\n') if periodic: file.write('animate style loop\n') else: file.write('animate style once\n') file.write('animate forward\n') file.write('file delete ' + pdbfile_tcl + '\n') file.write('file delete ' + dcdfile_tcl + '\n') if sys.platform != 'win32': # Under Windows, it seems to be impossible to delete # the script file while it is still in use. For the moment # we just don't delete it at all. file.write('file delete ' + script_tcl + '\n') file.close() else: file_list = [] for conf in conf_list: file = tempfile.mktemp() file_list.append(file) object.writeToFile(file, conf, 'pdb') tempfile.tempdir = None file = open(script, 'w') file.write('mol load pdb ' + file_list[0] + '\n') for conf in file_list[1:]: file.write('animate read pdb ' + conf.replace('\\', '\\\\') + '\n') if periodic: file.write('animate style loop\n') else: file.write('animate style once\n') file.write('animate forward\n') for conf in file_list: file.write('file delete ' + conf.replace('\\', '\\\\') + '\n') if sys.platform != 'win32': # Under Windows, it seems to be impossible to delete # the script file while it is still in use. For the moment # we just don't delete it at all. file.write('file delete ' + script_tcl + '\n') file.close() subprocess.Popen([viewer['pdb'][1], '-nt', '-e', script]) # # iMol support # # # View configuration # def viewConfigurationIMol(object, configuration = None, format = 'pdb', label = None): format = format.lower() if format != 'pdb': return genericViewConfiguration(object, configuration, format) tempfile.tempdir = tempdir filename = tempfile.mktemp() + '.pdb' tempfile.tempdir = None object.writeToFile(filename, configuration, format) subprocess.call(['open', '-a', prog, filename]) # # Animate sequence # def viewSequenceIMol(object, conf_list, periodic = 0, label=None): from MMTK import PDB tempfile.tempdir = tempdir filename = tempfile.mktemp() + '.pdb' file = PDB.PDBOutputFile(filename) for conf in conf_list: file.nextModel() file.write(object, conf) file.close() tempfile.tempdir = None subprocess.call(['open', '-a', prog, filename]) # # PyMOL support # from MMTK import PyMOL if PyMOL.in_pymol: _representation = None def viewConfiguration(object, configuration = None, format = 'pdb', label = None): global _representation if label is None: label = "MMTK Object" if _representation is not None: _representation.remove() _representation = PyMOL.Representation(object, label, configuration) _representation.show()
[docs] def viewSequence(object, conf_list, periodic = 0, label = None): global _representation if label is None: label = "MMTK Object" if _representation is not None: _representation.remove() _representation = PyMOL.Representation(object, label, conf_list[0]) _representation.movie(conf_list)
genericViewMode = viewMode def viewMode(mode, factor=1., subset=None, label=None): from pymol import cmd cmd.set("movie_delay","200",log=1) genericViewMode(mode, factor, subset, label)
MMTK-2.7.9/Doc/HTML/_modules/MMTK/XML.html0000644000076600000240000005370712013143453020077 0ustar hinsenstaff00000000000000 MMTK.XML — MMTK User Guide 2.7.7 documentation

Source code for MMTK.XML

# XML I/O
#
# Written by Konrad Hinsen
#

"""
XML format for describing molecular systems

Note: this format is not used by any other program at the moment. It should
be considered experimental and subject to change.
"""

__docformat__ = 'restructuredtext'

import MMTK
from MMTK.MoleculeFactory import MoleculeFactory
from xml.etree.ElementTree import iterparse
from Scientific import N


[docs]class XMLMoleculeFactory(MoleculeFactory): """ XML molecule factory An XML molecule factory reads an XML specification of a molecular system and builds the molecule objects and universe described therein. The universe can be obtained through the attribute *universe*. :param file: the name of an XML file, or a file object """ def __init__(self, file): MoleculeFactory.__init__(self) for event, element in iterparse(file): tag = element.tag ob_id = element.attrib.get('id', None) if tag == 'molecule' and ob_id is not None: self.makeGroup(element) element.clear() elif tag == 'templates': element.clear() elif tag == 'universe': self.makeUniverse(element) def makeGroup(self, element): group = element.attrib['id'] self.createGroup(group) for molecule_element in element.findall('molecule'): self.addSubgroup(group, molecule_element.attrib.get('title', ''), molecule_element.attrib['ref']) atom_array = element.find('atomArray') if atom_array is not None: for atom_element in atom_array: self.addAtom(group, atom_element.attrib['title'], atom_element.attrib['elementType']) bond_array = element.find('bondArray') if bond_array is not None: for bond_element in bond_array: atom1, atom2 = bond_element.attrib['atomRefs2'].split() atom1 = '.'.join(atom1.split(':')) atom2 = '.'.join(atom2.split(':')) self.addBond(group, atom1, atom2) def makeUniverse(self, element): topology = element.attrib.get('topology', 'infinite') if topology == 'infinite': universe = MMTK.InfiniteUniverse() elif topology == 'periodic3d': cellshape = element.attrib['cellshape'] if cellshape == 'orthorhombic': cellsize = element.attrib['cellsize'].split() units = element.attrib.get('units', 'units:nm') factor = getattr(MMTK.Units, units.split(':')[1]) universe = MMTK.OrthorhombicPeriodicUniverse(tuple( [factor*float(size) for size in cellsize])) else: raise ValueError("cell shape %s not implemented" % cellshape) else: raise ValueError("topology %s not implemented" % topology) atom_index = 0 for subelement in element: if subelement.tag == 'molecule': molecule = self.retrieveMolecule(subelement.attrib['ref']) for atom in molecule.atomList(): atom.index = atom_index atom_index += 1 universe.addObject(molecule) elif subelement.tag == 'atom': atom = MMTK.Atom(subelement.attrib['elementType']) atom.index = atom_index atom_index += 1 universe.addObject(atom) configuration = element.find('configuration') if configuration is not None: array = configuration.find('atomArray') x = map(float, array.attrib['x3'].split()) y = map(float, array.attrib['y3'].split()) z = map(float, array.attrib['z3'].split()) units = array.attrib.get('units', 'units:nm') factor = getattr(MMTK.Units, units.split(':')[1]) array = universe.configuration().array array[:, 0] = x array[:, 1] = y array[:, 2] = z N.multiply(array, factor, array) self.universe = universe
MMTK-2.7.9/Doc/HTML/_sources/0000755000076600000240000000000012156626563016020 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/0000755000076600000240000000000012156626561017574 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/DNA/0000755000076600000240000000000012156626563020200 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/DNA/construction.py.txt0000644000076600000240000000027012000014157024073 0ustar hinsenstaff00000000000000:orphan: Constructing a DNA strand with a ligand ####################################### .. literalinclude:: ../../../Examples/DNA/construction.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/0000755000076600000240000000000012156626561021636 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/ElectricField/0000755000076600000240000000000012156626561024334 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/ElectricField/Cython/0000755000076600000240000000000012156626563025602 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/ElectricField/Cython/ElectricField.py.txt0000644000076600000240000000032512000016335031442 0ustar hinsenstaff00000000000000:orphan: An electric field term (Python part) #################################### .. literalinclude:: ../../../../../Examples/Forcefield/ElectricField/Cython/ElectricField.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/ElectricField/Cython/MMTK_electric_field.pyx.txt0000644000076600000240000000033412000016343032720 0ustar hinsenstaff00000000000000:orphan: An electric field term (Cython part) #################################### .. literalinclude:: ../../../../../Examples/Forcefield/ElectricField/Cython/MMTK_electric_field.pyx :language: cython :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/ElectricField/Python/0000755000076600000240000000000012156626563025617 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/ElectricField/Python/ElectricField.py.txt0000644000076600000240000000035712000016353031464 0ustar hinsenstaff00000000000000:orphan: An electric field term implemented in pure Python ################################################# .. literalinclude:: ../../../../../Examples/Forcefield/ElectricField/Python/ElectricField.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/0000755000076600000240000000000012156626561025432 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Cython/0000755000076600000240000000000012156626563026700 5ustar hinsenstaff00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py.txtMMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py.t0000644000076600000240000000037712000016370033504 0ustar hinsenstaff00000000000000:orphan: An efficient harmonic oscillator term (Python part) ################################################### .. literalinclude:: ../../../../../Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py :language: python :linenos: ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.pyx.txtMMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.0000644000076600000240000000040412000016374033537 0ustar hinsenstaff00000000000000:orphan: An efficient harmonic oscillator term (Cython part) ################################################### .. literalinclude:: ../../../../../Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.pyx :language: cython :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Python/0000755000076600000240000000000012156626563026715 5ustar hinsenstaff00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py.txtMMTK-2.7.9/Doc/HTML/_sources/Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py.t0000644000076600000240000000035312000016403033510 0ustar hinsenstaff00000000000000:orphan: A harmonic oscillator term in pure Python ######################################### .. literalinclude:: ../../../../../Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/LangevinDynamics/0000755000076600000240000000000012156626563023031 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/LangevinDynamics/LangevinDynamics.py.txt0000644000076600000240000000033512000016420027422 0ustar hinsenstaff00000000000000:orphan: An integrator for Langevin dynamics (Python part) ################################################# .. literalinclude:: ../../../Examples/LangevinDynamics/LangevinDynamics.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/LangevinDynamics/MMTK_langevin.c.txt0000644000076600000240000000031212000016425026414 0ustar hinsenstaff00000000000000:orphan: An integrator for Langevin dynamics (C part) ############################################ .. literalinclude:: ../../../Examples/LangevinDynamics/MMTK_langevin.c :language: c :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MDIntegrator/0000755000076600000240000000000012156626563022135 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/MDIntegrator/VelocityVerlet.pyx.txt0000644000076600000240000000027412000016435026453 0ustar hinsenstaff00000000000000:orphan: A simple Velocity Verlet integrator ################################### .. literalinclude:: ../../../Examples/MDIntegrator/VelocityVerlet.pyx :language: cython :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Miscellaneous/0000755000076600000240000000000012156626563022401 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Miscellaneous/charge_fit.py.txt0000644000076600000240000000035012000016453025635 0ustar hinsenstaff00000000000000:orphan: Fitting point charges to an electrostatic potential surface ########################################################### .. literalinclude:: ../../../Examples/Miscellaneous/charge_fit.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Miscellaneous/construct_from_pdb.py.txt0000644000076600000240000000032212000016457027441 0ustar hinsenstaff00000000000000:orphan: Building a complete universe from a PDB file ############################################ .. literalinclude:: ../../../Examples/Miscellaneous/construct_from_pdb.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Miscellaneous/lattice.py.txt0000644000076600000240000000024712000016462025174 0ustar hinsenstaff00000000000000:orphan: Place molecules on a lattice ############################ .. literalinclude:: ../../../Examples/Miscellaneous/lattice.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Miscellaneous/vector_field.py.txt0000644000076600000240000000037012000016466026215 0ustar hinsenstaff00000000000000:orphan: Vector fields for analysis and visualization of collective motions ################################################################## .. literalinclude:: ../../../Examples/Miscellaneous/vector_field.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MolecularDynamics/0000755000076600000240000000000012156626563023211 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/MolecularDynamics/argon.py.txt0000644000076600000240000000024412000016501025454 0ustar hinsenstaff00000000000000:orphan: Simulation of liquid argon ########################## .. literalinclude:: ../../../Examples/MolecularDynamics/argon.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MolecularDynamics/protein.py.txt0000644000076600000240000000024012000016505026026 0ustar hinsenstaff00000000000000:orphan: Simulation of a protein ####################### .. literalinclude:: ../../../Examples/MolecularDynamics/protein.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MolecularDynamics/restart.py.txt0000644000076600000240000000026412000016510026034 0ustar hinsenstaff00000000000000:orphan: Restarting the protein simulation ################################# .. literalinclude:: ../../../Examples/MolecularDynamics/restart.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MolecularDynamics/solvation.py.txt0000644000076600000240000000026212000016513026367 0ustar hinsenstaff00000000000000:orphan: Solvation of a protein in water ############################### .. literalinclude:: ../../../Examples/MolecularDynamics/solvation.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MonteCarlo/0000755000076600000240000000000012156626563021641 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/MonteCarlo/backbone.py.txt0000644000076600000240000000031212000016524024543 0ustar hinsenstaff00000000000000:orphan: Sampling a backbone-only configuration ensemble ############################################### .. literalinclude:: ../../../Examples/MonteCarlo/backbone.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/MPI/0000755000076600000240000000000012156626563020223 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/MPI/md.py.txt0000644000076600000240000000023212000016445021764 0ustar hinsenstaff00000000000000:orphan: Protein solvation in parallel ############################# .. literalinclude:: ../../../Examples/MPI/md.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/NormalModes/0000755000076600000240000000000012156626563022016 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/NormalModes/calpha_modes.py.txt0000644000076600000240000000033212000016532025574 0ustar hinsenstaff00000000000000:orphan: Slow normal modes of a protein using a C-alpha model #################################################### .. literalinclude:: ../../../Examples/NormalModes/calpha_modes.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/NormalModes/constrained_modes.py.txt0000644000076600000240000000034112000016535026660 0ustar hinsenstaff00000000000000:orphan: Normal modes of a protein using a rigid-residue model ##################################################### .. literalinclude:: ../../../Examples/NormalModes/constrained_modes.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/NormalModes/harmonic_force_field.py.txt0000644000076600000240000000035212000016537027305 0ustar hinsenstaff00000000000000:orphan: Normal modes of a protein using a simplified force field ######################################################## .. literalinclude:: ../../../Examples/NormalModes/harmonic_force_field.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/NormalModes/modes.py.txt0000644000076600000240000000023512000016543024270 0ustar hinsenstaff00000000000000:orphan: Normal modes of a protein ######################### .. literalinclude:: ../../../Examples/NormalModes/modes.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Proteins/0000755000076600000240000000000012156626563021401 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Proteins/analysis.py.txt0000644000076600000240000000025312000016553024370 0ustar hinsenstaff00000000000000:orphan: Comparing protein configurations ################################ .. literalinclude:: ../../../Examples/Proteins/analysis.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Proteins/construction.py.txt0000644000076600000240000000030712000016557025303 0ustar hinsenstaff00000000000000:orphan: Protein construction beyond the simple cases ############################################ .. literalinclude:: ../../../Examples/Proteins/construction.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/0000755000076600000240000000000012156626563022234 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/calpha_trajectory.py.txt0000644000076600000240000000034712000016567027107 0ustar hinsenstaff00000000000000:orphan: Extract a C-alpha trajectory from an all-atom trajectory ######################################################## .. literalinclude:: ../../../Examples/Trajectories/calpha_trajectory.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/dcd_export.py.txt0000644000076600000240000000026412000016572025536 0ustar hinsenstaff00000000000000:orphan: Convert a trajectory to DCD format ################################## .. literalinclude:: ../../../Examples/Trajectories/dcd_export.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/dcd_import.py.txt0000644000076600000240000000027012000016575025527 0ustar hinsenstaff00000000000000:orphan: Importing a trajectory in DCD format #################################### .. literalinclude:: ../../../Examples/Trajectories/dcd_import.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/fluctuations.py.txt0000644000076600000240000000032412000017075026117 0ustar hinsenstaff00000000000000:orphan: Calculating atomic fluctuations from a trajectory ################################################# .. literalinclude:: ../../../Examples/Trajectories/fluctuations.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/pdb_export.py.txt0000644000076600000240000000032412000017165025545 0ustar hinsenstaff00000000000000:orphan: Converting a trajectory to a sequence of PDB files ################################################## .. literalinclude:: ../../../Examples/Trajectories/pdb_export.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/snapshot.py.txt0000644000076600000240000000026612000016601025235 0ustar hinsenstaff00000000000000:orphan: Assembling a trajectory step by step #################################### .. literalinclude:: ../../../Examples/Trajectories/snapshot.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/trajectory_average.py.txt0000644000076600000240000000032412000016605027255 0ustar hinsenstaff00000000000000:orphan: Compute an average structure from a trajectory ############################################## .. literalinclude:: ../../../Examples/Trajectories/trajectory_average.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/trajectory_extraction.py.txt0000644000076600000240000000027712000016611030027 0ustar hinsenstaff00000000000000:orphan: Extract a subset from a trajectory ################################## .. literalinclude:: ../../../Examples/Trajectories/trajectory_extraction.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Trajectories/view_trajectory.py.txt0000644000076600000240000000026712000016615026624 0ustar hinsenstaff00000000000000:orphan: Show an animation of a trajectory ################################# .. literalinclude:: ../../../Examples/Trajectories/view_trajectory.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Visualization/0000755000076600000240000000000012156626563022437 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_sources/Examples/Visualization/additional_objects.py.txt0000644000076600000240000000035612000016623027426 0ustar hinsenstaff00000000000000:orphan: Adding custom graphics to a molecular system visualization ########################################################## .. literalinclude:: ../../../Examples/Visualization/additional_objects.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Visualization/graphics_data.py.txt0000644000076600000240000000032612000017321026367 0ustar hinsenstaff00000000000000:orphan: Extracting numerical values from graphics objects ################################################# .. literalinclude:: ../../../Examples/Visualization/graphics_data.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Visualization/vector_field_chimera.py.txt0000644000076600000240000000036112000017463027741 0ustar hinsenstaff00000000000000:orphan: Vector field visualization of a normal mode (using Chimera) ########################################################### .. literalinclude:: ../../../Examples/Visualization/vector_field_chimera.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/Examples/Visualization/vector_field_vmd.py.txt0000644000076600000240000000034512000017427027121 0ustar hinsenstaff00000000000000:orphan: Vector field visualization of a normal mode (using VMD) ####################################################### .. literalinclude:: ../../../Examples/Visualization/vector_field_vmd.py :language: python :linenos: MMTK-2.7.9/Doc/HTML/_sources/examples.txt0000644000076600000240000002044112000020170020342 0ustar hinsenstaff00000000000000.. _Examples: .. |C_alpha| replace:: C\ :sub:`α` Code Examples ############# One of the best ways to learn how to use a new tool is to look at examples. The examples given in this manual were adapted from real-life MMTK applications. They are also contained in the MMTK distribution (directory "Examples") for direct use and modification. The example molecules, system sizes, parameters, etc., were chosen to reduce execution time as much as possible, in order to enable you to run the examples interactively step by step to see how they work. If you plan to modify an example program for your own use, don't forget to check all parameters carefully to make sure that you obtain reasonable results. .. _Example-MolecularDynamics: - Molecular Dynamics examples - The program :doc:`argon.py ` contains a simulation of liquid argon at constant temperature and pressure. - The program :doc:`protein.py ` contains a simulation of a small (very small) protein in vacuum. - The program :doc:`restart.py ` shows how the simulation started in :doc:`protein.py ` can be continued. - The program :doc:`solvation.py ` contains the solvation of a protein by water molecules. .. _Example-MonteCarlo: - Monte-Carlo examples - The program :doc:`backbone.py <../Examples/MonteCarlo/backbone.py>` generates an ensemble of backbone configuration (C-alpha atoms only) for a protein. .. _Example-Trajectories: - Trajectory examples - The program :doc:`snapshot.py <../Examples/Trajectories/snapshot.py>` shows how a trajectory can be built up step by step from arbitrary data. - The program :doc:`dcd_import.py <../Examples/Trajectories/dcd_import.py>` converts a trajectory in DCD format (used by the programs CHARMM, X-Plor, and NAMD) to MMTK's format. - The program :doc:`dcd_export.py <../Examples/Trajectories/dcd_export.py>` converts an MMTK trajectory to DCD format (used by the programs CHARMM, X-Plor, and NAMD). - The program :doc:`pdb_export.py <../Examples/Trajectories/pdb_export.py>` converts an MMTK trajectory to a sequence of PDB files. - The program :doc:`trajectory_average.py <../Examples/Trajectories/trajectory_average.py>` calculates an average structure from a trajectory. - The program :doc:`trajectory_extraction.py <../Examples/Trajectories/trajectory_extraction.py>` reads a trajectory and writes a new one containing only a subset of the original universe. - The program :doc:`view_trajectory.py <../Examples/Trajectories/view_trajectory.py>` shows an animation of a trajectory, provided that an external molecule viewer with animation is available. - The program :doc:`calpha_trajectory.py <../Examples/Trajectories/calpha_trajectory.py>` shows how a much smaller |C_alpha|-only trajectory can be extracted from a trajectory containing one or more proteins. - The program :doc:`fluctuations.py <../Examples/Trajectories/fluctuations.py>` shows how to calculate atomic fluctuations from a trajectory file. .. _Example-NormalModes: - Normal mode examples - The program :doc:`modes.py <../Examples/NormalModes/modes.py>` contains a standard normal mode calculation for a small protein. - The program :doc:`constrained_modes.py <../Examples/NormalModes/constrained_modes.py>` contains a normal mode calculation for a small protein using a model in which each amino acid residue is rigid. - The program :doc:`calpha_modes.py <../Examples/NormalModes/calpha_modes.py>` contains a normal mode calculation for a mid-size protein using a |C_alpha| model and an elastic network model. - The program :doc:`harmonic_force_field.py <../Examples/NormalModes/harmonic_force_field.py>` contains a normal mode calculation for a protein using a detailed but still simple harmonic force field. .. _Example-Proteins: - Protein examples - The program :doc:`construction.py <../Examples/Proteins/construction.py>` shows some more complex examples of protein construction from PDB files. - The program :doc:`analysis.py <../Examples/Proteins/analysis.py>` demonstrates a few analysis techniques for comparing protein conformations. .. _Example-DNA: - DNA examples - The program :doc:`construction.py <../Examples/DNA/construction.py>` contains the construction of a DNA strand with a ligand. .. _Example-Forcefield: - Forcefield examples - Electric field term - A pure Python implementation (rather slow in general, but tolerable for a simple term like this one) is given in :doc:`Python/ElectricField.py <../Examples/Forcefield/ElectricField/Python/ElectricField.py>`. - A more efficient implementation has the evaluation code written in Cython (:doc:`Cython/MMTK_electric_field.pyx <../Examples/Forcefield/ElectricField/Cython/MMTK_electric_field.pyx>`) while the bookkeeping part remains in Python (:doc:`Cython/ElectricField.py <../Examples/Forcefield/ElectricField/Cython/ElectricField.py>`). - Harmonic oscillator term - A pure Python implementation (rather slow in general, but tolerable for a simple term like this one) is given in :doc:`Python/HarmonicOscillatorFF.py <../Examples/Forcefield/HarmonicOscillator/Python/HarmonicOscillatorFF.py>`. - A more efficient implementation has the evaluation code written in Cython (:doc:`Cython/MMTK_harmonic_oscillator.pyx <../Examples/Forcefield/HarmonicOscillator/Cython/MMTK_harmonic_oscillator.pyx>`) while the bookkeeping part remains in Python (:doc:`Cython/HarmonicOscillatorFF.py <../Examples/Forcefield/HarmonicOscillator/Cython/HarmonicOscillatorFF.py>`). .. _Example-MPI: - MPI examples (parallelization) - The program :doc:`md.py <../Examples/MPI/md.py>` contains a parallelized version of :doc:`solvation.py <../Examples/MolecularDynamics/solvation.py>`. .. _Example-MDIntegrator: - Molecular Dynamics integrators - The program :doc:`md.py <../Examples/MDIntegrator/VelocityVerlet.pyx>` illustrates how Molecular Dynamics integrators can be implemented in Cython. .. _Example-LangevinDynamics: - Langevin dynamics integrator - The programs :doc:`LangevinDynamics.py <../Examples/LangevinDynamics/LangevinDynamics.py>` and :doc:`MMTK_langevinmodule.c <../Examples/LangevinDynamics/MMTK_langevin.c>` implement a simple integrator for Langevin dynamics. It is meant as an example of how to write integrators etc. in C, but of course it can also be used directly. .. _Example-Visualization: - Visualization examples - The program :doc:`additional_objects.py <../Examples/Visualization/additional_objects.py>` describes the addition of custom graphics objects to the representation of a molecular system. - The program :doc:`vector_field_chimera.py <../Examples/Visualization/vector_field_chimera.py>` shows how to create a vector-field visualization of a normal mode using the graphics program Chimera for visualization. The program :doc:`vector_field_vmd.py <../Examples/Visualization/vector_field_vmd.py>` does the same but uses the graphics program VMD. - The program :doc:`graphics_data.py <../Examples/Visualization/graphics_data.py>` shows how to use a fake graphics module to extract numerical values from a vector field object. .. _Example-Miscellaneous: - Micellaneous examples - The example :doc:`charge_fit.py <../Examples/Miscellaneous/charge_fit.py>` demonstrates fitting point charges to an electrostatic potential energy surface. - The program :doc:`construct_from_pdb.py <../Examples/Miscellaneous/construct_from_pdb.py>` shows how a universe can be built from a PDB file in such a way that the internal atom ordering is compatible. This is important for exchanging data with other programs. - The program :doc:`lattice.py <../Examples/Miscellaneous/lattice.py>` constructs molecules placed on a lattice. - The program :doc:`vector_field.py <../Examples/Miscellaneous/vector_field.py>` shows how vector fields can be used in the analysis and visualization of collective motions. MMTK-2.7.9/Doc/HTML/_sources/glossary.txt0000644000076600000240000000330411662461637020424 0ustar hinsenstaff00000000000000Glossary ======== .. glossary:: Abstract base class A :term:`Base Class` that is not directly usable by itself, but which defines the common properties of several subclasses. Example: the class :class:`MMTK.ChemicalObjects.ChemicalObject` is an abstract base class which defines the common properties of its subclasses :class:`MMTK.ChemicalObjects.Atom`, :class:`MMTK.ChemicalObjects.Group`, :class:`MMTK.ChemicalObjects.Molecule`, :class:`MMTK.ChemicaObjects.Complex`, and :class:`MMTK.ChemicalObjects.AtomCluster`. A :term:`Mix-in class` is a special kind of abstract base class. Base class A class from which another class inherits. In most cases, the inheriting class is a specialization of the base class. For example, the class :class:`MMTK.ChemicalObjects.Molecule` is a base class of :class:`MMTK.Proteins.PeptideChain`, because peptide chains are special molecules. Another common application is the :term:`Abstract base class`. Mix-in class A class that is used as a :term:`Base class` in other classes with the sole intention of providing methods that are common to these classes. Mix-in classes cannot be used to create instances. They are a special kind of :term:`Abstract base class`. Example: class :class:`MMTK.Collections.GroupOfAtoms`. Subclass A class that has another class as its :term:`Base class`. The subclass is usually a specialization of the base class, and can use all of the methods defined in the base class. Example: class :class:`MMTK.Proteins.Residue` is a subclass of :class:`MMTK.ChemicalObjects.Group`. MMTK-2.7.9/Doc/HTML/_sources/index.txt0000644000076600000240000000104611662461637017671 0ustar hinsenstaff00000000000000.. MMTK User Guide documentation master file, created by sphinx-quickstart on Thu Oct 21 18:14:14 2010. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. MMTK User Guide =============== .. toctree:: :maxdepth: 3 mmtk glossary Code examples ============= .. toctree:: :maxdepth: 3 examples Module reference ================ .. toctree:: :maxdepth: 3 modules Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` MMTK-2.7.9/Doc/HTML/_sources/mmtk.txt0000644000076600000240000024253411777777067017561 0ustar hinsenstaff00000000000000MMTK User's Guide ################# for MMTK |version| by Konrad Hinsen .. |C_alpha| replace:: C\ :sub:`α` Introduction ############ The Molecular Modelling Toolkit (MMTK) presents a new approach to molecular simulations. It is not a "simulation program" with a certain set of functions that can be used by writing more or less flexible "input files", but a collection of library modules written in an easy-to-learn high-level programming language, `Python `_. This approach offers three important advantages: - Application programs can use the full power of a general and well-designed programming language. - Application programs can profit from the large set of other libraries that are available for Python. These may be scientific or non-scientific; for example, it is very easy to write simulation or analysis programs with graphical user interfaces (using the module Tkinter in the Python standard library), or couple scientific calculations with a Web server. - Any user can provide useful additions in separate modules, whereas adding features to a monolithic program requires at least the cooperation of the original author. To further encourage collaborative code development, MMTK uses a very unrestrictive licensing policy. MMTK is free software, just like Python. Although MMTK is copyrighted, anyone is allowed to use it for any purpose, including commercial ones, as well as to modify and redistribute it (a more precise description is given in the copyright statement that comes with the code). This manual describes version |version| of MMTK. The 2.x versions contain some incompatible changes with respect to earlier versions (1.x), most importantly a package structure that reduces the risk of name conflicts with other Python packages, and facilitates future enhancements. There are also many new features and improvements to existing functions. Using MMTK requires a basic knowledge of object-oriented programming and Python. Newcomers to this subject should have a look at the introductory section in this manual and at the `Python tutorial `_ (which also comes with the Python interpreter). There are also numerous `books on Python `_ that are useful in getting started. Even without MMTK, Python is a very useful programming language for scientific use, allowing rapid development and testing and easy interfacing to code written in low-level languages such as Fortran or C. This manual consists of several introductory chapters and a :ref:`module reference `. The introductory chapters explain how common tasks are handled with MMTK, but they do not describe all of its features, nor do they contain a full documentation of functions or classes. This information can be found in the :ref:`module -reference `, which describes all classes and functions intended for end-user applications module by module, using documentation extracted directly from the source code. References from the introductory sections to the module reference facilitate finding the relevant documentation. Overview ######## This chapter explains the basic structure of MMTK and its view of molecular systems. Every MMTK user should read it at least once. Using MMTK ========== MMTK applications are ordinary Python programs, and can be written using any standard text editor. For interactive use it is recommended to use one of the many tools available for interactive Python programming. MMTK tries to be as user-friendly as possible for interactive use. For example, lengthy calculations can usually be interrupted by typing Control-C. This will result in an error message ("Keyboard Interrupt"), but you can simply go on typing other commands. Interruption is particularly useful for energy minimization and molecular dynamics: you can interrupt the calculation at any time, look at the current state or do some analysis, and then continue. Modules ======= MMTK is a package consisting of various modules, most of them written in Python, and some in C for efficiency. The individual modules are described in the :ref:`module -reference `. The basic definitions that almost every application needs are collected in the top-level module, MMTK. The first line of most applications is therefore:: from MMTK import * The definitions that are specific to particular applications reside in submodules within the package MMTK. For example, force fields are defined in :mod:`MMTK.ForceFields`, and peptide chain and protein objects are defined in :mod:`MMTK.Proteins`. Python provides two ways to access objects in modules and submodules. The first one is importing a module and referring to objects in it, e.g.:: import MMTK import MMTK.ForceFields universe = MMTK.InfiniteUniverse(MMTK.ForceFields.Amber99ForceField()) The second method is importing all or some objects *from* a module:: from MMTK import InfiniteUniverse from MMTK.ForceFields import Amber99ForceField universe = InfiniteUniverse(Amber99ForceField()) These two import styles can also be mixed according to convience. In order to prevent any confusion, all objects are referred to by their full names in this manual. The Amber force field object is thus called :class:`MMTK.ForceFields.Amber99ForceField`. Of course the user is free to use selective imports in order to be able to use such objects with shorter names. Objects ======= MMTK is an object-oriented system. Since objects are everywhere and everything is an object, it is useful to know the most important object types and what can be done with them. All object types in MMTK have meaningful names, so it is easy to identify them in practice. The following overview contains only those objects that a user will see directly. There are many more object types used by MMTK internally, and also some less common user objects that are not mentioned here. Chemical objects ---------------- These are the objects that represent the parts of a molecular system: - atoms - groups - molecules - molecular complexes These objects form a simple hierarchy: complexes consist of molecules, molecules consist of groups and atoms, groups consist of smaller groups and atoms. All of these, except for groups, can be used directly to construct a molecular system. Groups can only be used in the definitions of other groups and molecules in the :ref:`overview-database`. A number of operations can be performed on chemical objects, which can roughly be classified into inquiry (constituent atoms, bonds, center of mass etc.) and modification (translate, rotate). There are also specialized versions of some of these objects. For example, MMTK defines proteins as special complexes, consisting of peptide chains, which are special molecules. They offer a range of special operations (such as selecting residues or constructing the positions of missing hydrogen atoms) that do not make sense for molecules in general. Collections ----------- Collection objects represent arbitrary collections of chemical objects. They are used to be able to refer to collections as single entities. For example, you might want to call all water molecules collectively "solvent". Most of the operations on chemical objects are also available for collections. Force fields ------------ Force field objects represent a precise description of force fields, i.e. a complete recipe for calculating the potential energy (and its derivatives) for a given molecular system. In other words, they specify not only the functional form of the various interactions, but also all parameters and the prescriptions for applying these parameters to an actual molecular system. Universes --------- Universes define complete molecular systems, i.e. they contain chemical objects. In addition, they describe interactions within the system (by a force field), boundary conditions, external fields, etc. Many of the operations that can be used on chemical objects can also be applied to complete universes. Minimizers and integrators -------------------------- A minimizer object is a special "machine" that can find local minima in the potential energy surface of a universe. You may consider this a function, if you wish, but of course functions are just special objects. Similarly, an integrator is a special "machine" that can determine a dynamical trajectory for a system on a given potential energy surface. Trajectories ------------ Minimizers and integrators can produce trajectories, which are special files containing a sequence of configurations and/or other related information. Of course trajectory objects can also be read for analysis. Variables --------- Variable objects (not to be confused with standard Python variables) describe quantities that have a value for each atom in a system, for example positions, masses, or energy gradients. Their most common use is for storing various configurations of a system. Normal modes ------------ Normal mode objects contain normal mode frequencies and atomic displacements for a given universe. MMTK provides three kinds of normal modes which correspond to three different physical situations: - Energetic modes represent the principal axes of the potential energy surface. They each have a force constant that is smallest for the most collective motions and highest for the most localized motions. Energetic modes are appropriate for describing the potential energy surface without reference to any particular dynamics, e.g. in flexibility analysis or Monte-Carlo sampling. - Vibrational modes represent the vibrational motions of a system that have well-defined frequencies. Their use implies that the system follows Newtonian dynamics or quantum dynamics. This is appropriate for small molecules, and for the most localized motions of macromolecules. - Brownian modes represent diffusional motions of an overdamped system. They describe systems with Brownian dynamics and are appropriate for describing the slow motions of macromolecules. Non-MMTK objects ---------------- An MMTK application program will typically also make use of objects provided by Python or Python library modules. A particularly useful library is the package Scientific, which is also used by MMTK itself. The most important objects are - numbers (integers, real number, complex numbers), provided by Python - vectors (in 3D coordinate space) provided by the module Scientific.Geometry. - character strings, provided by Python - files, provided by Python Of course MMTK applications can make use of the Python standard library or any other Python modules. For example, it is possible to write a simulation program that provides status reports via an integrated Web server, using the Python standard module SimpleHTTPServer. .. _overview-database: The chemical database ===================== For defining the chemical objects described above, MMTK uses a database of descriptions. There is a database for atoms, one for groups, etc. When you ask MMTK to make a specific chemical object, for example a water molecule, MMTK looks for the definition of water in the molecule database. A database entry contains everything there is to know about the object it defines: its constituents and their names, configurations, other names used e.g. for I/O, and all information force fields might need about the objects. MMTK comes with database entries for many common objects (water, amino acids, etc.). For other objects you will have to write the definitions yourself. as described in the section :ref:`database`. Force fields ============ MMTK contains everything necessary to use the `Amber 99 force field `_ on proteins, DNA, and water molecules. It uses the standard Amber parameter and modification file format. In addition to the Amber force field, there is a simple Lennard-Jones force field for noble gases, and a deformation force field for normal mode calculations on large proteins. MMTK was designed to make the addition of force field terms and the implementation of other force fields as easy as possible. Force field terms can be defined in Python (for ease of implementation) or in Cython, C, or Fortran (for efficiency). This is described in the developer's guide. Units ===== Since MMTK is not a black-box program, but a modular library, it is essential for it to use a consistent unit system in which, for example, the inverse of a frequency is a time, and the product of a mass and the square of a velocity is an energy, without additional conversion factors. Black-box programs can (and usually do) use a consistent unit system internally and convert to "conventional" units for input and output. The unit system of MMTK consists mostly of SI units of appropriate magnitude for molecular systems: =============== ========= **Measurement** **Units** =============== ========= Length nm --------------- --------- Time ps --------------- --------- Mass amu (g/mol) --------------- --------- Energy kJ/mol --------------- --------- Frequency THz (1/ps) --------------- --------- Temperature K --------------- --------- Charge e =============== ========= The module :mod:`MMTK.Units` contains convenient conversion constants for the units commonly used in computational chemistry. For example, a length of 2 Ã…ngström can be written as ``2*Units.Ang``, and a frequency can be printed in wavenumbers with ``print frequency/Units.invcm``. A simple example ================ The following simple example shows how a typical MMTK application might look like. It constructs a system consisting of a single water molecule and runs a short molecular dynamics trajectory. There are many alternative ways to do this; this particular one was chosen because it makes each step explicit and clear. The individual steps are explained in the remaining chapters of the manual. :: # Import the necessary MMTK definitions. from MMTK import * from MMTK.ForceFields import Amber99ForceField from MMTK.Trajectory import Trajectory, TrajectoryOutput, StandardLogOutput from MMTK.Dynamics import VelocityVerletIntegrator # Create an infinite universe (i.e. no boundaries, non-periodic). universe = InfiniteUniverse(Amber99ForceField()) # Create a water molecule in the universe. # Water is defined in the database. universe.molecule = Molecule('water') # Generate random velocities. universe.initializeVelocitiesToTemperature(300*Units.K) # Create an integrator. integrator = VelocityVerletIntegrator(universe) # Generate a trajectory trajectory = Trajectory(universe, "water.nc", "w") # Run the integrator for 50 steps of 1 fs, printing time and energy # every fifth step and writing time, energy, temperature, and the positions # of all atoms to the trajectory at each step. t_actions = [StandardLogOutput(5), \ TrajectoryOutput("time", "energy", \ "thermodynamic", "configuration"), 0, None, 1] integrator(delta_t = 1.*Units.fs, steps = 50, actions = t_actions) # Close the trajectory trajectory.close() Constructing a molecular system ############################### The construction of a complete system for simulation or analysis involves some or all of the following operations: - Creating molecules and other chemical objects. - Defining the configuration of all objects. - Defining the "surroundings" (e.g. boundary conditions). - Choosing a force field. MMTK offers a large range of functions to deal with these tasks. Creating chemical objects ========================= Chemical objects (atoms, molecules, complexes) are created from definitions in the :ref:`database`. Since these definitions contain most of the necessary information, the subsequent creation of the objects is a simple procedure. All objects are created by their class name (:class:`MMTK.ChemicalObjects.Atom`, :class:`MMTK.ChemicalObjects.Molecule`, and :class:`MMTK.ChemicalObjects.Complex`) with the name of the definition file as first parameter. Additional optional parameters can be specified to modify the object being created. The following optional parameters can be used for all object types: - name=string Specifies a name for the object. The default name is the one given in the definition file. - position=vector Specifies the position of the center of mass. The default is the origin. - configuration=string Indicates a configuration from the configuration dictionary in the definition file. The default is 'default' if such an entry exists in the configuration dictionary. Otherwise the object is created without atomic positions. Some examples with additional explanations for specific types: - ``Atom('C')`` creates a carbon atom. - ``Molecule('water', position=Vector(0.,0.,1.))`` creates a water molecule using configuration 'default' and moves the center of mass to the indicated position. Proteins, peptide chains, and nucleotide chains ----------------------------------------------- MMTK contains special support for working with proteins, peptide chains, and nucleotide chains. As described in the chapter :ref:`database`, proteins can be described by a special database definition file. However, it is often simpler to create macromolecular objects directly in an application program. The classes are :class:`MMTK.Proteins.PeptideChain`, :class:`MMTK.Proteins.Protein`, and :class:`MMTK.NucleicAcids.NucleotideChain`. Proteins can be created from definition files in the database, from previously constructed peptide chain objects, or directly from PDB files if no special manipulations are necessary. Examples: - Protein('insulin') creates a protein object for insulin from a database file. - Protein('1mbd.pdb') creates a protein object for myoglobin directly from a PDB file, but leaving out the heme group, which is not a peptide chain. Peptide chains are created from a sequence of residues, which can be a :class:`MMTK.PDB.PDBPeptideChain` object, a list of three-letter residue codes, or a string containing one-letter residue codes. In the last two cases the atomic positions are not defined. MMTK provides several models for the residues which provide different levels of detail: an all-atom model, a model without hydrogen atoms, two models containing only polar hydrogens (using different definitions of polar hydrogens), and a model containing only the |C_alpha| atoms, with each |C_alpha| atom having the mass of the entire residue. The last model is useful for conformational analyses in which only the backbone conformations are important. The construction of nucleotide chains is very similar. The residue list can be either a :class:`MMTK.PDB.PDBNucleotideChain` object or a list of two-letter residue names. The first letter of a residue name indicates the sugar type ('R' for ribose and'D' for desoxyribose), and the second letter defines the base ('A', 'C', and'G', plus 'T' for DNA and'U' for RNA). The models are the same as for peptide chains, except that the |C_alpha| model does not exist. Most frequently proteins and nucleotide chains are created from a PDB file. The PDB files often contain solvent (water) as well, and perhaps some other molecules. MMTK provides convenient functions for extracting information from PDB files and for building molecules from them in the module :mod:`MMTK.PDB`. The first step is the creation of a :class:`MMTK.PDB.PDBConfiguration` object from the PDB file: :: from MMTK.PDB import PDBConfiguration configuration = PDBConfiguration('some_file.pdb') The easiest way to generate MMTK objects for all molecules in the PDB file is then :: molecules = configuration.createAll() The result is a collection of molecules, peptide chains, and nucleotide chains, depending on the contents of the PDB files. There are also methods for modifying the PDBConfiguration before creating MMTK objects from it, and for creating objects selectively. See the documentation for the modules :class:`MMTK.PDB` and Scientific.IO.PDB for details, as well as the :ref:`Proteins ` and :ref:`DNA ` examples. Lattices -------- Sometimes it is necessary to generate objects (atoms or molecules) positioned on a lattice. To facilitate this task, MMTK defines lattice objects which are essentially sequence objects containing points or objects at points. Lattices can therefore be used like lists with indexing and for-loops. The lattice classes are :class:`MMTK.Geometry.RhombicLattice`, :class:`MMTK.Geometry.BravaisLattice`, and :class:`MMTK.Geometry.SCLattice`. Random numbers -------------- The Python standard library and the Numerical Python package provide random number generators, and more are available in seperate packages. MMTK provides some convenience functions that return more specialized random quantities: random points in a universe, random velocities, random particle displacement vectors, random orientations. These functions are defined in module :class:`MMTK.Random`. Collections ----------- Often it is useful to treat a collection of several objects as a single entity. Examples are a large number of solvent molecules surrounding a solute, or all sidechains of a protein. MMTK has special collection objects for this purpose, defined as :class:`MMTK.Collections.Collection`. Most of the methods available for molecules can also be used on collections. A variant of a collection is the partitioned collection, implemented in class :class:`MMTK.Collections.PartitionedCollection`. This class acts much like a standard collection, but groups its elements by geometrical position in small sub-boxes. As a consequence, some geometrical algorithms (e.g. pair search within a cutoff) are much faster, but other operations become somewhat slower. Creating universes ------------------ A universe describes a complete molecular system consisting of any number of chemical objects and a specification of their interactions (i.e. a force field) and surroundings: boundary conditions, external fields, thermostats, etc. The universe classes are defined in module MMTK: - :class:`MMTK.Universe.InfiniteUniverse` represents an infinite universe, without any boundary or periodic boundary conditions. - :class:`MMTK.Universe.ParallelepipedicPeriodicUniverse` represents a general periodic universe defined by three basis vectors. - :class:`MMTK.Universe.OrthorhombicPeriodicUniverse` represents a periodic universe with an orthorhombic elementary cell, whose size is defined by the three edge lengths. The edges are oriented along the axes of the coordinate system. - :class:`MMTK.Universe.CubicPeriodicUniverse` is a special case of :class:`MMTK.Universe.OrthorhombicPeriodicUniverse` in which the elementary cell is cubic. Universes are created empty; the contents are then added to them. Three types of objects can be added to a universe: chemical objects (atoms, molecules, etc.), collections, and environment objects (thermostats etc.). It is also possible to remove objects from a universe. Force fields ------------ MMTK comes with several force fields, and permits the definition of additional force fields. Force fields are defined in module :class:`MMTK.ForceFields.ForceField`. The most important built-in force field is the `Amber 99 force field `_, represented by the class :class:`MMTK.ForceFields.Amber.AmberForceField.Amber99ForceField`. It offers several strategies for electrostatic interactions, including Ewald summation and cutoff with charge neutralization and optional screening :ref:`[Wolf1999] `.. In addition to the Amber 99 force field, there is the older Amber 94 forcefield, a Lennard-Jones force field for noble gases (Class :class:`MMTK.ForceFields.LennardJonesFF`) and an Elastic Network Model force field for protein normal mode calculations (:class:`MMTK.ForceFields.DeformationFF.CalphaForceField`). Referring to objects and parts of objects ========================================= Most MMTK objects (in fact all except for atoms) have a hierarchical structure of parts of which they consist. For many operations it is necessary to access specific parts in this hierarchy. In most cases, parts are attributes with a specific name. For example, the oxygen atom in every water molecule is an attribute with the name "O". Therefore if ``w`` refers to a water molecule, then ``w.O`` refers to its oxygen atom. For a more complicated example, if ``m`` refers to a molecule that has a methyl group called "M1", then ``m.M1.C`` refers to the carbon atom of that methyl group. The names of attributes are defined in the database. Some objects consist of parts that need not have unique names, for example the elements of a collection, the residues in a peptide chain, or the chains in a protein. Such parts are accessed by indices; the objects that contain them are Python sequence types. Some examples: - Asking for the number of items: if ``c`` refers to a collection, then ``len(c)`` is the number of its elements. - Extracting an item: if ``p`` refers to a protein, then ``p[0]`` is its first peptide chain. - Iterating over items: if ``p`` refers to a peptide chain, then ``for residue in p: print residue.position()`` will print the center of mass positions of all its residues. Peptide and nucleotide chains also allow the operation of slicing: if ``p`` refers to a peptide chain, then ``p[1:-1]`` is a subchain extending from the second to the next-to-last residue. The structure of peptide and nucleotide chains ---------------------------------------------- Since peptide and nucleotide chains are not constructed from an explicit definition file in the database, it is not evident where their hierarchical structure comes from. But it is only the top-level structure that is treated in a special way. The constituents of peptide and nucleotide chains, residues, are normal group objects. The definition files for these group objects are in the MMTK standard database and can be freely inspected and even modified or overriden by an entry in a database that is listed earlier in MMTKDATABASE. Peptide chains are made up of amino acid residues, each of which is a group consisting of two other groups, one being called "peptide" and the other "sidechain". The first group contains the peptide group and the C and H atoms; everything else is contained in the sidechain. The C atom of the fifth residue of peptide chain ``p`` is therefore referred to as ``p[4].peptide.C_alpha``. Nucleotide chains are made up of nucleotide residues, each of which is a group consisting of two or three other groups. One group is called "sugar" and is either a ribose or a desoxyribose group, the second one is called "base" and is one the five standard bases. All but the first residue in a nucleotide chain also have a subgroup called "phosphate" describing the phosphate group that links neighbouring residues. Analyzing and modifying atom properties ======================================= General operations ------------------ Many inquiry and modification operations act at the atom level and can equally well be applied to any object that is made up of atoms, i.e. atoms, molecules, collections, universes, etc. These operations are defined once in a :term:`Mix-in class` called :class:`MMTK.Collections.GroupOfAtoms`, but are available for all objects for which they make sense. They include inquiry-type functions (total mass, center of mass, moment of inertia, bounding box, total kinetic energy etc.), coordinate modifications (translation, rotation, application of :ref:`transformation`) and coordinate comparisons (RMS difference, optimal fits). .. _transformation: Coordinate transformations -------------------------- The most common coordinate manipulations involve translations and rotations of specific parts of a system. It is often useful to refer to such an operation by a special kind of object, which permits the combination and analysis of transformations as well as its application to atomic positions. Transformation objects specify a general displacement consisting of a rotation around the origin of the coordinate system followed by a translation. They are defined in the module ``Scientific.Geometry``, but for convenience the module ``MMTK`` contains a reference to them as well. Transformation objects corresponding to pure translations can be created with ``Translation(displacement)``; transformation objects describing pure rotations with ``Rotation(axis, angle)`` or ``Rotation(rotation_matrix)``. Multiplication of transformation objects returns a composite transformation. The translational component of any transformation can be obtained by calling the method ``translation()``; the rotational component is obtained analogously with ``rotation()``. The displacement vector for a pure translation can be extracted with the method ``displacement()``, a tuple of axis and angle can be extracted from a pure rotation by calling ``axisAndAngle()``. .. _atom_property: Atomic property objects ----------------------- Many properties in a molecular system are defined for each individual atom: position, velocity, mass, etc. Such properties are represented in special objects, defined in module MMTK: :class:`MMTK.ParticleProperties.ParticleScalar` for scalar quantities, :class:`MMTK.ParticleProperties.ParticleVector` for vector quantities, and :class:`MMTK.ParticleProperties.ParticleTensor` for rank-2 tensors. All these objects can be indexed with an atom object to retrieve or change the corresponding value. Standard arithmetic operations are also defined, as well as some useful methods. Configurations -------------- A configuration object, represented by the class :class:`MMTK.ParticleProperties.Configuration` is a special variant of a :class:`MMTK.ParticleProperties.ParticleVector` object. In addition to the atomic coordinates of a universe, it stores geometric parameters of a universe that are subject to change, e.g. the edge lengths of the elementary cell of a periodic universe. Every universe has a current configuration, which is what all operations act on by default. It is also the configuration that is updated by minimizations, molecular dynamics, etc. The current configuration can be obtained by calling the method ``configuration()``. There are two ways to create configuration objects: by making a copy of the current configuration (with ``universe.copyConfiguration()``, or by reading a configuration from a :ref:`trajectory `. Minimization and Molecular Dynamics ################################### .. _Trajectories: Trajectories ============ Minimization and dynamics algorithms produce sequences of configurations that are often stored for later analysis. In fact, they are often the most valuable result of a lengthy simulation run. To make sure that the use of trajectory files is not limited by machine compatibility, MMTK stores trajectories in `netCDF `_ files. These files contain binary data, minimizing disk space usage, but are freely interchangeable between different machines. In addition, there are a number of programs that can perform standard operations on arbitrary netCDF files, and which can therefore be used directly on MMTK trajectory files. Finally, netCDF files are self-describing, i.e. contain all the information needed to interpret their contents. An MMTK trajectory file can thus be inspected and processed without requiring any further information. For illustrations of trajectory operations, see the :ref:`examples `. Trajectory file objects are represented by the class :class:`MMTK.Trajectory.Trajectory`. They can be opened for reading, writing, or modification. The data in trajectory files can be stored in single precision or double precision; single-precision is usually sufficient, but double-precision files are required to reproduce a given state of the system exactly. A trajectory is closed by calling the method ``close()``. If anything has been written to a trajectory, closing it is required to guarantee that all data has been written to the file. Closing a trajectory after reading is recommended in order to prevent memory leakage, but is not strictly required. Newly created trajectories can contain all objects in a universe or any subset; this is useful for limiting the amount of disk space occupied by the file by not storing uninteresting parts of the system, e.g. the solvent surrounding a protein. It is even possible to create a trajectory for a subset of the atoms in a molecule, e.g. for only the |C_alpha| atoms of a protein. The universe description that is stored in the trajectory file contains all chemical objects of which at least one atom is represented. When a trajectory is opened for reading, no universe object needs to be specified. In that case, MMTK creates a universe from the description contained in the trajectory file. This universe will contain the same objects as the one for which the trajectory file was created, but not necessarily have all the properties of the original universe (the description contains only the names and types of the objects in the universe, but not, for example, the force field). The universe can be accessed via the attribute universe of the trajectory. If the trajectory was created with partial data for some of the objects, reading data from it will set the data for the missing parts to "undefined". Analysis operations on such systems must be done very carefully. In most cases, the trajectory data will contain the atomic configurations, and in that case the "defined" atoms can be extracted with the method ``atomsWithDefinedPositions()``. MMTK trajectory files can store various data: atomic positions, velocities, energies, energy gradients etc. Each trajectory-producing algorithm offers a set of quantities from which the user can choose what to put into the trajectory. Since a detailed selection would be tedious, the data is divided into classes, e.g. the class "energy" stands for potential energy, kinetic energy, and whatever other energy-related quantities an algorithm produces. For optimizing I/O efficiency, the data layout in a trajectory file can be modified by the block_size parameter. Small block sizes favour reading or writing all data for one time step, whereas large block sizes (up to the number of steps in the trajectory) favour accessing a few values for all time steps, e.g. scalar variables like energies or trajectories for individual atoms. The default value of the block size is one. Every trajectory file contains a history of its creation. The creation of the file is logged with time and date, as well as each operation that adds data to it with parameters and the time/date of start and end. This information, together with the comment and the number of atoms and steps contained in the file, can be obtained with the function :func:`MMTK.Trajectory.trajectoryInfo`. It is possible to read data from a trajectory file that is being written to by another process. For efficiency, trajectory data is not written to the file at every time step, but only approximately every 15 minutes. Therefore the amount of data available for reading may be somewhat less than what has been produced already. Options for minimization and dynamics ===================================== Minimizers and dynamics integrators accept various optional parameter specifications. All of them are selected by keywords, have reasonable default values, and can be specified when the minimizer or integrator is created or when it is called. In addition to parameters that are specific to each algorithm, there is a general parameter ``actions`` that specifies actions that are executed periodically, including trajectory and console output. Periodic actions ---------------- Periodic actions are specified by the keyword parameter ``actions`` whose value is a list of periodic actions, which defaults to an empty list. Some of these actions are applicable to any trajectory-generating algorithm, especially the output actions. Others make sense only for specific algorithms or specific universes, e.g. the periodic rescaling of velocities during a Molecular Dynamics simulation. Each action is described by an action object. The step numbers for which an action is executed are specified by three parameters. The parameter ``first`` indicates the number of the first step for which the action is executed, and defaults to 0. The parameter ``last`` indicates the last step for which the action is executed, and default to ``None``, meaning that the action is executed indefinitely. The parameter ``skip`` speficies how many steps are skipped between two executions of the action. The default value of 1 means that the action is executed at each step. Of course an action object may have additional parameters that are specific to its action. The output actions are defined in the module :mod:`MMTK.Trajectory` and can be used with any trajectory-generating algorithm. They are: - :class:`MMTK.Trajectory.TrajectoryOutput` for writing data to a trajectory. Note that it is possible to use several trajectory output actions simultaneously to write to multiple trajectories. It is thus possible, for example, to write a short dense trajectory during a dynamics run for analyzing short-time dynamics, and simultaneously a long-time trajectory with a larger step spacing, for analyzing long-time dynamics. - :class:`MMTK.Trajectory.RestartTrajectoryOutput`, which is a specialized version of :class:`MMTK.Trajectory.TrajectoryOutput`. It writes the data that the algorithm needs in order to be restarted to a restart trajectory file. A restart trajectory is a trajectory that stores a fixed number of steps which are reused cyclically, such that it always contain the last few steps of a trajectory. - :class:`MMTK.Trajectory.LogOutput` for text output of data to a file. - :class:`MMTK.Trajectory.StandardLogOutput`, a specialized version of :class:`MMTK.Trajectory.LogOutput` that writes the data classes "time" and "energy" during the whole simulation run to standard output. The other periodic actions are meaningful only for Molecular Dynamics simulations: - :class:`MMTK.Dynamics.VelocityScaler` is used for rescaling the velocities to force the kinetic energy to the value defined by some temperature. This is usually done during initial equilibration. - :class:`MMTK.Dynamics.BarostatReset` resets the barostat coordinate to zero and is during initial equilibration of systems in the NPT ensemble. - :class:`MMTK.Dynamics.Heater` rescales the velocities like :class:`MMTK.Dynamics.VelocityScaler`, but increases the temperature step by step. - :class:`MMTK.Dynamics.TranslationRemover` subtracts the global translational velocity of the system from all individual atomic velocities. This prevents a slow but systematic energy flow into the degrees of freedom of global translation, which occurs with most MD integrators due to non-perfect conservation of momentum. - :class:`MMTK.Dynamics.RotationRemover` subtracts the global angular velocity of the system from all individual atomic velocities. This prevents a slow but systematic energy flow into the degrees of freedom of global rotation, which occurs with most MD integrators due to non-perfect conservation of angular momentum. Fixed atoms ----------- During the course of a minimization or molecular dynamics algorithm, the atoms move to different positions. It is possible to exclude specific atoms from this movement, i.e. fixing them at their initial positions. This has no influence whatsoever on energy or force calculations; the only effect is that the atoms' positions never change. Fixed atoms are specified by giving them an attribute fixed with a value of one. Atoms that do not have an attributefixed, or one with a value of zero, move according to the selected algorithm. .. _energy_minimization: Energy minimization =================== MMTK has two energy minimizers using different algorithms: steepest descent (:class:`MMTK.Minimization.SteepestDescentMinimizer`) and conjugate gradient (:class:`MMTK.Minimization.ConjugateGradientMinimizer`) . Steepest descent minimization is very inefficient if the goal is to find a local minimum of the potential energy. However, it has the advantage of always moving towards the minimum that is closest to the starting point and is therefore ideal for removing bad contacts in a unreasonably high energy configuration. For finding local minima, the conjugate gradient algorithm should be used. Both minimizers accept three specific optional parameters: - ``steps`` (an integer) to specify the maximum number of steps (default is 100) - ``step_size`` (a number) to specify an initial step length used in the search for a minimum (default is 2 pm) - ``convergence`` (a number) to specify the gradient norm (more precisely the root-mean-square length) at which the minimization should stop (default is 0.01 kJ/mol/nm) There are three classes of trajectory data: "energy" includes the potential energy and the norm of its gradient, "configuration" stands for the atomic positions, and "gradients" stands for the energy gradients at each atom position. The following example performs 100 steps of steepest descent minimization without producing any trajectory or printed output: :: from MMTK import * from MMTK.ForceFields import Amber99ForceField from MMTK.Minimization import SteepestDescentMinimizer universe = InfiniteUniverse(Amber99ForceField()) universe.protein = Protein('insulin') minimizer = SteepestDescentMinimizer(universe) minimizer(steps = 100) See also the example file :ref:`modes.py `. Molecular dynamics ================== The techniques described in this section are illustrated by several :ref:`examples `. Velocities ---------- The integration of the classical equations of motion for an atomic system requires not only positions, but also velocities for all atoms. Usually the velocities are initialized to random values drawn from a normal distribution with a variance corresponding to a certain temperature. This is done by calling the method :func:`~MMTK.Universe.Universe.initializeVelocitiesToTemperature` on a universe. Note that the velocities are assigned atom by atom; no attempt is made to remove global translation or rotation of the total system or any part of the system. During equilibration of a system, it is common to multiply all velocities by a common factor to restore the intended temperature. This can done explicitly by calling the method :func:`~MMTK.Universe.Universe.scaleVelocitiesToTemperature` on a universe, or by using the action object :class:`MMTK.Dynamics.VelocityScaler`. Distance constraints -------------------- A common technique to eliminate the fastest (usually uninteresting) degrees of freedom, permitting a larger integration time step, is the use of distance constraints on some or all chemical bonds. MMTK allows the use of distance constraints on any pair of atoms, even though constraining anything but chemical bonds is not recommended due to considerable modifications of the dynamics of the system :ref:`[vanGunsteren1982] `, :ref:`[Hinsen1995] `. MMTK permits the definition of distance constraints on all atom pairs in an object that are connected by a chemical bond by calling the method setBondConstraints. Usually this is called for a complete universe, but it can also be called for a chemical object or a collection of chemical objects. The :func:`~MMTK.ChemicalObjects.ChemicalObject.removeDistanceConstraints` removes all distance constraints from the object for which it is called. Constraints defined as described above are automatically taken into account by Molecular Dynamics integrators. It is also possible to enforce the constraints explicitly by calling the method :func:`~MMTK.Universe.Universe.enforceConstraints` for a universe. This has the effect of modifying the configuration and the velocities (if velocities exist) in order to make them compatible with the constraints. Thermostats and barostats ------------------------- A standard Molecular Dynamics integration allows time averages corresponding to the NVE ensemble, in which the number of molecules, the system volume, and the total energy are constant. This ensemble does not represent typical experimental conditions very well. Alternative ensembles are the NVT ensemble, in which the temperature is kept constant by a thermostat, and the NPT ensemble, in which temperature and pressure are kept constant by a thermostat and a barostat. To obtain these ensembles in MMTK, thermostat and barostat objects must be added to a universe. In the presence of these objects, the Molecular Dynamics integrator will use the extended-systems method for producing the correct ensemble. The classes to be used are :class:`MMTK.Environment.NoseThermostat` and :class:`MMTK.Environment.AndersenBarostat`. Integration ----------- A Molecular Dynamics integrator based on the "Velocity Verlet" algorithm :ref:`[Swope1982] `, which was extended to handle distance constraints as well as thermostats and barostats :ref:`[Kneller1996] `, is implemented by the class :class:`MMTK.Dynamics.VelocityVerletIntegrator`. It has two optional keyword parameters: - ``steps`` (an integer) to specify the number of steps (default is 100) - ``delta_t`` (a number) to specify the time step (default 1 fs) There are three classes of trajectory data: "energy" includes the potential energy and the kinetic energy, as well as the energies of thermostat and barostat coordinates if they exist, "time" stands for the time, "thermodynamic" stand for temperature and pressure, "configuration" stands for the atomic positions, "velocities" stands for the atomic velocities, and "gradients" stands for the energy gradients at each atom position. The following example performs a 1000 step dynamics integration, storing every 10th step in a trajectory file and removing the total translation and rotation every 50th step: :: from MMTK import * from MMTK.ForceFields import Amber99ForceField from MMTK.Dynamics import VelocityVerletIntegrator, \ TranslationRemover, \ RotationRemover from MMTK.Trajectory import TrajectoryOutput universe = InfiniteUniverse(Amber99ForceField()) universe.protein = Protein('insulin') universe.initializeVelocitiesToTemperature(300.*Units.K) actions = [TranslationRemover(0, None, 50), \ RotationRemover(0, None, 50), \ TrajectoryOutput("insulin.nc", \ ("configuration", "energy", "time"), \ 0, None, 10)] integrator = VelocityVerletIntegrator(universe, delta_t = 1.*Units.fs, \ actions = actions) integrator(steps = 1000) Snapshots ========= A snapshot generator allows writing the current system state to a trajectory. It works much like a zero-step minimization or dynamics run, i.e. it takes the same optional arguments for specifying the trajectory and protocol output. A snapshot generator is created using the class :class:`MMTK.Trajectory.SnapshotGenerator`. Normal modes ############ Normal mode analysis provides an analytic description of the dynamics of a system near a minimum using an harmonic approximation to the potential. Before a normal mode analysis can be started, the system must be brought to a local minimum of the potential energy by :ref:`energy minimization `, except when special force fields designed only for normal mode analysis are used (e.g. :class:`MMTK.ForceFields.CalphaFF.CalphaForceField`). See also the :ref:`Normal Modes ` examples. A standard normal mode analysis is performed by creating a normal modes object, implemented in the classes :class:`MMTK.NormalModes.EnergeticModes.EnergeticModes`, :class:`MMTK.NormalModes.VibrationalModes.VibrationalModes`, and :class:`MMTK.NormalModes.BrownianModes.BrownianModes`. A normal mode object behaves like a sequence of mode objects, which store the atomic displacement vectors corresponding to each mode and the associated force constant, vibrational frequency, or inverse relaxation time. For short-ranged potentials, it is advantageous to store the second derivatives of the potential in a sparse-matrix form and to use an iterative method to determine some or all modes. This permits the treatment of larger systems that would normally require huge amounts of memory. Another approach to deal with large systems is the restriction to low-frequency modes which are supposed to be well representable by linear combinations of a given set of basis vectors. The basis vectors can be obtained from a basis for the full Cartesian space by elimination of known fast degrees of freedom (e.g. bonds); the module :mod:`MMTK.Subspace` contains support classes for this approach. It is also possible to construct a suitable basis vector set from small-deformation vector fields (e.g. :class:`MMTK.FourierBasis.FourierBasis`). Analysis operations ################### Analysis is the most non-standard part of molecular simulations. The quantities that must be calculated depend strongly on the system and the problem under study. MMTK provides a wide range of elementary operations that inquire the state of the system, as well as several more complex analysis tools. Some of them are demonstrated in the :ref:`example ` section. Properties of chemical objects and universes ============================================ Many operations access and modify various properties of an object. They are defined for the most general type of object: anything that can be broken down to atoms, i.e. atoms, molecules, collections, universes, etc., i.e. in the class :class:`MMTK.Collections.GroupOfAtoms`. The most elementary operations are inquiries about specific properties of an object: number of atoms, total mass, center of mass, total momentum, total charge, etc. There are also operations that compare two different conformations of a system. Finally, there are special operations for analyzing conformations of peptide chains and proteins. Geometrical operations in periodic universes require special care. Whenever a distance vector between two points in a systems is evaluated, the minimum-image convention must be used in order to obtain consistent results. MMTK provides routines for finding these distance vectors as well as distances, angles, and dihedral angles between any points. Because these operations depend on the topology and geometry of the universe, they are implemented as methods in class :class:`MMTK.Universe.Universe` and its subclasses. Of course they are available for non-periodic universes as well. Universes also provide methods for obtaining :ref:`atom property ` objects that describe the state of the system (configurations, velocities, masses), and for restoring the system state from a :ref:`trajectory ` file. Energy evaluation ================= Energy evaluation requires a force field, and therefore all the methods in this section are defined only for universe objects, i.e. in class :class:`MMTK.Universe.Universe`. However, they all take an optional arguments (anything that can be broken down into atoms) that indicates for which subset of the universe the energy is to be evaluated. In addition to the potential energy, energy gradients and second derivatives (force constants) can be obtained, if the force field implements them. There is also a method that returns a dictionary containing the values for all the individual force field terms, which is often useful for analysis. Surfaces and volumes ==================== Surfaces and volumes can be analyzed for anything consisting of atoms. Both quantities are defined by assigning a radius to each atom; the surface of the resulting conglomerate of overlapping spheres is taken to be the surface of the atom group. Atom radii for surface determination are usually called "van der Waals radii", but there is no unique method for determining them. MMTK uses the values from :ref:`[Bondi1964] `. However, users can change these values for each individual atom by assigning a new value to the attribute ``vdW_radius``. The operations provided in :mod:`MMTK.MolecularSurface` include basic surface and volume calculation, determination of exposed atoms, and identification of contacts between two objects. Miscellaneous operations ######################## Saving, loading, and copying objects ==================================== MMTK provides an easy way to store (almost) arbitrary objects in files and retrieve them later. All objects of interest to users can be stored, including chemical objects, collections, universes, normal modes, configurations, etc. It is also possible to store standard Python objects such as numbers, lists, dictionaries etc., as well as practically any user-defined objects. Storage is based on the standard Python module pickle. Objects are saved with :func:`MMTK.save` and restored with :func:`MMTK.load`. If several objects are to be stored in a single file, use tuples: ``save((object1, object2), filename)`` and ``object1, object2 = load(filename)`` to retrieve the objects. Note that storing an object in a file implies storing all objects referenced by it as well, such that the size of the file can become larger than expected. For example, a configuration object contains a reference to the universe for which it is defined. Therefore storing a configuration object means storing the whole universe as well. However, nothing is ever written twice to the same file. If you store a list or a tuple containing a universe and a configuration for it, the universe is written only once. Frequently it is also useful to copy an object, such as a molecule or a configuration. There are two functions (which are actually taken from the Python standard library module copy) for this purpose, which have a somewhat different behaviour for container-type objects (lists, dictionaries, collections etc.). :func:`MMTK.copy` returns a copy of the given object. For a container object, it returns a new container object which contains the same objects as the original one. If the intention is to get a container object which contains copies of the original contents, then ``MMTK.deepcopy(object)`` should be used. For objects that are not container-type objects, there is no difference between the two functions. Exporting to specific file formats and visualization ==================================================== MMTK can write objects in specific file formats that can be used by other programs. Three file formats are supported: the PDB format, widely used in computational chemistry, the DCD format for trajectories, written by the programs CHARMM, X-Plor, and NAMDm, and read by many visualization programs, and the VRML format, understood by VRML browsers as a representation of a three-dimensional scene for visualization. MMTK also provides a more general interface that can generate graphics objects in any representation if a special module for that representation exists. In addition to facilitating the implementation of new graphics file formats, this approach also permits the addition of custom graphics elements (lines, arrows, spheres, etc.) to molecular representations. PDB, VRML, and DCD files ------------------------ Any chemical object, collection, or universe can be written to a PDB or VRML file by calling the method ``writeToFile``, defined in class :class:`MMTK.Collections.GroupOfAtoms`. PDB files are read via the class :class:`MMTK.PDB.PDBConfiguration`. DCD files can be read by a :class:`MMTK.DCD.DCDReader` object. For writing DCD files, there is the function :func:`MMTK.DCD.writeDCDPDB`, which also creates a compatible PDB file without which the DCD file could not be interpreted. Special care must be taken to ensure a correct mapping of atom numbers when reading from a DCD file. In MMTK, each atom object has a unique identity and atom numbers, also used internally for efficiency, are not strictly necessary and are not used anywhere in MMTK's application programming interface. DCD file, however, simply list coordinates sorted by atom number. For interpreting DCD files, another file must be available which allows the identification of atoms from their number and vice versa; this can for example be a PDB file. When reading DCD files, MMTK assumes that the atom order in the DCD file is identical to the internal atom numbering of the universe for which the DCD file is read. This assumption is in general valid only if the universe has been created from a PDB file that is compatible with the DCD file, without any additions or removals. Visualization and animation --------------------------- The most common need for file export is visualization. All objects that can be visualized (chemical systems and subsets thereof, normal mode objects, trajectories) provide a method view which creates temporary export files, starts a visualization program, and deletes the temporary files. Depending on the object type there are various optional parameters. MMTK also allows visualization of normal modes and trajectories using animation. Since not all visualization programs permit animation, and since there is no standard way to ask for it, animation is implemented only for the programs `XMol `_ and `VMD `_. Animation is available for normal modes, trajectories, and arbitrary sequences of configurations (see function :func:`MMTK.Visualization.viewSequence`). For more specialized needs, MMTK permits the creation of graphical representations of most of its objects via general graphics modules that have to be provided externally. Suitable modules are provided in the package Scientific.Visualization and cover VRML (version 1), VRML2 (aka VRML97), and the molecular visualization program VMD. Modules for other representations (e.g. rendering programs) can be written easily; it is recommended to use the existing modules as an example. The generation of graphics objects is handled by the method graphicsObjects, defined in the class :class:`MMTK.Visualization.Viewable`, which is a :term:`Mix-in class` that makes graphics objects generation available for all objects that define chemical systems or parts thereof, as well as for certain other objects that are viewable. The explicit generation of graphics objects permits the mixture of different graphical representations for various parts of a system, as well as the combination of MMTK-generated graphics objects with arbitrary other graphics objects, such as lines, arrows, or spheres. All graphics objects are finally combined into a scene object (also defined in the various graphics modules) in order to be displayed. See also the :ref:`visualization ` examples. Fields ====== For analyzing or visualizing atomic properties that change little over short distances, it is often convenient to represent these properties as functions of position instead of one value per atom. Functions of position are also known as fields, and mathematical techniques for the analysis of fields have proven useful in many branches of physics. Such a field can be obtained by averaging over the values corresponding to the atoms in a small region of space. MMTK provides classes for scalar and vector field in module :mod:`MMTK.Field`. See also the example :ref:`vector_field.py `. Charge fitting ============== A frequent problem in determining force field parameters is the determination of partial charges for the atoms of a molecule by fitting to the electrostatic potential around the molecule, which is obtained from quantum chemistry programs. Although this is essentially a straightforward linear least-squares problem, many procedures that are in common use do not use state-of-the-art techniques and may yield erroneous results. MMTK provides a charge fitting method that is numerically stable and allows the imposition of constraints on the charges. It is implemented in module :mod:`MMTK.ChargeFit`. See also the example :ref:`charge_fit.py `. .. _database: Constructing the database ######################### MMTK uses a database of chemical entities to define the properties of atoms, molecules, and related objects. This database consists of plain text files, more precisely short Python programs, whose names are the names of the object types. This chapter explains how to construct and manage these files. Note that the standard database already contains many definitions, in particular for proteins and nucleic acids. You do not need to read this chapter unless you want to add your own molecule definitions. MMTK's database does not have to reside in a single place. It can consist of any number of subdatabases, each of which can be a directory or a URL. Typically the database consists of at least two parts: MMTK's standard definitions and a user's personal definitions. When looking up an object type in the database, MMTK checks the value of the environment variable MMTKDATABASE. The value of this variable must be a list of subdatabase locations seperated by white space. If the variable MMTKDATABASE is not defined, MMTK uses a default value that contains the path ".mmtk/Database" in the user's home directory followed by MMTK's standard database, which resides in the directory Database within the MMTK package directory (on many Unix systems this is ``/usr/local/lib/python2.x/site-packages/MMTK``). MMTK checks the subdatabases in the order in which they are mentioned in MMTKDATABASE. Each subdatabase contains directories corresponding to the object classes, i.e. Atoms (atom definitions), Groups (group definitions), Molecules (molecule definitions), Complexes (complex definitions), Proteins (protein definitions), and PDB (Protein Data Bank files). These directories contain the definition files, whose names may not contain any upper-case letters. These file names correspond to the object types, e.g. the call ``MMTK.Molecule('Water')`` will cause MMTK to look for the file Molecules/water in the database (note that the names are converted to lower case). The remaining sections of this chapter explain how the individual definition files are constructed. Keep in mind that each file is actually a Python program, so of course standard Python syntax rules apply. Atom definitions ================ An atom definition in MMTK describes a chemical element, such as "hydrogen". This should not be confused with the "atom types" used in force field descriptions and in some modelling programs. As a consequence, it is rarely necessary to add atom definitions to MMTK. Atom definition files are short and of essentially identical format. This is the definition for carbon: :: name = 'carbon' symbol = 'C' mass = [(12, 98.90), (13.003354826, 1.10)] color = 'black' vdW_radius = 0.17 The name should be meaningful to users, but is not used by MMTK itself. The symbol, however, is used to identify chemical elements. It must be exactly equal to the symbol defined by IUPAC, including capitalization (e.g. 'Cl' for chlorine). The mass can be either a number or a list of tuples, as shown above. Each tuple defines an isotope by its mass and its percentage of occurrence; the percentages must add up to 100. The color is used for VRML output and must equal one of the color names defined in the module VRML. The van der Waals radius is used for the calculation of molecular volumes and surfaces; the values are taken from :ref:`[Bondi1964] `. An application program can create an isolated atom with ``Atom('c')`` or, specifying an initial position, with ``Atom('c', position=Vector(0.,1.,0.))``. The element name can use any combination of upper and lower case letters, which are considered equivalent. Group definitions ================= Group definitions in MMTK exist to facilitate the definition of molecules by avoiding the frequent repetition of common combinations. MMTK doesn't give any physical meaning to groups. Groups can contain atoms and other groups. Their definitions look exactly like molecule definitions; the only difference between groups and molecules is the way they are used. This is the definition of a methyl group: :: name = 'methyl group' C = Atom('C') H1 = Atom('H') H2 = Atom('H') H3 = Atom('H') bonds = [Bond(C, H1), Bond(C, H2), Bond(C, H3)] pdbmap = [('MTH', {'C': C, 'H1': H1, 'H2': H2, 'H3': H3})] amber_atom_type = {C: 'CT', H1: 'HC', H2: 'HC', H3: 'HC'} amber_charge = {C: 0., H1: 0.1, H2: 0.1, H3: 0.1} The name should be meaningful to users, but is not used by MMTK itself. The following lines create the atoms in the group and assign them to variables. These variables become attributes of whatever object uses this group; their names can be anything that is a legal Python name. The list of bonds, however, must be assigned to the name ``bonds``. The bond list is used by force fields and for visualization. The name ``pdbmap`` is used for reading and writing PDB files. Its value must be a list of tuples, where each tuple defines one PDB residue. The first element of the tuple is the residue name, which is used only for output. The second element is a dictionary that maps PDB atom names to the actual atoms. The ``pdbmap`` entry of any object can be overridden by an entry in a higher-level object. Therefore the entry for a group is only used for atoms that do not occur in the entry for a molecule that contains this group. The remaining lines in the definition file contain information specific to force fields, in this case the Amber force field. The dictionary ``amber_atom_type`` defines the atom type for each atom; the dictionary ``amber_charge`` defines the partial charges. As for ``pdbmap`` entries, these definitions can be overridden by higher-level definitions. Molecule definitions ==================== Molecules are typically used directly in application programs, but they can also be used in the definition of complexes. Molecule definitions can use atoms and groups. This is the definition of a water molecule: :: name = 'water' structure = \ " O \n" + \ " / \ \n" + \ "H H \n" O = Atom('O') H1 = Atom('H') H2 = Atom('H') bonds = [Bond(O, H1), Bond(O, H2)] pdbmap = [('HOH', {'O': O, 'H1': H1, 'H2': H2})] pdb_alternative = {'OH2': 'O'} amber_atom_type = {O: 'OW', H1: 'HW', H2: 'HW'} amber_charge = {O: -0.83400, H1: 0.41700, H2: 0.41700} configurations = { 'default': ZMatrix([[H1], \ [O, H1, 0.9572*Ang], \ [H2, O, 0.9572*Ang, H1, 104.52*deg]]) } The name should be meaningful to users, but is not used by MMTK itself. The structure is optional and not used by MMTK either. The following lines create the atoms in the group and assign them to variables. These variables become attributes of the molecule, i.e. when a water molecule is created in an application program by ``w = Molecule('water')``, then ``w.H1`` will refer to its first hydrogen atom. The names of these variables can be any legal Python names. The list of bonds, however, must be assigned to the name ``bonds``. The bond list is used by force fields and for visualization. The name ``pdbmap`` is used for reading and writing PDB files. Its value must be a list of tuples, where each tuple defines one PDB residue. The first element of the tuple is the residue name, which is used only for output. The second element is a dictionary that maps PDB atom names to the actual atoms. The ``pdbmap`` entry of any object can be overridden by an entry in a higher-level object, i.e. in the case of a molecule a complex containing it. The name ``pdb_alternative`` allows to read PDB files that use non-standard names. When a PDB atom name is not found in the ``pdbmap``, an attempt is made to translate it to another name using ``pdb_alternative``. The two following lines in the definition file contain information specific to force fields, in this case the Amber force field. The dictionary ``amber_atom_type`` defines the atom type for each atom; the dictionary ``amber_charge`` defines the partial charges. As for pdbmap entries, these definitions can be overridden by higher-level definitions. The name ``configurations`` can be defined to be a dictionary of configurations for the molecule. During the construction of a molecule, a configuration can be specified via an optional parameter, e.g. ``w = Molecule('water', configuration='default')``. The names of the configurations can be arbitrary; only the name "default" has a special meaning; it is applied by default if no other configuration is specified when constructing the molecule. If there is no default configuration, and no other configuration is explicitly specified, then the molecule is created with undefined atomic positions. There are three ways of describing configurations: - By a Z-Matrix: :: ZMatrix([[H1], \ [O, H1, 0.9572*Ang], \ [H2, O, 0.9572*Ang, H1, 104.52*deg]]) - By Cartesian coordinates: :: Cartesian({O: ( 0.004, -0.00518, 0.0), H1: (-0.092, -0.00518, 0.0), H2: ( 0.028, 0.0875, 0.0)}) - By a PDB file: :: PDBFile('water.pdb') The PDB file must be in the database subdirectory PDB, unless a full path name is specified for it. Complex definitions =================== Complexes are defined much like molecules, except that they are composed of molecules and atoms; no groups are allowed, and neither are bonds. Protein definitions =================== Protein definitions can take many different forms, depending on the source of input data and the type of information that is to be stored. For proteins it is particularly useful that database definition files are Python programs with all their flexibility. The most common way of constructing a protein is from a PDB file. This is an example for a protein definition: :: name = 'insulin' # Read the PDB file. conf = PDBConfiguration('insulin.pdb') # Construct the peptide chains. chains = conf.createPeptideChains() # Clean up del conf The name should be meaningful to users, but is not used by MMTK itself. The second command reads the sequences of all peptide chains from a PDB file. Everything which is not a peptide chain is ignored. The following line constructs a PeptideChain object (a special molecule) for each chain from the PDB sequence. This involves constructing positions for any missing hydrogen atoms. Finally, the temporary data ("conf") is deleted, otherwise it would remain in memory forever. The net result of a protein definition file is the assignment of a list of molecules (usually PeptideChain objects) to the variable "chains". MMTK then constructs a protein object from it. To use the above example, an application program would use the command ``p = Protein('insulin')``. The construction of the protein involves one nontrivial (but automatic) step: the construction of disulfide bridges for pairs of cystein residues whose sulfur atoms have a distance of less then 2.5 Ã…ngström. Threads and parallelization ########################### This chapter explains the use of threads by MMTK and MMTK's parallelization support. This is an advanced topic, and not essential for the majority MMTK applications. You need to read this chapter only if you use multiprocessor computers, or if you want to implement multi-threaded programs that use MMTK. Threads are different execution paths through a program that are executed in parallel, at least in principle; real parallel execution is possible only on multiprocessor systems. MMTK makes use of threads in two ways, which are conceptually unrelated: parallelization of energy evaluation on shared-memory multiprocessor computers, and support for multithreaded applications. Thread support is not available on all machines; you can check if yous system supports threads by starting a Python interpreter and typing import threading. If this produces an error message, then your system does not support threads, otherwise it is available in Python and also in MMTK. If you do not have thread support in Python although you know that your operating system supports threads, you might have compiled your Python interpreter without thread support; in that case, MMTK does not have thread support either. Another approach to parallelization is message passing: several processors work on a program and communicate via a fast network to share results. A standard library, called MPI (Message Passing Interface), has been developped for sharing data by message passing, and implementations are available for all parallel computers currently on the market. MMTK contains elementary support for parallelization by message passing: only the energy evaluation has been paralellized, using a data-replication strategy, which is simple but not the most efficient for large systems. MPI support is disabled by default. Enabling it involves modifying the file Src/Setup.template prior to compilation of MMTK. Furthermore, an MPI-enabled installation of ScientificPython is required, and the mpipython executable must be used instead of the standard Python interpreter. Threads and message passing can be used together to use a cluster of shared-memory machines most efficiently. However, this requires that the thread and MPI implementations being used work together; sometimes there are conflicts, for example due to the use of the same signal in both libraries. Refer to your system documentation for details. The use of threads for parallelization on shared-memory systems is very simple: Just set the environment variable MMTK_ENERGY_THREADS to the desired value. If this variable is not defined, the default value is 1, i.e. energy evaluations are performed serially. For choosing an appropriate value for this environment variable, the following points should be considered: - The number of energy evaluation threads should not be larger than the number of processors that are fully dedicated to the MMTK application. A larger number of threads does not lead to wrong results, but it can increase the total execution time. - MMTK assumes that all processors are equally fast. If you use a heteregenous multiprocessor machine, in which the processors have different speeds, you might find that the total execution time is larger than without threads. - The use of threads incurs some computational overhead. For very small systems, it is usually faster not to use threads. - Not all energy terms necessarily support threads. Of the force field terms that part of MMTK, only the multipole algorithms for electrostatic interactions does not support threads, but additional force fields defined outside MMTK might also be affected. MMTK automatically evaluates such energy terms with a single thread, such that there is no risk of getting wrong results. However, you might not get the performance you expect. - If second derivatives of the potential energy are requested, energy evaluation is handled by a single thread. An efficient implementation of multi-threaded energy evaluation would require a separate copy of the second-derivative matrix per thread. This approach needs too much memory for big systems to be feasible. Since second derivatives are almost exclusively used for normal mode calculations, which need only a single energy evaluation, multi-thread support is not particularly important anyway. Parallelization via message passing is somewhat more complicated. In the current MMTK parallelization model, all processors execute the same program and replicate all tasks, with the important exception of energy evaluation. Energy terms are divided evenly between the processors, and at the end the energy and gradient values are shared by all machines. This is the only step involving network communication. Like thread-based parallelization, message-passing parallelization does not support the evaluation of second derivatives. A special problem with message-passing systems is input and output. The MMTK application must ensure that output files are written by only one processor, and that all processors correctly access input files, especially in the case of each processor having its own disk space. See the example :ref:`md.py ` for illustration. Multithreaded applications are applications that use multiple threads in order to simplify the implementation of certain algorithms, i.e. not necessarily with the goal of profiting from multiple processors. If you plan to write a multithreaded application that uses MMTK, you should first make sure you understand threading support in Python. In particular, you should keep in mind that the global interpreter lock prevents the effective use of multiple processors by Python code; only one thread at a time can execute interpreted Python code. C code called from Python can permit other threads to execute simultaneously; MMTK does this for energy evaluation, molecular dynamics integration, energy minimization, and normal mode calculation. A general problem in multithreaded applications is access to resources that are shared among the threads. In MMTK applications, the most important shared resource is the description of the chemical systems, i.e. universe objects and their contents. Chaos would result if two threads tried to modify the state of a universe simultaneously, or even if one thread uses information that is simultaneously being modified by another thread. Synchronization is therefore a critical part of multithreaded application. MMTK provides two synchronization aids, both of which described in the documentation of the class :class:`MMTK.Universe.Universe`: the configuration change lock (methods :func:`~MMTK.Universe.Universe.acquireConfigurationChangeLock` and :func:`~MMTK.Universe.Universe.releaseConfigurationChangeLock`), and the universe state lock (methods :func:`~MMTK.Universe.Universe.acquireReadStateChangeLock`, :func:`~MMTK.Universe.Universe.releaseReadStateChangeLock`, :func:`~MMTK.Universe.Universe.acquireWriteStateChangeLock`, and :func:`~MMTK.Universe.Universe.releaseWriteStateChangeLock`). Only a few common universe operations manipulate the universe state lock in order to avoid conflicts with other threads; these methods are marked as thread-safe in the description. All other operations should only be used inside a code section that is protected by the appropriate manipulation of the state lock. The configuration change lock is less critical; it is used only by the molecular dynamics and energy minimization algorithms in MMTK. Bibliography ############ .. _Bondi1964: [Bondi1964] | A. Bondi | `van der Waals Volumes and Radii `_ | 1964 .. _Eisenhaber1993: [Eisenhaber1993] | F. Eisenhaber, P. Argos | `Improved Strategy in Analytic Surface Calculation for Molecular Systems: Handling of Singularities and Computational Efficiency `_ | 1993 .. _Eisenhaber1995: [Eisenhaber1995] | F. Eisenhaber, P. Lijnzaad, P. Argos, M. Scharf | `The Double Cubic Lattice Method: Efficient Approaches to Numerical Integration of Surface Area and Volume and to Dot Surface Contouring of Molecular Assemblies `_ | 1995 .. _Hinsen1995: [Hinsen1995] | Konrad Hinsen, Gerald R. Kneller | `Influence of constraints on the dynamics of polypeptide chains `_ | 1995 .. _Hinsen1997: [Hinsen1997] | Konrad Hinsen, Benoit Roux | `An accurate potential for simulating proton transfer in acetylacetone `_ | 1997 .. _Hinsen1998: [Hinsen1998] | Konrad Hinsen | `Analysis of domain motions by approximate normal mode calculations `_ | 1998 .. _Hinsen1999: [Hinsen1999] | Konrad Hinsen, Aline Thomas, Martin J. Field | `Analysis of domain motions in large proteins `_ | 1999 .. _Hinsen1999a: [Hinsen1999a] | Konrad Hinsen, Gerald R. Kneller | `Projection methods for the analysis of complex motions in macromolecules `_ | 1999 .. _Hinsen1999b: [Hinsen1999b] | Konrad Hinsen, Gerald R. Kneller | `A simplified force field for describing vibrational protein dynamics over the whole frequency range `_ | 1999 .. _Hinsen2000: [Hinsen2000] | Konrad Hinsen, Andrei J. Petrescu, Serge Dellerue, Marie-Claire Bellissent-Funel, Gerald R. Kneller. | `Harmonicity in slow protein dynamics `_ | 2000 .. _Kneller1990: [Kneller1990] | Gerald R. Kneller | `Superposition of molecular structures using quaternions `_ | 1990 .. _Kneller1996: [Kneller1996] | Gerald R. Kneller, Thomas Mülders | `Nosé-Andersen dynamics of partially rigid molecules: Coupling of all degrees of freedom to heat and pressure baths `_ | 1996 .. _Swope1982: [Swope1982] | W.C. Swope, H.C. Andersen, P.H. Berens, K.R. Wilson | `A computer simulation method for the calculation of equilibrium constants for the formation of physical clusters of molecules: application to small water clusters `_ | 1982 .. _vanGunsteren1982: [vanGunsteren1982] | Wilfred F. van Gunsteren, Martin Karplus | `Effect of Constraints on the Dynamics of Macromolecules `_ | 1982 .. _Viduna2000: [Viduna2000] | David Viduna, Konrad Hinsen, Gerald R. Kneller | `The influence of molecular flexibility on DNA radiosensitivity: A simulation study `_ | 2000 .. _Wolf1999: [Wolf1999] | D. Wolf, P. Keblinski, S.R. Philpot, J. Eggebrecht | `Exact method for the simulation of Coulombic systems by spherically truncated, pairwise r\ :sup:`-1` summation `_ | 1999 MMTK-2.7.9/Doc/HTML/_sources/modules.txt0000644000076600000240000001135611662461637020237 0ustar hinsenstaff00000000000000.. _Reference: .. |C_alpha| replace:: C\ :sub:`α` Module Reference ################ .. Undocumented modules __pkginfo__ AtomEnvironment CCPNDataModel ComplexEnvironment CrystalEnvironment Database GroupEnvironment Features ForceFields.Amber.AmberData ForceFields.BondedInteractions ForceFields.ForceField ForceFields.NonBondedInteractions ForceFields.MMForceField MoleculeEnvironment NormalModes.Core PDBML ProteinEnvironment PyMOL surfm tess Skeleton ThreadManager Tk Tk.ProteinVisualization Utility MMTK ===== .. automodule:: MMTK MMTK.Biopolymers ================ .. automodule:: MMTK.Biopolymers MMTK.Bonds ========== .. automodule:: MMTK.Bonds MMTK.ChargeFit ============== .. automodule:: MMTK.ChargeFit MMTK.ChemicalObjects ==================== .. automodule:: MMTK.ChemicalObjects MMTK.Collections ================ .. automodule:: MMTK.Collections MMTK.ConfigIO ============= .. automodule:: MMTK.ConfigIO MMTK.DCD ======== .. automodule:: MMTK.DCD :exclude-members: writePDB, writeDCD MMTK.Deformation ================ .. automodule:: MMTK.Deformation MMTK.Dynamics ============= .. automodule:: MMTK.Dynamics MMTK.Environment ================ .. automodule:: MMTK.Environment MMTK.Field ========== .. automodule:: MMTK.Field MMTK.ForceFields ================ MMTK.ForceFields.AmberForceField -------------------------------- .. automodule:: MMTK.ForceFields.Amber.AmberForceField :no-members: MMTK.ForceFields.Amber94ForceField ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autoclass:: MMTK.ForceFields.Amber.Amber94ForceField MMTK.ForceFields.Amber99ForceField ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autoclass:: MMTK.ForceFields.Amber.Amber99ForceField MMTK.ForceFields.OPLSForceField ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. autoclass:: MMTK.ForceFields.Amber.OPLSForceField MMTK.ForceFields.AnisotropicNetworkForceField --------------------------------------------- .. autoclass:: MMTK.ForceFields.ANMFF.AnisotropicNetworkForceField MMTK.ForceFields.CalphaForceField -------------------------------------- .. autoclass:: MMTK.ForceFields.CalphaFF.CalphaForceField MMTK.ForceFields.DeformationForceField -------------------------------------- .. autoclass:: MMTK.ForceFields.DeformationFF.DeformationForceField MMTK.ForceFields.HarmonicForceField ----------------------------------- .. autoclass:: MMTK.ForceFields.BondFF.HarmonicForceField MMTK.ForceFields.LennardJonesForceField --------------------------------------- .. autoclass:: MMTK.ForceFields.LennardJonesFF.LennardJonesForceField MMTK.ForceFields.SPCEForceField ------------------------------- .. autoclass:: MMTK.ForceFields.SPCEFF.SPCEForceField MMTK.ForceFields.ForceFieldTest ------------------------------- .. automodule:: MMTK.ForceFields.ForceFieldTest MMTK.ForceFields.Restraints --------------------------- .. automodule:: MMTK.ForceFields.Restraints MMTK.FourierBasis ================= .. automodule:: MMTK.FourierBasis MMTK.Geometry ============= .. automodule:: MMTK.Geometry MMTK.InternalCoordinates ======================== .. automodule:: MMTK.InternalCoordinates :show-inheritance: MMTK.Minimization ================= .. automodule:: MMTK.Minimization MMTK.MolecularSurface ===================== .. automodule:: MMTK.MolecularSurface MMTK.MoleculeFactory ==================== .. automodule:: MMTK.MoleculeFactory MMTK.NormalModes ================ .. automodule:: MMTK.NormalModes MMTK.NormalModes.BrownianModes ------------------------------ .. automodule:: MMTK.NormalModes.BrownianModes MMTK.NormalModes.EnergeticModes ------------------------------- .. automodule:: MMTK.NormalModes.EnergeticModes MMTK.NormalModes.VibrationalModes --------------------------------- .. automodule:: MMTK.NormalModes.VibrationalModes MMTK.NucleicAcids ================= .. automodule:: MMTK.NucleicAcids MMTK.PDB ======== .. automodule:: MMTK.PDB MMTK.PDBMoleculeFactory ----------------------- .. automodule:: MMTK.PDBMoleculeFactory MMTK.ParticleProperties ======================= .. automodule:: MMTK.ParticleProperties MMTK.ProteinFriction ==================== .. automodule:: MMTK.ProteinFriction MMTK.Proteins ============= .. automodule:: MMTK.Proteins MMTK.Random =========== .. automodule:: MMTK.Random MMTK.Solvation ============== .. automodule:: MMTK.Solvation MMTK.Subspace ============= .. automodule:: MMTK.Subspace MMTK.Trajectory =============== .. automodule:: MMTK.Trajectory MMTK.Units ========== .. automodule:: MMTK.Units MMTK.Universe ============= .. automodule:: MMTK.Universe MMTK.Visualization ================== .. automodule:: MMTK.Visualization MMTK.XML ======== .. automodule:: MMTK.XML MMTK-2.7.9/Doc/HTML/_static/0000755000076600000240000000000012156626563015624 5ustar hinsenstaff00000000000000MMTK-2.7.9/Doc/HTML/_static/ajax-loader.gif0000644000076600000240000000124111761607760020477 0ustar hinsenstaff00000000000000GIF89aòÿÿÿU|ÆÖßN€U|l–®Š«¾™¶Æ!þCreated with ajaxload.info!ù !ÿ NETSCAPE2.0,3ºÜþ0ÊIkc:œN˜f E±1º™Á¶.`ÄÂqÐ-[9ݦ9 JkçH!ù ,4ºÜþNŒ! „ »°æŠDqBQT`1 `LE[¨|µußía€ ×â†C²%$*!ù ,6º2#+ÊAÈÌ”V/…côNñIBa˜«pð ̳½ƨ+YíüƒÃ2©dŸ¿!ù ,3ºb%+Ê2†‘ìœV_…‹¦ …! 1D‡aªF‚°ÑbR]ó=08,Ȥr9L!ù ,2ºr'+JçdðóL &vÃ`\bT”…„¹hYB)ÏÊ@é<Ã&,ȤR’!ù ,3º Â9ãtç¼Úž0Çà!.B¶ÊW¬¢1  sa»°5÷•0° ‰»Ÿm)J!ù ,2ºÜþð ÙœU]šîÚqp•`ˆÝaœÝ4–…AFÅ0`›¶ Â@›1€ÂÖΑ!ù ,2ºÜþ0ÊI«eBÔœ)×à ŽÇq10©Ê°®PÂaVÚ¥ ub‚ž[;MMTK-2.7.9/Doc/HTML/_static/basic.css0000644000076600000240000002041712013143456017406 0ustar hinsenstaff00000000000000/* * basic.css * ~~~~~~~~~ * * Sphinx stylesheet -- basic theme. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.sphinxsidebar ul { list-style: none; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar #searchbox input[type="text"] { width: 170px; } div.sphinxsidebar #searchbox input[type="submit"] { width: 30px; } img { border: 0; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable { width: 100%; } table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } div.modindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } div.genindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } p.rubric { margin-top: 30px; font-weight: bold; } img.align-left, .figure.align-left, object.align-left { clear: left; float: left; margin-right: 1em; } img.align-right, .figure.align-right, object.align-right { clear: right; float: right; margin-left: 1em; } img.align-center, .figure.align-center, object.align-center { display: block; margin-left: auto; margin-right: auto; } .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 5px; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.citation td { border-bottom: none; } /* -- other body styles ----------------------------------------------------- */ ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlighted { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .refcount { color: #060; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa; } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } .guilabel, .menuselection { font-family: sans-serif; } .accelerator { text-decoration: underline; } .classifier { font-style: oblique; } abbr, acronym { border-bottom: dotted 1px; cursor: help; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; overflow-y: hidden; /* fixes display issues on Chrome browsers */ } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .viewcode-link { float: right; } .viewcode-back { float: right; font-family: sans-serif; } div.viewcode-block:target { margin: -1px -10px; padding: 0 10px; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } }MMTK-2.7.9/Doc/HTML/_static/comment-bright.png0000644000076600000240000000665411761607760021263 0ustar hinsenstaff00000000000000‰PNG  IHDRóÿa OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛbKGDÿÿÿ ½§“ pHYs  šœtIMEÚ 6 B©\<ÞIDAT8Ë…’Kh]e…¿½ÿs1mAÛÄÚ`j‚Ïh[-ˆE(FEŠÁaAœ! bI« àÈ*–BX‘"Ø4)NŠõUR‚Zˆ¹­!’×Mhj“›ssÎùÿíà–¨àãmØ‹Å^‹-\ggßÏ ÷ßì]o|ÑÑÒ¬[3±¶4§Á§6»”û©òèø¯×>zd‘¿ ]½#Œ»î8ÙþüáÇOݺ±t{5·uIÍXN!I=@Vf¾®Ÿ=v×ÀÞþ1ûº}e>;ØÉö×fvìénøvËÍÅxaÉHrÏʪJ’¦Fȹ`œÈðDò¹WZ®]ÀžSíýŸø%S)ÌWAÌœb¹ |0K=âSo7D†~\~qâÍ-ïÀËŸ\ùaóMÅ“Z,S'*æô™‘È} óF`—†ÎNnzæ674¸öËUÈç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛbKGDÿÿÿ ½§“ pHYs  šœtIMEÚ!â›ÈÝ,IDAT8Ëe’_HuÇ?Ïï}ßsŽž3ÍyòË•¶¦‹U2MvQÉÖŠFÔE¬.ŠÑÍÃÅ‚­ÄŠbÑE$DD­‹ËZF5b@QÌ"š:2§›š¦¾ïû{Ÿn.êsõåçû<_ø yî?ô½m÷²ýè·wV™ê@t£R`}Z íÄÐ_£# _=œá_@ÝËý ßw^óRë®·•%6gC-έ(K>ä| $„Éï{¯}ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛbKGDÿÿÿ ½§“ pHYs  šœtIMEÚ 1;ïV·¿§IDAT8Ëu‘ËkÜUÇ?ßsgœ4ÔØøhª‚`µ©ÖG1 RQ‚”îܸp%èBªø”n"‚bРXJ ‹.4V iZð##T;m£µ!4™üæžãbâP­~7÷rîù>ιbwïý†cû†; m;‡oª”ÓAÜàΆ ζZ^«/®þôä£Ãç¸|îs¯ÝÉø{Óý;†¯y¿»Rº¥ð¸Â=È9(rÉt¦Vo¼¾û¡­ûG÷Í1±wíÞÿ#_àÓ©¹›{»¿ìî*•›E&ç å!€€ˆÀƒ(—Lç–VŸßuïÀ«oœéêûÁᲵ‘DŽÀ€ P„‡²G”“4ÿçÊ Ü:&€¯ç~™êî*ݳÖreˆuá: ‚ááS­-,ßUšœ©^Ÿ’ú›E&·™JY[ÃPà!RˆìB ŖޞʖR@_ÎôÈ€dBfó”€NvHfÂ"è2ØTÊî]­ˆR‘’ ³ö j§'BàÖ1‰ddAak…/DIJD… ’D2‘ÌH&L`&L† $Ex,6‹|Ö~_\©¿Pœ‘ $™ýMH`I˜©=Ÿ @¨±Z|õÈÎÁ|ttv´gcåЕ—WTZ'¤õ3rŽÈîje"ܵx¾9ÿö›¯°W> ¹mb©Ñ|by¥ˆ•fFRx{wí%Dúõå¹Z½±€áCíÿÞüô$õwdüÀôðÖ«ÞH¦mW÷nètaµ(ŠM<~;9¿ôáž]C/ñ_¸ãåŸ;÷ÉãÕ«§æã‹Õ#Ÿ}ûÀáÉïoÿ`zS§áÚ·ù_>:;x컓§?Ÿ©yóÝ©ÿ|}æ’~ûwam-/ž®7ž=¾0úìS÷5è»ØíR翚¾P"*Ö¯ IEND®B`‚MMTK-2.7.9/Doc/HTML/_static/default.css0000644000076600000240000000771012013143456017752 0ustar hinsenstaff00000000000000/* * default.css_t * ~~~~~~~~~~~~~ * * Sphinx stylesheet -- default theme. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ @import url("basic.css"); /* -- page layout ----------------------------------------------------------- */ body { font-family: sans-serif; font-size: 100%; background-color: #11303d; color: #000; margin: 0; padding: 0; } div.document { background-color: #1c4e63; } div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: #ffffff; color: #000000; padding: 0 20px 30px 20px; } div.footer { color: #ffffff; width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { color: #ffffff; text-decoration: underline; } div.related { background-color: #133f52; line-height: 30px; color: #ffffff; } div.related a { color: #ffffff; } div.sphinxsidebar { } div.sphinxsidebar h3 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h3 a { color: #ffffff; } div.sphinxsidebar h4 { font-family: 'Trebuchet MS', sans-serif; color: #ffffff; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: #ffffff; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; color: #ffffff; } div.sphinxsidebar a { color: #98dbcc; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } /* -- hyperlink styles ------------------------------------------------------ */ a { color: #355f7c; text-decoration: none; } a:visited { color: #355f7c; text-decoration: none; } a:hover { text-decoration: underline; } /* -- body styles ----------------------------------------------------------- */ div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: 'Trebuchet MS', sans-serif; background-color: #f2f2f2; font-weight: normal; color: #20435c; border-bottom: 1px solid #ccc; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; } a.headerlink:hover { background-color: #c60f0f; color: white; } div.body p, div.body dd, div.body li { text-align: justify; line-height: 130%; } div.admonition p.admonition-title + p { display: inline; } div.admonition p { margin-bottom: 5px; } div.admonition pre { margin-bottom: 5px; } div.admonition ul, div.admonition ol { margin-bottom: 5px; } div.note { background-color: #eee; border: 1px solid #ccc; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.topic { background-color: #eee; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } p.admonition-title { display: inline; } p.admonition-title:after { content: ":"; } pre { padding: 5px; background-color: #eeffcc; color: #333333; line-height: 120%; border: 1px solid #ac9; border-left: none; border-right: none; } tt { background-color: #ecf0f3; padding: 0 1px 0 1px; font-size: 0.95em; } th { background-color: #ede; } .warning tt { background: #efc2c2; } .note tt { background: #d6d6d6; } .viewcode-back { font-family: sans-serif; } div.viewcode-block:target { background-color: #f4debf; border-top: 1px solid #ac9; border-bottom: 1px solid #ac9; }MMTK-2.7.9/Doc/HTML/_static/doctools.js0000644000076600000240000001527011761607760020014 0ustar hinsenstaff00000000000000/* * doctools.js * ~~~~~~~~~~~ * * Sphinx JavaScript utilities for all documentation. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /** * select a different prefix for underscore */ $u = _.noConflict(); /** * make the code below compatible with browsers without * an installed firebug like debugger if (!window.console || !console.firebug) { var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; window.console = {}; for (var i = 0; i < names.length; ++i) window.console[names[i]] = function() {}; } */ /** * small helper function to urldecode strings */ jQuery.urldecode = function(x) { return decodeURIComponent(x).replace(/\+/g, ' '); } /** * small helper function to urlencode strings */ jQuery.urlencode = encodeURIComponent; /** * This function returns the parsed url parameters of the * current request. Multiple values per key are supported, * it will always return arrays of strings for the value parts. */ jQuery.getQueryParameters = function(s) { if (typeof s == 'undefined') s = document.location.search; var parts = s.substr(s.indexOf('?') + 1).split('&'); var result = {}; for (var i = 0; i < parts.length; i++) { var tmp = parts[i].split('=', 2); var key = jQuery.urldecode(tmp[0]); var value = jQuery.urldecode(tmp[1]); if (key in result) result[key].push(value); else result[key] = [value]; } return result; }; /** * small function to check if an array contains * a given item. */ jQuery.contains = function(arr, item) { for (var i = 0; i < arr.length; i++) { if (arr[i] == item) return true; } return false; }; /** * highlight a given string on a jquery object by wrapping it in * span elements with the given class name. */ jQuery.fn.highlightText = function(text, className) { function highlight(node) { if (node.nodeType == 3) { var val = node.nodeValue; var pos = val.toLowerCase().indexOf(text); if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { var span = document.createElement("span"); span.className = className; span.appendChild(document.createTextNode(val.substr(pos, text.length))); node.parentNode.insertBefore(span, node.parentNode.insertBefore( document.createTextNode(val.substr(pos + text.length)), node.nextSibling)); node.nodeValue = val.substr(0, pos); } } else if (!jQuery(node).is("button, select, textarea")) { jQuery.each(node.childNodes, function() { highlight(this); }); } } return this.each(function() { highlight(this); }); }; /** * Small JavaScript module for the documentation. */ var Documentation = { init : function() { this.fixFirefoxAnchorBug(); this.highlightSearchWords(); this.initIndexTable(); }, /** * i18n support */ TRANSLATIONS : {}, PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, LOCALE : 'unknown', // gettext and ngettext don't access this so that the functions // can safely bound to a different name (_ = Documentation.gettext) gettext : function(string) { var translated = Documentation.TRANSLATIONS[string]; if (typeof translated == 'undefined') return string; return (typeof translated == 'string') ? translated : translated[0]; }, ngettext : function(singular, plural, n) { var translated = Documentation.TRANSLATIONS[singular]; if (typeof translated == 'undefined') return (n == 1) ? singular : plural; return translated[Documentation.PLURALEXPR(n)]; }, addTranslations : function(catalog) { for (var key in catalog.messages) this.TRANSLATIONS[key] = catalog.messages[key]; this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); this.LOCALE = catalog.locale; }, /** * add context elements like header anchor links */ addContextElements : function() { $('div[id] > :header:first').each(function() { $('\u00B6'). attr('href', '#' + this.id). attr('title', _('Permalink to this headline')). appendTo(this); }); $('dt[id]').each(function() { $('\u00B6'). attr('href', '#' + this.id). attr('title', _('Permalink to this definition')). appendTo(this); }); }, /** * workaround a firefox stupidity */ fixFirefoxAnchorBug : function() { if (document.location.hash && $.browser.mozilla) window.setTimeout(function() { document.location.href += ''; }, 10); }, /** * highlight the search words provided in the url in the text */ highlightSearchWords : function() { var params = $.getQueryParameters(); var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; if (terms.length) { var body = $('div.body'); window.setTimeout(function() { $.each(terms, function() { body.highlightText(this.toLowerCase(), 'highlighted'); }); }, 10); $('') .appendTo($('#searchbox')); } }, /** * init the domain index toggle buttons */ initIndexTable : function() { var togglers = $('img.toggler').click(function() { var src = $(this).attr('src'); var idnum = $(this).attr('id').substr(7); $('tr.cg-' + idnum).toggle(); if (src.substr(-9) == 'minus.png') $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); else $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); }).css('display', ''); if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { togglers.click(); } }, /** * helper function to hide the search marks again */ hideSearchWords : function() { $('#searchbox .highlight-link').fadeOut(300); $('span.highlighted').removeClass('highlighted'); }, /** * make the url absolute */ makeURL : function(relativeURL) { return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; }, /** * get the current relative url */ getCurrentURL : function() { var path = document.location.pathname; var parts = path.split(/\//); $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { if (this == '..') parts.pop(); }); var url = parts.join('/'); return path.substring(url.lastIndexOf('/') + 1, path.length - 1); } }; // quick alias for translations _ = Documentation.gettext; $(document).ready(function() { Documentation.init(); }); MMTK-2.7.9/Doc/HTML/_static/down-pressed.png0000644000076600000240000000056011761607760020744 0ustar hinsenstaff00000000000000‰PNG  IHDRóÿasRGB®ÎébKGDùC» pHYs × ×B(›xtIMEÚ -vF#ðIDAT8ËÍÒ!OAàïÚJ, ++@ I v¢bÿ@Wñ7F’ HNâ±ú# ‚4¡8Ì6¹4×6Tñ’MvvÞ¼7³»êœûöDs¿‡aóxâ1†U îq‚;<¦ˆÏ E¸Â-f)âºj%ßpˆo4xFà78G…>æ)â-ƒ ž ¡ÂEYm4%7YTk-¾–Q¶a–"NWAo-y†eqÒá¾,)â ÓÒYÓÑú´ptŽÐå½\hóq´Îím˜sÔz¦ìG]ÄNñ‡Òa…‡röçß¶¨s^lã vh\î2Ù%ðâßãŽ0EeRvØIEND®B`‚MMTK-2.7.9/Doc/HTML/_static/down.png0000644000076600000240000000055311761607760017303 0ustar hinsenstaff00000000000000‰PNG  IHDRóÿasRGB®ÎébKGDùC» pHYs × ×B(›xtIMEÚ"ÅíU{ëIDAT8ËÍÒ¡NCAÐóÚJ, ++@ ™4>‡¨â/ÐUü’¤^,†~T&Ô3M^^^ÛPÅM6ÙÙ¹sïÌî*¥ôí‰RJ¿‡a)e¼GñÃ*ƒœàñ¹¡èW¸Å<"®«Fò ‡øFgÜã78G…>q ƒ†ÁOI¨p‘«‰:s“õAÕjñ5GÙ†yDœ®ƒ^+y†U:ép_%G§@D|ašÕ­O“£s„Æ(ïy¡M,"â¨Íím˜sÔx:÷£.b§@D|`–V˜åÙŸÛ²”²ÜÆìиÜe²KàÅ¿Ç/êG!‚ ™IEND®B`‚MMTK-2.7.9/Doc/HTML/_static/file.png0000644000076600000240000000061011761607760017245 0ustar hinsenstaff00000000000000‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ  )¶TIDAT8Ë­‘±JÄ@†¿Ir('[ "&xØÙYZ ‚Xø0‚!i|†_@±Ô÷•t§ÓDÄæÏ] ¹#¹Äxÿjv˜ùç› Y–ÐN‡ažE‘i«(ŠÌÄÉ™yž£µ@D¦£&±ˆ`Û6®ë–P¦Zk’$)5%"ôz½Ê.NñA#Aœba‘`Vsø¾_3ñc°,«™àä2m¼Ýñþjó [kŸìlv¹y|!IÕ´ðþyô;ÀðvÈé "Œß®°—a©?ŸAúðÄ7Œ`ô˜ñÇc^énôk?¸²Bg}»TЙ¹D#ÁÑÞ "R¹D1÷£çyüEŽRê*ŽãÝ6MJ©3þK_U«t8F~ÇIEND®B`‚MMTK-2.7.9/Doc/HTML/_static/jquery.js0000644000076600000240000021475611761607760017517 0ustar hinsenstaff00000000000000/*! * jQuery JavaScript Library v1.4.2 * http://jquery.com/ * * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2010, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Sat Feb 13 22:33:48 2010 -0500 */ (function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& (d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== "find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, "_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== "="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); (function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= {},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== "string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== 1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, ""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", ""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, "border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== "string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? "&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== 1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== "json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== "number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": "pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); MMTK-2.7.9/Doc/HTML/_static/minus.png0000644000076600000240000000030711761607760017464 0ustar hinsenstaff00000000000000‰PNG  IHDR &Îàq pHYs  šœtIME× <®8åtEXtCommentöÌ–¿RIDATÓczô(BÅñãÇáÒpö¿ÿ¨èˆip»‘¹P÷îÝÃc· ¸ |¶IEND®B`‚MMTK-2.7.9/Doc/HTML/_static/pygments.css0000644000076600000240000000753412013143456020200 0ustar hinsenstaff00000000000000.highlight .hll { background-color: #ffffcc } .highlight { background: #eeffcc; } .highlight .c { color: #408090; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #303030 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #208050 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #208050 } /* Literal.Number.Float */ .highlight .mh { color: #208050 } /* Literal.Number.Hex */ .highlight .mi { color: #208050 } /* Literal.Number.Integer */ .highlight .mo { color: #208050 } /* Literal.Number.Oct */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */MMTK-2.7.9/Doc/HTML/_static/searchtools.js0000644000076600000240000003725312013143456020505 0ustar hinsenstaff00000000000000/* * searchtools.js_t * ~~~~~~~~~~~~~~~~ * * Sphinx JavaScript utilties for the full-text search. * * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /** * helper function to return a node containing the * search summary for a given text. keywords is a list * of stemmed words, hlwords is the list of normal, unstemmed * words. the first one is used to find the occurance, the * latter for highlighting it. */ jQuery.makeSearchSummary = function(text, keywords, hlwords) { var textLower = text.toLowerCase(); var start = 0; $.each(keywords, function() { var i = textLower.indexOf(this.toLowerCase()); if (i > -1) start = i; }); start = Math.max(start - 120, 0); var excerpt = ((start > 0) ? '...' : '') + $.trim(text.substr(start, 240)) + ((start + 240 - text.length) ? '...' : ''); var rv = $('
').text(excerpt); $.each(hlwords, function() { rv = rv.highlightText(this, 'highlighted'); }); return rv; } /** * Porter Stemmer */ var Stemmer = function() { var step2list = { ational: 'ate', tional: 'tion', enci: 'ence', anci: 'ance', izer: 'ize', bli: 'ble', alli: 'al', entli: 'ent', eli: 'e', ousli: 'ous', ization: 'ize', ation: 'ate', ator: 'ate', alism: 'al', iveness: 'ive', fulness: 'ful', ousness: 'ous', aliti: 'al', iviti: 'ive', biliti: 'ble', logi: 'log' }; var step3list = { icate: 'ic', ative: '', alize: 'al', iciti: 'ic', ical: 'ic', ful: '', ness: '' }; var c = "[^aeiou]"; // consonant var v = "[aeiouy]"; // vowel var C = c + "[^aeiouy]*"; // consonant sequence var V = v + "[aeiou]*"; // vowel sequence var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 var s_v = "^(" + C + ")?" + v; // vowel in stem this.stemWord = function (w) { var stem; var suffix; var firstch; var origword = w; if (w.length < 3) return w; var re; var re2; var re3; var re4; firstch = w.substr(0,1); if (firstch == "y") w = firstch.toUpperCase() + w.substr(1); // Step 1a re = /^(.+?)(ss|i)es$/; re2 = /^(.+?)([^s])s$/; if (re.test(w)) w = w.replace(re,"$1$2"); else if (re2.test(w)) w = w.replace(re2,"$1$2"); // Step 1b re = /^(.+?)eed$/; re2 = /^(.+?)(ed|ing)$/; if (re.test(w)) { var fp = re.exec(w); re = new RegExp(mgr0); if (re.test(fp[1])) { re = /.$/; w = w.replace(re,""); } } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1]; re2 = new RegExp(s_v); if (re2.test(stem)) { w = stem; re2 = /(at|bl|iz)$/; re3 = new RegExp("([^aeiouylsz])\\1$"); re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re2.test(w)) w = w + "e"; else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); } else if (re4.test(w)) w = w + "e"; } } // Step 1c re = /^(.+?)y$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(s_v); if (re.test(stem)) w = stem + "i"; } // Step 2 re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step2list[suffix]; } // Step 3 re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step3list[suffix]; } // Step 4 re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; re2 = /^(.+?)(s|t)(ion)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); if (re.test(stem)) w = stem; } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1] + fp[2]; re2 = new RegExp(mgr1); if (re2.test(stem)) w = stem; } // Step 5 re = /^(.+?)e$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); re2 = new RegExp(meq1); re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) w = stem; } re = /ll$/; re2 = new RegExp(mgr1); if (re.test(w) && re2.test(w)) { re = /.$/; w = w.replace(re,""); } // and turn initial Y back to y if (firstch == "y") w = firstch.toLowerCase() + w.substr(1); return w; } } /** * Search Module */ var Search = { _index : null, _queued_query : null, _pulse_status : -1, init : function() { var params = $.getQueryParameters(); if (params.q) { var query = params.q[0]; $('input[name="q"]')[0].value = query; this.performSearch(query); } }, loadIndex : function(url) { $.ajax({type: "GET", url: url, data: null, success: null, dataType: "script", cache: true}); }, setIndex : function(index) { var q; this._index = index; if ((q = this._queued_query) !== null) { this._queued_query = null; Search.query(q); } }, hasIndex : function() { return this._index !== null; }, deferQuery : function(query) { this._queued_query = query; }, stopPulse : function() { this._pulse_status = 0; }, startPulse : function() { if (this._pulse_status >= 0) return; function pulse() { Search._pulse_status = (Search._pulse_status + 1) % 4; var dotString = ''; for (var i = 0; i < Search._pulse_status; i++) dotString += '.'; Search.dots.text(dotString); if (Search._pulse_status > -1) window.setTimeout(pulse, 500); }; pulse(); }, /** * perform a search for something */ performSearch : function(query) { // create the required interface elements this.out = $('#search-results'); this.title = $('

' + _('Searching') + '

').appendTo(this.out); this.dots = $('').appendTo(this.title); this.status = $('

').appendTo(this.out); this.output = $('