def test_lammps_versions(commands): """ Tests the primary and alternative lammps commands listed in the loaded commands file. Only works when called on the machine where the executables are accessible, i.e. don't use if preparing on a different resource! Issues an error if the primary lammps command is inaccessible or too old. Ignores alternate lammps commands if they are inaccessible or not in the required date range. lammps_command - must be 30 Oct 2019 or newer. lammps_command_snap_1 - must be between 8 Aug 2014 and 30 May 2017. lammps_command_snap_2 - must be between 3 Dec 2018 and 12 Jun 2019. lammps_command_old - must be before 30 Oct 2019. Parameters ---------- commands : dict The mpi and lammps commands to use when preparing. """ # Test main LAMMPS command lammps_command = commands['lammps_command'] lammpsdate = lmp.checkversion(lammps_command)['date'] assert lammpsdate >= date(2019, 10, 30) # Define test for older LAMMPS commands def test_old(commands, key, startdate=None, enddate=None): if key in commands: command = commands[key] else: return True try: lammpsdate = lmp.checkversion(command)['date'] except: print(f'{key} not found or not working') else: if startdate is not None and lammpsdate < startdate: print(f'{key} too old') elif enddate is not None and lammpsdate > enddate: print(f'{key} too new') else: return True return False # Test older LAMMPS commands if not test_old(commands, 'lammps_command_snap_1', date(2014, 8, 8), date(2017, 5, 30)): del commands['lammps_command_snap_1'] if not test_old(commands, 'lammps_command_snap_2', date(2018, 12, 3), date(2019, 6, 12)): del commands['lammps_command_snap_2'] if not test_old(commands, 'lammps_command_old', None, date(2019, 10, 30)): del commands['lammps_command_old']
def lammps_commands(input_dict, **kwargs): """ Interprets calculation parameters associated with lammps_command and mpi_command input_dict keys. The input_dict keys used by this function (which can be renamed using the function's keyword arguments): - **'lammps_command'** the LAMMPS command to use for running simulations. - **'mpi_command'** the specific MPI command to run LAMMPS in parallel. - **'lammps_version'** the version of LAMMPS associated with lammps_command. - **'lammps_date'** a datetime.date object of the LAMMPS version. Parameters ---------- input_dict : dict Dictionary containing input parameter key-value pairs. lammps_command : str Replacement parameter key name for 'lammps_command'. mpi_command : str Replacement parameter key name for 'mpi_command'. lammps_version : str Replacement parameter key name for 'lammps_version'. lammps_date : str Replacement parameter key name for 'lammps_date'. """ # Set default keynames keynames = ['lammps_command', 'mpi_command', 'lammps_version', 'lammps_date'] for keyname in keynames: kwargs[keyname] = kwargs.get(keyname, keyname) # Extract input values and assign default values lammps_command = input_dict[kwargs['lammps_command']] mpi_command = input_dict.get(kwargs['mpi_command'], None) # Retrieve lammps_version info lammps_version = lmp.checkversion(lammps_command) # Save processed terms input_dict[kwargs['mpi_command']] = mpi_command input_dict[kwargs['lammps_version']] = lammps_version['version'] input_dict[kwargs['lammps_date']] = lammps_version['date']
def test_old(commands, key, startdate=None, enddate=None): if key in commands: command = commands[key] else: return True try: lammpsdate = lmp.checkversion(command)['date'] except: print(f'{key} not found or not working') else: if startdate is not None and lammpsdate < startdate: print(f'{key} too old') elif enddate is not None and lammpsdate > enddate: print(f'{key} too new') else: return True return False
def interpret(self, input_dict): """ Interprets calculation parameters. Parameters ---------- input_dict : dict Dictionary containing input parameter key-value pairs. """ # Set default keynames keymap = self.keymap # Extract input values and assign default values lammps_command = input_dict[keymap['lammps_command']] mpi_command = input_dict.get(keymap['mpi_command'], None) # Retrieve lammps_version info lammps_version = lmp.checkversion(lammps_command) # Save processed terms input_dict[keymap['mpi_command']] = mpi_command input_dict[keymap['lammps_version']] = lammps_version['version'] input_dict[keymap['lammps_date']] = lammps_version['date']
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_dynamic(lammps_command: str, system: am.System, potential: lmp.Potential, mpi_command: Optional[str] = None, p_xx: float = 0.0, p_yy: float = 0.0, p_zz: float = 0.0, p_xy: float = 0.0, p_xz: float = 0.0, p_yz: float = 0.0, temperature: float = 0.0, integrator: Optional[str] = None, runsteps: int = 220000, thermosteps: int = 100, dumpsteps: Optional[int] = None, restartsteps: Optional[int] = None, equilsteps: int = 20000, randomseed: Optional[int] = None) -> dict: """ Performs a full dynamic relax on a given system at the given temperature to the specified pressure state. 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. symbols : list of str The list of element-model symbols for the Potential that correspond to system's atypes. 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). temperature : float, optional The temperature to relax at (default is 0.0). runsteps : int, optional The number of integration steps to perform (default is 220000). integrator : str or None, optional The integration method to use. Options are 'npt', 'nvt', 'nph', 'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat. (Default is None, which will use 'nph+l' for temperature == 0, and 'npt' otherwise.) thermosteps : int, optional Thermo values will be reported every this many steps (default is 100). dumpsteps : int or None, optional Dump files will be saved every this many steps (default is None, which sets dumpsteps equal to runsteps). restartsteps : int or None, optional Restart files will be saved every this many steps (default is None, which sets restartsteps equal to runsteps). equilsteps : int, optional The number of timesteps at the beginning of the simulation to exclude when computing average values (default is 20000). randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) Returns ------- dict Dictionary of results consisting of keys: - **'dumpfile_initial'** (*str*) - The name of the initial dump file created. - **'symbols_initial'** (*list*) - The symbols associated with the initial dump file. - **'dumpfile_final'** (*str*) - The name of the final dump file created. - **'symbols_final'** (*list*) - The symbols associated with the final dump file. - **'nsamples'** (*int*) - The number of thermodynamic samples included in the mean and standard deviation estimates. Can also be used to estimate standard error values assuming that the thermo step size is large enough (typically >= 100) to assume the samples to be independent. - **'E_pot'** (*float*) - The mean measured potential energy. - **'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. - **'temp'** (*float*) - The mean measured temperature. - **'E_pot_std'** (*float*) - The standard deviation in the measured potential energy values. - **'measured_pxx_std'** (*float*) - The standard deviation in the measured x tensile pressure of the relaxed system. - **'measured_pyy_std'** (*float*) - The standard deviation in the measured y tensile pressure of the relaxed system. - **'measured_pzz_std'** (*float*) - The standard deviation in the measured z tensile pressure of the relaxed system. - **'measured_pxy_std'** (*float*) - The standard deviation in the measured xy shear pressure of the relaxed system. - **'measured_pxz_std'** (*float*) - The standard deviation in the measured xz shear pressure of the relaxed system. - **'measured_pyz_std'** (*float*) - The standard deviation in the measured yz shear pressure of the relaxed system. - **'temp_std'** (*float*) - The standard deviation in the measured temperature values. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Handle default values if dumpsteps is None: dumpsteps = runsteps if restartsteps is None: restartsteps = runsteps # Define lammps variables lammps_variables = {} # Dump initial system as data and build LAMMPS inputs system_info = system.dump('atom_data', f='init.dat', potential=potential) lammps_variables['atomman_system_pair_info'] = system_info # Generate LAMMPS inputs for restarting system_info2 = potential.pair_restart_info('*.restart', system.symbols) lammps_variables['atomman_pair_restart_info'] = system_info2 # Integrator lines for main run integ_info = integrator_info(integrator=integrator, p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, p_xy=p_xy, p_xz=p_xz, p_yz=p_yz, temperature=temperature, randomseed=randomseed, units=potential.units, lammps_date=lammps_date) lammps_variables['integrator_info'] = integ_info # Integrator lines for restarts integ_info2 = integrator_info(integrator=integrator, p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, p_xy=p_xy, p_xz=p_xz, p_yz=p_yz, temperature=temperature, velocity_temperature=0.0, randomseed=randomseed, units=potential.units, lammps_date=lammps_date) lammps_variables['integrator_restart_info'] = integ_info2 # Other run settings lammps_variables['thermosteps'] = thermosteps lammps_variables['runsteps'] = runsteps lammps_variables['dumpsteps'] = dumpsteps lammps_variables['restartsteps'] = restartsteps # Set compute stress/atom based on LAMMPS version if lammps_date < datetime.date(2014, 2, 12): lammps_variables['stressterm'] = '' else: lammps_variables['stressterm'] = 'NULL' # Set dump_keys based on atom_style if potential.atom_style in ['charge']: lammps_variables['dump_keys'] = 'id type q xu yu zu c_pe c_ke &\n' lammps_variables[ 'dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]' else: lammps_variables['dump_keys'] = 'id type xu yu zu c_pe c_ke &\n' lammps_variables[ 'dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]' # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): if potential.atom_style in ['charge']: lammps_variables['dump_modify_format'] = f'"%d %d{12 * " %.13e"}"' else: lammps_variables['dump_modify_format'] = f'"%d %d{11 * " %.13e"}"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script lammps_script = 'full_relax.in' template = read_calc_file('iprPy.calculation.relax_dynamic', 'full_relax.template') with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Write lammps restart input script restart_script = 'full_relax_restart.in' template = read_calc_file('iprPy.calculation.relax_dynamic', 'full_relax_restart.template') with open(restart_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run lammps output = lmp.run(lammps_command, script_name=lammps_script, restart_script_name=restart_script, mpi_command=mpi_command, screen=False) # Extract LAMMPS thermo data. results = {} thermo = output.flatten()['thermo'] results['dumpfile_initial'] = '0.dump' results['symbols_initial'] = system.symbols # Load relaxed system from dump file last_dump_file = f'{thermo.Step.values[-1]}.dump' results['dumpfile_final'] = last_dump_file system = am.load('atom_dump', last_dump_file, symbols=system.symbols) results['symbols_final'] = system.symbols # Only consider values where Step >= equilsteps thermo = thermo[thermo.Step >= equilsteps] results['nsamples'] = len(thermo) # Get cohesive energy estimates natoms = system.natoms results['E_pot'] = uc.set_in_units(thermo.PotEng.mean() / natoms, lammps_units['energy']) results['E_pot_std'] = uc.set_in_units(thermo.PotEng.std() / natoms, lammps_units['energy']) results['E_total'] = uc.set_in_units(thermo.TotEng.mean() / natoms, lammps_units['energy']) results['E_total_std'] = uc.set_in_units(thermo.TotEng.std() / natoms, lammps_units['energy']) results['lx'] = uc.set_in_units(thermo.Lx.mean(), lammps_units['length']) results['lx_std'] = uc.set_in_units(thermo.Lx.std(), lammps_units['length']) results['ly'] = uc.set_in_units(thermo.Ly.mean(), lammps_units['length']) results['ly_std'] = uc.set_in_units(thermo.Ly.std(), lammps_units['length']) results['lz'] = uc.set_in_units(thermo.Lz.mean(), lammps_units['length']) results['lz_std'] = uc.set_in_units(thermo.Lz.std(), lammps_units['length']) results['xy'] = uc.set_in_units(thermo.Xy.mean(), lammps_units['length']) results['xy_std'] = uc.set_in_units(thermo.Xy.std(), lammps_units['length']) results['xz'] = uc.set_in_units(thermo.Xz.mean(), lammps_units['length']) results['xz_std'] = uc.set_in_units(thermo.Xz.std(), lammps_units['length']) results['yz'] = uc.set_in_units(thermo.Yz.mean(), lammps_units['length']) results['yz_std'] = uc.set_in_units(thermo.Yz.std(), lammps_units['length']) results['measured_pxx'] = uc.set_in_units(thermo.Pxx.mean(), lammps_units['pressure']) results['measured_pxx_std'] = uc.set_in_units(thermo.Pxx.std(), lammps_units['pressure']) results['measured_pyy'] = uc.set_in_units(thermo.Pyy.mean(), lammps_units['pressure']) results['measured_pyy_std'] = uc.set_in_units(thermo.Pyy.std(), lammps_units['pressure']) results['measured_pzz'] = uc.set_in_units(thermo.Pzz.mean(), lammps_units['pressure']) results['measured_pzz_std'] = uc.set_in_units(thermo.Pzz.std(), lammps_units['pressure']) results['measured_pxy'] = uc.set_in_units(thermo.Pxy.mean(), lammps_units['pressure']) results['measured_pxy_std'] = uc.set_in_units(thermo.Pxy.std(), lammps_units['pressure']) results['measured_pxz'] = uc.set_in_units(thermo.Pxz.mean(), lammps_units['pressure']) results['measured_pxz_std'] = uc.set_in_units(thermo.Pxz.std(), lammps_units['pressure']) results['measured_pyz'] = uc.set_in_units(thermo.Pyz.mean(), lammps_units['pressure']) results['measured_pyz_std'] = uc.set_in_units(thermo.Pyz.std(), lammps_units['pressure']) results['temp'] = thermo.Temp.mean() results['temp_std'] = thermo.Temp.std() return results
def calc_neb(lammps_command, system_info, potential, initial_defect_number, defect_pair_number, defect_mobility_kwargs, allSymbols, mpi_command=None, etol=0.0, ftol=1e-4, dmax=uc.set_in_units(0.01, 'angstrom'), nreplicas=11, springconst=5, thermosteps=100, dumpsteps=10000, timestep=0.01, minsteps=10000, climbsteps=10000): #Section for making alterations to the overall simulation structure #Defining the initial perfect system using the original info initial_system = system_info #Adding defects to the system which will stay in the same position during the simulation for defectIndex in range(int(initial_defect_number)): point_kwargs = defect_mobility_kwargs['initial'][defectIndex] if point_kwargs['ptd_type'].lower() == 'v': initial_system = am.defect.vacancy(initial_system, pos=point_kwargs['pos']) elif point_kwargs['ptd_type'].lower() == 'i': initial_system = am.defect.interstitial( initial_system, pos=point_kwargs['pos'], atype=point_kwargs['atype']) elif point_kwargs['ptd_type'].lower() == 's': initial_system = am.defect.substitutional( initial_system, pos=point_kwargs['pos'], atype=point_kwargs['atype']) elif point_kwargs['ptd_type'].lower() == 'db': initial_system = am.defect.dumbbell( initial_system, pos=point_kwargs['pos'], db_vect=point_kwargs['db_vect']) else: raise ValueError('Invalid Defect Type') currentSymbols = [] for x in range( initial_system.natypes ): #Checking to see if the alteration of the system has added any new atom types currentSymbols.append( allSymbols[x] ) #and adding the appropriate symbols for those new atoms initial_system.symbols = currentSymbols #Adding moving defects to the system in their start position start_system = initial_system for defectIndex in range(int(defect_pair_number)): point_kwargs = defect_mobility_kwargs['start'][defectIndex] if point_kwargs['ptd_type'].lower() == 'v': start_system = am.defect.vacancy(start_system, pos=point_kwargs['pos']) elif point_kwargs['ptd_type'].lower() == 'i': start_system = am.defect.interstitial(start_system, pos=point_kwargs['pos'], atype=point_kwargs['atype']) elif point_kwargs['ptd_type'].lower() == 'db': start_system = am.defect.dumbbell(start_system, pos=point_kwargs['pos'], db_vect=point_kwargs['db_vect']) elif point_kwargs['ptd_type'].lower() == 's': print( 'not implemented for subsutitutional defect type. To simulate a substitutional vacancy jump, define the position of a subsitutional atom in the initial system, and make the end position of the vacancy the same position as the subsitutional' ) else: raise ValueError('Invalid Defect Type') #Checking again to see if symbols need to be added currentSymbols = [] for x in range(start_system.natypes): currentSymbols.append(allSymbols[x]) #Creating a list of atoms in the start system which the next segment uses to find the atom id for atoms in a certain position start_system.symbols = currentSymbols start_system_atoms = start_system.atoms start_natoms = start_system.natoms #Dumps the start system to be used for NEB simulations start_system_info = start_system.dump('atom_data', f='init.dat') #Check to see which defect type is being used, finding the position of that atom, extracting #the atom id, and then generating a list of new positions used to define the final system for #the neb simulation final_system = start_system movedAtomIndexes = [] for defectIndex in range(int(defect_pair_number)): point_kwargs = defect_mobility_kwargs['end'][ defectIndex] #End kwargs, used to define the end positions point_kwargs_start = defect_mobility_kwargs['start'][ defectIndex] #Start kwargs, used to find the original position of the atoms if point_kwargs['ptd_type'].lower() == 'v': for x in range(start_natoms): xPos = round(start_system_atoms.pos[x, 0], 3) == round(point_kwargs['pos'][0], 3) yPos = round(start_system_atoms.pos[x, 1], 3) == round(point_kwargs['pos'][1], 3) zPos = round(start_system_atoms.pos[x, 2], 3) == round(point_kwargs['pos'][2], 3) if ((xPos and yPos) and zPos): final_system.atoms.pos[x] = point_kwargs_start['pos'] movedAtomIndexes.append(x) elif point_kwargs['ptd_type'].lower() == 'i': for x in range(start_natoms): xPos = round(start_system_atoms.pos[x, 0], 3) == round(point_kwargs_start['pos'][0], 3) yPos = round(start_system_atoms.pos[x, 1], 3) == round(point_kwargs_start['pos'][1], 3) zPos = round(start_system_atoms.pos[x, 2], 3) == round(point_kwargs_start['pos'][2], 3) if ((xPos and yPos) and zPos): final_system.atoms.pos[x] = point_kwargs['pos'] movedAtomIndexes.append(x) elif point_kwargs['ptd_type'].lower() == 's': print( 'not implemented for subsutitutional defect type. To simulate a substitutional vacancy jump, define the position of a subsitutional atom in the initial system, and make the end position of the vacancy the same position as the subsitutional' ) elif point_kwargs['ptd_type'].lower() == 'db': for x in range(start_natoms): xPos = round(start_system_atoms.pos[x, 0], 3) == round( point_kwargs_start['pos'][0] - point_kwargs_start['db_vect'][0], 3) yPos = round(start_system_atoms.pos[x, 1], 3) == round( point_kwargs_start['pos'][1] - point_kwargs_start['db_vect'][1], 3) zPos = round(start_system_atoms.pos[x, 2], 3) == round( point_kwargs_start['pos'][2] - point_kwargs_start['db_vect'][2], 3) if ((xPos and yPos) and zPos): final_system.atoms.pos[x] = point_kwargs['pos'] movedAtomIndexes.append(x) for y in range(start_natoms): xPos = round(start_system_atoms.pos[y, 0], 3) == round( point_kwargs_start['pos'][0] + point_kwargs_start['db_vect'][0], 3) yPos = round(start_system_atoms.pos[y, 1], 3) == round( point_kwargs_start['pos'][1] + point_kwargs_start['db_vect'][1], 3) zPos = round(start_system_atoms.pos[y, 2], 3) == round( point_kwargs_start['pos'][2] + point_kwargs_start['db_vect'][2], 3) if ((xPos and yPos) and zPos): final_system.atoms.pos[ y] = point_kwargs['pos'] - point_kwargs['db_vect'] movedAtomIndexes.append(y) for z in range(start_natoms): xPos = round(start_system_atoms.pos[z, 0], 3) == round(point_kwargs['pos'][0], 3) yPos = round(start_system_atoms.pos[z, 1], 3) == round(point_kwargs['pos'][1], 3) zPos = round(start_system_atoms.pos[z, 2], 3) == round(point_kwargs['pos'][2], 3) if ((xPos and yPos) and zPos): final_system.atoms.pos[ z] = point_kwargs['pos'] + point_kwargs['db_vect'] movedAtomIndexes.append(z) else: raise ValueError('Invalid Defect Type') final_system_pos = final_system.atoms.pos[movedAtomIndexes] movedAtomIds = [x + 1 for x in movedAtomIndexes] #Writing the final position of the moving atoms to be used in the final system with open('final.dat', 'w') as f: f.write('%i\n' % len(movedAtomIds)) for x in range(len(movedAtomIds)): f.write('%i ' % movedAtomIds[x]) for y in range(len(final_system_pos[x])): f.write('%.13f ' % final_system_pos[x][y]) f.write('\n') #Get script's location script_dir = Path(__file__).parent # 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_terms = {} lammps_terms['nreplicas'] = nreplicas lammps_terms['springconst'] = springconst lammps_terms['thermosteps'] = thermosteps lammps_terms['dumpsteps'] = dumpsteps lammps_terms['timestep'] = timestep lammps_terms['minsteps'] = minsteps lammps_terms['climbsteps'] = climbsteps lammps_terms['dmax'] = dmax lammps_terms['etol'] = etol lammps_terms['ftol'] = ftol lammps_terms['atomman_system_info'] = start_system_info lammps_terms['atomman_pair_info'] = potential.pair_info( start_system.symbols) lammps_terms['final_system'] = 'final.dat' # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): lammps_terms[ 'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e %.13e"' else: lammps_terms['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = Path(script_dir, 'neb_lammps.template') input_file = 'neb_lammps.in' with open(template_file) as f: template = f.read() with open(input_file, 'w') as f: f.write(am.tools.filltemplate(template, lammps_terms, '<', '>')) #Running the calc_neb output = lmp.run(lammps_command, 'neb_lammps.in', mpi_command=mpi_command) neb = lmp.NEBLog() return neb
def phononcalc(lammps_command, ucell, potential, mpi_command=None, a_mult=3, b_mult=3, c_mult=3, distance=0.01, symprec=1e-5, savefile='phonopy_params.yaml', plot=True, lammps_date=None): """ Uses phonopy to compute the phonons for a unit cell structure using a LAMMPS interatomic potential. Parameters ---------- lammps_command :str Command for running LAMMPS. ucell : atomman.System The unit cell 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. a_mult : int, optional The a size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. b_mult : int, optional The b size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. c_mult : int, optional The c size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. distance : float, optional The atomic displacement distance used for computing the phonons. Default value is 0.01. symprec : float, optional Absolute length tolerance to use in identifying symmetry of atomic sites and system boundaries. Default value is 1e-5. savefile: str, optional The name of the phonopy yaml backup file. Default value is 'phonopy_params.yaml'. plot : bool, optional Flag indicating if band structure and DOS figures are to be generated. Default value is True. lammps_date : datetime.date, optional The version date associated with lammps_command. If not given, the version will be identified. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date if lammps_date is None: lammps_date = lmp.checkversion(lammps_command)['date'] # Use spglib to find primitive unit cell of ucell convcell = ucell.dump('spglib_cell') primcell = spglib.find_primitive(convcell, symprec=symprec) primucell = am.load('spglib_cell', primcell, symbols=ucell.symbols).normalize() # Initialize Phonopy object phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms', symbols=potential.elements( primucell.symbols)), [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]], factor=phonopy.units.VaspToTHz) phonon.generate_displacements(distance=distance) # Loop over displaced supercells to compute forces forcearrays = [] for supercell in phonon.supercells_with_displacements: # Save to LAMMPS data file system = am.load('phonopy_Atoms', supercell, symbols=primucell.symbols) system_info = system.dump('atom_data', f='disp.dat', potential=potential) # Define lammps variables lammps_variables = {} lammps_variables['atomman_system_pair_info'] = system_info # 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 %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script lammps_script = 'phonon.in' template = read_calc_file('iprPy.calculation.phonon', 'phonon.template') with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS lmp.run(lammps_command, script_name=lammps_script, mpi_command=mpi_command) # Extract forces from dump file forcestructure = am.load('atom_dump', 'forces.dump') forces = uc.set_in_units(forcestructure.atoms.force, lammps_units['force']) forcearrays.append(forces) results = {} # Set computed forces phonon.set_forces(forcearrays) # Save to yaml file phonon.save(savefile) # Compute band structure phonon.produce_force_constants() phonon.auto_band_structure(plot=plot) results['band_structure'] = phonon.get_band_structure_dict() if plot: plt.ylabel('Frequency (THz)') plt.savefig(Path('.', 'band.png'), dpi=400) plt.close() # Compute density of states phonon.auto_total_dos(plot=False) phonon.auto_projected_dos(plot=False) dos = phonon.get_total_dos_dict() dos['frequency'] = uc.set_in_units(dos.pop('frequency_points'), 'THz') dos['projected_dos'] = phonon.get_projected_dos_dict()['projected_dos'] results['dos'] = dos # Compute thermal properties phonon.run_thermal_properties() results['thermal_properties'] = phonon.get_thermal_properties_dict() results['phonon'] = phonon return results
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 stackingfaultrelax(lammps_command, system, potential, mpi_command=None, sim_directory=None, cutboxvector='c', etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom'), lammps_date=None): """ Perform a stacking fault relaxation simulation for a single faultshift. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system containing a stacking fault. 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. sim_directory : str, optional The path to the directory to perform the simulation in. If not given, will use the current working directory. cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', has the non-periodic boundary (default is 'c'). Fault plane normal is defined by the cross of the other two box vectors. 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). lammps_date : datetime.date or None, optional The date version of the LAMMPS executable. If None, will be identified from the lammps_command (default is None). Returns ------- dict Dictionary of results consisting of keys: - **'logfile'** (*str*) - The filename of the LAMMPS log file. - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file of the relaxed system. - **'system'** (*atomman.System*) - The relaxed system. - **'E_total'** (*float*) - The total potential energy of the relaxed system. Raises ------ ValueError For invalid cutboxvectors. """ # Get script's location script_dir = Path(__file__).parent # Give correct LAMMPS fix setforce command if cutboxvector == 'a': fix_cut_setforce = 'fix cut all setforce NULL 0 0' elif cutboxvector == 'b': fix_cut_setforce = 'fix cut all setforce 0 NULL 0' elif cutboxvector == 'c': fix_cut_setforce = 'fix cut all setforce 0 0 NULL' else: raise ValueError('Invalid cutboxvector') if sim_directory is not None: # Create sim_directory if it doesn't exist sim_directory = Path(sim_directory) if not sim_directory.is_dir(): sim_directory.mkdir() sim_directory = sim_directory.as_posix()+'/' else: # Set sim_directory if is None sim_directory = '' # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date if lammps_date is None: lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f=Path(sim_directory, 'system.dat').as_posix(), 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['fix_cut_setforce'] = fix_cut_setforce lammps_variables['sim_directory'] = sim_directory 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = Path(script_dir, 'sfmin.template') lammps_script = Path(sim_directory, 'sfmin.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.as_posix(), mpi_command, logfile=Path(sim_directory, 'log.lammps').as_posix()) # Extract output values thermo = output.simulations[-1]['thermo'] logfile = Path(sim_directory, 'log.lammps').as_posix() dumpfile = Path(sim_directory, '%i.dump' % thermo.Step.values[-1]).as_posix() E_total = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) # Load relaxed system system = am.load('atom_dump', dumpfile, symbols=system.symbols) # Return results results_dict = {} results_dict['logfile'] = logfile results_dict['dumpfile'] = dumpfile results_dict['system'] = system results_dict['E_total'] = E_total return results_dict
def relax_system(lammps_command: str, system: am.System, potential: lmp.Potential, mpi_command: Optional[str] = None, etol: float = 0.0, ftol: float = 0.0, maxiter: int = 10000, maxeval: int = 100000, dmax: float = uc.set_in_units(0.01, 'angstrom'), lammps_date: Optional[datetime.date] = None) -> dict: """ Sets up and runs the min.in LAMMPS script for performing an energy/force minimization to relax a system. 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. 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). lammps_date : datetime.date or None, optional The date version of the LAMMPS executable. If None, will be identified from the lammps_command (default is None). Returns ------- dict Dictionary of results consisting of keys: - **'logfile'** (*str*) - The name of the LAMMPS log file. - **'initialdatafile'** (*str*) - The name of the LAMMPS data file used to import an inital configuration. - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file corresponding to the inital configuration. - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file corresponding to the relaxed configuration. - **'potentialenergy'** (*float*) - The total potential energy of the relaxed system. """ # Ensure all atoms are within the system's box system.wrap() # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date if lammps_date is None: lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='system.dat', potential=potential) lammps_variables['atomman_system_pair_info'] = system_info 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = 'min.template' lammps_script = 'min.in' template = read_calc_file('iprPy.calculation.surface_energy_static', 'min.template') with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS output = lmp.run(lammps_command, script_name=lammps_script, mpi_command=mpi_command) # Extract output values thermo = output.simulations[-1]['thermo'] results = {} results['logfile'] = 'log.lammps' results['initialdatafile'] = 'system.dat' results['initialdumpfile'] = 'atom.0' results['finaldumpfile'] = 'atom.%i' % thermo.Step.values[-1] results['potentialenergy'] = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) return results
def surface_energy_static( lammps_command: str, ucell: am.System, potential: lmp.Potential, hkl: Union[list, np.ndarray], mpi_command: Optional[str] = None, sizemults: Union[list, tuple, None] = None, minwidth: float = None, even: bool = False, conventional_setting: str = 'p', cutboxvector: str = 'c', atomshift: Union[list, np.ndarray, None] = None, shiftindex: Optional[int] = None, etol: float = 0.0, ftol: float = 0.0, maxiter: int = 10000, maxeval: int = 100000, dmax: float = uc.set_in_units(0.01, 'angstrom') ) -> dict: """ Evaluates surface formation energies by slicing along one periodic boundary of a bulk system. Parameters ---------- lammps_command :str Command for running LAMMPS. ucell : atomman.System The crystal unit cell to use as the basis of the stacking fault configurations. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. hkl : array-like object The Miller(-Bravais) crystal fault plane relative to ucell. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. sizemults : list or tuple, optional The three System.supersize multipliers [a_mult, b_mult, c_mult] to use on the rotated cell to build the final system. Note that the cutboxvector sizemult must be an integer and not a tuple. Default value is [1, 1, 1]. minwidth : float, optional If given, the sizemult along the cutboxvector will be selected such that the width of the resulting final system in that direction will be at least this value. If both sizemults and minwidth are given, then the larger of the two in the cutboxvector direction will be used. even : bool, optional A True value means that the sizemult for cutboxvector will be made an even number by adding 1 if it is odd. Default value is False. conventional_setting : str, optional Allows for rotations of a primitive unit cell to be determined from (hkl) indices specified relative to a conventional unit cell. Allowed settings: 'p' for primitive (no conversion), 'f' for face-centered, 'i' for body-centered, and 'a', 'b', or 'c' for side-centered. Default behavior is to perform no conversion, i.e. take (hkl) relative to the given ucell. cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). atomshift : array-like object, optional A Cartesian vector shift to apply to all atoms. Can be used to shift atoms perpendicular to the fault plane to allow different termination planes to be cut. Cannot be given with shiftindex. shiftindex : int, optional Allows for selection of different termination planes based on the preferred shift values determined by the underlying fault generation. Cannot be given with atomshift. If neither atomshift nor shiftindex given, then shiftindex will be set to 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). Returns ------- dict Dictionary of results consisting of keys: - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file of the relaxed bulk system. - **'dumpfile_surf'** (*str*) - The filename of the LAMMPS dump file of the relaxed system containing the free surfaces. - **'E_total_base'** (*float*) - The total potential energy of the relaxed bulk system. - **'E_total_surf'** (*float*) - The total potential energy of the relaxed system containing the free surfaces. - **'A_surf'** (*float*) - The area of the free surface. - **'E_pot'** (*float*) - The per-atom potential energy of the relaxed bulk system. - **'E_surf_f'** (*float*) - The computed surface formation energy. Raises ------ ValueError For invalid cutboxvectors """ # Construct free surface configuration generator surf_gen = am.defect.FreeSurface(hkl, ucell, cutboxvector=cutboxvector, conventional_setting=conventional_setting) # Check shift parameters if shiftindex is not None: assert atomshift is None, 'shiftindex and atomshift cannot both be given' atomshift = surf_gen.shifts[shiftindex] elif atomshift is None: atomshift = surf_gen.shifts[0] # Generate the free surface configuration system = surf_gen.surface(shift=atomshift, minwidth=minwidth, sizemults=sizemults, even=even) A_surf = surf_gen.surfacearea # Identify lammps_date version lammps_date = lmp.checkversion(lammps_command)['date'] # Evaluate system with free surface surf_results = relax_system(lammps_command, system, potential, mpi_command=mpi_command, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date) # Extract results from system with free surface dumpfile_surf = 'surface.dump' shutil.move(surf_results['finaldumpfile'], dumpfile_surf) shutil.move('log.lammps', 'surface-log.lammps') E_total_surf = surf_results['potentialenergy'] # Evaluate perfect system (all pbc removes cut) system.pbc = [True, True, True] perf_results = relax_system(lammps_command, system, potential, mpi_command=mpi_command, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date) # Extract results from perfect system dumpfile_base = 'perfect.dump' shutil.move(perf_results['finaldumpfile'], dumpfile_base) shutil.move('log.lammps', 'perfect-log.lammps') E_total_base = perf_results['potentialenergy'] # Compute the free surface formation energy E_surf_f = (E_total_surf - E_total_base) / (2 * A_surf) # Save values to results dictionary results_dict = {} results_dict['dumpfile_base'] = dumpfile_base results_dict['dumpfile_surf'] = dumpfile_surf results_dict['E_total_base'] = E_total_base results_dict['E_total_surf'] = E_total_surf results_dict['A_surf'] = A_surf results_dict['E_pot'] = E_total_base / system.natoms results_dict['E_surf_f'] = E_surf_f return results_dict
def stackingfaultmap(lammps_command, system, potential, shiftvector1, shiftvector2, mpi_command=None, numshifts1=11, numshifts2=11, cutboxvector=None, faultpos=0.5, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Computes a generalized stacking fault map for shifts along a regular 2D grid. 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. shiftvector1 : list of floats or numpy.array One of the generalized stacking fault shifting vectors. shiftvector2 : list of floats or numpy.array One of the generalized stacking fault shifting vectors. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. 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). cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). numshifts1 : int, optional The number of equally spaced shiftfractions to evaluate along shiftvector1. numshifts2 : int, optional The number of equally spaced shiftfractions to evaluate along shiftvector2. Returns ------- dict Dictionary of results consisting of keys: - **'shift1'** (*numpy.array of float*) - The fractional shifts along shiftvector1 where the stacking fault was evaluated. - **'shift2'** (*numpy.array of float*) - The fractional shifts along shiftvector2 where the stacking fault was evaluated. - **'E_gsf'** (*numpy.array of float*) - The stacking fault formation energies measured for all the (shift1, shift2) coordinates. - **'delta_disp'** (*numpy.array of float*) - The change in the center of mass difference between before and after applying the faultshift for all the (shift1, shift2) coordinates. - **'A_fault'** (*float*) - The area of the fault surface. """ # Start sf_df as empty list sf_df = [] # Construct mesh of regular points shifts1, shifts2 = np.meshgrid(np.linspace(0, 1, numshifts1), np.linspace(0, 1, numshifts2)) # Identify lammps_date version lammps_date = lmp.checkversion(lammps_command)['date'] # Loop over all shift combinations for shiftfraction1, shiftfraction2 in zip(shifts1.flat, shifts2.flat): # Evaluate the system at the shift sf_df.append( stackingfaultworker(lammps_command, system, potential, shiftvector1, shiftvector2, shiftfraction1, shiftfraction2, mpi_command=mpi_command, cutboxvector=cutboxvector, faultpos=faultpos, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date)) # Convert sf_df to pandas DataFrame sf_df = pd.DataFrame(sf_df) # Identify the zeroshift column zeroshift = sf_df[(np.isclose(sf_df.shift1, 0.0, atol=1e-10, rtol=0.0) & np.isclose(sf_df.shift2, 0.0, atol=1e-10, rtol=0.0))] assert len(zeroshift) == 1, 'zeroshift simulation not uniquely identified' # Get zeroshift values E_total_0 = zeroshift.E_total.values[0] A_fault = zeroshift.A_fault.values[0] disp_0 = zeroshift.disp.values[0] # Compute the stacking fault energy E_gsf = (sf_df.E_total.values - E_total_0) / A_fault # Compute the change in displacement normal to fault plane delta_disp = sf_df.disp.values - disp_0 results_dict = {} results_dict['shift1'] = sf_df.shift1.values results_dict['shift2'] = sf_df.shift2.values results_dict['E_gsf'] = E_gsf results_dict['delta_disp'] = delta_disp results_dict['A_fault'] = A_fault return results_dict
def stackingfault( lammps_command: str, ucell: am.System, potential: lmp.Potential, hkl: Union[list, np.ndarray], mpi_command: Optional[str] = None, sizemults: Union[list, tuple, None] = None, minwidth: float = None, even: bool = False, a1vect_uvw: Union[list, np.ndarray, None] = None, a2vect_uvw: Union[list, np.ndarray, None] = None, conventional_setting: str = 'p', cutboxvector: str = 'c', faultpos_rel: Optional[float] = None, faultpos_cart: Optional[float] = None, a1: float = 0.0, a2: float = 0.0, atomshift: Union[list, np.ndarray, None] = None, shiftindex: Optional[int] = None, etol: float = 0.0, ftol: float = 0.0, maxiter: int = 10000, maxeval: int = 100000, dmax: float = uc.set_in_units(0.01, 'angstrom') ) -> dict: """ Computes the generalized stacking fault value for a single faultshift. Parameters ---------- lammps_command :str Command for running LAMMPS. ucell : atomman.System The crystal unit cell to use as the basis of the stacking fault configurations. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. hkl : array-like object The Miller(-Bravais) crystal fault plane relative to ucell. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. sizemults : list or tuple, optional The three System.supersize multipliers [a_mult, b_mult, c_mult] to use on the rotated cell to build the final system. Note that the cutboxvector sizemult must be an integer and not a tuple. Default value is [1, 1, 1]. minwidth : float, optional If given, the sizemult along the cutboxvector will be selected such that the width of the resulting final system in that direction will be at least this value. If both sizemults and minwidth are given, then the larger of the two in the cutboxvector direction will be used. even : bool, optional A True value means that the sizemult for cutboxvector will be made an even number by adding 1 if it is odd. Default value is False. a1vect_uvw : array-like object, optional The crystal vector to use for one of the two shifting vectors. If not given, will be set to the shortest in-plane lattice vector. a2vect_uvw : array-like object, optional The crystal vector to use for one of the two shifting vectors. If not given, will be set to the shortest in-plane lattice vector not parallel to a1vect_uvw. conventional_setting : str, optional Allows for rotations of a primitive unit cell to be determined from (hkl) indices specified relative to a conventional unit cell. Allowed settings: 'p' for primitive (no conversion), 'f' for face-centered, 'i' for body-centered, and 'a', 'b', or 'c' for side-centered. Default behavior is to perform no conversion, i.e. take (hkl) relative to the given ucell. cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). faultpos_rel : float, optional The position to place the slip plane within the system given as a relative coordinate along the out-of-plane direction. faultpos_rel and faultpos_cart cannot both be given. Default value is 0.5 if faultpos_cart is also not given. faultpos_cart : float, optional The position to place the slip plane within the system given as a Cartesian coordinate along the out-of-plane direction. faultpos_rel and faultpos_cart cannot both be given. a1 : float, optional The fractional coordinate to evaluate along a1vect_uvw. Default value is 0.0. a2 : float, optional The fractional coordinate to evaluate along a2vect_uvw. Default value is 0.0. atomshift : array-like object, optional A Cartesian vector shift to apply to all atoms. Can be used to shift atoms perpendicular to the fault plane to allow different termination planes to be cut. Cannot be given with shiftindex. shiftindex : int, optional Allows for selection of different termination planes based on the preferred shift values determined by the underlying fault generation. Cannot be given with atomshift. If neither atomshift nor shiftindex given, then shiftindex will be set to 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). Returns ------- dict Dictionary of results consisting of keys: - **'E_gsf'** (*float*) - The stacking fault formation energy. - **'E_total_0'** (*float*) - The total potential energy of the system before applying the faultshift. - **'E_total_sf'** (*float*) - The total potential energy of the system after applying the faultshift. - **'delta_disp'** (*float*) - The change in the center of mass difference between before and after applying the faultshift. - **'disp_0'** (*float*) - The center of mass difference between atoms above and below the fault plane in the cutboxvector direction for the system before applying the faultshift. - **'disp_sf'** (*float*) - The center of mass difference between atoms above and below the fault plane in the cutboxvector direction for the system after applying the faultshift. - **'A_fault'** (*float*) - The area of the fault surface. - **'dumpfile_0'** (*str*) - The name of the LAMMMPS dump file associated with the relaxed system before applying the faultshift. - **'dumpfile_sf'** (*str*) - The name of the LAMMMPS dump file associated with the relaxed system after applying the faultshift. """ # Construct stacking fault configuration generator gsf_gen = am.defect.StackingFault( hkl, ucell, cutboxvector=cutboxvector, a1vect_uvw=a1vect_uvw, a2vect_uvw=a2vect_uvw, conventional_setting=conventional_setting) # Check shift parameters if shiftindex is not None: assert atomshift is None, 'shiftindex and atomshift cannot both be given' atomshift = gsf_gen.shifts[shiftindex] elif atomshift is None: atomshift = gsf_gen.shifts[0] # Generate the free surface (zero-shift) configuration sfsystem = gsf_gen.surface(shift=atomshift, minwidth=minwidth, sizemults=sizemults, even=even, faultpos_rel=faultpos_rel, faultpos_cart=faultpos_cart) abovefault = gsf_gen.abovefault cutindex = gsf_gen.cutindex A_fault = gsf_gen.surfacearea # Identify lammps_date version lammps_date = lmp.checkversion(lammps_command)['date'] # Evaluate the zero shift configuration zeroshift = stackingfaultrelax(lammps_command, sfsystem, potential, mpi_command=mpi_command, cutboxvector=cutboxvector, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date) # Extract terms E_total_0 = zeroshift['E_total'] pos_0 = zeroshift['system'].atoms.pos shutil.move('log.lammps', 'zeroshift-log.lammps') shutil.move(zeroshift['dumpfile'], 'zeroshift.dump') # Evaluate the system after shifting along the fault plane sfsystem = gsf_gen.fault(a1=a1, a2=a2) shifted = stackingfaultrelax(lammps_command, sfsystem, potential, mpi_command=mpi_command, cutboxvector=cutboxvector, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date) # Extract terms E_total_sf = shifted['E_total'] pos_sf = shifted['system'].atoms.pos shutil.move('log.lammps', 'shifted-log.lammps') shutil.move(shifted['dumpfile'], 'shifted.dump') # Compute the stacking fault energy E_gsf = (E_total_sf - E_total_0) / A_fault # Compute the change in displacement normal to fault plane disp_0 = (pos_0[abovefault, cutindex].mean() - pos_0[~abovefault, cutindex].mean()) disp_sf = (pos_sf[abovefault, cutindex].mean() - pos_sf[~abovefault, cutindex].mean()) delta_disp = disp_sf - disp_0 # Return processed results results = {} results['E_gsf'] = E_gsf results['E_total_0'] = E_total_0 results['E_total_sf'] = E_total_sf results['delta_disp'] = delta_disp results['disp_0'] = disp_0 results['disp_sf'] = disp_sf results['A_fault'] = A_fault results['dumpfile_0'] = 'zeroshift.dump' results['dumpfile_sf'] = 'shifted.dump' return results
def dislocationarray(lammps_command, system, potential, burgers, C, mpi_command=None, axes=None, m=[0,1,0], n=[0,0,1], etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=None, annealtemp=0.0, annealsteps=None, randomseed=None, bwidth=None, cutoff=None, onlyuselinear=False): """ Creates and relaxes a dislocation monopole system. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The bulk system to add the defect to. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. burgers : list or numpy.array of float The burgers vector for the dislocation being added. C : atomman.ElasticConstants The system's elastic constants. mpi_command : str or None, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. axes : numpy.array of float or None, optional The 3x3 axes used to rotate the system by during creation. If given, will be used to transform burgers and C from the standard crystallographic orientations to the system's Cartesian units. randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. Default is None which will select a random int between 1 and 900000000. 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). annealtemp : float, optional The temperature to perform a dynamic relaxation at. Default is 0.0, which will skip the dynamic relaxation. annealsteps : int, optional The number of time steps to run the dynamic relaxation for. Default is None, which will run for 10000 steps if annealtemp is not 0.0. bshape : str, optional The shape to make the boundary region. Options are 'circle' and 'rect' (default is 'circle'). cutoff : float, optional Cutoff distance to use for identifying duplicate atoms to remove. For dislocations with an edge component, applying the displacements creates an extra half-plane of atoms that will have (nearly) identical positions with other atoms after altering the boundary conditions. Default cutoff value is 0.5 Angstrom. useonlylinear : bool, optional If False (default) the atoms in the middle of the system will be displaced according to an elasticity solution for a dislocation core and boundary atoms displaced by a linear gradient. If True, all atoms are displaced by the linear gradient. Returns ------- dict Dictionary of results consisting of keys: - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file for the relaxed base system. - **'symbols_base'** (*list of str*) - The list of element-model symbols for the Potential that correspond to the base system's atypes. - **'Stroh_preln'** (*float*) - The pre-logarithmic factor in the dislocation's self-energy expression. - **'Stroh_K_tensor'** (*numpy.array of float*) - The energy coefficient tensor based on the dislocation's Stroh solution. - **'dumpfile_disl'** (*str*) - The filename of the LAMMPS dump file for the relaxed dislocation monopole system. - **'symbols_disl'** (*list of str*) - The list of element-model symbols for the Potential that correspond to the dislocation monopole system's atypes. - **'E_total_disl'** (*float*) - The total potential energy of the dislocation monopole system. """ try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Set default values if dmax is None: dmax = uc.set_in_units(0.01, 'angstrom') # Save base system system.dump('atom_dump', f='base.dump') # Generate periodic array of dislocations if onlyuselinear is False: dislsol = am.defect.solve_volterra_dislocation(C, burgers, axes=axes, m=m, n=n) dislsystem = am.defect.dislocation_array(system, dislsol=dislsol, bwidth=bwidth, cutoff=cutoff) else: if axes is not None: T = am.tools.axes_check(axes) burgers = T.dot(burgers) dislsystem = am.defect.dislocation_array(system, burgers=burgers, m=m, n=n, cutoff=cutoff) # 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 = dislsystem.dump('atom_data', f='initial.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(dislsystem.symbols) lammps_variables['anneal_info'] = anneal_info(annealtemp, annealsteps, randomseed, potential.units) 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'] = dmax lammps_variables['bwidth'] = uc.get_in_units(bwidth, lammps_units['length']) # Set dump_modify format based on dump_modify_version 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 = Path(script_dir, 'dislarray_relax.template') lammps_script = 'dislarray_relax.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) thermo = output.simulations[-1]['thermo'] # Extract output values results_dict = {} results_dict['logfile'] = 'log.lammps' results_dict['dumpfile_base'] = 'base.dump' results_dict['symbols_base'] = system.symbols results_dict['dumpfile_disl'] = '%i.dump' % thermo.Step.values[-1] results_dict['symbols_disl'] = dislsystem.symbols return results_dict
def relax_system(lammps_command, system, potential, mpi_command=None, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Sets up and runs the min.in LAMMPS script for performing an energy/force minimization to relax a system. 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. 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: - **'logfile'** (*str*) - The name of the LAMMPS log file. - **'initialdatafile'** (*str*) - The name of the LAMMPS data file used to import an inital configuration. - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file corresponding to the inital configuration. - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file corresponding to the relaxed configuration. - **'potentialenergy'** (*float*) - The total potential energy of the relaxed system. """ # Ensure all atoms are within the system's box system.wrap() # 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 = {} # Generate system and pair info system_info = system.dump('atom_data', f='system.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) # Pass in run parameters 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = 'min.template' lammps_script = 'min.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) # Extract output values thermo = output.simulations[-1]['thermo'] results = {} results['logfile'] = 'log.lammps' results['initialdatafile'] = 'system.dat' results['initialdumpfile'] = 'atom.0' results['finaldumpfile'] = 'atom.%i' % thermo.Step.values[-1] results['potentialenergy'] = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) return results
def baintransformation(lammps_command, a_bcc, a_fcc, symbol, sizemults, potential, mpi_command=None, num_a=23, num_c=23, min_a=-0.05, max_a=1.05, min_c=-0.05, max_c=1.05, etol=0.0, ftol=1e-6, maxiter=100000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): # Build filedict if function was called from iprPy try: assert __name__ == pkg_name calc = iprPy.load_calculation(calculation_style) filedict = calc.filedict except: filedict = {} # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Get lammps template template_file = 'min.template' template = iprPy.tools.read_calc_file(template_file, filedict) # Define constant lammps variables lammps_variables = {} 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Define bain object bain = Bain(a_bcc=a_bcc, a_fcc=a_fcc, symbol=symbol) energies = [] a_scales = [] c_scales = [] i = 0 # Iterate over cells for a_scale, c_scale, ucell in bain.itercellmap(num_a=num_a, num_c=num_c, min_a=min_a, max_a=max_a, min_c=min_c, max_c=max_c): system = ucell.supersize(*sizemults) sim_directory = Path(str(i)) if not sim_directory.is_dir(): sim_directory.mkdir() sim_directory = sim_directory.as_posix() + '/' # Define lammps variables system_info = system.dump('atom_data', f=Path(sim_directory, 'atom.dat').as_posix(), potential=potential) lammps_variables['atomman_system_pair_info'] = system_info lammps_variables['sim_directory'] = sim_directory # Write lammps input script lammps_script = Path(sim_directory, 'min.in') with open(lammps_script, 'w') as f: f.write( iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS output = lmp.run(lammps_command, lammps_script.as_posix(), mpi_command, logfile=Path(sim_directory, 'log.lammps').as_posix()) # Extract output values thermo = output.simulations[-1]['thermo'] E_total = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) a_scales.append(a_scale) c_scales.append(c_scale) energies.append(E_total / system.natoms) i += 1 # Return results results_dict = {} results_dict['a_scale'] = a_scales results_dict['c_scale'] = c_scales results_dict['E_coh'] = energies return results_dict
def pointdefect(lammps_command, system, potential, point_kwargs, mpi_command=None, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Adds one or more point defects to a system and evaluates the defect formation energy. 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. point_kwargs : dict or list of dict One or more dictionaries containing the keyword arguments for the atomman.defect.point() function to generate specific point defect configuration(s). mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. sim_directory : str, optional The path to the directory to perform the simuation in. If not given, will use the current working directory. 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: - **'E_coh'** (*float*) - The cohesive energy of the bulk system. - **'E_ptd_f'** (*float*) - The point.defect formation energy. - **'E_total_base'** (*float*) - The total potential energy of the relaxed bulk system. - **'E_total_ptd'** (*float*) - The total potential energy of the relaxed defect system. - **'system_base'** (*atomman.System*) - The relaxed bulk system. - **'system_ptd'** (*atomman.System*) - The relaxed defect system. - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file for the relaxed bulk system. - **'dumpfile_ptd'** (*str*) - The filename of the LAMMPS dump file for the relaxed defect system. """ # 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='perfect.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['etol'] = etol lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maxiter'] = maxiter lammps_variables['maxeval'] = maxeval lammps_variables['dmax'] = dmax # 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 = 'min.template' lammps_script = 'min.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 to relax perfect.dat output = lmp.run(lammps_command, lammps_script, mpi_command) # Extract LAMMPS thermo data. thermo = output.simulations[0]['thermo'] E_total_base = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) E_coh = E_total_base / system.natoms # Rename log file shutil.move('log.lammps', 'min-perfect-log.lammps') # Load relaxed system from dump file and copy old box vectors because # dump files crop the values. last_dump_file = 'atom.' + str(thermo.Step.values[-1]) system_base = am.load('atom_dump', last_dump_file, symbols=system.symbols) system_base.box_set(vects=system.box.vects) system_base.dump('atom_dump', f='perfect.dump') # Add defect(s) system_ptd = deepcopy(system_base) if not isinstance(point_kwargs, (list, tuple)): point_kwargs = [point_kwargs] for pkwargs in point_kwargs: system_ptd = am.defect.point(system_ptd, **pkwargs) # Update lammps variables system_info = system_ptd.dump('atom_data', f='defect.dat', units = potential.units, atom_style = potential.atom_style) lammps_variables['atomman_system_info'] = system_info # Write lammps input script 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) # Extract lammps thermo data thermo = output.simulations[0]['thermo'] E_total_ptd = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) # Rename log file shutil.move('log.lammps', 'min-defect-log.lammps') # Load relaxed system from dump file and copy old vects as # the dump files crop the values last_dump_file = 'atom.'+str(thermo.Step.values[-1]) system_ptd = am.load('atom_dump', last_dump_file, symbols=system_ptd.symbols) system_ptd.box_set(vects=system.box.vects) system_ptd.dump('atom_dump', f='defect.dump') # Compute defect formation energy E_ptd_f = E_total_ptd - E_coh * system_ptd.natoms # Cleanup files for fname in glob.iglob('atom.*'): os.remove(fname) for dumpjsonfile in glob.iglob('*.dump.json'): os.remove(dumpjsonfile) # Return results results_dict = {} results_dict['E_coh'] = E_coh results_dict['E_ptd_f'] = E_ptd_f results_dict['E_total_base'] = E_total_base results_dict['E_total_ptd'] = E_total_ptd results_dict['system_base'] = system_base results_dict['system_ptd'] = system_ptd results_dict['dumpfile_base'] = 'perfect.dump' results_dict['dumpfile_ptd'] = 'defect.dump' return results_dict
def stackingfaultmap(lammps_command, ucell, potential, hkl, mpi_command=None, sizemults=None, minwidth=None, even=False, a1vect_uvw=None, a2vect_uvw=None, conventional_setting='p', cutboxvector='c', faultpos_rel=None, faultpos_cart=None, num_a1=10, num_a2=10, atomshift=None, shiftindex=None, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Computes a generalized stacking fault map for shifts along a regular 2D grid. Parameters ---------- lammps_command :str Command for running LAMMPS. ucell : atomman.System The crystal unit cell to use as the basis of the stacking fault configurations. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. hkl : array-like object The Miller(-Bravais) crystal fault plane relative to ucell. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. sizemults : list or tuple, optional The three System.supersize multipliers [a_mult, b_mult, c_mult] to use on the rotated cell to build the final system. Note that the cutboxvector sizemult must be an integer and not a tuple. Default value is [1, 1, 1]. minwidth : float, optional If given, the sizemult along the cutboxvector will be selected such that the width of the resulting final system in that direction will be at least this value. If both sizemults and minwidth are given, then the larger of the two in the cutboxvector direction will be used. even : bool, optional A True value means that the sizemult for cutboxvector will be made an even number by adding 1 if it is odd. Default value is False. a1vect_uvw : array-like object, optional The crystal vector to use for one of the two shifting vectors. If not given, will be set to the shortest in-plane lattice vector. a2vect_uvw : array-like object, optional The crystal vector to use for one of the two shifting vectors. If not given, will be set to the shortest in-plane lattice vector not parallel to a1vect_uvw. conventional_setting : str, optional Allows for rotations of a primitive unit cell to be determined from (hkl) indices specified relative to a conventional unit cell. Allowed settings: 'p' for primitive (no conversion), 'f' for face-centered, 'i' for body-centered, and 'a', 'b', or 'c' for side-centered. Default behavior is to perform no conversion, i.e. take (hkl) relative to the given ucell. cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). faultpos_rel : float, optional The position to place the slip plane within the system given as a relative coordinate along the out-of-plane direction. faultpos_rel and faultpos_cart cannot both be given. Default value is 0.5 if faultpos_cart is also not given. faultpos_cart : float, optional The position to place the slip plane within the system given as a Cartesian coordinate along the out-of-plane direction. faultpos_rel and faultpos_cart cannot both be given. num_a1 : int, optional The number of fractional coordinates to evaluate along a1vect_uvw. Default value is 10. num_a2 : int, optional The number of fractional coordinates to evaluate along a2vect_uvw. Default value is 10. atomshift : array-like object, optional A Cartesian vector shift to apply to all atoms. Can be used to shift atoms perpendicular to the fault plane to allow different termination planes to be cut. Cannot be given with shiftindex. shiftindex : int, optional Allows for selection of different termination planes based on the preferred shift values determined by the underlying fault generation. Cannot be given with atomshift. If neither atomshift nor shiftindex given, then shiftindex will be set to 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). Returns ------- dict Dictionary of results consisting of keys: - **'A_fault'** (*float*) - The area of the fault surface. - **'gamma'** (*atomman.defect.GammaSurface*) - A gamma surface plotting object. """ # Construct stacking fault configuration generator gsf_gen = am.defect.StackingFault( hkl, ucell, cutboxvector=cutboxvector, a1vect_uvw=a1vect_uvw, a2vect_uvw=a2vect_uvw, conventional_setting=conventional_setting) # Check shift parameters if shiftindex is not None: assert atomshift is None, 'shiftindex and atomshift cannot both be given' atomshift = gsf_gen.shifts[shiftindex] elif atomshift is None: atomshift = gsf_gen.shifts[0] # Generate the free surface (zero-shift) configuration gsf_gen.surface(shift=atomshift, minwidth=minwidth, sizemults=sizemults, even=even, faultpos_rel=faultpos_rel) abovefault = gsf_gen.abovefault cutindex = gsf_gen.cutindex A_fault = gsf_gen.surfacearea # Identify lammps_date version lammps_date = lmp.checkversion(lammps_command)['date'] # Define lists a1vals = [] a2vals = [] E_totals = [] disps = [] # Loop over all shift combinations for a1, a2, sfsystem in gsf_gen.iterfaultmap(num_a1=num_a1, num_a2=num_a2): a1vals.append(a1) a2vals.append(a2) # Evaluate the system at the shift sim_directory = Path('a%.10f-b%.10f' % (a1, a2)) relax = stackingfaultrelax(lammps_command, sfsystem, potential, mpi_command=mpi_command, sim_directory=sim_directory, cutboxvector=cutboxvector, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date) # Extract terms E_totals.append(relax['E_total']) pos = relax['system'].atoms.pos disps.append(pos[abovefault, cutindex].mean() - pos[~abovefault, cutindex].mean()) E_totals = np.array(E_totals) disps = np.array(disps) # Get zeroshift values E_total_0 = E_totals[0] disp_0 = disps[0] # Compute the stacking fault energies E_gsfs = (E_totals - E_total_0) / A_fault # Compute the change in displacement normal to fault plane delta_disps = disps - disp_0 results_dict = {} results_dict['A_fault'] = A_fault results_dict['gamma'] = am.defect.GammaSurface(a1vect=gsf_gen.a1vect_uvw, a2vect=gsf_gen.a2vect_uvw, box=gsf_gen.ucell.box, a1=a1vals, a2=a2vals, E_gsf=E_gsfs, delta=delta_disps) return results_dict
def relax_system(lammps_command, system, potential, mpi_command=None, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Sets up and runs the min.in LAMMPS script for performing an energy/force minimization to relax a system. 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. 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: - **'logfile'** (*str*) - The name of the LAMMPS log file. - **'initialdatafile'** (*str*) - The name of the LAMMPS data file used to import an inital configuration. - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file corresponding to the inital configuration. - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file corresponding to the relaxed configuration. - **'potentialenergy'** (*float*) - The total potential energy of the relaxed system. """ try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Ensure all atoms are within the system's box system.wrap() # 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 = {} # Generate system and pair info system_info = system.dump('atom_data', f='system.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) # Pass in run parameters 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = Path(script_dir, 'min.template') lammps_script = 'min.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) # Extract output values thermo = output.simulations[-1]['thermo'] results = {} results['logfile'] = 'log.lammps' results['initialdatafile'] = 'system.dat' results['initialdumpfile'] = 'atom.0' results['finaldumpfile'] = 'atom.%i' % thermo.Step.values[-1] results['potentialenergy'] = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) return results
def relax_dynamic(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, temperature=0.0, integrator=None, runsteps=220000, thermosteps=100, dumpsteps=None, equilsteps=20000, randomseed=None): """ Performs a full dynamic relax on a given system at the given temperature to the specified pressure state. 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. symbols : list of str The list of element-model symbols for the Potential that correspond to system's atypes. 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). temperature : float, optional The temperature to relax at (default is 0.0). runsteps : int, optional The number of integration steps to perform (default is 220000). integrator : str or None, optional The integration method to use. Options are 'npt', 'nvt', 'nph', 'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat. (Default is None, which will use 'nph+l' for temperature == 0, and 'npt' otherwise.) thermosteps : int, optional Thermo values will be reported every this many steps (default is 100). dumpsteps : int or None, optional Dump files will be saved every this many steps (default is None, which sets dumpsteps equal to runsteps). equilsteps : int, optional The number of timesteps at the beginning of the simulation to exclude when computing average values (default is 20000). randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) Returns ------- dict Dictionary of results consisting of keys: - **'relaxed_system'** (*float*) - The relaxed system. - **'E_coh'** (*float*) - The mean measured cohesive energy. - **'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. - **'temp'** (*float*) - The mean measured temperature. - **'E_coh_std'** (*float*) - The standard deviation in the measured cohesive energy values. - **'measured_pxx_std'** (*float*) - The standard deviation in the measured x tensile pressure of the relaxed system. - **'measured_pyy_std'** (*float*) - The standard deviation in the measured y tensile pressure of the relaxed system. - **'measured_pzz_std'** (*float*) - The standard deviation in the measured z tensile pressure of the relaxed system. - **'measured_pxy_std'** (*float*) - The standard deviation in the measured xy shear pressure of the relaxed system. - **'measured_pxz_std'** (*float*) - The standard deviation in the measured xz shear pressure of the relaxed system. - **'measured_pyz_std'** (*float*) - The standard deviation in the measured yz shear pressure of the relaxed system. - **'temp_std'** (*float*) - The standard deviation in the measured temperature values. """ try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Handle default values if dumpsteps is None: dumpsteps = runsteps # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init.dat', potential=potential, return_pair_info=True) lammps_variables['atomman_system_pair_info'] = system_info integ_info = integrator_info(integrator=integrator, p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, p_xy=p_xy, p_xz=p_xz, p_yz=p_yz, temperature=temperature, randomseed=randomseed, units=potential.units) lammps_variables['integrator_info'] = integ_info lammps_variables['thermosteps'] = thermosteps lammps_variables['runsteps'] = runsteps lammps_variables['dumpsteps'] = dumpsteps # Set compute stress/atom based on LAMMPS version if lammps_date < datetime.date(2014, 2, 12): lammps_variables['stressterm'] = '' else: lammps_variables['stressterm'] = 'NULL' # Set dump_keys based on atom_style if potential.atom_style in ['charge']: lammps_variables['dump_keys'] = 'id type q xu yu zu c_pe c_ke &\n' lammps_variables['dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]' else: lammps_variables['dump_keys'] = 'id type xu yu zu c_pe c_ke &\n' lammps_variables['dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]' # Set dump_modify_format based on lammps_date if lammps_date < datetime.date(2016, 8, 3): if potential.atom_style in ['charge']: lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = Path(script_dir, 'full_relax.template') lammps_script = 'full_relax.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) # Extract LAMMPS thermo data. results = {} thermo = output.simulations[0]['thermo'] results['dumpfile_initial'] = '0.dump' results['symbols_initial'] = system.symbols # Load relaxed system from dump file last_dump_file = str(thermo.Step.values[-1])+'.dump' results['dumpfile_final'] = last_dump_file system = am.load('atom_dump', last_dump_file, symbols=system.symbols) results['symbols_final'] = system.symbols # Only consider values where Step >= equilsteps thermo = thermo[thermo.Step >= equilsteps] results['nsamples'] = len(thermo) # Get cohesive energy estimates natoms = system.natoms results['E_coh'] = uc.set_in_units(thermo.PotEng.mean() / natoms, lammps_units['energy']) results['E_coh_std'] = uc.set_in_units(thermo.PotEng.std() / natoms, lammps_units['energy']) results['lx'] = uc.set_in_units(thermo.Lx.mean(), lammps_units['length']) results['lx_std'] = uc.set_in_units(thermo.Lx.std(), lammps_units['length']) results['ly'] = uc.set_in_units(thermo.Ly.mean(), lammps_units['length']) results['ly_std'] = uc.set_in_units(thermo.Ly.std(), lammps_units['length']) results['lz'] = uc.set_in_units(thermo.Lz.mean(), lammps_units['length']) results['lz_std'] = uc.set_in_units(thermo.Lz.std(), lammps_units['length']) results['xy'] = uc.set_in_units(thermo.Xy.mean(), lammps_units['length']) results['xy_std'] = uc.set_in_units(thermo.Xy.std(), lammps_units['length']) results['xz'] = uc.set_in_units(thermo.Xz.mean(), lammps_units['length']) results['xz_std'] = uc.set_in_units(thermo.Xz.std(), lammps_units['length']) results['yz'] = uc.set_in_units(thermo.Yz.mean(), lammps_units['length']) results['yz_std'] = uc.set_in_units(thermo.Yz.std(), lammps_units['length']) results['measured_pxx'] = uc.set_in_units(thermo.Pxx.mean(), lammps_units['pressure']) results['measured_pxx_std'] = uc.set_in_units(thermo.Pxx.std(), lammps_units['pressure']) results['measured_pyy'] = uc.set_in_units(thermo.Pyy.mean(), lammps_units['pressure']) results['measured_pyy_std'] = uc.set_in_units(thermo.Pyy.std(), lammps_units['pressure']) results['measured_pzz'] = uc.set_in_units(thermo.Pzz.mean(), lammps_units['pressure']) results['measured_pzz_std'] = uc.set_in_units(thermo.Pzz.std(), lammps_units['pressure']) results['measured_pxy'] = uc.set_in_units(thermo.Pxy.mean(), lammps_units['pressure']) results['measured_pxy_std'] = uc.set_in_units(thermo.Pxy.std(), lammps_units['pressure']) results['measured_pxz'] = uc.set_in_units(thermo.Pxz.mean(), lammps_units['pressure']) results['measured_pxz_std'] = uc.set_in_units(thermo.Pxz.std(), lammps_units['pressure']) results['measured_pyz'] = uc.set_in_units(thermo.Pyz.mean(), lammps_units['pressure']) results['measured_pyz_std'] = uc.set_in_units(thermo.Pyz.std(), lammps_units['pressure']) results['temp'] = thermo.Temp.mean() results['temp_std'] = thermo.Temp.std() return results
def dislocationarraystress(lammps_command, system, potential, temperature, mpi_command=None, sigma_xz=0.0, sigma_yz=0.0, runsteps=100000, thermosteps=100, dumpsteps=None, randomseed=None, bwidth=None, rigidbounds=False): """ Applies a constant external shearing stress to a periodic array of dislocations atomic system. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The bulk system to add the defect to. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. temperature : float The temperature to run the simulation at. mpi_command : str or None, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. sigma_xz : float, optional The xz shear stress to apply to the system through the boundary atoms. Default value is 0.0. sigma_yz : float, optional The yz shear stress to apply to the system through the boundary atoms. Default value is 0.0. runsteps : int, optional The total number of steps to run the simulation for. Default value is 100000. thermosteps : int, optional The system-wide thermo data will be outputted every this many steps. Default value is 100. dumpsteps : int, optional The atomic configurations will be saved to LAMMPS dump files every this many steps. Default value is equal to runsteps, which will only save the initial and final configurations. randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities. Default is None which will select a random int between 1 and 900000000. bwidth : float, optional The thickness of the boundary region. Default value is 10 Angstroms. rigidbounds : bool, optional If True, the atoms in the boundary regions will be treated as a rigid block such that they move as one and do not allow internal atomic relaxations. Default value is False, in which case the boundaries will be free surfaces. Returns ------- dict Dictionary of results consisting of keys: - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file for the relaxed base system. - **'symbols_base'** (*list of str*) - The list of element-model symbols for the Potential that correspond to the base system's atypes. - **'Stroh_preln'** (*float*) - The pre-logarithmic factor in the dislocation's self-energy expression. - **'Stroh_K_tensor'** (*numpy.array of float*) - The energy coefficient tensor based on the dislocation's Stroh solution. - **'dumpfile_disl'** (*str*) - The filename of the LAMMPS dump file for the relaxed dislocation monopole system. - **'symbols_disl'** (*list of str*) - The list of element-model symbols for the Potential that correspond to the dislocation monopole system's atypes. - **'E_total_disl'** (*float*) - The total potential energy of the dislocation monopole system. """ try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Set default values if bwidth is None: bwidth = uc.set_in_units(10, 'angstrom') if randomseed is None: randomseed = random.randint(1, 900000000) # 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='initial.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['temperature'] = temperature stress_unit = lammps_units['force'] + '/' + lammps_units['length'] + '^2' lammps_variables['sigma_xz'] = uc.get_in_units(sigma_xz, stress_unit) lammps_variables['sigma_yz'] = uc.get_in_units(sigma_yz, stress_unit) lammps_variables['dumpsteps'] = dumpsteps lammps_variables['runsteps'] = runsteps lammps_variables['thermosteps'] = thermosteps lammps_variables['randomseed'] = randomseed lammps_variables['bwidth'] = uc.get_in_units(bwidth, lammps_units['length']) lammps_variables['tdamp'] = 100 * lmp.style.timestep(potential.units) lammps_variables['timestep'] = lmp.style.timestep(potential.units) # Set dump_modify format based on dump_modify_version 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 if rigidbounds is True: template_file = os.path.join(script_dir, 'dislarray_rigid_stress.template') else: template_file = os.path.join(script_dir, 'dislarray_free_stress.template') lammps_script = 'dislarray_stress.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) thermo = output.simulations[-1]['thermo'] steps = thermo.Step.values times = uc.set_in_units(steps * lmp.style.timestep(potential.units), lammps_units['time']) # Read user-defined thermo data if output.lammps_date < datetime.date(2016, 8, 1): strains_xz = thermo['strain_xz'].values strains_yz = thermo['strain_yz'].values else: strains_xz = thermo['v_strain_xz'].values strains_yz = thermo['v_strain_yz'].values # Compute average strain rates strainrate_xz, b = np.polyfit(times, strains_xz, 1) strainrate_yz, b = np.polyfit(times, strains_yz, 1) # Extract output values results_dict = {} results_dict['times'] = times results_dict['strains_xz'] = strains_xz results_dict['strains_yz'] = strains_yz results_dict['strainrate_xz'] = strainrate_xz results_dict['strainrate_yz'] = strainrate_yz return results_dict
def pointdefect( lammps_command: str, system: am.System, potential: lmp.Potential, point_kwargs: Union[list, dict], mpi_command: Optional[str] = None, etol: float = 0.0, ftol: float = 0.0, maxiter: int = 10000, maxeval: int = 100000, dmax: float = uc.set_in_units(0.01, 'angstrom') ) -> dict: """ Adds one or more point defects to a system and evaluates the defect formation energy. 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. point_kwargs : dict or list of dict One or more dictionaries containing the keyword arguments for the atomman.defect.point() function to generate specific point defect configuration(s). mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. sim_directory : str, optional The path to the directory to perform the simulation in. If not given, will use the current working directory. 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: - **'E_pot'** (*float*) - The per-atom potential energy of the bulk system. - **'E_ptd_f'** (*float*) - The point defect formation energy. - **'E_total_base'** (*float*) - The total potential energy of the relaxed bulk system. - **'E_total_ptd'** (*float*) - The total potential energy of the relaxed defect system. - **'pij_tensor'** (*numpy.ndarray of float*) - The elastic dipole tensor associated with the defect. - **'system_base'** (*atomman.System*) - The relaxed bulk system. - **'system_ptd'** (*atomman.System*) - The relaxed defect system. - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file for the relaxed bulk system. - **'dumpfile_ptd'** (*str*) - The filename of the LAMMPS dump file for the relaxed defect system. """ # 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='perfect.dat', potential=potential) lammps_variables['atomman_system_pair_info'] = system_info 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'] = dmax # 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 %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script lammps_script = 'min.in' template = read_calc_file('iprPy.calculation.point_defect_static', 'min.template') with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run lammps to relax perfect.dat output = lmp.run(lammps_command, script_name=lammps_script, mpi_command=mpi_command) # Extract LAMMPS thermo data. thermo = output.simulations[0]['thermo'] E_total_base = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) E_pot = E_total_base / system.natoms 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']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pressure_base = np.array([[pxx, pxy, pxz], [pxy, pyy, pyz], [pxz, pyz, pzz]]) # Rename log file shutil.move('log.lammps', 'min-perfect-log.lammps') # Load relaxed system from dump file and copy old box vectors because # dump files crop the values. last_dump_file = 'atom.' + str(thermo.Step.values[-1]) system_base = am.load('atom_dump', last_dump_file, symbols=system.symbols) system_base.box_set(vects=system.box.vects) system_base.dump('atom_dump', f='perfect.dump') # Add defect(s) system_ptd = deepcopy(system_base) if not isinstance(point_kwargs, (list, tuple)): point_kwargs = [point_kwargs] for pkwargs in point_kwargs: system_ptd = am.defect.point(system_ptd, **pkwargs) # Update lammps variables system_info = system_ptd.dump('atom_data', f='defect.dat', potential=potential) lammps_variables['atomman_system_pair_info'] = system_info # Write lammps input script with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run lammps output = lmp.run(lammps_command, script_name=lammps_script, mpi_command=mpi_command) # Extract lammps thermo data thermo = output.simulations[0]['thermo'] E_total_ptd = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) 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']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pressure_ptd = np.array([[pxx, pxy, pxz], [pxy, pyy, pyz], [pxz, pyz, pzz]]) # Rename log file shutil.move('log.lammps', 'min-defect-log.lammps') # Load relaxed system from dump file and copy old vects as # the dump files crop the values last_dump_file = 'atom.' + str(thermo.Step.values[-1]) system_ptd = am.load('atom_dump', last_dump_file, symbols=system_ptd.symbols) system_ptd.box_set(vects=system.box.vects) system_ptd.dump('atom_dump', f='defect.dump') # Compute defect formation energy E_ptd_f = E_total_ptd - E_pot * system_ptd.natoms # Compute strain tensor pij = -(pressure_base - pressure_ptd) * system_base.box.volume # Cleanup files for fname in Path.cwd().glob('atom.*'): fname.unlink() for dumpjsonfile in Path.cwd().glob('*.dump.json'): dumpjsonfile.unlink() # Return results results_dict = {} results_dict['E_pot'] = E_pot results_dict['E_ptd_f'] = E_ptd_f results_dict['E_total_base'] = E_total_base results_dict['E_total_ptd'] = E_total_ptd results_dict['pij_tensor'] = pij results_dict['system_base'] = system_base results_dict['system_ptd'] = system_ptd results_dict['dumpfile_base'] = 'perfect.dump' results_dict['dumpfile_ptd'] = 'defect.dump' return results_dict
def phonon_quasiharmonic(lammps_command: str, ucell: am.System, potential: lmp.Potential, mpi_command: Optional[str] = None, a_mult: int = 3, b_mult: int = 3, c_mult: int = 3, distance: float = 0.01, symprec: float = 1e-5, strainrange: float = 0.01, numstrains: int = 5) -> dict: """ Function that performs phonon and quasiharmonic approximation calculations using phonopy and LAMMPS. Parameters ---------- lammps_command :str Command for running LAMMPS. ucell : atomman.System The unit cell 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. a_mult : int, optional The a size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. b_mult : int, optional The b size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. c_mult : int, optional The c size multiplier to use on ucell before running the phonon calculation. Must be an int and not a tuple. Default value is 3. distance : float, optional The atomic displacement distance used for computing the phonons. Default value is 0.01. symprec : float, optional Absolute length tolerance to use in identifying symmetry of atomic sites and system boundaries. Default value is 1e-5. strainrange : float, optional The range of strains to apply to the unit cell to use with the quasiharmonic calculations. Default value is 0.01. numstrains : int, optional The number of strains to use for the quasiharmonic calculations. Must be an odd integer. If 1, then the quasiharmonic calculations will not be performed. Default value is 5. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Get original box vectors vects = ucell.box.vects # Generate the range of strains if numstrains == 1: zerostrain = phononcalc(lammps_command, ucell, potential, mpi_command=mpi_command, a_mult=a_mult, b_mult=b_mult, c_mult=c_mult, distance=distance, symprec=symprec, lammps_date=lammps_date) phonons = [zerostrain['phonon']] qha = None elif numstrains % 2 == 0 or numstrains < 5: raise ValueError( 'Invalid number of strains: must be odd and 1 or >= 5') else: strains = np.linspace(-strainrange, strainrange, numstrains) istrains = np.linspace(-(numstrains - 1) / 2, (numstrains - 1) / 2, numstrains, dtype=int) volumes = [] energies = [] phonons = [] temperatures = None free_energy = None heat_capacity = None entropy = None # Loop over all strains for istrain, strain in zip(istrains, strains): # Identify the zero strain run if istrain == 0: zerostrainrun = True savefile = 'phonopy_params.yaml' else: zerostrainrun = False savefile = f'phonopy_params_{istrain}.yaml' # Generate system at the strain newvects = vects * (1 + strain) ucell.box_set(vects=newvects, scale=True) volumes.append(ucell.box.volume) system = ucell.supersize(a_mult, b_mult, c_mult) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='disp.dat', potential=potential) lammps_variables['atomman_system_pair_info'] = system_info # 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 %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script lammps_script = 'phonon.in' template = read_calc_file('iprPy.calculation.phonon', 'phonon.template') with open(lammps_script, 'w') as f: f.write(filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS output = lmp.run(lammps_command, script_name='phonon.in', mpi_command=mpi_command) # Extract system energy thermo = output.simulations[0]['thermo'] energy = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) # Scale energy by sizemults and append to list energies.append(energy / (a_mult * b_mult * c_mult)) # Compute phonon info for ucell phononinfo = phononcalc(lammps_command, ucell, potential, mpi_command=mpi_command, a_mult=a_mult, b_mult=b_mult, c_mult=c_mult, distance=distance, symprec=symprec, savefile=savefile, plot=zerostrainrun, lammps_date=lammps_date) phonons.append(phononinfo['phonon']) # Extract temperature values from the first run if temperatures is None: temperatures = phononinfo['thermal_properties']['temperatures'] # Initialize QHA input arrays free_energy = np.empty((len(temperatures), len(strains))) heat_capacity = np.empty((len(temperatures), len(strains))) entropy = np.empty((len(temperatures), len(strains))) # Get values for zerostrainrun if zerostrainrun is True: zerostrain = phononinfo # Copy values to qha input arrays free_energy[:, istrain] = phononinfo['thermal_properties'][ 'free_energy'] entropy[:, istrain] = phononinfo['thermal_properties']['entropy'] heat_capacity[:, istrain] = phononinfo['thermal_properties'][ 'heat_capacity'] # Compute qha try: qha = phonopy.PhonopyQHA(volumes=volumes, electronic_energies=energies, temperatures=temperatures, free_energy=free_energy, cv=heat_capacity, entropy=entropy) except: qha = None results = {} # Add phonopy objects results['phonon_objects'] = phonons results['qha_object'] = qha # Extract zerostrain properties results['band_structure'] = zerostrain['band_structure'] results['density_of_states'] = zerostrain['dos'] # Convert units on thermal properties results['thermal_properties'] = zerostrain['thermal_properties'] results['thermal_properties']['temperature'] = results[ 'thermal_properties'].pop('temperatures') results['thermal_properties']['Helmholtz'] = uc.set_in_units( results['thermal_properties'].pop('free_energy'), 'kJ/mol') results['thermal_properties']['entropy'] = uc.set_in_units( results['thermal_properties'].pop('entropy'), 'J/K/mol') results['thermal_properties']['heat_capacity_v'] = uc.set_in_units( results['thermal_properties'].pop('heat_capacity'), 'J/K/mol') if qha is not None: # Create QHA plots qha.plot_bulk_modulus() plt.xlabel('Volume ($Ã…^3$)', size='large') plt.ylabel('Energy ($eV$)', size='large') plt.savefig('bulk_modulus.png', dpi=400, bbox_inches='tight') plt.close() qha.plot_helmholtz_volume() plt.savefig('helmholtz_volume.png', dpi=400) plt.close() # Package volume vs energy scans results['volume_scan'] = {} results['volume_scan']['volume'] = np.array(volumes) results['volume_scan']['strain'] = strains results['volume_scan']['energy'] = np.array(energies) # Compute and add QHA properties properties = qha.get_bulk_modulus_parameters() results['E0'] = uc.set_in_units(properties[0], 'eV') results['B0'] = uc.set_in_units(properties[1], 'eV/angstrom^3') results['B0prime'] = uc.set_in_units(properties[2], 'eV/angstrom^3') results['V0'] = uc.set_in_units(properties[3], 'angstrom^3') results['thermal_properties']['volume'] = uc.set_in_units( np.hstack([qha.volume_temperature, np.nan]), 'angstrom^3') results['thermal_properties']['thermal_expansion'] = np.hstack( [qha.thermal_expansion, np.nan]) results['thermal_properties']['Gibbs'] = uc.set_in_units( np.hstack([qha.gibbs_temperature, np.nan]), 'eV') results['thermal_properties']['bulk_modulus'] = uc.set_in_units( np.hstack([qha.bulk_modulus_temperature, np.nan]), 'GPa') results['thermal_properties'][ 'heat_capacity_p_numerical'] = uc.set_in_units( np.hstack([qha.heat_capacity_P_numerical, np.nan]), 'J/K/mol') results['thermal_properties'][ 'heat_capacity_p_polyfit'] = uc.set_in_units( np.hstack([qha.heat_capacity_P_polyfit, np.nan]), 'J/K/mol') results['thermal_properties']['gruneisen'] = np.hstack( [qha.gruneisen_temperature, np.nan]) return results
def stackingfaultpoint(lammps_command, system, potential, mpi_command=None, sim_directory=None, cutboxvector='c', faultpos=0.5, faultshift=[0.0, 0.0, 0.0], etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom'), lammps_date=None): """ Perform a stacking fault relaxation simulation for a single faultshift. 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. sim_directory : str, optional The path to the directory to perform the simuation in. If not given, will use the current working directory. 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). cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). faultpos : float, optional The fractional position along the cutboxvector where the stacking fault plane will be placed (default is 0.5). faultshift : list of float, optional The vector shift to apply to all atoms above the fault plane defined by faultpos (default is [0,0,0], i.e. no shift applied). lammps_date : datetime.date or None, optional The date version of the LAMMPS executable. If None, will be identified from the lammps_command (default is None). Returns ------- dict Dictionary of results consisting of keys: - **'logfile'** (*str*) - The filename of the LAMMPS log file. - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file of the relaxed system. - **'system'** (*atomman.System*) - The relaxed system. - **'A_fault'** (*float*) - The area of the fault surface. - **'E_total'** (*float*) - The total potential energy of the relaxed system. - **'disp'** (*float*) - The center of mass difference between atoms above and below the fault plane in the cutboxvector direction. Raises ------ ValueError For invalid cutboxvectors. """ # Set options based on cutboxvector if cutboxvector == 'a': # Assert system is compatible with planeaxis value if system.box.xy != 0.0 or system.box.xz != 0.0: raise ValueError( "box tilts xy and xz must be 0 for cutboxvector='a'") # Specify cutindex cutindex = 0 # Identify atoms above fault faultpos = system.box.xlo + system.box.lx * faultpos abovefault = system.atoms.pos[:, cutindex] > (faultpos) # Compute fault area faultarea = np.linalg.norm(np.cross(system.box.bvect, system.box.cvect)) # Give correct LAMMPS fix setforce command fix_cut_setforce = 'fix cut all setforce NULL 0 0' elif cutboxvector == 'b': # Assert system is compatible with planeaxis value if system.box.yz != 0.0: raise ValueError("box tilt yz must be 0 for cutboxvector='b'") # Specify cutindex cutindex = 1 # Identify atoms above fault faultpos = system.box.ylo + system.box.ly * faultpos abovefault = system.atoms.pos[:, cutindex] > (faultpos) # Compute fault area faultarea = np.linalg.norm(np.cross(system.box.avect, system.box.cvect)) # Give correct LAMMPS fix setforce command fix_cut_setforce = 'fix cut all setforce 0 NULL 0' elif cutboxvector == 'c': # Specify cutindex cutindex = 2 # Identify atoms above fault faultpos = system.box.zlo + system.box.lz * faultpos abovefault = system.atoms.pos[:, cutindex] > (faultpos) # Compute fault area faultarea = np.linalg.norm(np.cross(system.box.avect, system.box.bvect)) # Give correct LAMMPS fix setforce command fix_cut_setforce = 'fix cut all setforce 0 0 NULL' else: raise ValueError('Invalid cutboxvector') # Assert faultshift is in cut plane if faultshift[cutindex] != 0.0: raise ValueError('faultshift must be in cut plane') # Generate stacking fault system by shifting atoms above the fault sfsystem = deepcopy(system) sfsystem.pbc = [True, True, True] sfsystem.pbc[cutindex] = False sfsystem.atoms.pos[abovefault] += faultshift sfsystem.wrap() if sim_directory is not None: # Create sim_directory if it doesn't exist if not os.path.isdir(sim_directory): os.mkdir(sim_directory) # Add '/' to end of sim_directory string if needed if sim_directory[-1] != '/': sim_directory = sim_directory + '/' else: # Set sim_directory if is None sim_directory = '' # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date if lammps_date is None: lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = sfsystem.dump('atom_data', f=os.path.join(sim_directory, 'system.dat'), units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info( sfsystem.symbols) lammps_variables['fix_cut_setforce'] = fix_cut_setforce lammps_variables['sim_directory'] = sim_directory 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables[ 'dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = 'sfmin.template' lammps_script = os.path.join(sim_directory, 'sfmin.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, logfile=os.path.join(sim_directory, 'log.lammps')) # Extract output values thermo = output.simulations[-1]['thermo'] logfile = os.path.join(sim_directory, 'log.lammps') dumpfile = os.path.join(sim_directory, '%i.dump' % thermo.Step.values[-1]) E_total = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) # Load relaxed system sfsystem = am.load('atom_dump', dumpfile, symbols=sfsystem.symbols) # Find center of mass difference in top/bottom planes disp = (sfsystem.atoms.pos[abovefault, cutindex].mean() - sfsystem.atoms.pos[~abovefault, cutindex].mean()) # Return results results_dict = {} results_dict['logfile'] = logfile results_dict['dumpfile'] = dumpfile results_dict['system'] = sfsystem results_dict['A_fault'] = faultarea results_dict['E_total'] = E_total results_dict['disp'] = disp return results_dict
def disl_relax(lammps_command, system, potential, mpi_command=None, annealtemp=0.0, randomseed=None, etol=0.0, ftol=1e-6, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Sets up and runs the disl_relax.in LAMMPS script for relaxing a dislocation monopole system. 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. annealtemp : float, optional The temperature to perform a dynamic relaxation at. (Default is 0.0, which will skip the dynamic relaxation.) randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) 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: - **'logfile'** (*str*) - The name of the LAMMPS log file. - **'dumpfile'** (*str*) - The name of the LAMMPS dump file for the relaxed system. - **'E_total'** (*float*) - The total potential energy for the relaxed system. """ # 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='system.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['anneal_info'] = anneal_info(annealtemp, randomseed, potential.units) 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'] = dmax lammps_variables['group_move'] = ' '.join(np.array(range(1, system.natypes // 2 + 1), dtype=str)) # Set dump_modify format based on dump_modify_version 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 = 'disl_relax.template' lammps_script = 'disl_relax.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) thermo = output.simulations[-1]['thermo'] # Extract output values results = {} results['logfile'] = 'log.lammps' results['dumpfile'] = '%i.dump' % thermo.Step.values[-1] results['E_total'] = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) return results
def lammps_date(self): if self.__lammps_version is None and self.lammps_command is not None: lammps_version = lmp.checkversion(self.lammps_command) self.__lammps_version = lammps_version['version'] self.__lammps_date = lammps_version['date'] return self.__lammps_date
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 pointdiffusion(lammps_command, system, potential, point_kwargs, mpi_command=None, temperature=300, runsteps=200000, thermosteps=None, dumpsteps=0, equilsteps=20000, randomseed=None): """ Evaluates the diffusion rate of a point defect at a given temperature. This method will run two simulations: an NVT run at the specified temperature to equilibrate the system, then an NVE run to measure the defect's diffusion rate. The diffusion rate is evaluated using the mean squared displacement of all atoms in the system, and using the assumption that diffusion is only due to the added defect(s). 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. point_kwargs : dict or list of dict One or more dictionaries containing the keyword arguments for the atomman.defect.point() function to generate specific point defect configuration(s). mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. temperature : float, optional The temperature to run at (default is 300.0). runsteps : int, optional The number of integration steps to perform (default is 200000). thermosteps : int, optional Thermo values will be reported every this many steps (default is 100). dumpsteps : int or None, optional Dump files will be saved every this many steps (default is 0, which does not output dump files). equilsteps : int, optional The number of timesteps at the beginning of the simulation to exclude when computing average values (default is 20000). randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) Returns ------- dict Dictionary of results consisting of keys: - **'natoms'** (*int*) - The number of atoms in the system. - **'temp'** (*float*) - The mean measured temperature. - **'pxx'** (*float*) - The mean measured normal xx pressure. - **'pyy'** (*float*) - The mean measured normal yy pressure. - **'pzz'** (*float*) - The mean measured normal zz pressure. - **'Epot'** (*numpy.array*) - The mean measured total potential energy. - **'temp_std'** (*float*) - The standard deviation in the measured temperature values. - **'pxx_std'** (*float*) - The standard deviation in the measured normal xx pressure values. - **'pyy_std'** (*float*) - The standard deviation in the measured normal yy pressure values. - **'pzz_std'** (*float*) - The standard deviation in the measured normal zz pressure values. - **'Epot_std'** (*float*) - The standard deviation in the measured total potential energy values. - **'dx'** (*float*) - The computed diffusion constant along the x-direction. - **'dy'** (*float*) - The computed diffusion constant along the y-direction. - **'dz'** (*float*) - The computed diffusion constant along the y-direction. - **'d'** (*float*) - The total computed diffusion constant. """ # Add defect(s) to the initially perfect system if not isinstance(point_kwargs, (list, tuple)): point_kwargs = [point_kwargs] for pkwargs in point_kwargs: system = am.defect.point(system, **pkwargs) # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Check that temperature is greater than zero if temperature <= 0.0: raise ValueError('Temperature must be greater than zero') # Handle default values if thermosteps is None: thermosteps = runsteps // 1000 if thermosteps == 0: thermosteps = 1 if dumpsteps is None: dumpsteps = runsteps if randomseed is None: randomseed = random.randint(1, 900000000) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='initial.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['temperature'] = temperature lammps_variables['runsteps'] = runsteps lammps_variables['equilsteps'] = equilsteps lammps_variables['thermosteps'] = thermosteps lammps_variables['dumpsteps'] = dumpsteps lammps_variables['randomseed'] = randomseed lammps_variables['timestep'] = lmp.style.timestep(potential.units) # Set dump_info if dumpsteps == 0: lammps_variables['dump_info'] = '' else: lammps_variables['dump_info'] = '\n'.join([ '', '# Define dump files', 'dump dumpit all custom ${dumpsteps} *.dump id type x y z c_peatom', 'dump_modify dumpit format <dump_modify_format>', '', ]) # 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 = 'diffusion.template' lammps_script = 'diffusion.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, 'diffusion.in', mpi_command) # Extract LAMMPS thermo data. thermo = output.simulations[1]['thermo'] temps = thermo.Temp.values pxxs = uc.set_in_units(thermo.Pxx.values, lammps_units['pressure']) pyys = uc.set_in_units(thermo.Pyy.values, lammps_units['pressure']) pzzs = uc.set_in_units(thermo.Pzz.values, lammps_units['pressure']) potengs = uc.set_in_units(thermo.PotEng.values, lammps_units['energy']) steps = thermo.Step.values # Read user-defined thermo data if output.lammps_date < datetime.date(2016, 8, 1): msd_x = uc.set_in_units(thermo['msd[1]'].values, lammps_units['length']+'^2') msd_y = uc.set_in_units(thermo['msd[2]'].values, lammps_units['length']+'^2') msd_z = uc.set_in_units(thermo['msd[3]'].values, lammps_units['length']+'^2') msd = uc.set_in_units(thermo['msd[4]'].values, lammps_units['length']+'^2') else: msd_x = uc.set_in_units(thermo['c_msd[1]'].values, lammps_units['length']+'^2') msd_y = uc.set_in_units(thermo['c_msd[2]'].values, lammps_units['length']+'^2') msd_z = uc.set_in_units(thermo['c_msd[3]'].values, lammps_units['length']+'^2') msd = uc.set_in_units(thermo['c_msd[4]'].values, lammps_units['length']+'^2') # Initialize results dict results = {} results['natoms'] = system.natoms # Get mean and std for temperature, pressure, and potential energy results['temp'] = np.mean(temps) results['temp_std'] = np.std(temps) results['pxx'] = np.mean(pxxs) results['pxx_std'] = np.std(pxxs) results['pyy'] = np.mean(pyys) results['pyy_std'] = np.std(pyys) results['pzz'] = np.mean(pzzs) results['pzz_std'] = np.std(pzzs) results['Epot'] = np.mean(potengs) results['Epot_std'] = np.std(potengs) # Convert steps to times times = steps * uc.set_in_units(lammps_variables['timestep'], lammps_units['time']) # Estimate diffusion rates # MSD_ptd = natoms * MSD_atoms (if one defect in system) # MSD = 2 * ndim * D * t --> D = MSD/t / (2 * ndim) mx = np.polyfit(times, system.natoms * msd_x, 1)[0] my = np.polyfit(times, system.natoms * msd_y, 1)[0] mz = np.polyfit(times, system.natoms * msd_z, 1)[0] m = np.polyfit(times, system.natoms * msd, 1)[0] results['dx'] = mx / 2 results['dy'] = my / 2 results['dz'] = mz / 2 results['d'] = m / 6 return results
def pointdiffusion(lammps_command, system, potential, point_kwargs, mpi_command=None, temperature=300, runsteps=200000, thermosteps=None, dumpsteps=0, equilsteps=20000, randomseed=None): """ Evaluates the diffusion rate of a point defect at a given temperature. This method will run two simulations: an NVT run at the specified temperature to equilibrate the system, then an NVE run to measure the defect's diffusion rate. The diffusion rate is evaluated using the mean squared displacement of all atoms in the system, and using the assumption that diffusion is only due to the added defect(s). 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. point_kwargs : dict or list of dict One or more dictionaries containing the keyword arguments for the atomman.defect.point() function to generate specific point defect configuration(s). mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. temperature : float, optional The temperature to run at (default is 300.0). runsteps : int, optional The number of integration steps to perform (default is 200000). thermosteps : int, optional Thermo values will be reported every this many steps (default is 100). dumpsteps : int or None, optional Dump files will be saved every this many steps (default is 0, which does not output dump files). equilsteps : int, optional The number of timesteps at the beginning of the simulation to exclude when computing average values (default is 20000). randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) Returns ------- dict Dictionary of results consisting of keys: - **'natoms'** (*int*) - The number of atoms in the system. - **'temp'** (*float*) - The mean measured temperature. - **'pxx'** (*float*) - The mean measured normal xx pressure. - **'pyy'** (*float*) - The mean measured normal yy pressure. - **'pzz'** (*float*) - The mean measured normal zz pressure. - **'Epot'** (*numpy.array*) - The mean measured total potential energy. - **'temp_std'** (*float*) - The standard deviation in the measured temperature values. - **'pxx_std'** (*float*) - The standard deviation in the measured normal xx pressure values. - **'pyy_std'** (*float*) - The standard deviation in the measured normal yy pressure values. - **'pzz_std'** (*float*) - The standard deviation in the measured normal zz pressure values. - **'Epot_std'** (*float*) - The standard deviation in the measured total potential energy values. - **'dx'** (*float*) - The computed diffusion constant along the x-direction. - **'dy'** (*float*) - The computed diffusion constant along the y-direction. - **'dz'** (*float*) - The computed diffusion constant along the y-direction. - **'d'** (*float*) - The total computed diffusion constant. """ try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Add defect(s) to the initially perfect system if not isinstance(point_kwargs, (list, tuple)): point_kwargs = [point_kwargs] for pkwargs in point_kwargs: system = am.defect.point(system, **pkwargs) # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Check that temperature is greater than zero if temperature <= 0.0: raise ValueError('Temperature must be greater than zero') # Handle default values if thermosteps is None: thermosteps = runsteps // 1000 if thermosteps == 0: thermosteps = 1 if dumpsteps is None: dumpsteps = runsteps if randomseed is None: randomseed = random.randint(1, 900000000) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='initial.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['temperature'] = temperature lammps_variables['runsteps'] = runsteps lammps_variables['equilsteps'] = equilsteps lammps_variables['thermosteps'] = thermosteps lammps_variables['dumpsteps'] = dumpsteps lammps_variables['randomseed'] = randomseed lammps_variables['timestep'] = lmp.style.timestep(potential.units) # Set dump_info if dumpsteps == 0: lammps_variables['dump_info'] = '' else: lammps_variables['dump_info'] = '\n'.join([ '', '# Define dump files', 'dump dumpit all custom ${dumpsteps} *.dump id type x y z c_peatom', 'dump_modify dumpit format <dump_modify_format>', '', ]) # 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 = Path(script_dir, 'diffusion.template') lammps_script = 'diffusion.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, 'diffusion.in', mpi_command) # Extract LAMMPS thermo data. thermo = output.simulations[1]['thermo'] temps = thermo.Temp.values pxxs = uc.set_in_units(thermo.Pxx.values, lammps_units['pressure']) pyys = uc.set_in_units(thermo.Pyy.values, lammps_units['pressure']) pzzs = uc.set_in_units(thermo.Pzz.values, lammps_units['pressure']) potengs = uc.set_in_units(thermo.PotEng.values, lammps_units['energy']) steps = thermo.Step.values # Read user-defined thermo data if output.lammps_date < datetime.date(2016, 8, 1): msd_x = uc.set_in_units(thermo['msd[1]'].values, lammps_units['length'] + '^2') msd_y = uc.set_in_units(thermo['msd[2]'].values, lammps_units['length'] + '^2') msd_z = uc.set_in_units(thermo['msd[3]'].values, lammps_units['length'] + '^2') msd = uc.set_in_units(thermo['msd[4]'].values, lammps_units['length'] + '^2') else: msd_x = uc.set_in_units(thermo['c_msd[1]'].values, lammps_units['length'] + '^2') msd_y = uc.set_in_units(thermo['c_msd[2]'].values, lammps_units['length'] + '^2') msd_z = uc.set_in_units(thermo['c_msd[3]'].values, lammps_units['length'] + '^2') msd = uc.set_in_units(thermo['c_msd[4]'].values, lammps_units['length'] + '^2') # Initialize results dict results = {} results['natoms'] = system.natoms # Get mean and std for temperature, pressure, and potential energy results['temp'] = np.mean(temps) results['temp_std'] = np.std(temps) results['pxx'] = np.mean(pxxs) results['pxx_std'] = np.std(pxxs) results['pyy'] = np.mean(pyys) results['pyy_std'] = np.std(pyys) results['pzz'] = np.mean(pzzs) results['pzz_std'] = np.std(pzzs) results['Epot'] = np.mean(potengs) results['Epot_std'] = np.std(potengs) # Convert steps to times times = steps * uc.set_in_units(lammps_variables['timestep'], lammps_units['time']) # Estimate diffusion rates # MSD_ptd = natoms * MSD_atoms (if one defect in system) # MSD = 2 * ndim * D * t --> D = MSD/t / (2 * ndim) mx = np.polyfit(times, system.natoms * msd_x, 1)[0] my = np.polyfit(times, system.natoms * msd_y, 1)[0] mz = np.polyfit(times, system.natoms * msd_z, 1)[0] m = np.polyfit(times, system.natoms * msd, 1)[0] results['dx'] = mx / 2 results['dy'] = my / 2 results['dz'] = mz / 2 results['d'] = m / 6 return results
def stackingfaultmap(lammps_command, system, potential, shiftvector1, shiftvector2, mpi_command=None, numshifts1=11, numshifts2=11, cutboxvector=None, faultpos=0.5, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Computes a generalized stacking fault map for shifts along a regular 2D grid. 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. shiftvector1 : list of floats or numpy.array One of the generalized stacking fault shifting vectors. shiftvector2 : list of floats or numpy.array One of the generalized stacking fault shifting vectors. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. 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). cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). numshifts1 : int, optional The number of equally spaced shiftfractions to evaluate along shiftvector1. numshifts2 : int, optional The number of equally spaced shiftfractions to evaluate along shiftvector2. Returns ------- dict Dictionary of results consisting of keys: - **'shift1'** (*numpy.array of float*) - The fractional shifts along shiftvector1 where the stacking fault was evaluated. - **'shift2'** (*numpy.array of float*) - The fractional shifts along shiftvector2 where the stacking fault was evaluated. - **'E_gsf'** (*numpy.array of float*) - The stacking fault formation energies measured for all the (shift1, shift2) coordinates. - **'delta_disp'** (*numpy.array of float*) - The change in the center of mass difference between before and after applying the faultshift for all the (shift1, shift2) coordinates. - **'A_fault'** (*float*) - The area of the fault surface. """ # Start sf_df as empty list sf_df = [] # Construct mesh of regular points shifts1, shifts2 = np.meshgrid(np.linspace(0, 1, numshifts1), np.linspace(0, 1, numshifts2)) # Identify lammps_date version lammps_date = lmp.checkversion(lammps_command)['date'] # Loop over all shift combinations for shiftfraction1, shiftfraction2 in zip(shifts1.flat, shifts2.flat): # Evaluate the system at the shift sf_df.append(stackingfaultworker(lammps_command, system, potential, shiftvector1, shiftvector2, shiftfraction1, shiftfraction2, mpi_command=mpi_command, cutboxvector=cutboxvector, faultpos=faultpos, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date)) # Convert sf_df to pandas DataFrame sf_df = pd.DataFrame(sf_df) # Identify the zeroshift column zeroshift = sf_df[(np.isclose(sf_df.shift1, 0.0, atol=1e-10, rtol=0.0) & np.isclose(sf_df.shift2, 0.0, atol=1e-10, rtol=0.0))] assert len(zeroshift) == 1, 'zeroshift simulation not uniquely identified' # Get zeroshift values E_total_0 = zeroshift.E_total.values[0] A_fault = zeroshift.A_fault.values[0] disp_0 = zeroshift.disp.values[0] # Compute the stacking fault energy E_gsf = (sf_df.E_total.values - E_total_0) / A_fault # Compute the change in displacement normal to fault plane delta_disp = sf_df.disp.values - disp_0 results_dict = {} results_dict['shift1'] = sf_df.shift1.values results_dict['shift2'] = sf_df.shift2.values results_dict['E_gsf'] = E_gsf results_dict['delta_disp'] = delta_disp results_dict['A_fault'] = A_fault 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 stackingfaultpoint(lammps_command, system, potential, mpi_command=None, sim_directory=None, cutboxvector='c', faultpos=0.5, faultshift=[0.0, 0.0, 0.0], etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom'), lammps_date=None): """ Perform a stacking fault relaxation simulation for a single faultshift. 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. sim_directory : str, optional The path to the directory to perform the simuation in. If not given, will use the current working directory. 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). cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). faultpos : float, optional The fractional position along the cutboxvector where the stacking fault plane will be placed (default is 0.5). faultshift : list of float, optional The vector shift to apply to all atoms above the fault plane defined by faultpos (default is [0,0,0], i.e. no shift applied). lammps_date : datetime.date or None, optional The date version of the LAMMPS executable. If None, will be identified from the lammps_command (default is None). Returns ------- dict Dictionary of results consisting of keys: - **'logfile'** (*str*) - The filename of the LAMMPS log file. - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file of the relaxed system. - **'system'** (*atomman.System*) - The relaxed system. - **'A_fault'** (*float*) - The area of the fault surface. - **'E_total'** (*float*) - The total potential energy of the relaxed system. - **'disp'** (*float*) - The center of mass difference between atoms above and below the fault plane in the cutboxvector direction. Raises ------ ValueError For invalid cutboxvectors. """ # Set options based on cutboxvector if cutboxvector == 'a': # Assert system is compatible with planeaxis value if system.box.xy != 0.0 or system.box.xz != 0.0: raise ValueError("box tilts xy and xz must be 0 for cutboxvector='a'") # Specify cutindex cutindex = 0 # Identify atoms above fault faultpos = system.box.xlo + system.box.lx * faultpos abovefault = system.atoms.pos[:, cutindex] > (faultpos) # Compute fault area faultarea = np.linalg.norm(np.cross(system.box.bvect, system.box.cvect)) # Give correct LAMMPS fix setforce command fix_cut_setforce = 'fix cut all setforce NULL 0 0' elif cutboxvector == 'b': # Assert system is compatible with planeaxis value if system.box.yz != 0.0: raise ValueError("box tilt yz must be 0 for cutboxvector='b'") # Specify cutindex cutindex = 1 # Identify atoms above fault faultpos = system.box.ylo + system.box.ly * faultpos abovefault = system.atoms.pos[:, cutindex] > (faultpos) # Compute fault area faultarea = np.linalg.norm(np.cross(system.box.avect, system.box.cvect)) # Give correct LAMMPS fix setforce command fix_cut_setforce = 'fix cut all setforce 0 NULL 0' elif cutboxvector == 'c': # Specify cutindex cutindex = 2 # Identify atoms above fault faultpos = system.box.zlo + system.box.lz * faultpos abovefault = system.atoms.pos[:, cutindex] > (faultpos) # Compute fault area faultarea = np.linalg.norm(np.cross(system.box.avect, system.box.bvect)) # Give correct LAMMPS fix setforce command fix_cut_setforce = 'fix cut all setforce 0 0 NULL' else: raise ValueError('Invalid cutboxvector') # Assert faultshift is in cut plane if faultshift[cutindex] != 0.0: raise ValueError('faultshift must be in cut plane') # Generate stacking fault system by shifting atoms above the fault sfsystem = deepcopy(system) sfsystem.pbc = [True, True, True] sfsystem.pbc[cutindex] = False sfsystem.atoms.pos[abovefault] += faultshift sfsystem.wrap() if sim_directory is not None: # Create sim_directory if it doesn't exist if not os.path.isdir(sim_directory): os.mkdir(sim_directory) # Add '/' to end of sim_directory string if needed if sim_directory[-1] != '/': sim_directory = sim_directory + '/' else: # Set sim_directory if is None sim_directory = '' # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date if lammps_date is None: lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = sfsystem.dump('atom_data', f=os.path.join(sim_directory, 'system.dat'), units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(sfsystem.symbols) lammps_variables['fix_cut_setforce'] = fix_cut_setforce lammps_variables['sim_directory'] = sim_directory 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 dump_modify_version if lammps_date < datetime.date(2016, 8, 3): lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = 'sfmin.template' lammps_script = os.path.join(sim_directory, 'sfmin.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, logfile=os.path.join(sim_directory, 'log.lammps')) # Extract output values thermo = output.simulations[-1]['thermo'] logfile = os.path.join(sim_directory, 'log.lammps') dumpfile = os.path.join(sim_directory, '%i.dump' % thermo.Step.values[-1]) E_total = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) #Load relaxed system sfsystem = am.load('atom_dump', dumpfile, symbols=sfsystem.symbols) # Find center of mass difference in top/bottom planes disp = (sfsystem.atoms.pos[abovefault, cutindex].mean() - sfsystem.atoms.pos[~abovefault, cutindex].mean()) # Return results results_dict = {} results_dict['logfile'] = logfile results_dict['dumpfile'] = dumpfile results_dict['system'] = sfsystem results_dict['A_fault'] = faultarea results_dict['E_total'] = E_total results_dict['disp'] = disp return results_dict
def phonon(lammps_command, ucell, potential, mpi_command=None, a_mult=3, b_mult=3, c_mult=3, distance=0.01, symprec=1e-5): try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Generate pair_info pair_info = potential.pair_info(ucell.symbols) # Use spglib to find primitive unit cell of ucell convcell = ucell.dump('spglib_cell') primcell = spglib.find_primitive(convcell, symprec=symprec) primucell = am.load('spglib_cell', primcell, symbols=ucell.symbols).normalize() # Initialize Phonopy object phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms'), [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]]) phonon.generate_displacements(distance=distance) # Loop over displaced supercells to compute forces forcearrays = [] for supercell in phonon.supercells_with_displacements: # Save to LAMMPS data file system = am.load('phonopy_Atoms', supercell) system_info = system.dump('atom_data', f='disp.dat') # Define lammps variables lammps_variables = {} lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = pair_info # 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 %.13e %.13e"' else: lammps_variables['dump_modify_format'] = 'float %.13e' # Write lammps input script template_file = Path(script_dir, 'phonon.template') lammps_script = 'phonon.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 lmp.run(lammps_command, 'phonon.in', mpi_command=mpi_command) # Extract forces from dump file results = am.load('atom_dump', 'forces.dump') forces = uc.set_in_units(results.atoms.force, lammps_units['force']) forcearrays.append(forces) # Set computed forces phonon.set_forces(forcearrays) # Save to yaml file phonon.save('phonopy_params.yaml') # Compute band structure phonon.produce_force_constants() phonon.auto_band_structure(plot=True) plt.savefig(Path('.', 'band.png'), dpi=400) plt.close() # Compute total density of states phonon.auto_total_dos(plot=True) plt.savefig('total_dos.png', dpi=400) plt.close() # Compute partial density of states phonon.auto_projected_dos(plot=True) plt.savefig('projected_dos.png', dpi=400) plt.close() # Compute thermal properties phonon.run_thermal_properties() phonon.plot_thermal_properties() plt.savefig('thermal.png', dpi=400) plt.close() return {}
def stackingfaultmap(lammps_command, system, potential, mpi_command=None, a1vect=None, a2vect=None, ucell=None, transform=None, cutboxvector=None, faultposrel=0.5, num_a1=10, num_a2=10, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Computes a generalized stacking fault map for shifts along a regular 2D grid. 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. a1vect : array-like object, optional A slip vector within the slip plane. Depending on if ucellbox and transform are given, this can be either a Miller crystal vector or a Cartesian vector relative to the supplied system. If a1vect is not given and a2vect is, then a1vect is set to [0,0,0]. a2vect : array-like object, optional A slip vector within the slip plane. Depending on if ucellbox and transform are given, this can be either a Miller crystal vector or a Cartesian vector relative to the supplied system. If a2vect is not given and a1vect is, then a2vect is set to [0,0,0]. ucell : atomman.System, optional If ucell is given, then the a1vect and a2vect slip vectors are taken as Miller crystal vectors relative to ucell.box. If not given, then the slip vectors are taken as Cartesian vectors. transform : array-like object, optional A transformation tensor to apply to the a1vect and a2vect slip vectors. This is needed if system is oriented differently than ucell, i.e. system is rotated. cutboxvector : str, optional Indicates which of the three system box vectors, 'a', 'b', or 'c', to cut with a non-periodic boundary (default is 'c'). faultposrel : float, optional The fractional position along the cutboxvector where the stacking fault plane will be placed (default is 0.5). num_a1 : int, optional The number of fractional coordinates to evaluate along a1vect. Default value is 10. num_a2 : int, optional The number of fractional coordinates to evaluate along a2vect. Default value is 10. 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_fault'** (*float*) - The area of the fault surface. - **'gamma'** (*atomman.defect.GammaSurface*) - A gamma surface plotting object. """ # Construct stacking fault configuration generator gsf_gen = am.defect.StackingFault(system, a1vect=a1vect, a2vect=a2vect, ucellbox=ucell.box, transform=transform, cutboxvector=cutboxvector, faultposrel=faultposrel) abovefault = gsf_gen.abovefault cutindex = gsf_gen.cutindex A_fault = gsf_gen.faultarea # Identify lammps_date version lammps_date = lmp.checkversion(lammps_command)['date'] # Define lists a1vals = [] a2vals = [] E_totals = [] disps = [] # Loop over all shift combinations for a1, a2, sfsystem in gsf_gen.iterfaultmap(num_a1=num_a1, num_a2=num_a2): a1vals.append(a1) a2vals.append(a2) # Evaluate the system at the shift sim_directory = Path('a%.10f-b%.10f' % (a1, a2)) relax = stackingfaultrelax(lammps_command, sfsystem, potential, mpi_command=mpi_command, sim_directory=sim_directory, cutboxvector=cutboxvector, etol=etol, ftol=ftol, maxiter=maxiter, maxeval=maxeval, dmax=dmax, lammps_date=lammps_date) # Extract terms E_totals.append(relax['E_total']) pos = relax['system'].atoms.pos disps.append(pos[abovefault, cutindex].mean() - pos[~abovefault, cutindex].mean()) E_totals = np.array(E_totals) disps = np.array(disps) # Get zeroshift values E_total_0 = E_totals[0] disp_0 = disps[0] # Compute the stacking fault energies E_gsfs = (E_totals - E_total_0) / A_fault # Compute the change in displacement normal to fault plane delta_disps = disps - disp_0 results_dict = {} results_dict['A_fault'] = A_fault results_dict['gamma'] = am.defect.GammaSurface(a1vect = a1vect, a2vect = a2vect, box = ucell.box, a1 = a1vals, a2 = a2vals, E_gsf = E_gsfs, delta = delta_disps) return results_dict
def disl_relax(lammps_command, system, potential, mpi_command=None, annealtemp=0.0, randomseed=None, etol=0.0, ftol=1e-6, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Sets up and runs the disl_relax.in LAMMPS script for relaxing a dislocation monopole system. 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. annealtemp : float, optional The temperature to perform a dynamic relaxation at. (Default is 0.0, which will skip the dynamic relaxation.) randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) 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: - **'logfile'** (*str*) - The name of the LAMMPS log file. - **'dumpfile'** (*str*) - The name of the LAMMPS dump file for the relaxed system. - **'E_total'** (*float*) - The total potential energy for the relaxed system. """ # 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='system.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['anneal_info'] = anneal_info(annealtemp, randomseed, potential.units) 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'] = dmax lammps_variables['group_move'] = ' '.join( np.array(range(1, system.natypes // 2 + 1), dtype=str)) # Set dump_modify format based on dump_modify_version 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 = 'disl_relax.template' lammps_script = 'disl_relax.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) thermo = output.simulations[-1]['thermo'] # Extract output values results = {} results['logfile'] = 'log.lammps' results['dumpfile'] = '%i.dump' % thermo.Step.values[-1] results['E_total'] = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy']) return results
def relax_dynamic(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, temperature=0.0, integrator=None, runsteps=220000, thermosteps=100, dumpsteps=None, equilsteps=20000, randomseed=None): """ Performs a full dynamic relax on a given system at the given temperature to the specified pressure state. 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. symbols : list of str The list of element-model symbols for the Potential that correspond to system's atypes. 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). temperature : float, optional The temperature to relax at (default is 0.0). runsteps : int, optional The number of integration steps to perform (default is 220000). integrator : str or None, optional The integration method to use. Options are 'npt', 'nvt', 'nph', 'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat. (Default is None, which will use 'nph+l' for temperature == 0, and 'npt' otherwise.) thermosteps : int, optional Thermo values will be reported every this many steps (default is 100). dumpsteps : int or None, optional Dump files will be saved every this many steps (default is None, which sets dumpsteps equal to runsteps). equilsteps : int, optional The number of timesteps at the beginning of the simulation to exclude when computing average values (default is 20000). randomseed : int or None, optional Random number seed used by LAMMPS in creating velocities and with the Langevin thermostat. (Default is None which will select a random int between 1 and 900000000.) Returns ------- dict Dictionary of results consisting of keys: - **'relaxed_system'** (*float*) - The relaxed system. - **'E_coh'** (*float*) - The mean measured cohesive energy. - **'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. - **'temp'** (*float*) - The mean measured temperature. - **'E_coh_std'** (*float*) - The standard deviation in the measured cohesive energy values. - **'measured_pxx_std'** (*float*) - The standard deviation in the measured x tensile pressure of the relaxed system. - **'measured_pyy_std'** (*float*) - The standard deviation in the measured y tensile pressure of the relaxed system. - **'measured_pzz_std'** (*float*) - The standard deviation in the measured z tensile pressure of the relaxed system. - **'measured_pxy_std'** (*float*) - The standard deviation in the measured xy shear pressure of the relaxed system. - **'measured_pxz_std'** (*float*) - The standard deviation in the measured xz shear pressure of the relaxed system. - **'measured_pyz_std'** (*float*) - The standard deviation in the measured yz shear pressure of the relaxed system. - **'temp_std'** (*float*) - The standard deviation in the measured temperature values. """ # Get lammps units lammps_units = lmp.style.unit(potential.units) #Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Handle default values if dumpsteps is None: dumpsteps = runsteps # 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) integ_info = integrator_info(integrator=integrator, p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, p_xy=p_xy, p_xz=p_xz, p_yz=p_yz, temperature=temperature, randomseed=randomseed, units=potential.units) lammps_variables['integrator_info'] = integ_info lammps_variables['thermosteps'] = thermosteps lammps_variables['runsteps'] = runsteps lammps_variables['dumpsteps'] = dumpsteps # Set compute stress/atom based on LAMMPS version if lammps_date < datetime.date(2014, 2, 12): lammps_variables['stressterm'] = '' else: lammps_variables['stressterm'] = 'NULL' # 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 = 'full_relax.template' lammps_script = 'full_relax.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) # Extract LAMMPS thermo data. results = {} thermo = output.simulations[0]['thermo'] results['dumpfile_initial'] = '0.dump' results['symbols_initial'] = system.symbols # Load relaxed system from dump file last_dump_file = str(thermo.Step.values[-1])+'.dump' results['dumpfile_final'] = last_dump_file system = am.load('atom_dump', last_dump_file, symbols=system.symbols) results['symbols_final'] = system.symbols # Only consider values where Step >= equilsteps thermo = thermo[thermo.Step >= equilsteps] results['nsamples'] = len(thermo) # Get cohesive energy estimates natoms = system.natoms results['E_coh'] = uc.set_in_units(thermo.PotEng.mean() / natoms, lammps_units['energy']) results['E_coh_std'] = uc.set_in_units(thermo.PotEng.std() / natoms, lammps_units['energy']) results['lx'] = uc.set_in_units(thermo.Lx.mean(), lammps_units['length']) results['lx_std'] = uc.set_in_units(thermo.Lx.std(), lammps_units['length']) results['ly'] = uc.set_in_units(thermo.Ly.mean(), lammps_units['length']) results['ly_std'] = uc.set_in_units(thermo.Ly.std(), lammps_units['length']) results['lz'] = uc.set_in_units(thermo.Lz.mean(), lammps_units['length']) results['lz_std'] = uc.set_in_units(thermo.Lz.std(), lammps_units['length']) results['xy'] = uc.set_in_units(thermo.Xy.mean(), lammps_units['length']) results['xy_std'] = uc.set_in_units(thermo.Xy.std(), lammps_units['length']) results['xz'] = uc.set_in_units(thermo.Xz.mean(), lammps_units['length']) results['xz_std'] = uc.set_in_units(thermo.Xz.std(), lammps_units['length']) results['yz'] = uc.set_in_units(thermo.Yz.mean(), lammps_units['length']) results['yz_std'] = uc.set_in_units(thermo.Yz.std(), lammps_units['length']) results['measured_pxx'] = uc.set_in_units(thermo.Pxx.mean(), lammps_units['pressure']) results['measured_pxx_std'] = uc.set_in_units(thermo.Pxx.std(), lammps_units['pressure']) results['measured_pyy'] = uc.set_in_units(thermo.Pyy.mean(), lammps_units['pressure']) results['measured_pyy_std'] = uc.set_in_units(thermo.Pyy.std(), lammps_units['pressure']) results['measured_pzz'] = uc.set_in_units(thermo.Pzz.mean(), lammps_units['pressure']) results['measured_pzz_std'] = uc.set_in_units(thermo.Pzz.std(), lammps_units['pressure']) results['measured_pxy'] = uc.set_in_units(thermo.Pxy.mean(), lammps_units['pressure']) results['measured_pxy_std'] = uc.set_in_units(thermo.Pxy.std(), lammps_units['pressure']) results['measured_pxz'] = uc.set_in_units(thermo.Pxz.mean(), lammps_units['pressure']) results['measured_pxz_std'] = uc.set_in_units(thermo.Pxz.std(), lammps_units['pressure']) results['measured_pyz'] = uc.set_in_units(thermo.Pyz.mean(), lammps_units['pressure']) results['measured_pyz_std'] = uc.set_in_units(thermo.Pyz.std(), lammps_units['pressure']) results['temp'] = thermo.Temp.mean() results['temp_std'] = thermo.Temp.std() return results