git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@12769 f3b2605a-c512-4ea7-a41b-209d697bcdaa
This commit is contained in:
549
tools/i-pi/ipi/engine/initializer.py
Normal file
549
tools/i-pi/ipi/engine/initializer.py
Normal file
@ -0,0 +1,549 @@
|
||||
"""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 specifiying 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 specifiying 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
|
||||
Reference in New Issue
Block a user