def bind(self, beads, ensemble): """ Initializes the normal modes object and binds to beads and ensemble. Do all the work down here as we need a full-formed necklace and ensemble to know how this should be done. Args: beads: A beads object to be bound. ensemble: An ensemble object to be bound. """ self.nbeads = beads.nbeads self.natoms = beads.natoms # stores a reference to the bound beads and ensemble objects self.beads = beads self.ensemble = ensemble # sets up what's necessary to perform nm transformation. if self.transform_method == "fft": self.transform = nmtransform.nm_fft(nbeads=self.nbeads, natoms=self.natoms) elif self.transform_method == "matrix": self.transform = nmtransform.nm_trans(nbeads=self.nbeads) # creates arrays to store normal modes representation of the path. # must do a lot of piping to create "ex post" a synchronization between the beads and the nm sync_q = synchronizer() sync_p = synchronizer() dset( self, "qnm", depend_array( name="qnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func={ "q": (lambda: self.transform.b2nm(depstrip(self.beads.q))) }, synchro=sync_q)) dset( self, "pnm", depend_array( name="pnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func={ "p": (lambda: self.transform.b2nm(depstrip(self.beads.p))) }, synchro=sync_p)) # must overwrite the functions dget(self.beads, "q")._func = { "qnm": (lambda: self.transform.nm2b(depstrip(self.qnm))) } dget(self.beads, "p")._func = { "pnm": (lambda: self.transform.nm2b(depstrip(self.pnm))) } dget(self.beads, "q").add_synchro(sync_q) dget(self.beads, "p").add_synchro(sync_p) # also within the "atomic" interface to beads for b in range(self.nbeads): dget(self.beads._blist[b], "q")._func = { "qnm": (lambda: self.transform.nm2b(depstrip(self.qnm))) } dget(self.beads._blist[b], "p")._func = { "pnm": (lambda: self.transform.nm2b(depstrip(self.pnm))) } dget(self.beads._blist[b], "q").add_synchro(sync_q) dget(self.beads._blist[b], "p").add_synchro(sync_p) # finally, we mark the beads as those containing the set positions dget(self.beads, "q").update_man() dget(self.beads, "p").update_man() # create path-frequencies related properties dset( self, "omegan", depend_value(name='omegan', func=self.get_omegan, dependencies=[dget(self.ensemble, "temp")])) dset( self, "omegan2", depend_value(name='omegan2', func=self.get_omegan2, dependencies=[dget(self, "omegan")])) dset( self, "omegak", depend_array(name='omegak', value=np.zeros(self.beads.nbeads, float), func=self.get_omegak, dependencies=[dget(self, "omegan")])) # sets up "dynamical" masses -- mass-scalings to give the correct RPMD/CMD dynamics dset( self, "nm_factor", depend_array( name="nmm", value=np.zeros(self.nbeads, float), func=self.get_nmm, dependencies=[dget(self, "nm_freqs"), dget(self, "mode")])) dset( self, "dynm3", depend_array( name="dm3", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=self.get_dynm3, dependencies=[dget(self, "nm_factor"), dget(self.beads, "m3")])) dset( self, "dynomegak", depend_array( name="dynomegak", value=np.zeros(self.nbeads, float), func=self.get_dynwk, dependencies=[dget(self, "nm_factor"), dget(self, "omegak")])) dset( self, "prop_pq", depend_array(name='prop_pq', value=np.zeros((self.beads.nbeads, 2, 2)), func=self.get_prop_pq, dependencies=[ dget(self, "omegak"), dget(self, "nm_factor"), dget(self.ensemble, "dt") ])) # if the mass matrix is not the RPMD one, the MD kinetic energy can't be # obtained in the bead representation because the masses are all mixed up dset( self, "kins", depend_array(name="kins", value=np.zeros(self.nbeads, float), func=self.get_kins, dependencies=[ dget(self, "pnm"), dget(self.beads, "sm3"), dget(self, "nm_factor") ])) dset( self, "kin", depend_value(name="kin", func=self.get_kin, dependencies=[dget(self, "kins")])) dset( self, "kstress", depend_array(name="kstress", value=np.zeros((3, 3), float), func=self.get_kstress, dependencies=[ dget(self, "pnm"), dget(self.beads, "sm3"), dget(self, "nm_factor") ]))
def bind(self, ensemble, motion, beads=None, forces=None): """ Initializes the normal modes object and binds to beads and ensemble. Do all the work down here as we need a full-formed necklace and ensemble to know how this should be done. Args: beads: A beads object to be bound. ensemble: An ensemble object to be bound. """ self.ensemble = ensemble self.motion = motion if beads is None: self.beads = motion.beads else: self.beads = beads self.forces = forces self.nbeads = beads.nbeads self.natoms = beads.natoms dself = dd(self) # stores a reference to the bound beads and ensemble objects self.ensemble = ensemble dpipe(dd(motion).dt, dself.dt) # sets up what's necessary to perform nm transformation. if self.transform_method == "fft": self.transform = nmtransform.nm_fft(nbeads=self.nbeads, natoms=self.natoms, open_paths=self.open_paths) elif self.transform_method == "matrix": self.transform = nmtransform.nm_trans(nbeads=self.nbeads, open_paths=self.open_paths) # creates arrays to store normal modes representation of the path. # must do a lot of piping to create "ex post" a synchronization between the beads and the nm sync_q = synchronizer() sync_p = synchronizer() dself.qnm = depend_array(name="qnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func={"q": (lambda: self.transform.b2nm(dstrip(self.beads.q)))}, synchro=sync_q) dself.pnm = depend_array(name="pnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func={"p": (lambda: self.transform.b2nm(dstrip(self.beads.p)))}, synchro=sync_p) # must overwrite the functions dd(self.beads).q._func = {"qnm": (lambda: self.transform.nm2b(dstrip(self.qnm)))} dd(self.beads).p._func = {"pnm": (lambda: self.transform.nm2b(dstrip(self.pnm)))} dd(self.beads).q.add_synchro(sync_q) dd(self.beads).p.add_synchro(sync_p) # also within the "atomic" interface to beads for b in range(self.nbeads): dd(self.beads._blist[b]).q._func = {"qnm": (lambda: self.transform.nm2b(dstrip(self.qnm)))} dd(self.beads._blist[b]).p._func = {"pnm": (lambda: self.transform.nm2b(dstrip(self.pnm)))} dd(self.beads._blist[b]).q.add_synchro(sync_q) dd(self.beads._blist[b]).p.add_synchro(sync_p) # finally, we mark the beads as those containing the set positions dd(self.beads).q.update_man() dd(self.beads).p.update_man() # forces can be converted in nm representation, but here it makes no sense to set up a sync mechanism, # as they always get computed in the bead rep if not self.forces is None: dself.fnm = depend_array(name="fnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=(lambda: self.transform.b2nm(dstrip(self.forces.f))), dependencies=[dd(self.forces).f]) else: # have a fall-back plan when we don't want to initialize a force mechanism, e.g. for ring-polymer initialization dself.fnm = depend_array(name="fnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=(lambda: depraise(ValueError("Cannot access NM forces when initializing the NM object without providing a force reference!"))), dependencies=[]) # create path-frequencies related properties dself.omegan = depend_value(name='omegan', func=self.get_omegan, dependencies=[dd(self.ensemble).temp]) dself.omegan2 = depend_value(name='omegan2', func=self.get_omegan2, dependencies=[dself.omegan]) dself.omegak = depend_array(name='omegak', value=np.zeros(self.beads.nbeads, float), func=self.get_omegak, dependencies=[dself.omegan]) dself.omegak2 = depend_array(name='omegak2', value=np.zeros(self.beads.nbeads, float), func=(lambda: self.omegak**2), dependencies=[dself.omegak]) # Add o_omegak to calculate the freq in the case of open path dself.o_omegak = depend_array(name='o_omegak', value=np.zeros(self.beads.nbeads, float), func=self.get_o_omegak, dependencies=[dself.omegan]) # sets up "dynamical" masses -- mass-scalings to give the correct RPMD/CMD dynamics dself.nm_factor = depend_array(name="nm_factor", value=np.zeros(self.nbeads, float), func=self.get_nmm, dependencies=[dself.nm_freqs, dself.mode]) # add o_nm_factor for the dynamical mass in the case of open paths dself.o_nm_factor = depend_array(name="nmm", value=np.zeros(self.nbeads, float), func=self.get_o_nmm, dependencies=[dself.nm_freqs, dself.mode]) dself.dynm3 = depend_array(name="dynm3", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=self.get_dynm3, dependencies=[dself.nm_factor, dd(self.beads).m3]) dself.dynomegak = depend_array(name="dynomegak", value=np.zeros(self.nbeads, float), func=self.get_dynwk, dependencies=[dself.nm_factor, dself.omegak]) dself.dt = depend_value(name="dt", value=1.0) dpipe(dd(self.motion).dt, dself.dt) dself.prop_pq = depend_array(name='prop_pq', value=np.zeros((self.beads.nbeads, 2, 2)), func=self.get_prop_pq, dependencies=[dself.omegak, dself.nm_factor, dself.dt]) dself.o_prop_pq = depend_array(name='o_prop_pq', value=np.zeros((self.beads.nbeads, 2, 2)), func=self.get_o_prop_pq, dependencies=[dself.o_omegak, dself.o_nm_factor, dself.dt]) # if the mass matrix is not the RPMD one, the MD kinetic energy can't be # obtained in the bead representation because the masses are all mixed up dself.kins = depend_array(name="kins", value=np.zeros(self.nbeads, float), func=self.get_kins, dependencies=[dself.pnm, dd(self.beads).sm3, dself.nm_factor]) dself.kin = depend_value(name="kin", func=self.get_kin, dependencies=[dself.kins]) dself.kstress = depend_array(name="kstress", value=np.zeros((3, 3), float), func=self.get_kstress, dependencies=[dself.pnm, dd(self.beads).sm3, dself.nm_factor]) # spring energy, calculated in normal modes dself.vspring = depend_value(name="vspring", value=0.0, func=self.get_vspring, dependencies=[dself.qnm, dself.omegak, dd(self.beads).m3]) # spring forces on normal modes dself.fspringnm = depend_array(name="fspringnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=self.get_fspringnm, dependencies=[dself.qnm, dself.omegak, dd(self.beads).m3]) # spring forces on beads, transformed from normal modes dself.fspring = depend_array(name="fs", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=(lambda: self.transform.nm2b(dstrip(self.fspringnm))), dependencies=[dself.fspringnm])
def bind(self, ensemble, motion, beads=None, forces=None): """ Initializes the normal modes object and binds to beads and ensemble. Do all the work down here as we need a full-formed necklace and ensemble to know how this should be done. Args: beads: A beads object to be bound. ensemble: An ensemble object to be bound. """ self.ensemble = ensemble self.motion = motion if beads is None: self.beads = motion.beads else: self.beads = beads self.forces = forces self.nbeads = beads.nbeads self.natoms = beads.natoms dself = dd(self) # stores a reference to the bound beads and ensemble objects self.ensemble = ensemble dpipe(dd(motion).dt, dself.dt) # sets up what's necessary to perform nm transformation. if self.nbeads == 1: # classical trajectory! don't waste time doing anything! self.transform = nmtransform.nm_noop(nbeads = self.nbeads) elif self.transform_method == "fft": self.transform = nmtransform.nm_fft(nbeads=self.nbeads, natoms=self.natoms, open_paths=self.open_paths) elif self.transform_method == "matrix": self.transform = nmtransform.nm_trans(nbeads=self.nbeads, open_paths=self.open_paths) # creates arrays to store normal modes representation of the path. # must do a lot of piping to create "ex post" a synchronization between the beads and the nm sync_q = synchronizer() sync_p = synchronizer() dself.qnm = depend_array(name="qnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func={"q": (lambda: self.transform.b2nm(dstrip(self.beads.q)))}, synchro=sync_q) dself.pnm = depend_array(name="pnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func={"p": (lambda: self.transform.b2nm(dstrip(self.beads.p)))}, synchro=sync_p) # must overwrite the functions dd(self.beads).q._func = {"qnm": (lambda: self.transform.nm2b(dstrip(self.qnm)))} dd(self.beads).p._func = {"pnm": (lambda: self.transform.nm2b(dstrip(self.pnm)))} dd(self.beads).q.add_synchro(sync_q) dd(self.beads).p.add_synchro(sync_p) # also within the "atomic" interface to beads for b in range(self.nbeads): dd(self.beads._blist[b]).q._func = {"qnm": (lambda: self.transform.nm2b(dstrip(self.qnm)))} dd(self.beads._blist[b]).p._func = {"pnm": (lambda: self.transform.nm2b(dstrip(self.pnm)))} dd(self.beads._blist[b]).q.add_synchro(sync_q) dd(self.beads._blist[b]).p.add_synchro(sync_p) # finally, we mark the beads as those containing the set positions dd(self.beads).q.update_man() dd(self.beads).p.update_man() # forces can be converted in nm representation, but here it makes no sense to set up a sync mechanism, # as they always get computed in the bead rep if not self.forces is None: dself.fnm = depend_array(name="fnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=(lambda: self.transform.b2nm(dstrip(self.forces.f))), dependencies=[dd(self.forces).f]) else: # have a fall-back plan when we don't want to initialize a force mechanism, e.g. for ring-polymer initialization dself.fnm = depend_array(name="fnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=(lambda: depraise(ValueError("Cannot access NM forces when initializing the NM object without providing a force reference!"))), dependencies=[]) # create path-frequencies related properties dself.omegan = depend_value(name='omegan', func=self.get_omegan, dependencies=[dd(self.ensemble).temp]) dself.omegan2 = depend_value(name='omegan2', func=self.get_omegan2, dependencies=[dself.omegan]) dself.omegak = depend_array(name='omegak', value=np.zeros(self.beads.nbeads, float), func=self.get_omegak, dependencies=[dself.omegan]) dself.omegak2 = depend_array(name='omegak2', value=np.zeros(self.beads.nbeads, float), func=(lambda: self.omegak**2), dependencies=[dself.omegak]) # Add o_omegak to calculate the freq in the case of open path dself.o_omegak = depend_array(name='o_omegak', value=np.zeros(self.beads.nbeads, float), func=self.get_o_omegak, dependencies=[dself.omegan]) # sets up "dynamical" masses -- mass-scalings to give the correct RPMD/CMD dynamics dself.nm_factor = depend_array(name="nm_factor", value=np.zeros(self.nbeads, float), func=self.get_nmm, dependencies=[dself.nm_freqs, dself.mode]) # add o_nm_factor for the dynamical mass in the case of open paths dself.o_nm_factor = depend_array(name="nmm", value=np.zeros(self.nbeads, float), func=self.get_o_nmm, dependencies=[dself.nm_freqs, dself.mode]) dself.dynm3 = depend_array(name="dynm3", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=self.get_dynm3, dependencies=[dself.nm_factor, dd(self.beads).m3]) dself.dynomegak = depend_array(name="dynomegak", value=np.zeros(self.nbeads, float), func=self.get_dynwk, dependencies=[dself.nm_factor, dself.omegak]) dself.dt = depend_value(name="dt", value=1.0) dpipe(dd(self.motion).dt, dself.dt) dself.prop_pq = depend_array(name='prop_pq', value=np.zeros((self.beads.nbeads, 2, 2)), func=self.get_prop_pq, dependencies=[dself.omegak, dself.nm_factor, dself.dt]) dself.o_prop_pq = depend_array(name='o_prop_pq', value=np.zeros((self.beads.nbeads, 2, 2)), func=self.get_o_prop_pq, dependencies=[dself.o_omegak, dself.o_nm_factor, dself.dt]) # if the mass matrix is not the RPMD one, the MD kinetic energy can't be # obtained in the bead representation because the masses are all mixed up dself.kins = depend_array(name="kins", value=np.zeros(self.nbeads, float), func=self.get_kins, dependencies=[dself.pnm, dd(self.beads).sm3, dself.nm_factor]) dself.kin = depend_value(name="kin", func=self.get_kin, dependencies=[dself.kins]) dself.kstress = depend_array(name="kstress", value=np.zeros((3, 3), float), func=self.get_kstress, dependencies=[dself.pnm, dd(self.beads).sm3, dself.nm_factor]) # spring energy, calculated in normal modes dself.vspring = depend_value(name="vspring", value=0.0, func=self.get_vspring, dependencies=[dself.qnm, dself.omegak, dself.o_omegak, dd(self.beads).m3]) # spring forces on normal modes dself.fspringnm = depend_array(name="fspringnm", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=self.get_fspringnm, dependencies=[dself.qnm, dself.omegak, dd(self.beads).m3]) # spring forces on beads, transformed from normal modes dself.fspring = depend_array(name="fs", value=np.zeros((self.nbeads, 3 * self.natoms), float), func=(lambda: self.transform.nm2b(dstrip(self.fspringnm))), dependencies=[dself.fspringnm])
def bind(self, ensemble, motion, beads=None, forces=None): """ Initializes the normal modes object and binds to beads and ensemble. Do all the work down here as we need a full-formed necklace and ensemble to know how this should be done. Args: beads: A beads object to be bound. ensemble: An ensemble object to be bound. """ self.ensemble = ensemble self.motion = motion if beads is None: self.beads = motion.beads else: self.beads = beads self.forces = forces self.nbeads = beads.nbeads self.natoms = beads.natoms # stores a reference to the bound beads and ensemble objects self.ensemble = ensemble deppipe(motion, "dt", self, "dt") # sets up what's necessary to perform nm transformation. if self.transform_method == "fft": self.transform = nmtransform.nm_fft(nbeads=self.nbeads, natoms=self.natoms) elif self.transform_method == "matrix": self.transform = nmtransform.nm_trans(nbeads=self.nbeads) # creates arrays to store normal modes representation of the path. # must do a lot of piping to create "ex post" a synchronization between the beads and the nm sync_q = synchronizer() sync_p = synchronizer() dset(self,"qnm", depend_array(name="qnm", value=np.zeros((self.nbeads,3*self.natoms), float), func={"q": (lambda : self.transform.b2nm(depstrip(self.beads.q)) ) }, synchro=sync_q ) ) dset(self,"pnm", depend_array(name="pnm", value=np.zeros((self.nbeads,3*self.natoms), float), func={"p": (lambda : self.transform.b2nm(depstrip(self.beads.p)) ) }, synchro=sync_p ) ) # must overwrite the functions dget(self.beads, "q")._func = { "qnm": (lambda : self.transform.nm2b(depstrip(self.qnm)) ) } dget(self.beads, "p")._func = { "pnm": (lambda : self.transform.nm2b(depstrip(self.pnm)) ) } dget(self.beads, "q").add_synchro(sync_q) dget(self.beads, "p").add_synchro(sync_p) # also within the "atomic" interface to beads for b in range(self.nbeads): dget(self.beads._blist[b],"q")._func = { "qnm": (lambda : self.transform.nm2b(depstrip(self.qnm)) ) } dget(self.beads._blist[b],"p")._func = { "pnm": (lambda : self.transform.nm2b(depstrip(self.pnm)) ) } dget(self.beads._blist[b],"q").add_synchro(sync_q) dget(self.beads._blist[b],"p").add_synchro(sync_p) # finally, we mark the beads as those containing the set positions dget(self.beads, "q").update_man() dget(self.beads, "p").update_man() # forces can be converted in nm representation, but here it makes no sense to set up a sync mechanism, # as they always get computed in the bead rep if not self.forces is None: dset(self,"fnm", depend_array(name="fnm", value=np.zeros((self.nbeads,3*self.natoms), float),func=(lambda : self.transform.b2nm(depstrip(self.forces.f)) ), dependencies=[dget(self.forces,"f")] ) ) else: # have a fall-back plan when we don't want to initialize a force mechanism, e.g. for ring-polymer initialization dset(self,"fnm", depend_array(name="fnm", value=np.zeros((self.nbeads,3*self.natoms), float), func=(lambda: depraise(ValueError("Cannot access NM forces when initializing the NM object without providing a force reference!") ) ), dependencies=[] ) ) # create path-frequencies related properties dset(self,"omegan", depend_value(name='omegan', func=self.get_omegan, dependencies=[dget(self.ensemble,"temp")]) ) dset(self,"omegan2", depend_value(name='omegan2',func=self.get_omegan2, dependencies=[dget(self,"omegan")]) ) dset(self,"omegak", depend_array(name='omegak', value=np.zeros(self.beads.nbeads,float), func=self.get_omegak, dependencies=[dget(self,"omegan")]) ) dset(self,"omegak2", depend_array(name='omegak2', value=np.zeros(self.beads.nbeads,float), func=(lambda: (self.omegak)**2), dependencies=[dget(self,"omegak")]) ) # sets up "dynamical" masses -- mass-scalings to give the correct RPMD/CMD dynamics # TODO: Do we really need different names and variable names? Seems confusing. dset(self,"nm_factor", depend_array(name="nmm", value=np.zeros(self.nbeads, float), func=self.get_nmm, dependencies=[dget(self,"nm_freqs"), dget(self,"mode") ]) ) dset(self,"dynm3", depend_array(name="dm3", value=np.zeros((self.nbeads,3*self.natoms), float),func=self.get_dynm3, dependencies=[dget(self,"nm_factor"), dget(self.beads, "m3")] ) ) dset(self,"dynomegak", depend_array(name="dynomegak", value=np.zeros(self.nbeads, float), func=self.get_dynwk, dependencies=[dget(self,"nm_factor"), dget(self,"omegak") ]) ) dset(self, "dt", depend_value(name="dt", value = 1.0) ) deppipe(self.motion, "dt", self, "dt") dset(self,"prop_pq", depend_array(name='prop_pq',value=np.zeros((self.beads.nbeads,2,2)), func=self.get_prop_pq, dependencies=[dget(self,"omegak"), dget(self,"nm_factor"), dget(self,"dt")]) ) # if the mass matrix is not the RPMD one, the MD kinetic energy can't be # obtained in the bead representation because the masses are all mixed up dset(self,"kins", depend_array(name="kins",value=np.zeros(self.nbeads, float), func=self.get_kins, dependencies=[dget(self,"pnm"), dget(self.beads,"sm3"), dget(self, "nm_factor") ] )) dset(self,"kin", depend_value(name="kin", func=self.get_kin, dependencies=[dget(self,"kins")] )) dset(self,"kstress", depend_array(name="kstress",value=np.zeros((3,3), float), func=self.get_kstress, dependencies=[dget(self,"pnm"), dget(self.beads,"sm3"), dget(self, "nm_factor") ] )) # spring energy, calculated in normal modes dset(self, "vspring", depend_value(name="vspring", value=0.0, func=self.get_vspring, dependencies=[dget(self, "qnm"), dget(self, "omegak"), dget(self.beads, "m3")])) # spring forces on normal modes dset(self, "fspringnm", depend_array(name="fspringnm", value=np.zeros((self.nbeads, 3*self.natoms), float), func=self.get_fspringnm, dependencies=[dget(self, "qnm"), dget(self, "omegak"), dget(self.beads, "m3")])) # spring forces on beads, transformed from normal modes dset(self,"fspring", depend_array(name="fs", value=np.zeros((self.nbeads,3*self.natoms), float), func=(lambda: self.transform.nm2b(depstrip(self.fspringnm))), dependencies = [dget(self, "fspringnm")]))
def bind(self, beads, ensemble): """ Initializes the normal modes object and binds to beads and ensemble. Do all the work down here as we need a full-formed necklace and ensemble to know how this should be done. Args: beads: A beads object to be bound. ensemble: An ensemble object to be bound. """ self.nbeads = beads.nbeads self.natoms = beads.natoms # stores a reference to the bound beads and ensemble objects self.beads = beads self.ensemble = ensemble # sets up what's necessary to perform nm transformation. if self.transform_method == "fft": self.transform = nmtransform.nm_fft(nbeads=self.nbeads, natoms=self.natoms) elif self.transform_method == "matrix": self.transform = nmtransform.nm_trans(nbeads=self.nbeads) # creates arrays to store normal modes representation of the path. # must do a lot of piping to create "ex post" a synchronization between the beads and the nm sync_q = synchronizer() sync_p = synchronizer() dset(self,"qnm", depend_array(name="qnm", value=np.zeros((self.nbeads,3*self.natoms), float), func={"q": (lambda : self.transform.b2nm(depstrip(self.beads.q)) ) }, synchro=sync_q ) ) dset(self,"pnm", depend_array(name="pnm", value=np.zeros((self.nbeads,3*self.natoms), float), func={"p": (lambda : self.transform.b2nm(depstrip(self.beads.p)) ) }, synchro=sync_p ) ) # must overwrite the functions dget(self.beads, "q")._func = { "qnm": (lambda : self.transform.nm2b(depstrip(self.qnm)) ) } dget(self.beads, "p")._func = { "pnm": (lambda : self.transform.nm2b(depstrip(self.pnm)) ) } dget(self.beads, "q").add_synchro(sync_q) dget(self.beads, "p").add_synchro(sync_p) # also within the "atomic" interface to beads for b in range(self.nbeads): dget(self.beads._blist[b],"q")._func = { "qnm": (lambda : self.transform.nm2b(depstrip(self.qnm)) ) } dget(self.beads._blist[b],"p")._func = { "pnm": (lambda : self.transform.nm2b(depstrip(self.pnm)) ) } dget(self.beads._blist[b],"q").add_synchro(sync_q) dget(self.beads._blist[b],"p").add_synchro(sync_p) # finally, we mark the beads as those containing the set positions dget(self.beads, "q").update_man() dget(self.beads, "p").update_man() # create path-frequencies related properties dset(self,"omegan", depend_value(name='omegan', func=self.get_omegan, dependencies=[dget(self.ensemble,"temp")]) ) dset(self,"omegan2", depend_value(name='omegan2',func=self.get_omegan2, dependencies=[dget(self,"omegan")]) ) dset(self,"omegak", depend_array(name='omegak', value=np.zeros(self.beads.nbeads,float), func=self.get_omegak, dependencies=[dget(self,"omegan")]) ) # sets up "dynamical" masses -- mass-scalings to give the correct RPMD/CMD dynamics dset(self,"nm_factor", depend_array(name="nmm", value=np.zeros(self.nbeads, float), func=self.get_nmm, dependencies=[dget(self,"nm_freqs"), dget(self,"mode") ]) ) dset(self,"dynm3", depend_array(name="dm3", value=np.zeros((self.nbeads,3*self.natoms), float),func=self.get_dynm3, dependencies=[dget(self,"nm_factor"), dget(self.beads, "m3")] ) ) dset(self,"dynomegak", depend_array(name="dynomegak", value=np.zeros(self.nbeads, float), func=self.get_dynwk, dependencies=[dget(self,"nm_factor"), dget(self,"omegak") ]) ) dset(self,"prop_pq", depend_array(name='prop_pq',value=np.zeros((self.beads.nbeads,2,2)), func=self.get_prop_pq, dependencies=[dget(self,"omegak"), dget(self,"nm_factor"), dget(self.ensemble,"dt")]) ) # if the mass matrix is not the RPMD one, the MD kinetic energy can't be # obtained in the bead representation because the masses are all mixed up dset(self,"kins", depend_array(name="kins",value=np.zeros(self.nbeads, float), func=self.get_kins, dependencies=[dget(self,"pnm"), dget(self.beads,"sm3"), dget(self, "nm_factor") ] )) dset(self,"kin", depend_value(name="kin", func=self.get_kin, dependencies=[dget(self,"kins")] )) dset(self,"kstress", depend_array(name="kstress",value=np.zeros((3,3), float), func=self.get_kstress, dependencies=[dget(self,"pnm"), dget(self.beads,"sm3"), dget(self, "nm_factor") ] ))