forked from lijiext/lammps
550 lines
21 KiB
Python
550 lines
21 KiB
Python
"""Contains the classes that are used to initialize data in the simulation.
|
|
|
|
Copyright (C) 2013, Joshua More and Michele Ceriotti
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http.//www.gnu.org/licenses/>.
|
|
|
|
|
|
These classes can either be used to restart a simulation with some different
|
|
data or used to start a calculation. Any data given in these classes will
|
|
overwrite data given elsewhere.
|
|
|
|
Classes:
|
|
Initializer: Holds the functions that are required to initialize objects in
|
|
the code. Data can be initialized from a file, or according to a
|
|
particular parameter. An example of the former would be initializing
|
|
the configurations from a xyz file, an example of the latter would be
|
|
initializing the velocities according to the physical temperature.
|
|
InitBase: Simple class that reads data from a string or file.
|
|
InitIndexed: The same as init base, but can also optionally hold
|
|
information about which atom or bead to initialize from.
|
|
|
|
Functions:
|
|
init_xyz: Reads beads data from a xyz file.
|
|
init_pdb: Reads beads and cell data from a pdb file.
|
|
init_chk: Reads beads, cell and thermostat data from a checkpoint file.
|
|
init_beads: Initializes a beads object from an Initializer object.
|
|
init_vector: Initializes a vector from an Initializer object.
|
|
set_vector: Initializes a vector from another vector.
|
|
"""
|
|
|
|
import numpy as np
|
|
|
|
from ipi.engine.beads import Beads
|
|
from ipi.engine.cell import Cell
|
|
from ipi.engine.normalmodes import NormalModes
|
|
from ipi.engine.ensembles import Ensemble
|
|
from ipi.utils.io.io_xyz import read_xyz
|
|
from ipi.utils.io.io_pdb import read_pdb
|
|
from ipi.utils.io.io_xml import xml_parse_file
|
|
from ipi.utils.depend import dobject
|
|
from ipi.utils.units import Constants, unit_to_internal
|
|
from ipi.utils.nmtransform import nm_rescale
|
|
from ipi.utils.messages import verbosity, warning, info
|
|
|
|
__all__ = ['Initializer', 'InitBase', 'InitIndexed']
|
|
|
|
class InitBase(dobject):
|
|
"""Base class for initializer objects.
|
|
|
|
Attributes:
|
|
value: A duck-typed stored value.
|
|
mode: A string that determines how the value is to be interpreted.
|
|
units: A string giving which unit the value is in.
|
|
"""
|
|
|
|
def __init__(self, value="", mode="", units="", **others):
|
|
"""Initializes InitFile.
|
|
|
|
Args:
|
|
value: A string which specifies what value to initialize the
|
|
simulation property to.
|
|
mode: A string specifying what style of initialization should be
|
|
used to read the data.
|
|
units: A string giving which unit the value is in.
|
|
"""
|
|
|
|
self.value = value
|
|
self.mode = mode
|
|
self.units = units
|
|
|
|
for (o, v) in others.items():
|
|
self.__dict__[o] = v
|
|
|
|
|
|
class InitIndexed(InitBase):
|
|
"""Class to initialize objects which can be set for a particular bead.
|
|
|
|
Attributes:
|
|
index: Which atom to initialize the value of.
|
|
bead: Which bead to initialize the value of.
|
|
"""
|
|
|
|
def __init__(self, value="", mode="", units="", index=-1, bead=-1):
|
|
"""Initializes InitFile.
|
|
|
|
Args:
|
|
value: A string which specifies what value to initialize the
|
|
simulation property to.
|
|
mode: A string specifying what style of initialization should be
|
|
used to read the data.
|
|
units: A string giving which unit the value is in.
|
|
index: Which atom to initialize the value of.
|
|
bead: Which bead to initialize the value of.
|
|
"""
|
|
|
|
super(InitIndexed,self).__init__(value=value, mode=mode, units=units, index=index, bead=bead)
|
|
|
|
|
|
def init_xyz(filename):
|
|
"""Reads an xyz file and returns the data contained in it.
|
|
|
|
Args:
|
|
filename: A string giving the name of the xyz file to be read from.
|
|
|
|
Returns:
|
|
A list of Atoms objects as read from each frame of the xyz file.
|
|
"""
|
|
|
|
rfile = open(filename,"r")
|
|
ratoms = []
|
|
while True:
|
|
#while loop, so that more than one configuration can be given
|
|
#so multiple beads can be initialized at once.
|
|
try:
|
|
myatoms = read_xyz(rfile)
|
|
except EOFError:
|
|
break
|
|
ratoms.append(myatoms)
|
|
return ratoms
|
|
|
|
def init_pdb(filename):
|
|
"""Reads an pdb file and returns the data contained in it.
|
|
|
|
Args:
|
|
filename: A string giving the name of the pdb file to be read from.
|
|
|
|
Returns:
|
|
A list of Atoms objects as read from each frame of the pdb file, and
|
|
a Cell object as read from the final pdb frame.
|
|
"""
|
|
|
|
rfile = open(filename,"r")
|
|
ratoms = []
|
|
while True:
|
|
#while loop, so that more than one configuration can be given
|
|
#so multiple beads can be initialized at once.
|
|
try:
|
|
myatoms, rcell = read_pdb(rfile)
|
|
except EOFError:
|
|
break
|
|
ratoms.append(myatoms)
|
|
return ( ratoms, rcell ) # if multiple frames, the last cell is returned
|
|
|
|
def init_chk(filename):
|
|
"""Reads an checkpoint file and returns the data contained in it.
|
|
|
|
Args:
|
|
filename: A string giving the name of the checkpoint file to be read from.
|
|
|
|
Returns:
|
|
A Beads object, Cell object and Thermostat object as read from the
|
|
checkpoint file.
|
|
"""
|
|
|
|
# reads configuration from a checkpoint file
|
|
rfile = open(filename,"r")
|
|
xmlchk = xml_parse_file(rfile) # Parses the file.
|
|
|
|
from ipi.inputs.simulation import InputSimulation
|
|
simchk = InputSimulation()
|
|
simchk.parse(xmlchk.fields[0][1])
|
|
rcell = simchk.cell.fetch()
|
|
rbeads = simchk.beads.fetch()
|
|
rthermo = simchk.ensemble.thermostat.fetch()
|
|
|
|
return (rbeads, rcell, rthermo)
|
|
|
|
def init_beads(iif, nbeads):
|
|
"""A file to initialize a beads object from an appropriate initializer
|
|
object.
|
|
|
|
Args:
|
|
iif: An Initializer object which has information on the bead positions.
|
|
nbeads: The number of beads.
|
|
|
|
Raises:
|
|
ValueError: If called using an Initializer object with a 'manual' mode.
|
|
"""
|
|
|
|
mode = iif.mode; value = iif.value
|
|
if mode == "xyz" or mode == "pdb":
|
|
if mode == "xyz": ratoms = init_xyz(value)
|
|
if mode == "pdb": ratoms = init_pdb(value)[0]
|
|
rbeads = Beads(ratoms[0].natoms,len(ratoms))
|
|
for i in range(len(ratoms)): rbeads[i] = ratoms[i]
|
|
elif mode == "chk":
|
|
rbeads = init_chk(value)[0]
|
|
elif mode == "manual":
|
|
raise ValueError("Cannot initialize manually a whole beads object.")
|
|
|
|
return rbeads
|
|
|
|
def init_vector(iif, nbeads, momenta=False):
|
|
"""A file to initialize a vector from an appropriate initializer
|
|
object.
|
|
|
|
Args:
|
|
iif: An Initializer object specifying the value of a vector.
|
|
nbeads: The number of beads.
|
|
momenta: If bead momenta rather than positions are being initialized
|
|
from a checkpoint file, this is set to True.
|
|
"""
|
|
|
|
mode = iif.mode; value = iif.value
|
|
if mode == "xyz" or mode == "pdb":
|
|
rq = init_beads(iif, nbeads).q
|
|
elif mode == "chk":
|
|
if momenta: rq = init_beads(iif, nbeads).p
|
|
else: rq = init_beads(iif, nbeads).q
|
|
elif mode == "manual":
|
|
rq = value
|
|
|
|
# determines the size of the input data
|
|
if mode == "manual":
|
|
if iif.bead >= 0: # if there is a bead specifier then we return a single bead slice
|
|
nbeads = 1
|
|
natoms = len(rq)/nbeads/3
|
|
rq.shape = (nbeads,3*natoms)
|
|
|
|
return rq
|
|
|
|
def set_vector(iif, dq, rq):
|
|
"""A file to initialize a vector from an another vector.
|
|
|
|
If the first dimension is different, i.e. the two vectors correspond
|
|
to a different number of beads, then the ring polymer contraction/expansion
|
|
is used to rescale the original vector to the one used in the simulation,
|
|
as described in the paper T. E. Markland and D. E. Manolopoulos, J. Chem.
|
|
Phys. 129, 024105, (2008).
|
|
|
|
Args:
|
|
iif: An Initializer object specifying the value of a vector.
|
|
dq: The vector to be initialized.
|
|
rq: The vector to initialize from.
|
|
"""
|
|
|
|
(nbeads, natoms) = rq.shape; natoms /= 3
|
|
(dbeads, datoms) = dq.shape; datoms /= 3
|
|
|
|
# Check that indices make sense
|
|
if iif.index < 0 and natoms != datoms:
|
|
raise ValueError("Initialization tries to mix up structures with different atom numbers.")
|
|
if iif.index >= datoms:
|
|
raise ValueError("Cannot initialize single atom as atom index %d is larger than the number of atoms" % iif.index)
|
|
if iif.bead >= dbeads:
|
|
raise ValueError("Cannot initialize single bead as bead index %d is larger than the number of beads" % iif.bead)
|
|
|
|
if iif.bead < 0: # we are initializing the path
|
|
res = nm_rescale(nbeads,dbeads) # path rescaler
|
|
if nbeads != dbeads:
|
|
info(" # Initialize is rescaling from %5d beads to %5d beads" % (nbeads, dbeads), verbosity.low)
|
|
if iif.index < 0:
|
|
dq[:] = res.b1tob2(rq)
|
|
else: # we are initializing a specific atom
|
|
dq[:,3*iif.index:3*(iif.index+1)] = res.b1tob2(rq)
|
|
else: # we are initializing a specific bead
|
|
if iif.index < 0:
|
|
dq[iif.bead] = rq
|
|
else:
|
|
dq[iif.bead,3*iif.index:3*(iif.index+1)] = rq
|
|
|
|
class Initializer(dobject):
|
|
"""Class that deals with the initialization of data.
|
|
|
|
This can either be used to initialize the atom positions and the cell data
|
|
from a file, or to initialize them from a beads, atoms or cell object.
|
|
|
|
Currently, we use a ring polymer contraction scheme to create a new beads
|
|
object from one given in initialize if they have different numbers of beads,
|
|
as described in the paper T. E. Markland and D. E. Manolopoulos, J. Chem.
|
|
Phys. 129, 024105, (2008). If the new beads object has more beads than
|
|
the beads object it was initialized from, we set the higher ring polymer
|
|
normal modes to zero.
|
|
|
|
Attributes:
|
|
queue: A list of things to initialize. Each member of the list is a tuple
|
|
of the form ('type', 'object'), where 'type' specifies what kind of
|
|
initialization is being done, and 'object' gives the data to
|
|
initialize it from.
|
|
"""
|
|
|
|
def __init__(self, nbeads=0, queue=None):
|
|
"""Initializes Initializer.
|
|
|
|
Arguments:
|
|
nbeads: The number of beads that we need in the simulation. Not
|
|
necessarily the same as the number of beads of the objects we are
|
|
initializing the data from.
|
|
queue: A list of things to initialize. Each member of the list is a
|
|
tuple of the form ('type', 'object'), where 'type' specifies what
|
|
kind of initialization is being done, and 'object' gives the data to
|
|
initialize it from.
|
|
"""
|
|
|
|
self.nbeads = nbeads
|
|
|
|
if queue is None:
|
|
self.queue = []
|
|
else:
|
|
self.queue = queue
|
|
|
|
def init_stage1(self, simul):
|
|
"""Initializes the simulation -- first stage.
|
|
|
|
Takes a simulation object, and uses all the data in the initialization
|
|
queue to fill up the beads and cell data needed to run the simulation.
|
|
|
|
Args:
|
|
simul: A simulation object to be initialized.
|
|
|
|
Raises:
|
|
ValueError: Raised if there is a problem with the initialization,
|
|
if something that should have been has not been, or if the objects
|
|
that have been specified are not compatible with each other.
|
|
"""
|
|
|
|
if simul.beads.nbeads == 0:
|
|
fpos = fmom = fmass = flab = fcell = False # we don't have an explicitly defined beads object yet
|
|
else:
|
|
fpos = fmom = fmass = flab = fcell = True
|
|
|
|
for (k,v) in self.queue:
|
|
info(" # Initializer (stage 1) parsing " + str(k) + " object.", verbosity.high)
|
|
|
|
if k == "cell":
|
|
if fcell :
|
|
warning("Overwriting previous cell parameters", verbosity.medium)
|
|
if v.mode == "pdb":
|
|
rh = init_pdb(v.value)[1].h
|
|
elif v.mode == "chk":
|
|
rh = init_chk(v.value)[1].h
|
|
else:
|
|
rh = v.value.reshape((3,3))
|
|
rh *= unit_to_internal("length",v.units,1.0)
|
|
|
|
simul.cell.h = rh
|
|
if simul.cell.V == 0.0:
|
|
ValueError("Cell provided has zero volume")
|
|
|
|
fcell = True
|
|
elif k == "masses":
|
|
if simul.beads.nbeads == 0:
|
|
raise ValueError("Cannot initialize the masses before the size of the system is known")
|
|
if fmass:
|
|
warning("Overwriting previous atomic masses", verbosity.medium)
|
|
if v.mode == "manual":
|
|
rm = v.value
|
|
else:
|
|
rm = init_beads(v, self.nbeads).m
|
|
rm *= unit_to_internal("mass",v.units,1.0)
|
|
|
|
if v.bead < 0: # we are initializing the path
|
|
if (fmom and fmass):
|
|
warning("Rescaling momenta to make up for changed mass", verbosity.medium)
|
|
simul.beads.p /= simul.beads.sm3 # go to mass-scaled momenta, that are mass-invariant
|
|
if v.index < 0:
|
|
simul.beads.m = rm
|
|
else: # we are initializing a specific atom
|
|
simul.beads.m[v.index:v.index+1] = rm
|
|
if (fmom and fmass): # finishes correcting the momenta
|
|
simul.beads.p *= simul.beads.sm3 # back to normal momenta
|
|
else:
|
|
raise ValueError("Cannot change the mass of a single bead")
|
|
fmass = True
|
|
|
|
elif k == "labels":
|
|
if simul.beads.nbeads == 0:
|
|
raise ValueError("Cannot initialize the labels before the size of the system is known")
|
|
if flab:
|
|
warning("Overwriting previous atomic labels", verbosity.medium)
|
|
if v.mode == "manual":
|
|
rn = v.value
|
|
else:
|
|
rn = init_beads(v, self.nbeads).names
|
|
|
|
if v.bead < 0: # we are initializing the path
|
|
if v.index < 0:
|
|
simul.beads.names = rn
|
|
else: # we are initializing a specific atom
|
|
simul.beads.names[v.index:v.index+1] = rn
|
|
else:
|
|
raise ValueError("Cannot change the label of a single bead")
|
|
flab = True
|
|
|
|
elif k == "positions":
|
|
if fpos:
|
|
warning("Overwriting previous atomic positions", verbosity.medium)
|
|
# read the atomic positions as a vector
|
|
rq = init_vector(v, self.nbeads)
|
|
rq *= unit_to_internal("length",v.units,1.0)
|
|
(nbeads, natoms) = rq.shape; natoms /= 3
|
|
|
|
# check if we must initialize the simulation beads
|
|
if simul.beads.nbeads == 0:
|
|
if v.index >= 0:
|
|
raise ValueError("Cannot initialize single atoms before the size of the system is known")
|
|
simul.beads.resize(natoms,self.nbeads)
|
|
|
|
set_vector(v, simul.beads.q, rq)
|
|
fpos = True
|
|
|
|
elif (k == "velocities" or k == "momenta") and v.mode == "thermal" : # intercept here thermal initialization, so we don't need to check further down
|
|
if fmom:
|
|
warning("Overwriting previous atomic momenta", verbosity.medium)
|
|
if simul.beads.natoms == 0:
|
|
raise ValueError("Cannot initialize momenta before the size of the system is known.")
|
|
if not fmass:
|
|
raise ValueError("Trying to resample velocities before having masses.")
|
|
|
|
rtemp = v.value * unit_to_internal("temperature",v.units,1.0)
|
|
if rtemp <= 0:
|
|
warning("Using the simulation temperature to resample velocities", verbosity.low)
|
|
rtemp = simul.ensemble.temp
|
|
else:
|
|
info(" # Resampling velocities at temperature %s %s" % (v.value, v.units), verbosity.low)
|
|
|
|
# pull together a mock initialization to get NM masses right
|
|
#without too much code duplication
|
|
if v.bead >= 0:
|
|
raise ValueError("Cannot thermalize a single bead")
|
|
if v.index >= 0:
|
|
rnatoms = 1
|
|
else:
|
|
rnatoms = simul.beads.natoms
|
|
rbeads = Beads(rnatoms, simul.beads.nbeads)
|
|
if v.index < 0:
|
|
rbeads.m[:] = simul.beads.m
|
|
else:
|
|
rbeads.m[:] = simul.beads.m[v.index]
|
|
rnm = NormalModes(mode=simul.nm.mode, transform_method=simul.nm.transform_method, freqs=simul.nm.nm_freqs)
|
|
rens = Ensemble(dt=simul.ensemble.dt, temp=simul.ensemble.temp)
|
|
rnm.bind(rbeads,rens)
|
|
# then we exploit the sync magic to do a complicated initialization
|
|
# in the NM representation
|
|
# with (possibly) shifted-frequencies NM
|
|
rnm.pnm = simul.prng.gvec((rbeads.nbeads,3*rbeads.natoms))*np.sqrt(rnm.dynm3)*np.sqrt(rbeads.nbeads*rtemp*Constants.kb)
|
|
|
|
if v.index < 0:
|
|
simul.beads.p = rbeads.p
|
|
else:
|
|
simul.beads.p[:,3*v.index:3*(v.index+1)] = rbeads.p
|
|
fmom = True
|
|
|
|
elif k == "momenta":
|
|
if fmom:
|
|
warning("Overwriting previous atomic momenta", verbosity.medium)
|
|
# read the atomic momenta as a vector
|
|
rp = init_vector(v, self.nbeads, momenta = True)
|
|
rp *= unit_to_internal("momentum",v.units,1.0)
|
|
(nbeads, natoms) = rp.shape; natoms /= 3
|
|
|
|
# checks if we must initialize the simulation beads
|
|
if simul.beads.nbeads == 0:
|
|
if v.index >= 0 :
|
|
raise ValueError("Cannot initialize single atoms before the size of the system is known")
|
|
simul.beads.resize(natoms,self.nbeads)
|
|
|
|
rp *= np.sqrt(self.nbeads/nbeads)
|
|
set_vector(v, simul.beads.p, rp)
|
|
fmom = True
|
|
|
|
elif k == "velocities":
|
|
if fmom:
|
|
warning("Overwriting previous atomic momenta", verbosity.medium)
|
|
# read the atomic velocities as a vector
|
|
rv = init_vector(v, self.nbeads)
|
|
rv *= unit_to_internal("velocity",v.units,1.0)
|
|
(nbeads, natoms) = rv.shape; natoms /= 3
|
|
|
|
# checks if we must initialize the simulation beads
|
|
if simul.beads.nbeads == 0 or not fmass:
|
|
ValueError("Cannot initialize velocities before the masses of the atoms are known")
|
|
simul.beads.resize(natoms,self.nbeads)
|
|
|
|
warning("Initializing from velocities uses the previously defined masses -- not the masses inferred from the file -- to build momenta", verbosity.low)
|
|
if v.index >= 0:
|
|
rv *= simul.beads.m[v.index]
|
|
elif v.bead >= 0:
|
|
rv *= simul.beads.m3[0]
|
|
else:
|
|
rv *= simul.beads.m3
|
|
rv *= np.sqrt(self.nbeads/nbeads)
|
|
set_vector(v, simul.beads.p, rv)
|
|
fmom = True
|
|
elif k == "thermostat": pass # thermostats must be initialised in a second stage
|
|
|
|
if simul.beads.natoms == 0:
|
|
raise ValueError("Initializer could not initialize the atomic positions")
|
|
if simul.cell.V == 0:
|
|
raise ValueError("Initializer could not initialize the cell")
|
|
for i in range(simul.beads.natoms):
|
|
if simul.beads.m[i] <= 0:
|
|
raise ValueError("Initializer could not initialize the masses")
|
|
if simul.beads.names[i] == "":
|
|
raise ValueError("Initializer could not initialize the atom labels")
|
|
if not fmom:
|
|
warning("Momenta not specified in initialize. Will start with zero velocity if they are not specified in beads.", verbosity.low)
|
|
|
|
def init_stage2(self, simul):
|
|
"""Initializes the simulation -- second stage.
|
|
|
|
Takes a simulation object which has been fully generated,
|
|
and restarts additional information such as the thermostat internal state.
|
|
|
|
Args:
|
|
simul: A simulation object to be initialized.
|
|
|
|
Raises:
|
|
ValueError: Raised if there is a problem with the initialization,
|
|
if something that should have been has not been, or if the objects
|
|
that have been specified are not compatible with each other.
|
|
"""
|
|
|
|
for (k,v) in self.queue:
|
|
info(" # Initializer (stage 2) parsing " + str(k) + " object.", verbosity.high)
|
|
|
|
if k == "gle":
|
|
# read thermostat parameters from file
|
|
if not ( hasattr(simul.ensemble, "thermostat") ):
|
|
raise ValueError("Ensemble does not have a thermostat to initialize")
|
|
if not ( hasattr(simul.ensemble.thermostat, "s") ):
|
|
raise ValueError("There is nothing to initialize in non-GLE thermostats")
|
|
ssimul = simul.ensemble.thermostat.s
|
|
if v.mode == "manual":
|
|
sinput = v.value.copy()
|
|
if (sinput.size() != ssimul.size() ):
|
|
raise ValueError("Size mismatch in thermostat initialization data")
|
|
sinput.shape = ssimul.shape
|
|
elif v.mode == "chk":
|
|
rthermo = init_chk(v.value)[2]
|
|
if not hasattr(rthermo,"s"):
|
|
raise ValueError("Checkpoint file does not contain usable thermostat data")
|
|
sinput = rthermo.s.copy()
|
|
if sinput.shape != ssimul.shape :
|
|
raise ValueError("Shape mismatch in thermostat initialization data")
|
|
|
|
# if all the preliminary checks are good, we can initialize the s's
|
|
ssimul[:] = sinput
|