def ipynbformat(lines): """Converts raw file lines to the JSON content lines used in ipynb""" line = repr(lines[0])[1:-1].replace('"', r'\"') alllines = line + '",\n' for i in range(1, len(lines) - 1): line = repr(lines[i])[1:-1].replace('"', r'\"') alllines += ' "' + line + '",\n' line = repr(lines[-1])[1:-1].replace('"', r'\"') alllines += ' "' + line return alllines.replace(r"\'", r"'")
def ipynbformat(lines): """Converts raw file lines to the JSON content lines used in ipynb""" line = repr(lines[0])[1:-1].replace('"', r'\"') alllines = line + '",\n' for i in range(1, len(lines)-1): line = repr(lines[i])[1:-1].replace('"', r'\"') alllines += ' "' + line + '",\n' line = repr(lines[-1])[1:-1].replace('"', r'\"') alllines += ' "' + line return alllines.replace(r"\'", r"'")
def r_a_ratio(ucell): """ Calculates the r/a ratio by identifying the shortest interatomic spacing, r, for a unit cell. Parameters ---------- ucell : atomman.System The unit cell system to evaluate. Returns ------- float The shortest interatomic spacing, r, divided by the unit cell's a lattice parameter. """ r_a = ucell.box.a for i in range(ucell.natoms): for j in range(i): dmag = np.linalg.norm(ucell.dvect(i,j)) if dmag < r_a: r_a = dmag return r_a / ucell.box.a
def r_a_ratio(ucell): """ Calculates the r/a ratio by identifying the shortest interatomic spacing, r, for a unit cell. Parameters ---------- ucell : atomman.System The unit cell system to evaluate. Returns ------- float The shortest interatomic spacing, r, divided by the unit cell's a lattice parameter. """ r_a = ucell.box.a for i in range(ucell.natoms): for j in range(i): dmag = np.linalg.norm(ucell.dvect(i, j)) if dmag < r_a: r_a = dmag return r_a / ucell.box.a
def parsefunctions(lines): """ Parses out the lines of a Python script corresponding to primary functions. """ name = None for i in range(len(lines)): if lines[i].strip() != '': if lines[i][0] != ' ' and name is not None: yield name, lines[start:i] name = None if lines[i][:4] == 'def ': start = i name = lines[i][4:].split('(')[0].strip()
def peierlsnabarrostress(pnsolution, delta_tau, tausteps=1, cdiffstress=False, fullstress=True, min_method='Powell', min_options={}): """ Applies stress to a semi-discrete Peierls-Nabarro solution. """ total_energies = [] tau_xys = [] tau_yys = [] tau_yzs = [] # Loop over stress states for i in range(0, tausteps + 1): tau = i * delta_tau pnsolution.solve(tau=tau, cdiffstress=cdiffstress, fullstress=fullstress, min_method=min_method, min_options=min_options) sys.stdout.flush() # Save values total_energies.append(pnsolution.total_energy()) tau_xys.append(tau[1, 0]) tau_yys.append(tau[1, 1]) tau_yzs.append(tau[1, 2]) # Save solution to file model = pnsolution.model() with open(os.path.join(str(i) + '.json'), 'w') as f: model.json(fp=f) # Initialize results dict results_dict = {} results_dict['total_energy'] = total_energies results_dict['tau_xy'] = tau_xys results_dict['tau_yy'] = tau_yys results_dict['tau_yz'] = tau_yzs return results_dict
def elastic_constants_static(lammps_command, system, potential, mpi_command=None, strainrange=1e-6, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Repeatedly runs the ELASTIC example distributed with LAMMPS until box dimensions converge within a tolerance. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. strainrange : float, optional The small strain value to apply when calculating the elastic constants (default is 1e-6). etol : float, optional The energy tolerance for the structure minimization. This value is unitless. (Default is 0.0). ftol : float, optional The force tolerance for the structure minimization. This value is in units of force. (Default is 0.0). maxiter : int, optional The maximum number of minimization iterations to use (default is 10000). maxeval : int, optional The maximum number of minimization evaluations to use (default is 100000). dmax : float, optional The maximum distance in length units that any atom is allowed to relax in any direction during a single minimization iteration (default is 0.01 Angstroms). Returns ------- dict Dictionary of results consisting of keys: - **'a_lat'** (*float*) - The relaxed a lattice constant. - **'b_lat'** (*float*) - The relaxed b lattice constant. - **'c_lat'** (*float*) - The relaxed c lattice constant. - **'alpha_lat'** (*float*) - The alpha lattice angle. - **'beta_lat'** (*float*) - The beta lattice angle. - **'gamma_lat'** (*float*) - The gamma lattice angle. - **'E_coh'** (*float*) - The cohesive energy of the relaxed system. - **'stress'** (*numpy.array*) - The measured stress state of the relaxed system. - **'C_elastic'** (*atomman.ElasticConstants*) - The relaxed system's elastic constants. - **'system_relaxed'** (*atomman.System*) - The relaxed system. """ # Convert hexagonal cells to orthorhombic to avoid LAMMPS tilt issues if am.tools.ishexagonal(system.box): system = system.rotate([[2, -1, -1, 0], [0, 1, -1, 0], [0, 0, 0, 1]]) # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) lammps_variables['strainrange'] = strainrange lammps_variables['etol'] = etol lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maxiter'] = maxiter lammps_variables['maxeval'] = maxeval lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length']) # Fill in template files template_file = 'cij.template' lammps_script = 'cij.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) template_file2 = 'potential.template' lammps_script2 = 'potential.in' with open(template_file2) as f: template = f.read() with open(lammps_script2, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS output = lmp.run(lammps_command, lammps_script, mpi_command) # Pull out initial state thermo = output.simulations[0]['thermo'] pxx0 = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy0 = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz0 = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz0 = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz0 = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy0 = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Negative strains cij_n = np.empty((6, 6)) for i in range(6): j = 1 + i * 2 # Pull out strained state thermo = output.simulations[j]['thermo'] pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Calculate cij_n using stress changes cij_n[i] = np.array([ pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0, pxy - pxy0 ]) / strainrange # Positive strains cij_p = np.empty((6, 6)) for i in range(6): j = 2 + i * 2 # Pull out strained state thermo = output.simulations[j]['thermo'] pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Calculate cij_p using stress changes cij_p[i] = -np.array([ pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0, pxy - pxy0 ]) / strainrange # Average symmetric values cij = (cij_n + cij_p) / 2 for i in range(6): for j in range(i): cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2 # Define results_dict results_dict = {} results_dict['raw_cij_negative'] = cij_n results_dict['raw_cij_positive'] = cij_p results_dict['C'] = am.ElasticConstants(Cij=cij) return results_dict
def relax_static(lammps_command, system, potential, mpi_command=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0, p_xz=0.0, p_yz=0.0, dispmult=0.0, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom'), maxcycles=100, ctol=1e-10): """ Repeatedly runs the ELASTIC example distributed with LAMMPS until box dimensions converge within a tolerance. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. p_xx : float, optional The value to relax the x tensile pressure component to (default is 0.0). p_yy : float, optional The value to relax the y tensile pressure component to (default is 0.0). p_zz : float, optional The value to relax the z tensile pressure component to (default is 0.0). p_xy : float, optional The value to relax the xy shear pressure component to (default is 0.0). p_xz : float, optional The value to relax the xz shear pressure component to (default is 0.0). p_yz : float, optional The value to relax the yz shear pressure component to (default is 0.0). dispmult : float, optional Multiplier for applying a random displacement to all atomic positions prior to relaxing. Default value is 0.0. etol : float, optional The energy tolerance for the structure minimization. This value is unitless. (Default is 0.0). ftol : float, optional The force tolerance for the structure minimization. This value is in units of force. (Default is 0.0). maxiter : int, optional The maximum number of minimization iterations to use (default is 10000). maxeval : int, optional The maximum number of minimization evaluations to use (default is 100000). dmax : float, optional The maximum distance in length units that any atom is allowed to relax in any direction during a single minimization iteration (default is 0.01 Angstroms). pressure_unit : str, optional The unit of pressure to calculate the elastic constants in (default is 'GPa'). maxcycles : int, optional The maximum number of times the minimization algorithm is called. Default value is 100. ctol : float, optional The relative tolerance used to determine if the lattice constants have converged (default is 1e-10). Returns ------- dict Dictionary of results consisting of keys: - **'relaxed_system'** (*float*) - The relaxed system. - **'E_coh'** (*float*) - The cohesive energy of the relaxed system. - **'measured_pxx'** (*float*) - The measured x tensile pressure of the relaxed system. - **'measured_pyy'** (*float*) - The measured y tensile pressure of the relaxed system. - **'measured_pzz'** (*float*) - The measured z tensile pressure of the relaxed system. - **'measured_pxy'** (*float*) - The measured xy shear pressure of the relaxed system. - **'measured_pxz'** (*float*) - The measured xz shear pressure of the relaxed system. - **'measured_pyz'** (*float*) - The measured yz shear pressure of the relaxed system. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Save initial configuration as a dump file system.dump('atom_dump', f='initial.dump') # Apply small random distortions to atoms system.atoms.pos += dispmult * np.random.rand(*system.atoms.pos.shape) - dispmult / 2 # Initialize parameters old_vects = system.box.vects converged = False # Run minimizations up to maxcycles times for cycle in range(maxcycles): old_system = deepcopy(system) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) lammps_variables['p_xx'] = uc.get_in_units(p_xx, lammps_units['pressure']) lammps_variables['p_yy'] = uc.get_in_units(p_yy, lammps_units['pressure']) lammps_variables['p_zz'] = uc.get_in_units(p_zz, lammps_units['pressure']) lammps_variables['p_xy'] = uc.get_in_units(p_xy, lammps_units['pressure']) lammps_variables['p_xz'] = uc.get_in_units(p_xz, lammps_units['pressure']) lammps_variables['p_yz'] = uc.get_in_units(p_yz, lammps_units['pressure']) lammps_variables['etol'] = etol lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maxiter'] = maxiter lammps_variables['maxeval'] = maxeval lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length']) # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = 'minbox.template' lammps_script = 'minbox.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS and extract thermo data logfile = 'log-' + str(cycle) + '.lammps' output = lmp.run(lammps_command, lammps_script, mpi_command, logfile=logfile) thermo = output.simulations[0]['thermo'] # Clean up dump files os.remove('0.dump') last_dump_file = str(thermo.Step.values[-1]) + '.dump' renamed_dump_file = 'relax_static-' + str(cycle) + '.dump' shutil.move(last_dump_file, renamed_dump_file) # Load relaxed system system = am.load('atom_dump', renamed_dump_file, symbols=system.symbols) # Test if box dimensions have converged if np.allclose(old_vects, system.box.vects, rtol=ctol, atol=0): converged = True break else: old_vects = system.box.vects # Check for convergence if converged is False: raise RuntimeError('Failed to converge after ' + str(maxcycles) + ' cycles') # Zero out near-zero tilt factors lx = system.box.lx ly = system.box.ly lz = system.box.lz xy = system.box.xy xz = system.box.xz yz = system.box.yz if np.isclose(xy/ly, 0.0, rtol=0.0, atol=1e-10): xy = 0.0 if np.isclose(xz/lz, 0.0, rtol=0.0, atol=1e-10): xz = 0.0 if np.isclose(yz/lz, 0.0, rtol=0.0, atol=1e-10): yz = 0.0 system.box.set(lx=lx, ly=ly, lz=lz, xy=xy, xz=xz, yz=yz) system.wrap() # Build results_dict results_dict = {} results_dict['dumpfile_initial'] = 'initial.dump' results_dict['symbols_initial'] = system.symbols results_dict['dumpfile_final'] = renamed_dump_file results_dict['symbols_final'] = system.symbols results_dict['E_coh'] = uc.set_in_units(thermo.PotEng.values[-1] / system.natoms, lammps_units['energy']) results_dict['lx'] = uc.set_in_units(lx, lammps_units['length']) results_dict['ly'] = uc.set_in_units(ly, lammps_units['length']) results_dict['lz'] = uc.set_in_units(lz, lammps_units['length']) results_dict['xy'] = uc.set_in_units(xy, lammps_units['length']) results_dict['xz'] = uc.set_in_units(xz, lammps_units['length']) results_dict['yz'] = uc.set_in_units(yz, lammps_units['length']) results_dict['measured_pxx'] = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) results_dict['measured_pyy'] = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) results_dict['measured_pzz'] = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) results_dict['measured_pxy'] = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) results_dict['measured_pxz'] = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) results_dict['measured_pyz'] = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) return results_dict
def relax_static(lammps_command, system, potential, mpi_command=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0, p_xz=0.0, p_yz=0.0, dispmult=0.0, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom'), maxcycles=100, ctol=1e-10): """ Repeatedly runs the ELASTIC example distributed with LAMMPS until box dimensions converge within a tolerance. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. p_xx : float, optional The value to relax the x tensile pressure component to (default is 0.0). p_yy : float, optional The value to relax the y tensile pressure component to (default is 0.0). p_zz : float, optional The value to relax the z tensile pressure component to (default is 0.0). p_xy : float, optional The value to relax the xy shear pressure component to (default is 0.0). p_xz : float, optional The value to relax the xz shear pressure component to (default is 0.0). p_yz : float, optional The value to relax the yz shear pressure component to (default is 0.0). dispmult : float, optional Multiplier for applying a random displacement to all atomic positions prior to relaxing. Default value is 0.0. etol : float, optional The energy tolerance for the structure minimization. This value is unitless. (Default is 0.0). ftol : float, optional The force tolerance for the structure minimization. This value is in units of force. (Default is 0.0). maxiter : int, optional The maximum number of minimization iterations to use (default is 10000). maxeval : int, optional The maximum number of minimization evaluations to use (default is 100000). dmax : float, optional The maximum distance in length units that any atom is allowed to relax in any direction during a single minimization iteration (default is 0.01 Angstroms). pressure_unit : str, optional The unit of pressure to calculate the elastic constants in (default is 'GPa'). maxcycles : int, optional The maximum number of times the minimization algorithm is called. Default value is 100. ctol : float, optional The relative tolerance used to determine if the lattice constants have converged (default is 1e-10). Returns ------- dict Dictionary of results consisting of keys: - **'relaxed_system'** (*float*) - The relaxed system. - **'E_coh'** (*float*) - The cohesive energy of the relaxed system. - **'measured_pxx'** (*float*) - The measured x tensile pressure of the relaxed system. - **'measured_pyy'** (*float*) - The measured y tensile pressure of the relaxed system. - **'measured_pzz'** (*float*) - The measured z tensile pressure of the relaxed system. - **'measured_pxy'** (*float*) - The measured xy shear pressure of the relaxed system. - **'measured_pxz'** (*float*) - The measured xz shear pressure of the relaxed system. - **'measured_pyz'** (*float*) - The measured yz shear pressure of the relaxed system. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Save initial configuration as a dump file system.dump('atom_dump', f='initial.dump') # Apply small random distortions to atoms system.atoms.pos += dispmult * np.random.rand( *system.atoms.pos.shape) - dispmult / 2 # Initialize parameters old_vects = system.box.vects converged = False # Run minimizations up to maxcycles times for cycle in range(maxcycles): old_system = deepcopy(system) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info( system.symbols) lammps_variables['p_xx'] = uc.get_in_units(p_xx, lammps_units['pressure']) lammps_variables['p_yy'] = uc.get_in_units(p_yy, lammps_units['pressure']) lammps_variables['p_zz'] = uc.get_in_units(p_zz, lammps_units['pressure']) lammps_variables['p_xy'] = uc.get_in_units(p_xy, lammps_units['pressure']) lammps_variables['p_xz'] = uc.get_in_units(p_xz, lammps_units['pressure']) lammps_variables['p_yz'] = uc.get_in_units(p_yz, lammps_units['pressure']) lammps_variables['etol'] = etol lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maxiter'] = maxiter lammps_variables['maxeval'] = maxeval lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length']) # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = 'minbox.template' lammps_script = 'minbox.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write( iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS and extract thermo data logfile = 'log-' + str(cycle) + '.lammps' output = lmp.run(lammps_command, lammps_script, mpi_command, logfile=logfile) thermo = output.simulations[0]['thermo'] # Clean up dump files os.remove('0.dump') last_dump_file = str(thermo.Step.values[-1]) + '.dump' renamed_dump_file = 'relax_static-' + str(cycle) + '.dump' shutil.move(last_dump_file, renamed_dump_file) # Load relaxed system system = am.load('atom_dump', renamed_dump_file, symbols=system.symbols) # Test if box dimensions have converged if np.allclose(old_vects, system.box.vects, rtol=ctol, atol=0): converged = True break else: old_vects = system.box.vects # Check for convergence if converged is False: raise RuntimeError('Failed to converge after ' + str(maxcycles) + ' cycles') # Zero out near-zero tilt factors lx = system.box.lx ly = system.box.ly lz = system.box.lz xy = system.box.xy xz = system.box.xz yz = system.box.yz if np.isclose(xy / ly, 0.0, rtol=0.0, atol=1e-10): xy = 0.0 if np.isclose(xz / lz, 0.0, rtol=0.0, atol=1e-10): xz = 0.0 if np.isclose(yz / lz, 0.0, rtol=0.0, atol=1e-10): yz = 0.0 system.box.set(lx=lx, ly=ly, lz=lz, xy=xy, xz=xz, yz=yz) system.wrap() # Build results_dict results_dict = {} results_dict['dumpfile_initial'] = 'initial.dump' results_dict['symbols_initial'] = system.symbols results_dict['dumpfile_final'] = renamed_dump_file results_dict['symbols_final'] = system.symbols results_dict['E_coh'] = uc.set_in_units( thermo.PotEng.values[-1] / system.natoms, lammps_units['energy']) results_dict['lx'] = uc.set_in_units(lx, lammps_units['length']) results_dict['ly'] = uc.set_in_units(ly, lammps_units['length']) results_dict['lz'] = uc.set_in_units(lz, lammps_units['length']) results_dict['xy'] = uc.set_in_units(xy, lammps_units['length']) results_dict['xz'] = uc.set_in_units(xz, lammps_units['length']) results_dict['yz'] = uc.set_in_units(yz, lammps_units['length']) results_dict['measured_pxx'] = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) results_dict['measured_pyy'] = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) results_dict['measured_pzz'] = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) results_dict['measured_pxy'] = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) results_dict['measured_pxz'] = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) results_dict['measured_pyz'] = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) return results_dict
def relax_box(lammps_command, system, potential, mpi_command=None, strainrange=1e-6, p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0, p_xz=0.0, p_yz=0.0, tol=1e-10, diverge_scale=3.): """ Quickly refines static orthorhombic system by evaluating the elastic constants and the virial pressure. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. strainrange : float, optional The small strain value to apply when calculating the elastic constants (default is 1e-6). p_xx : float, optional The value to relax the x tensile pressure component to (default is 0.0). p_yy : float, optional The value to relax the y tensile pressure component to (default is 0.0). p_zz : float, optional The value to relax the z tensile pressure component to (default is 0.0). tol : float, optional The relative tolerance used to determine if the lattice constants have converged (default is 1e-10). diverge_scale : float, optional Factor to identify if the system's dimensions have diverged. Divergence is identified if either any current box dimension is greater than the original dimension multiplied by diverge_scale, or if any current box dimension is less than the original dimension divided by diverge_scale. (Default is 3.0). Returns ------- dict Dictionary of results consisting of keys: - **'a_lat'** (*float*) - The relaxed a lattice constant. - **'b_lat'** (*float*) - The relaxed b lattice constant. - **'c_lat'** (*float*) - The relaxed c lattice constant. - **'alpha_lat'** (*float*) - The alpha lattice angle. - **'beta_lat'** (*float*) - The beta lattice angle. - **'gamma_lat'** (*float*) - The gamma lattice angle. - **'E_coh'** (*float*) - The cohesive energy of the relaxed system. - **'stress'** (*numpy.array*) - The measured stress state of the relaxed system. - **'C_elastic'** (*atomman.ElasticConstants*) - The relaxed system's elastic constants. - **'system_relaxed'** (*atomman.System*) - The relaxed system. Raises ------ RuntimeError If system diverges or no convergence reached after 100 cycles. """ # Flag for if values have converged converged = False # Define current and old systems system_current = deepcopy(system) system_old = None system.dump('atom_dump', f='initial.dump') for cycle in range(100): # Run LAMMPS and evaluate results based on system_old results = calc_cij(lammps_command, system_current, potential, mpi_command=mpi_command, p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, strainrange=strainrange, cycle=cycle) system_new = results['system_new'] # Compare new and current to test for convergence if np.allclose(system_new.box.vects, system_current.box.vects, rtol=tol, atol=0): converged = True break # Compare old and new to test for double-value convergence elif system_old is not None and np.allclose( system_new.box.vects, system_old.box.vects, rtol=tol, atol=0): # Update current to average of old and new system_current.box_set( a=(system_new.box.a + system_old.box.a) / 2., b=(system_new.box.b + system_old.box.b) / 2., c=(system_new.box.c + system_old.box.c) / 2., scale=True) # Calculate Cij for the averaged system results = calc_cij(lammps_command, system_current, potential, mpi_command=mpi_command, p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, strainrange=strainrange, cycle=cycle + 1) system_new = results['system_new'] converged = True break # Test for divergence elif system_new.box.a < system.box.a / diverge_scale: raise RuntimeError('Divergence of box dimensions') elif system_new.box.a > system.box.a * diverge_scale: raise RuntimeError('Divergence of box dimensions') elif system_new.box.b < system.box.b / diverge_scale: raise RuntimeError('Divergence of box dimensions') elif system_new.box.b > system.box.b * diverge_scale: raise RuntimeError('Divergence of box dimensions') elif system_new.box.c < system.box.c / diverge_scale: raise RuntimeError('Divergence of box dimensions') elif system_new.box.c > system.box.c * diverge_scale: raise RuntimeError('Divergence of box dimensions') elif results['E_coh'] == 0.0: raise RuntimeError('Divergence: cohesive energy is 0') # If not converged or diverged, current -> old and new -> current else: system_old, system_current = system_current, system_new # Return values when converged if converged: system_new.dump('atom_dump', f='final.dump') # Build results_dict results_dict = {} results_dict['dumpfile_initial'] = 'initial.dump' results_dict['symbols_initial'] = system.symbols results_dict['dumpfile_final'] = 'final.dump' results_dict['symbols_final'] = system.symbols results_dict['lx'] = system_new.box.lx results_dict['ly'] = system_new.box.ly results_dict['lz'] = system_new.box.lz results_dict['xy'] = system_new.box.xy results_dict['xz'] = system_new.box.xz results_dict['yz'] = system_new.box.yz results_dict['E_coh'] = results['E_coh'] results_dict['measured_pxx'] = results['measured_pxx'] results_dict['measured_pyy'] = results['measured_pyy'] results_dict['measured_pzz'] = results['measured_pzz'] results_dict['measured_pxy'] = results['measured_pxy'] results_dict['measured_pxz'] = results['measured_pxz'] results_dict['measured_pyz'] = results['measured_pyz'] return results_dict else: raise RuntimeError('Failed to converge after 100 cycles')
def calc_cij(lammps_command, system, potential, mpi_command=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, strainrange=1e-6, cycle=0): """ Runs cij.in LAMMPS script to evaluate Cij, and E_coh of the current system, and define a new system with updated box dimensions to test. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. strainrange : float, optional The small strain value to apply when calculating the elastic constants (default is 1e-6). p_xx : float, optional The value to relax the x tensile pressure component to (default is 0.0). p_yy : float, optional The value to relax the y tensile pressure component to (default is 0.0). p_zz : float, optional The value to relax the z tensile pressure component to (default is 0.0). cycle : int, optional Indicates the iteration cycle of quick_a_Cij(). This is used to uniquely save the LAMMPS input and output files. Returns ------- dict Dictionary of results consisting of keys: - **'E_coh'** (*float*) - The cohesive energy of the supplied system. - **'stress'** (*numpy.array*) - The measured stress state of the supplied system. - **'C_elastic'** (*atomman.ElasticConstants*) - The supplied system's elastic constants. - **'system_new'** (*atomman.System*) - System with updated box dimensions. Raises ------ RuntimeError If any of the new box dimensions are less than zero. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init' + str(cycle) + '.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) lammps_variables['delta'] = strainrange lammps_variables['steps'] = 2 # Write lammps input script template_file = 'cij.template' lammps_script = 'cij.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run lammps output = lmp.run(lammps_command, lammps_script, mpi_command=mpi_command, return_style='model') shutil.move('log.lammps', 'cij-' + str(cycle) + '-log.lammps') # Extract LAMMPS thermo data. Each term ranges i=0-12 where i=0 is undeformed # The remaining values are for -/+ strain pairs in the six unique directions lx = uc.set_in_units(np.array(output.finds('Lx')), lammps_units['length']) ly = uc.set_in_units(np.array(output.finds('Ly')), lammps_units['length']) lz = uc.set_in_units(np.array(output.finds('Lz')), lammps_units['length']) xy = uc.set_in_units(np.array(output.finds('Xy')), lammps_units['length']) xz = uc.set_in_units(np.array(output.finds('Xz')), lammps_units['length']) yz = uc.set_in_units(np.array(output.finds('Yz')), lammps_units['length']) pxx = uc.set_in_units(np.array(output.finds('Pxx')), lammps_units['pressure']) pyy = uc.set_in_units(np.array(output.finds('Pyy')), lammps_units['pressure']) pzz = uc.set_in_units(np.array(output.finds('Pzz')), lammps_units['pressure']) pxy = uc.set_in_units(np.array(output.finds('Pxy')), lammps_units['pressure']) pxz = uc.set_in_units(np.array(output.finds('Pxz')), lammps_units['pressure']) pyz = uc.set_in_units(np.array(output.finds('Pyz')), lammps_units['pressure']) pe = uc.set_in_units( np.array(output.finds('PotEng')) / system.natoms, lammps_units['energy']) # Set the six non-zero strain values strains = np.array([(lx[2] - lx[1]) / lx[0], (ly[4] - ly[3]) / ly[0], (lz[6] - lz[5]) / lz[0], (yz[8] - yz[7]) / lz[0], (xz[10] - xz[9]) / lz[0], (xy[12] - xy[11]) / ly[0]]) # Calculate cij using stress changes associated with each non-zero strain cij = np.empty((6, 6)) for i in range(6): delta_stress = np.array([ pxx[2 * i + 1] - pxx[2 * i + 2], pyy[2 * i + 1] - pyy[2 * i + 2], pzz[2 * i + 1] - pzz[2 * i + 2], pyz[2 * i + 1] - pyz[2 * i + 2], pxz[2 * i + 1] - pxz[2 * i + 2], pxy[2 * i + 1] - pxy[2 * i + 2] ]) cij[i] = delta_stress / strains[i] for i in range(6): for j in range(i): cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2 C = am.ElasticConstants(Cij=cij) S = C.Sij # Extract the current stress state stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]], [pxy[0], pyy[0], pyz[0]], [pxz[0], pyz[0], pzz[0]]]) s_xx = stress[0, 0] + p_xx s_yy = stress[1, 1] + p_yy s_zz = stress[2, 2] + p_zz new_a = system.box.a / (S[0, 0] * s_xx + S[0, 1] * s_yy + S[0, 2] * s_zz + 1) new_b = system.box.b / (S[1, 0] * s_xx + S[1, 1] * s_yy + S[1, 2] * s_zz + 1) new_c = system.box.c / (S[2, 0] * s_xx + S[2, 1] * s_yy + S[2, 2] * s_zz + 1) if new_a <= 0 or new_b <= 0 or new_c <= 0: raise RuntimeError('Divergence of box dimensions to <= 0') system_new = deepcopy(system) system_new.box_set(a=new_a, b=new_b, c=new_c, scale=True) results_dict = {} results_dict['E_coh'] = pe[0] results_dict['system_new'] = system_new results_dict['measured_pxx'] = pxx[0] results_dict['measured_pyy'] = pyy[0] results_dict['measured_pzz'] = pzz[0] results_dict['measured_pxy'] = pxy[0] results_dict['measured_pxz'] = pxz[0] results_dict['measured_pyz'] = pyz[0] return results_dict
def elastic_constants_static(lammps_command, system, potential, mpi_command=None, strainrange=1e-6, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Repeatedly runs the ELASTIC example distributed with LAMMPS until box dimensions converge within a tolerance. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. strainrange : float, optional The small strain value to apply when calculating the elastic constants (default is 1e-6). etol : float, optional The energy tolerance for the structure minimization. This value is unitless. (Default is 0.0). ftol : float, optional The force tolerance for the structure minimization. This value is in units of force. (Default is 0.0). maxiter : int, optional The maximum number of minimization iterations to use (default is 10000). maxeval : int, optional The maximum number of minimization evaluations to use (default is 100000). dmax : float, optional The maximum distance in length units that any atom is allowed to relax in any direction during a single minimization iteration (default is 0.01 Angstroms). Returns ------- dict Dictionary of results consisting of keys: - **'a_lat'** (*float*) - The relaxed a lattice constant. - **'b_lat'** (*float*) - The relaxed b lattice constant. - **'c_lat'** (*float*) - The relaxed c lattice constant. - **'alpha_lat'** (*float*) - The alpha lattice angle. - **'beta_lat'** (*float*) - The beta lattice angle. - **'gamma_lat'** (*float*) - The gamma lattice angle. - **'E_coh'** (*float*) - The cohesive energy of the relaxed system. - **'stress'** (*numpy.array*) - The measured stress state of the relaxed system. - **'C_elastic'** (*atomman.ElasticConstants*) - The relaxed system's elastic constants. - **'system_relaxed'** (*atomman.System*) - The relaxed system. """ # Convert hexagonal cells to orthorhombic to avoid LAMMPS tilt issues if am.tools.ishexagonal(system.box): system = system.rotate([[2,-1,-1,0], [0, 1, -1, 0], [0,0,0,1]]) # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) lammps_variables['strainrange'] = strainrange lammps_variables['etol'] = etol lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maxiter'] = maxiter lammps_variables['maxeval'] = maxeval lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length']) # Fill in template files template_file = 'cij.template' lammps_script = 'cij.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) template_file2 = 'potential.template' lammps_script2 = 'potential.in' with open(template_file2) as f: template = f.read() with open(lammps_script2, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS output = lmp.run(lammps_command, lammps_script, mpi_command) # Pull out initial state thermo = output.simulations[0]['thermo'] pxx0 = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy0 = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz0 = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz0 = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz0 = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy0 = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Negative strains cij_n = np.empty((6,6)) for i in range(6): j = 1 + i * 2 # Pull out strained state thermo = output.simulations[j]['thermo'] pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Calculate cij_n using stress changes cij_n[i] = np.array([pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0, pxy - pxy0])/ strainrange # Positive strains cij_p = np.empty((6,6)) for i in range(6): j = 2 + i * 2 # Pull out strained state thermo = output.simulations[j]['thermo'] pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Calculate cij_p using stress changes cij_p[i] = -np.array([pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0, pxy - pxy0])/ strainrange # Average symmetric values cij = (cij_n + cij_p) / 2 for i in range(6): for j in range(i): cij[i,j] = cij[j,i] = (cij[i,j] + cij[j,i]) / 2 # Define results_dict results_dict = {} results_dict['raw_cij_negative'] = cij_n results_dict['raw_cij_positive'] = cij_p results_dict['C'] = am.ElasticConstants(Cij=cij) return results_dict
def e_vs_r(lammps_command, system, potential, mpi_command=None, ucell=None, rmin=uc.set_in_units(2.0, 'angstrom'), rmax=uc.set_in_units(6.0, 'angstrom'), rsteps=200): """ Performs a cohesive energy scan over a range of interatomic spaces, r. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. ucell : atomman.System, optional The fundamental unit cell correspodning to system. This is used to convert system dimensions to cell dimensions. If not given, ucell will be taken as system. rmin : float, optional The minimum r spacing to use (default value is 2.0 angstroms). rmax : float, optional The maximum r spacing to use (default value is 6.0 angstroms). rsteps : int, optional The number of r spacing steps to evaluate (default value is 200). Returns ------- dict Dictionary of results consisting of keys: - **'r_values'** (*numpy.array of float*) - All interatomic spacings, r, explored. - **'a_values'** (*numpy.array of float*) - All unit cell a lattice constants corresponding to the values explored. - **'Ecoh_values'** (*numpy.array of float*) - The computed cohesive energies for each r value. - **'min_cell'** (*list of atomman.System*) - Systems corresponding to the minima identified in the Ecoh_values. """ # Make system a deepcopy of itself (protect original from changes) system = deepcopy(system) # Set ucell = system if ucell not given if ucell is None: ucell = system # Calculate the r/a ratio for the unit cell r_a = r_a_ratio(ucell) # Get ratios of lx, ly, and lz of system relative to a of ucell lx_a = system.box.a / ucell.box.a ly_a = system.box.b / ucell.box.a lz_a = system.box.c / ucell.box.a alpha = system.box.alpha beta = system.box.beta gamma = system.box.gamma # Build lists of values r_values = np.linspace(rmin, rmax, rsteps) a_values = r_values / r_a Ecoh_values = np.empty(rsteps) # Loop over values for i in range(rsteps): # Rescale system's box a = a_values[i] system.box_set(a = a * lx_a, b = a * ly_a, c = a * lz_a, alpha=alpha, beta=beta, gamma=gamma, scale=True) # Get lammps units lammps_units = lmp.style.unit(potential.units) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='atom.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) # Write lammps input script template_file = 'run0.template' lammps_script = 'run0.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run lammps and extract data output = lmp.run(lammps_command, lammps_script, mpi_command) thermo = output.simulations[0]['thermo'] if output.lammps_date < datetime.date(2016, 8, 1): Ecoh_values[i] = uc.set_in_units(thermo.peatom.values[-1], lammps_units['energy']) else: Ecoh_values[i] = uc.set_in_units(thermo.v_peatom.values[-1], lammps_units['energy']) # Rename log.lammps shutil.move('log.lammps', 'run0-'+str(i)+'-log.lammps') # Find unit cell systems at the energy minimums min_cells = [] for i in range(1, rsteps - 1): if (Ecoh_values[i] < Ecoh_values[i-1] and Ecoh_values[i] < Ecoh_values[i+1]): a = a_values[i] cell = deepcopy(ucell) cell.box_set(a = a, b = a * ucell.box.b / ucell.box.a, c = a * ucell.box.c / ucell.box.a, alpha=alpha, beta=beta, gamma=gamma, scale=True) min_cells.append(cell) # Collect results results_dict = {} results_dict['r_values'] = r_values results_dict['a_values'] = a_values results_dict['Ecoh_values'] = Ecoh_values results_dict['min_cell'] = min_cells return results_dict
def e_vs_r(lammps_command, system, potential, mpi_command=None, ucell=None, rmin=uc.set_in_units(2.0, 'angstrom'), rmax=uc.set_in_units(6.0, 'angstrom'), rsteps=200): """ Performs a cohesive energy scan over a range of interatomic spaces, r. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. ucell : atomman.System, optional The fundamental unit cell correspodning to system. This is used to convert system dimensions to cell dimensions. If not given, ucell will be taken as system. rmin : float, optional The minimum r spacing to use (default value is 2.0 angstroms). rmax : float, optional The maximum r spacing to use (default value is 6.0 angstroms). rsteps : int, optional The number of r spacing steps to evaluate (default value is 200). Returns ------- dict Dictionary of results consisting of keys: - **'r_values'** (*numpy.array of float*) - All interatomic spacings, r, explored. - **'a_values'** (*numpy.array of float*) - All unit cell a lattice constants corresponding to the values explored. - **'Ecoh_values'** (*numpy.array of float*) - The computed cohesive energies for each r value. - **'min_cell'** (*list of atomman.System*) - Systems corresponding to the minima identified in the Ecoh_values. """ # Make system a deepcopy of itself (protect original from changes) system = deepcopy(system) # Set ucell = system if ucell not given if ucell is None: ucell = system # Calculate the r/a ratio for the unit cell r_a = r_a_ratio(ucell) # Get ratios of lx, ly, and lz of system relative to a of ucell lx_a = system.box.a / ucell.box.a ly_a = system.box.b / ucell.box.a lz_a = system.box.c / ucell.box.a alpha = system.box.alpha beta = system.box.beta gamma = system.box.gamma # Build lists of values r_values = np.linspace(rmin, rmax, rsteps) a_values = r_values / r_a Ecoh_values = np.empty(rsteps) # Loop over values for i in range(rsteps): # Rescale system's box a = a_values[i] system.box_set(a=a * lx_a, b=a * ly_a, c=a * lz_a, alpha=alpha, beta=beta, gamma=gamma, scale=True) # Get lammps units lammps_units = lmp.style.unit(potential.units) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='atom.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info( system.symbols) # Write lammps input script template_file = 'run0.template' lammps_script = 'run0.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write( iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run lammps and extract data output = lmp.run(lammps_command, lammps_script, mpi_command) thermo = output.simulations[0]['thermo'] if output.lammps_date < datetime.date(2016, 8, 1): Ecoh_values[i] = uc.set_in_units(thermo.peatom.values[-1], lammps_units['energy']) else: Ecoh_values[i] = uc.set_in_units(thermo.v_peatom.values[-1], lammps_units['energy']) # Rename log.lammps shutil.move('log.lammps', 'run0-' + str(i) + '-log.lammps') # Find unit cell systems at the energy minimums min_cells = [] for i in range(1, rsteps - 1): if (Ecoh_values[i] < Ecoh_values[i - 1] and Ecoh_values[i] < Ecoh_values[i + 1]): a = a_values[i] cell = deepcopy(ucell) cell.box_set(a=a, b=a * ucell.box.b / ucell.box.a, c=a * ucell.box.c / ucell.box.a, alpha=alpha, beta=beta, gamma=gamma, scale=True) min_cells.append(cell) # Collect results results_dict = {} results_dict['r_values'] = r_values results_dict['a_values'] = a_values results_dict['Ecoh_values'] = Ecoh_values results_dict['min_cell'] = min_cells return results_dict