def _molecular_dynamics(self, resume=None): """Performs a molecular dynamics simulation, until mdmin is exceeded. If resuming, the file number (md%05i) is expected.""" self._log('msg', 'Molecular dynamics: md%05i' % self._counter) mincount = 0 energies, oldpositions = [], [] thermalized = False if resume: self._log('msg', 'Resuming MD from md%05i.traj' % resume) if os.path.getsize('md%05i.traj' % resume) == 0: self._log( 'msg', 'md%05i.traj is empty. Resuming from ' 'qn%05i.traj.' % (resume, resume - 1)) atoms = io.read('qn%05i.traj' % (resume - 1), index=-1) else: images = io.Trajectory('md%05i.traj' % resume, 'r') for atoms in images: energies.append(atoms.get_potential_energy()) oldpositions.append(atoms.positions.copy()) passedmin = self._passedminimum(energies) if passedmin: mincount += 1 self._atoms.set_momenta(atoms.get_momenta()) thermalized = True self._atoms.positions = atoms.get_positions() self._log('msg', 'Starting MD with %i existing energies.' % len(energies)) if not thermalized: MaxwellBoltzmannDistribution(self._atoms, temp=self._temperature * units.kB, force_temp=True) traj = io.Trajectory('md%05i.traj' % self._counter, 'a', self._atoms) self._constrain() dyn = NPT(self._atoms, timestep=self._timestep * units.fs, temperature=self._temperature * units.kB, externalstress=self._externalstress, ttime=self._ttime * units.fs, pfactor=self._pfactor * units.fs**2) # dyn = NPTber(self._atoms, timestep=self._timestep * units.fs, temperature=self._temperature, fixcm=True, pressure=self._pressure, taut=self._taut * units.fs, taup=self._taup * units.fs, compressibility=self._compressibility) log = MDLogger(dyn, self._atoms, 'md%05i.log' % self._counter, header=True, stress=False, peratom=False) dyn.attach(log, interval=1) dyn.attach(traj, interval=1) while mincount < self._mdmin: # self._constrain() dyn.run(1) # del self._atoms.constraints energies.append(self._atoms.get_potential_energy()) passedmin = self._passedminimum(energies) if passedmin: mincount += 1 oldpositions.append(self._atoms.positions.copy()) # Reset atoms to minimum point. self._atoms.positions = oldpositions[passedmin[0]]
def mlmd(atoms, covariance=None, calc_script=None, dt=None, tem=300., picos=100, bulk_modulus=None, stress=0., mask=None, group=None, tape='model.sgpr', trajectory='md.traj', loginterval=1, append=False, rattle=0.0, ediff=0.041, fdiff=0.082, coveps=1e-4, random_seed=None): """ atoms: ASE atoms covariance: kernel or model calc_script: path to a python script where the DFT calculator is defined dt: time-step in fs tem: temperature in Kelvin picos: pico-seconds for md bulk_modulus: bulk_modulus for NPT simulations. if None, NVT is performed stress: external stress (GPa) for NPT mask: see ase.npt.NPT group: process-group for MPI. if None, it is initiated tape: checkpoint for the ML potential trajectory: traj file name loginterval: for traj file append: append to traj file rattle: rattle atoms at initial step (recommended ~0.05) ediff: energy sensitivity fdiff: force sensitivity coveps: skips ML update if covloss < coveps """ # set calculator if calc_script is not None: calc_script = SocketCalculator(script=calc_script) calc = ActiveCalculator(covariance=covariance, calculator=calc_script, process_group=group or mpi_init(), tape=tape, fdiff=fdiff, ediff=ediff, coveps=coveps) if random_seed: np.random.seed(random_seed) # process atoms if type(atoms) == str: if atoms.startswith('export'): folder = atoms.split('-')[1] if '-' in atoms else 'model' calc.model.to_folder(folder) return else: atoms = read(atoms) atoms.rattle(rattle, rng=np.random) # rattle after mpi_init atoms.set_calculator(calc) # define and run Nose-Hoover dynamics if dt is None: if (atoms.numbers == 1).any(): dt = 0.25 else: dt = 1. ttime = 25 * units.fs ptime = 100 * units.fs if bulk_modulus: pfactor = (ptime**2) * bulk_modulus * units.GPa else: pfactor = None init_velocities(atoms, tem) make_cell_upper_triangular(atoms) filtered = FilterDeltas(atoms) steps_for_1ps = int(1000 / dt) dyn = NPT(filtered, dt * units.fs, tem * units.kB, stress * units.GPa, ttime, pfactor, mask=mask, trajectory=trajectory, append_trajectory=append, loginterval=loginterval) dyn.run(picos * steps_for_1ps)
from ase.io.trajectory import Trajectory from ase.io import read from ase.md.npt import NPT from ase.units import fs, kB atoms = read('Na_in_water.xyz') calc = GPAW(mode='lcao', xc='PBE', basis='dzp', symmetry={'point_group': False}, charge=1, txt='gpawOutput.gpaw-out') atoms.set_calculator(calc) dyn = NPT(atoms, pfactor=None, externalstress=0, temperature=350*kB, timestep=0.5*fs, ttime=20*fs, logfile='mdOutput.log') trajectory = Trajectory('nptDyn.traj', 'w', atoms) dyn.attach(trajectory.write, interval=1) dyn.run(4000)
calc = GPAW(mode='lcao', xc='PBE', basis='dzp', symmetry={'point_group': False}, charge=1, h=0.2, txt='out_mdTask1.txt') a.set_calculator(calc) dyn = NPT(atoms=a, timestep=dt, temperature=350 * kB, externalstress=0, ttime=20 * fs, pfactor=None, logfile='log_mdTask1.txt') # Using the Nosé-Hoover thermostat trajectory = Trajectory('mdTask1.traj', 'w', a) dyn.attach( trajectory.write, interval=1) # Write state of the system to trajectory at every timestep dyn.run(N_steps) end = time.time() if world.rank == 0: print('-------- MD simulation finished in: ' + f'{(end-start):.2f} s --------'.rjust(34)) print( '----------------------------------------------------------------------' )
# atoms, # d_t, # trajectory = label+'.traj', # logfile = 'log_'+label+'.txt', # ) # from ase.md import Langevin # dyn = Langevin( # atoms = atoms, # timestep = 10 *units.fs, # temperature_K = temp, # friction = 1e-02, # trajectory = label+'.traj', # logfile = 'log_'+label+'.txt', # ) from ase.md.npt import NPT dyn = NPT( atoms=atoms, timestep=d_t, temperature_K=temp, externalstress=0., ttime=75 * units.fs, pfactor=(75. * units.fs)**2 * 100. * units.GPa, trajectory=label + '.traj', logfile='log_' + label + '.txt', ) ### relax option dyn.set_fraction_traceless(0) # 0 --> no shape change but yes volume change dyn.run( steps=t_step ) #MD simulation of object 'dyn' is performed by 'run' method of VV class
os.remove(ase_db) kspacing = 0.2 steps = 10 struc = bulk("Si", 'diamond', a=5.468728, cubic=True) mliap = "potentials/Si-20-20.pth" ff = PyXtal_FF(model={'system': ["Si"]}, logo=False) ff.run(mode='predict', mliap=mliap) calc = PyXtalFFCalculator(ff=ff) struc.set_calculator(calc) #struc.set_calculator(set_vasp('single', kspacing)) #energy, forces = dft_run(struc, path=dumpfolder, ncpu=16, max_time=10) # total energy # MD traj = Trajectory("Si.traj", 'w', struc) MaxwellBoltzmannDistribution(struc, temperature_K=300) dyn = NPT(struc, 0.1 * units.fs, externalstress=0., ttime=1000 * units.fs, pfactor=1000 * units.fs, temperature_K=300) dyn.attach(traj.write, interval=steps) for i in range(1000): dyn.run(steps=steps) PrintEnergy(struc) save_to_ASE_db(struc, path=ase_db) traj.close()
class AseInterface: """ Interface for ASE calculations (optimization and molecular dynamics) Args: molecule_path (str): Path to initial geometry ml_model (object): Trained model working_dir (str): Path to directory where files should be stored device (str): cpu or cuda """ def __init__( self, molecule_path, ml_model, working_dir, device="cpu", energy="energy", forces="forces", energy_units="eV", forces_units="eV/Angstrom", environment_provider=SimpleEnvironmentProvider(), ): # Setup directory self.working_dir = working_dir if not os.path.exists(self.working_dir): os.makedirs(self.working_dir) # Load the molecule self.molecule = None self._load_molecule(molecule_path) # Set up calculator calculator = SpkCalculator( ml_model, device=device, energy=energy, forces=forces, energy_units=energy_units, forces_units=forces_units, environment_provider=environment_provider, ) self.molecule.set_calculator(calculator) # Unless initialized, set dynamics to False self.dynamics = False def _load_molecule(self, molecule_path): """ Load molecule from file (can handle all ase formats). Args: molecule_path (str): Path to molecular geometry """ file_format = os.path.splitext(molecule_path)[-1] if file_format == "xyz": self.molecule = read_xyz(molecule_path) else: self.molecule = read(molecule_path) def save_molecule(self, name, file_format="xyz", append=False): """ Save the current molecular geometry. Args: name (str): Name of save-file. file_format (str): Format to store geometry (default xyz). append (bool): If set to true, geometry is added to end of file (default False). """ molecule_path = os.path.join(self.working_dir, "%s.%s" % (name, file_format)) if file_format == "xyz": # For extended xyz format, plain is needed since ase can not parse # the extxyz it writes write_xyz(molecule_path, self.molecule, plain=True) else: write(molecule_path, self.molecule, format=file_format, append=append) def calculate_single_point(self): """ Perform a single point computation of the energies and forces and store them to the working directory. The format used is the extended xyz format. This functionality is mainly intended to be used for interfaces. """ energy = self.molecule.get_potential_energy() forces = self.molecule.get_forces() self.molecule.energy = energy self.molecule.forces = forces self.save_molecule("single_point", file_format="extxyz") def init_md( self, name, time_step=0.5, temp_init=300, temp_bath=None, external_stress=None, ttime=None, pfactor=None, reset=False, interval=1, ): """ Initialize an ase molecular dynamics trajectory. The logfile needs to be specifies, so that old trajectories are not overwritten. This functionality can be used to subsequently carry out equilibration and production. Args: name (str): Basic name of logfile and trajectory time_step (float): Time step in fs (default=0.5) temp_init (float): Initial temperature of the system in K (default is 300) temp_bath (float): Carry out Langevin NVT dynamics at the specified temperature. If set to None, NVE dynamics are performed instead (default=None) reset (bool): Whether dynamics should be restarted with new initial conditions (default=False) interval (int): Data is stored every interval steps (default=1) """ # If a previous dynamics run has been performed, don't reinitialize # velocities unless explicitly requested via restart=True if not self.dynamics or reset: self._init_velocities(temp_init=temp_init) # Set up dynamics if temp_bath is None and external_stress is None: self.dynamics = VelocityVerlet(self.molecule, time_step * units.fs) elif external_stress is None: self.dynamics = Langevin( self.molecule, time_step * units.fs, temp_bath * units.kB, 1.0 / (100.0 * units.fs), ) else: self.dynamics = NPT( self.molecule, time_step * units.fs, temp_bath * units.kB, external_stress * units.GPa, ttime * units.fs, pfactor * units.fs**2 * units.GPa, ) # Create monitors for logfile and a trajectory file logfile = os.path.join(self.working_dir, "%s.log" % name) trajfile = os.path.join(self.working_dir, "%s.traj" % name) logger = MDLogger( self.dynamics, self.molecule, logfile, stress=False, peratom=False, header=True, mode="a", ) trajectory = Trajectory(trajfile, "w", self.molecule) # Attach monitors to trajectory self.dynamics.attach(logger, interval=interval) self.dynamics.attach(trajectory.write, interval=interval) def _init_velocities(self, temp_init=300, remove_translation=True, remove_rotation=True): """ Initialize velocities for molecular dynamics Args: temp_init (float): Initial temperature in Kelvin (default 300) remove_translation (bool): Remove translation components of velocity (default True) remove_rotation (bool): Remove rotation components of velocity (default True) """ MaxwellBoltzmannDistribution(self.molecule, temp_init * units.kB) if remove_translation: Stationary(self.molecule) if remove_rotation: ZeroRotation(self.molecule) def run_md(self, steps): """ Perform a molecular dynamics simulation using the settings specified upon initializing the class. Args: steps (int): Number of simulation steps performed """ if not self.dynamics: raise AttributeError("Dynamics need to be initialized using the" " 'setup_md' function") self.dynamics.run(steps) def optimize(self, fmax=1.0e-2, steps=1000): """ Optimize a molecular geometry using the Quasi Newton optimizer in ase (BFGS + line search) Args: fmax (float): Maximum residual force change (default 1.e-2) steps (int): Maximum number of steps (default 1000) """ name = "optimization" optimize_file = os.path.join(self.working_dir, name) optimizer = QuasiNewton( self.molecule, trajectory="%s.traj" % optimize_file, restart="%s.pkl" % optimize_file, ) optimizer.run(fmax, steps) # Save final geometry in xyz format self.save_molecule(name) def compute_normal_modes(self, write_jmol=True): """ Use ase calculator to compute numerical frequencies for the molecule Args: write_jmol (bool): Write frequencies to input file for visualization in jmol (default=True) """ freq_file = os.path.join(self.working_dir, "normal_modes") # Compute frequencies frequencies = Vibrations(self.molecule, name=freq_file) frequencies.run() # Print a summary frequencies.summary() # Write jmol file if requested if write_jmol: frequencies.write_jmol()
init_velocities(atoms, tem) # make_cell_upper_triangular(atoms) filtered = FilterDeltas(atoms) dyn = NPT(filtered, dt * units.fs, temperature_K=tem, externalstress=stress * units.GPa, ttime=ttime, pfactor=pfactor if npt else None, mask=None, trajectory='md.traj', append_trajectory=False, loginterval=1) # F. run md dyn.run(1000) # G. save model manually (necessary only if pckl=None above) # calc.model.to_folder('model.pckl') # H. restart # calc = ActiveCalculator(covariance='model.pckl/', ...) # I. using the saved tapes (.sgpr files) # these files can be used for faster retraining the model e. g. # with different parameters, etc. # calc.include_tape('model.sgpr') # this mechanism can also be used for high level parallelism. # one can train independent models at different domains and # combine them by including their respective tapes.