def plot_force(self, force_unit=None, length_unit=None, ax=None, **kwargs): """ Creates a plot of the force to move along the path as a function of the arc coordinates. Parameters ---------- force_unit : str or None, optional If given, the force values will be converted from atomman's working units to the specified units. Default value of None will do no conversions, which is useful if the energyfxn is not reporting in atomman's working units. length_unit : str or None, optional If given, the arc coordinates will be converted from atomman's working units to the specified units. Default value of None will do no conversions, which is useful if the coords are not given in atomman's working units. ax : matplotlib.pyplot.axis A pre-existing plotting axis. Allows for more control and the use of subplots. **kwargs : any, optional All additional keyword arguments will be passed to matplotlib.pyplot.figure(). Returns ------- matplotlib.pyplot.figure The generated figure allowing for further modifications. Returned if ax is None. """ if ax is None: fig = plt.figure(**kwargs) ax = fig.add_subplot(111) returnfig = True else: assert len(kwargs) == 0, 'ax and extra kwargs cannot both be given' returnfig = False # Handle force units force = self.force if force_unit is None: ax.set_ylabel('Force', size='x-large') else: ax.set_ylabel(f'Force ({force_unit})', size='x-large') force = uc.get_in_units(force, force_unit) # Handle length unit arccoord = self.arccoord if length_unit is None: ax.set_xlabel('Arc coordinate', size='x-large') else: ax.set_xlabel(f'Arc coordinate ({length_unit})', size='x-large') arccoord = uc.get_in_units(arccoord, length_unit) ax.set_xlim(0, arccoord[-1]) ax.plot(arccoord, force, lw=3) if returnfig: return fig
def interpolate_contour(system, property, index=None, plotbounds=None, bins=200, dots=True, czero=True, save=False, show=True, length_unit='angstrom', property_unit=None, cmap='jet'): """Creates a contour plot of a system's per-atom properties by interpolating between all of the atoms.""" if save is False and show is False: print 'Figure not saved or displayed!' values = system.atoms_prop(key=property) assert values is not None, 'atomic property not found!' name = str(property) if index is not None: if isinstance(index, (int, long)): index = [index] else: index = list(index) for i in index: name += '[' + str(i+1) + ']' values = values[[Ellipsis] + index] assert values.shape == (system.natoms, ), 'Identified property[index] must be a per-atom scalar' if plotbounds is None: plotbounds = np.array([[system.box.xlo, system.box.xhi], [system.box.ylo, system.box.yhi], [system.box.zlo, system.box.zhi]]) plotbounds = uc.get_in_units(plotbounds, length_unit) #Build arrays containing atomic information pos = uc.get_in_units(system.atoms_prop(key='pos'), length_unit) #identify only atoms in plotbounds in_bounds = ((pos[:,0] > plotbounds[0,0] - 5.) & (pos[:,0] < plotbounds[0,1] + 5.) & (pos[:,1] > plotbounds[1,0] - 5.) & (pos[:,1] < plotbounds[1,1] + 5.) & (pos[:,2] > plotbounds[2,0]) & (pos[:,2] < plotbounds[2,1])) x = pos[in_bounds, 0] y = pos[in_bounds, 1] v = values[in_bounds] box = plotbounds[:2,:2] grid, xedges, yedges = grid_interpolate_2d(x, y, v, bins=bins, range=box) intsum = np.sum(grid) avsum = intsum / (bins-1) / (bins-1) intsum = intsum * (plotbounds[0,1]-plotbounds[0,0]) / (bins-1) * (plotbounds[1,1]-plotbounds[1,0]) / (bins-1) fig = prettygrid(grid, xedges, yedges, cmap=cmap, propname=name, czero=czero) if dots: adddots(x, y, xedges, yedges) if save: plt.savefig(name + '.png', dpi=800) if show==False: plt.close(fig) plt.show() return intsum, avsum
def lammps_ELASTIC(lammps_command, system, potential, symbols, mpi_command=None, strainrange=1e-6, etol=0.0, ftol=0.0, maxiter=100, maxeval=1000, dmax=0.01, pressure_unit='GPa'): """Sets up and runs the ELASTIC example distributed with LAMMPS""" #Get lammps units lammps_units = lmp.style.unit(potential.units) #Define lammps variables lammps_variables = {} lammps_variables['atomman_system_info'] = lmp.atom_data.dump(system, 'init.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_pair_info'] = potential.pair_info(symbols) lammps_variables['strainrange'] = strainrange lammps_variables['pressure_unit_scaling'] = uc.get_in_units(uc.set_in_units(1.0, lammps_units['pressure']), pressure_unit) lammps_variables['pressure_unit'] = pressure_unit 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 mod.template files with open('init.mod.template') as template_file: template = template_file.read() with open('init.mod', 'w') as in_file: in_file.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) with open('potential.mod.template') as template_file: template = template_file.read() with open('potential.mod', 'w') as in_file: in_file.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) output = lmp.run(lammps_command, 'in.elastic', mpi_command) #Extract output values relaxed = output['LAMMPS-log-thermo-data']['simulation'][0] results = {} results['E_coh'] = uc.set_in_units(relaxed['thermo']['PotEng'][-1], lammps_units['energy']) / system.natoms results['lx'] = uc.set_in_units(relaxed['thermo']['Lx'][-1], lammps_units['length']) results['ly'] = uc.set_in_units(relaxed['thermo']['Ly'][-1], lammps_units['length']) results['lz'] = uc.set_in_units(relaxed['thermo']['Lz'][-1], lammps_units['length']) with open('log.lammps') as log_file: log = log_file.read() start = log.find('print "Elastic Constant C11all = ${C11all} ${cunits}"') lines = log[start+54:].split('\n') for line in lines: terms = line.split() if len(terms) > 0 and terms[0] == 'Elastic': c_term = terms[2][:3] c_value = terms[4] results['c_unit'] = terms[5] results[c_term] = c_value return results
def disregistry_plot(self, x=None, disregistry=None, figsize=None, length_unit='Å'): """ Creates a simple matplotlib figure showing the disregistry profiles. Parameters ---------- x : numpy.ndarray, optional x-coordinates. Default value is the stored x-coordinates. disregistry : numpy.ndarray, optional (N, 3) shaped array of disregistry vectors at each x-coordinate. Default value is the stored disregistry values. figsize : tuple, optional matplotlib figure figsize parameter. Default value is (10, 6). length_unit : str, optional The unit of length to display positions and disregistry values in. Default value is 'Å'. Returns ------- matplotlib.pyplot.figure The generated figure allowing users to perform additional modifications. """ hasvals = True # Default values are class properties if x is None: try: x = self.x except: hasvals = False if disregistry is None: try: disregistry = self.disregistry except: hasvals = False if hasvals: if figsize is None: figsize = (10, 6) fig = plt.figure(figsize=figsize) x = uc.get_in_units(x, length_unit) disregistry = uc.get_in_units(disregistry, length_unit) plt.plot(x, disregistry[:, 0], label='edge disregistry') plt.plot(x, disregistry[:, 1], label='normal disregistry') plt.plot(x, disregistry[:, 2], label='screw disregistry') plt.legend(fontsize='xx-large') plt.xlabel(f'x (${length_unit}$)', size='xx-large') plt.ylabel(f'disregistry (${length_unit}$', size='xx-large') return fig else: print('x and disregistry must be set/given to plot')
def fcc_edge(self): axes = np.array([[1, 0, -1], [1, 1, 1], [1, -2, 1]]) alat = uc.set_in_units(4.0248, 'angstrom') C11 = uc.set_in_units(113.76, 'GPa') C12 = uc.set_in_units(61.71, 'GPa') C44 = uc.set_in_units(31.25, 'GPa') c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat / 2 * np.array([1., 0., -1.]) # initializing a new Stroh object using the data stroh = am.defect.Stroh(c, burgers, axes=axes) pos_test = uc.set_in_units(np.array([12.4, 13.5, -10.6]), 'angstrom') disp = stroh.displacement(pos_test) print("displacement =", uc.get_in_units(disp, 'angstrom'), 'angstrom') # monopole system box = am.Box(a=alat, b=alat, c=alat) atoms = am.Atoms(natoms=4, prop={ 'atype': 1, 'pos': [[0.0, 0.0, 0.0], [0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]] }) ucell = am.System(atoms=atoms, box=box, scale=True) system = am.rotate_cubic(ucell, axes) shift = np.array( [0.12500000000000, 0.50000000000000, 0.00000000000000]) new_pos = system.atoms_prop(key='pos', scale=True) + shift system.atoms_prop(key='pos', value=new_pos, scale=True) system.supersize((-7, 7), (-6, 6), (0, 1)) disp = stroh.displacement(system.atoms_prop(key='pos')) system.atoms_prop(key='pos', value=system.atoms_prop(key='pos') + disp) system.pbc = (False, False, True) system.wrap() pos = system.atoms_prop(key='pos') x = uc.get_in_units(pos[:, 0], 'angstrom') y = uc.get_in_units(pos[:, 1], 'angstrom') plt.figure(figsize=(8, 8)) plt.scatter(x, y, s=30) plt.xlim(min(x), max(x)) plt.ylim(min(y), max(y)) plt.xlabel('x-position (Angstrom)', fontsize='large') plt.ylabel('y-position (Angstrom)', fontsize='large') plt.show()
def data_model(input_dict, results_dict=None): """Creates a DataModelDict containing the input and results data""" #Create the root of the DataModelDict output = DM() output['calculation-bain-transformation'] = calc = DM() #Assign uuid calc['calculation'] = DM() calc['calculation']['id'] = input_dict['uuid'] calc['calculation']['script'] = __calc_name__ calc['calculation']['run-parameter'] = run_params = DM() run_params['size-multipliers'] = DM() run_params['size-multipliers']['a'] = list(input_dict['size_mults'][0]) run_params['size-multipliers']['b'] = list(input_dict['size_mults'][1]) run_params['size-multipliers']['c'] = list(input_dict['size_mults'][2]) run_params['energy_tolerance'] = input_dict['energy_tolerance'] run_params['force_tolerance'] = input_dict['force_tolerance'] run_params['maximum_iterations'] = input_dict['maximum_iterations'] run_params['maximum_evaluations'] = input_dict['maximum_evaluations'] #Copy over potential data model info calc['potential'] = input_dict['potential']['LAMMPS-potential'][ 'potential'] #Save info on system file loaded system_load = input_dict['load'].split(' ') calc['system-info'] = DM() calc['system-info']['artifact'] = DM() calc['system-info']['artifact']['file'] = os.path.basename(' '.join( system_load[1:])) calc['system-info']['artifact']['format'] = system_load[0] calc['system-info']['artifact']['family'] = input_dict['system_family'] calc['system-info']['symbols'] = input_dict['symbols'] calc['bain-a-scale'] = input_dict['bain_a_scale'] calc['bain-c-scale'] = input_dict['bain_c_scale'] if results_dict is None: calc['status'] = 'not calculated' else: #print results_dict #Save the cohesive energy for the initial system calc['initial'] = DM([('value', uc.get_in_units(results_dict['initial'], input_dict['energy_unit'])), ('unit', input_dict['energy_unit'])]) #Save the cohesive energy for the bain shifted system calc['bain'] = DM([('value', uc.get_in_units(results_dict['bain'], input_dict['energy_unit'])), ('unit', input_dict['energy_unit'])]) return output
def gb_energy(lammps_command, potential, symbols, alat, axes_1, axes_2, E_coh, mpi_command=None, xshift=0.0, zshift=0.0): """Computes the grain boundary energy using the grain_boundary.in LAMMPS script""" axes_1 = np.asarray(axes_1, dtype=int) axes_2 = np.asarray(axes_2, dtype=int) lx = alat * np.linalg.norm(axes_1[0]) lz = 2 * alat * np.linalg.norm(axes_1[2]) mesh_dir = 'mesh-%.8f-%.8f' %(xshift, zshift) if not os.path.isdir(mesh_dir): os.makedirs(mesh_dir) #Get lammps units lammps_units = lmp.style.unit(potential.units) #Define lammps variables lammps_variables = {} lammps_variables['units'] = potential.units lammps_variables['atom_style'] = potential.atom_style lammps_variables['atomman_pair_info'] = potential.pair_info(symbols) lammps_variables['alat'] = uc.get_in_units(alat, lammps_units['length']) lammps_variables['xsize'] = uc.get_in_units(lx, lammps_units['length']) lammps_variables['zsize'] = uc.get_in_units(lz, lammps_units['length']) lammps_variables['xshift'] = uc.get_in_units(xshift, lammps_units['length']) lammps_variables['zshift'] = uc.get_in_units(zshift, lammps_units['length']) lammps_variables['x_axis_1'] = str(axes_1[0]).strip('[] ') lammps_variables['y_axis_1'] = str(axes_1[1]).strip('[] ') lammps_variables['z_axis_1'] = str(axes_1[2]).strip('[] ') lammps_variables['x_axis_2'] = str(axes_2[0]).strip('[] ') lammps_variables['y_axis_2'] = str(axes_2[1]).strip('[] ') lammps_variables['z_axis_2'] = str(axes_2[2]).strip('[] ') lammps_variables['mesh_dir'] = 'mesh-%.8f-%.8f' %(xshift, zshift) #Fill in mod.template files with open('grain_boundary.template') as template_file: template = template_file.read() lammps_input = os.path.join(mesh_dir, 'grain_boundary.in') with open(lammps_input, 'w') as in_file: in_file.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) output = lmp.run(lammps_command, lammps_input, mpi_command) #Extract output values try: E_total = uc.set_in_units(output.finds('c_eatoms')[-1], lammps_units['energy']) natoms = output.finds('v_natoms')[-1] except: E_total = uc.set_in_units(output.finds('eatoms')[-1], lammps_units['energy']) natoms = output.finds('natoms')[-1] #Compute grain boundary energy E_gb = (E_total - E_coh*natoms) / (lx * lz) return E_gb
def box_content(system, units, float_format): """ Generates the data file lines associated with box dimensions. Parameters ---------- system : atomman.System units : str float_format : str """ # Get unit information according to the units style units_dict = style.unit(units) length_unit = units_dict['length'] # Define line format strings xf2 = float_format + ' ' + float_format xf3 = float_format + ' ' + float_format + ' ' + float_format # Extract and convert box values xlo = uc.get_in_units(system.box.xlo, length_unit) xhi = uc.get_in_units(system.box.xhi, length_unit) ylo = uc.get_in_units(system.box.ylo, length_unit) yhi = uc.get_in_units(system.box.yhi, length_unit) zlo = uc.get_in_units(system.box.zlo, length_unit) zhi = uc.get_in_units(system.box.zhi, length_unit) xy = uc.get_in_units(system.box.xy, length_unit) xz = uc.get_in_units(system.box.xz, length_unit) yz = uc.get_in_units(system.box.yz, length_unit) # Write box values content = '' content += xf2 % (xlo, xhi) + ' xlo xhi\n' content += xf2 % (ylo, yhi) + ' ylo yhi\n' content += xf2 % (zlo, zhi) + ' zlo zhi\n' if xy != 0.0 or xz != 0.0 or yz != 0.0: content += xf3 % (xy, xz, yz) + ' xy xz yz\n' return content
def test_newton(self): newton = uc.set_in_units(1e5, 'dyn') assert pytest.approx(uc.get_in_units(newton, 'kg*m/s^2'), 1.0)
def data_model(input_dict, results_dict=None): """Creates a DataModelDict containing the input and results data""" #Create the root of the DataModelDict output = DM() output['calculation-dynamic-elastic'] = calc = DM() #Assign uuid calc['key'] = input_dict['calc_key'] calc['calculation'] = DM() calc['calculation']['script'] = __calc_name__ calc['calculation']['run-parameter'] = run_params = DM() run_params['size-multipliers'] = DM() run_params['size-multipliers']['a'] = list(input_dict['sizemults'][0]) run_params['size-multipliers']['b'] = list(input_dict['sizemults'][1]) run_params['size-multipliers']['c'] = list(input_dict['sizemults'][2]) run_params['load_options'] = input_dict['load_options'] run_params['thermosteps'] = input_dict['thermosteps'] run_params['runsteps'] = input_dict['runsteps'] run_params['randomseed'] = input_dict['randomseed'] run_params['integrator'] = input_dict['integrator'] #Copy over potential data model info calc['potential'] = DM() calc['potential']['key'] = input_dict['potential'].key calc['potential']['id'] = input_dict['potential'].id #Save info on system file loaded system_load = input_dict['load'].split(' ') calc['system-info'] = DM() calc['system-info']['artifact'] = DM() calc['system-info']['artifact']['file'] = os.path.basename(' '.join(system_load[1:])) calc['system-info']['artifact']['format'] = system_load[0] calc['system-info']['artifact']['family'] = input_dict['system_family'] calc['system-info']['symbols'] = input_dict['symbols'] #Save phase-state info calc['phase-state'] = DM() calc['phase-state']['temperature'] = DM() calc['phase-state']['temperature']['value'] = input_dict['temperature'] calc['phase-state']['temperature']['unit'] = 'K' calc['phase-state']['pressure-xx'] = DM() calc['phase-state']['pressure-xx']['value'] = uc.get_in_units(input_dict['pressure_xx'], input_dict['pressure_unit']) calc['phase-state']['pressure-xx']['unit'] = input_dict['pressure_unit'] calc['phase-state']['pressure-yy'] = DM() calc['phase-state']['pressure-yy']['value'] = uc.get_in_units(input_dict['pressure_yy'], input_dict['pressure_unit']) calc['phase-state']['pressure-yy']['unit'] = input_dict['pressure_unit'] calc['phase-state']['pressure-zz'] = DM() calc['phase-state']['pressure-zz']['value'] = uc.get_in_units(input_dict['pressure_zz'], input_dict['pressure_unit']) calc['phase-state']['pressure-zz']['unit'] = input_dict['pressure_unit'] if results_dict is None: calc['status'] = 'not calculated' else: calc['equilibrium-averages'] = avgs = DM() avgs['temperature'] = DM() avgs['temperature']['value'] = results_dict['temp'][0] avgs['temperature']['error'] = results_dict['temp'][1] avgs['temperature']['unit'] = 'K' avgs['pressure-xx'] = DM() avgs['pressure-xx']['value'] = uc.get_in_units(results_dict['pxx'][0], input_dict['pressure_unit']) avgs['pressure-xx']['error'] = uc.get_in_units(results_dict['pxx'][1], input_dict['pressure_unit']) avgs['pressure-xx']['unit'] = input_dict['pressure_unit'] avgs['pressure-yy'] = DM() avgs['pressure-yy']['value'] = uc.get_in_units(results_dict['pyy'][0], input_dict['pressure_unit']) avgs['pressure-yy']['error'] = uc.get_in_units(results_dict['pyy'][1], input_dict['pressure_unit']) avgs['pressure-yy']['unit'] = input_dict['pressure_unit'] avgs['pressure-zz'] = DM() avgs['pressure-zz']['value'] = uc.get_in_units(results_dict['pzz'][0], input_dict['pressure_unit']) avgs['pressure-zz']['error'] = uc.get_in_units(results_dict['pzz'][1], input_dict['pressure_unit']) avgs['pressure-zz']['unit'] = input_dict['pressure_unit'] avgs['cohesive-energy'] = DM() avgs['cohesive-energy']['value'] = uc.get_in_units(results_dict['E_coh'][0], input_dict['energy_unit']) avgs['cohesive-energy']['error'] = uc.get_in_units(results_dict['E_coh'][1], input_dict['energy_unit']) avgs['cohesive-energy']['unit'] = input_dict['energy_unit'] avgs['a'] = DM() avgs['a']['value'] = uc.get_in_units(results_dict['a'][0], input_dict['length_unit']) avgs['a']['error'] = uc.get_in_units(results_dict['a'][1], input_dict['length_unit']) avgs['a']['unit'] = input_dict['length_unit'] avgs['b'] = DM() avgs['b']['value'] = uc.get_in_units(results_dict['b'][0], input_dict['length_unit']) avgs['b']['error'] = uc.get_in_units(results_dict['b'][1], input_dict['length_unit']) avgs['b']['unit'] = input_dict['length_unit'] avgs['c'] = DM() avgs['c']['value'] = uc.get_in_units(results_dict['c'][0], input_dict['length_unit']) avgs['c']['error'] = uc.get_in_units(results_dict['c'][1], input_dict['length_unit']) avgs['c']['unit'] = input_dict['length_unit'] return output
def integrator_info(integrator: 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, velocity_temperature: Optional[float] = None, randomseed: Optional[int] = None, units: str = 'metal', lammps_date=None) -> str: """ Generates LAMMPS commands for velocity creation and fix integrators. Parameters ---------- 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.) 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). temperature : float, optional The temperature to relax at (default is 0.0). velocity_temperature : float or None, optional The temperature to use for generating initial atomic velocities with integrators containing thermostats. If None (default) then it will be set to 2 * temperature + 1. If 0.0 then the velocities will not be (re)set. 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.) units : str, optional The LAMMPS units style to use (default is 'metal'). Returns ------- str The generated LAMMPS input lines for velocity create and fix integration commands. """ # Get lammps units lammps_units = lmp.style.unit(units) # Convert pressures to lammps units p_xx = uc.get_in_units(p_xx, lammps_units['pressure']) p_yy = uc.get_in_units(p_yy, lammps_units['pressure']) p_zz = uc.get_in_units(p_zz, lammps_units['pressure']) p_xy = uc.get_in_units(p_xy, lammps_units['pressure']) p_xz = uc.get_in_units(p_xz, lammps_units['pressure']) p_yz = uc.get_in_units(p_yz, lammps_units['pressure']) # Check temperature and set default integrator if temperature == 0.0: if integrator is None: integrator = 'nph+l' assert integrator not in ['npt', 'nvt'], 'npt and nvt cannot run at 0 K' elif temperature > 0: if integrator is None: integrator = 'npt' else: raise ValueError('Temperature must be positive') # Set dampening parameters based on timestep values temperature_damp = 100 * lmp.style.timestep(units) pressure_damp = 1000 * lmp.style.timestep(units) # Set default randomseed if randomseed is None: randomseed = random.randint(1, 900000000) # Set default velocity_temperature if velocity_temperature is None: velocity_temperature = 2.0 * temperature + 1 # Build info_lines info_lines = [] if integrator == 'npt': if velocity_temperature > 0.0: info_lines.append( f'velocity all create {velocity_temperature} {randomseed}') info_lines.extend([ f'fix npt all npt temp {temperature} {temperature} {temperature_damp} &', f' x {p_xx} {p_xx} {pressure_damp} &', f' y {p_yy} {p_yy} {pressure_damp} &', f' z {p_zz} {p_zz} {pressure_damp} &', f' xy {p_xy} {p_xy} {pressure_damp} &', f' xz {p_xz} {p_xz} {pressure_damp} &', f' yz {p_yz} {p_yz} {pressure_damp}' ]) elif integrator == 'nvt': if velocity_temperature > 0.0: info_lines.append( f'velocity all create {velocity_temperature} {randomseed}') info_lines.extend([ f'fix nvt all nvt temp {temperature} {temperature} {temperature_damp}' ]) elif integrator == 'nph': info_lines.extend([ f'fix nph all nph x {p_xx} {p_xx} {pressure_damp} &', f' y {p_yy} {p_yy} {pressure_damp} &', f' z {p_zz} {p_zz} {pressure_damp} &', f' xy {p_xy} {p_xy} {pressure_damp} &', f' xz {p_xz} {p_xz} {pressure_damp} &', f' yz {p_yz} {p_yz} {pressure_damp}' ]) elif integrator == 'nve': info_lines.extend(['fix nve all nve']) elif integrator == 'nve+l': if velocity_temperature > 0.0: info_lines.append( f'velocity all create {velocity_temperature} {randomseed}') info_lines.extend([ 'fix nve all nve', f'fix langevin all langevin {temperature} {temperature} {temperature_damp} {randomseed}' ]) elif integrator == 'nph+l': info_lines.extend([ f'fix nph all nph x {p_xx} {p_xx} {pressure_damp} &', f' y {p_yy} {p_yy} {pressure_damp} &', f' z {p_zz} {p_zz} {pressure_damp} &', f' xy {p_xy} {p_xy} {pressure_damp} &', f' xz {p_xz} {p_xz} {pressure_damp} &', f' yz {p_yz} {p_yz} {pressure_damp}', ]) # Add ptemp if LAMMPS is newer than June 2020 and temperature is zero if np.isclose(temperature, 0.0) and lammps_date >= datetime.date(2020, 6, 9): info_lines[-1] += ' &' info_lines.extend([' ptemp 1.0']) info_lines.extend([ f'fix langevin all langevin {temperature} {temperature} {temperature_damp} {randomseed}' ]) else: raise ValueError('Invalid integrator style') return '\n'.join(info_lines)
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 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 check_energies(self, x=None, disregistry=None, energyperlength_unit='eV/Å'): """ Prints a summary string of all computed energy components. Parameters ---------- x : numpy.ndarray, optional x-coordinates. Default value is the stored x-coordinates. disregistry : numpy.ndarray, optional (N, 3) shaped array of disregistry vectors at each x-coordinate. Default value is the stored disregistry values. energyperlength_unit : str, optional The units of energy per length to report the dislocation line energies in. Default value is 'eV/Å'. """ hasvals = True # Default values are class properties if x is None: try: x = self.x except: hasvals = False if disregistry is None: try: disregistry = self.disregistry except: hasvals = False if hasvals: print(f'Dislocation energy terms in {energyperlength_unit}:') print( 'Misfit energy = ', uc.get_in_units(self.misfit_energy(x, disregistry), energyperlength_unit)) print( 'Elastic energy = ', uc.get_in_units(self.elastic_energy(x, disregistry), energyperlength_unit)) print( 'Long-range energy =', uc.get_in_units(self.longrange_energy(), energyperlength_unit)) print( 'Stress energy = ', uc.get_in_units(self.stress_energy(x, disregistry), energyperlength_unit)) print( 'Surface energy = ', uc.get_in_units(self.surface_energy(x, disregistry), energyperlength_unit)) print( 'Nonlocal energy = ', uc.get_in_units(self.nonlocal_energy(x, disregistry), energyperlength_unit)) print( 'Total energy = ', uc.get_in_units(self.total_energy(x, disregistry), energyperlength_unit)) else: print('x and disregistry must be set/given to check energies')
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 table_dump(system, f=None, prop_info=None, float_format ='%.13f'): """ Converts a system's atoms' values to a string table. Modified from table.dump to handle alternate pos fields that dump files can use. Parameters ---------- system : atomman.System An atomman representation of a system. f : str or file-like object, optional File path or file-like object to write the content to. If not given, then the content is returned as a str. prop_info : list of dict, optional Structured form of property conversion parameters, in which each dictionary in the list corresponds to a single atoms property. Each dictionary must have a 'prop_name' field, and can optionally have 'table_name', 'shape', 'unit', and 'dtype' fields. float_format : str, optional c-style formatting string for floating point values. Default value is '%.13f'. Returns ------- str The generated data table. Only returned if fp is None. """ # Set parameters natoms = system.natoms key_rename = OrderedDict() # Build list of properties to scale and alternate pos scale = [] altpos = [] for prop in prop_info: if prop['prop_name'] in ['spos', 'upos', 'supos']: altpos.append(prop['prop_name']) if prop['unit'] == 'scaled': scale.append(prop['prop_name']) prop['unit'] = None # Transform to dataframe df = system.atoms_df(scale) # Check atom_id values if 'atom_id' not in df: df['atom_id'] = range(1, natoms+1) assert len(df.atom_id) == len(set(df.atom_id)), 'atom_id is not unique for all atoms' # Add alternate pos terms if 'upos' in altpos: df['upos[0]'] = df['pos[0]'] df['upos[1]'] = df['pos[1]'] df['upos[2]'] = df['pos[2]'] if 'spos' in altpos or 'supos' in altpos: spos = system.atoms_prop(key='pos', scale=True) if 'spos' in altpos: df['spos[0]'] = spos[:,0] df['spos[1]'] = spos[:,1] df['spos[2]'] = spos[:,2] if 'supos' in altpos: df['supos[0]'] = spos[:,0] df['supos[1]'] = spos[:,1] df['supos[2]'] = spos[:,2] # Loop over all properties for prop in prop_info: pname = prop['prop_name'] # loop over all table names and property indexes for tname, (index, istr) in zip(prop['table_name'], indexstr(prop['shape'])): # Build name change dict key_rename[pname + istr] = tname # Convert units if needed if prop['unit'] is not None: df[pname + istr] = uc.get_in_units(df[pname + istr], prop['unit']) # Rename and reorganize df = df.rename(columns=key_rename)[list(key_rename.values())] # Generate table sep = ' ' if ispython2: sep = sep.encode('utf-8') return df.to_csv(path_or_buf=f, sep=sep, index=None, header=False, float_format=float_format)
def dump(system, f=None, lammps_units='metal', scale=False, prop_name=None, table_name=None, shape=None, unit=None, dtype=None, prop_info=None, float_format ='%.13f', return_prop_info=False): """ Write a LAMMPS-style atom data file from a System. Parameters ---------- system : atomman.System The system to write to the atom data file. f : str or file-like object, optional File path or file-like object to write the content to. If not given, then the content is returned as a str. lammps_units : str, optional The LAMMPS units option associated with the table values. This is used for the box dimensions and default units for standard dump properties (not compute/fix definitions). scale : bool, optional Flag indicating if atom positions are to be scaled relative to the box (True) or given in absolute Cartesian values (False, default). prop_name : list, optional The Atoms properties to include. If neither prop_name or prop_info are given, all system properties will be included. table_name : list, optional The dump table column name(s) that correspond to each prop_name. If not given, the table_name values will be based on the prop_name and shape values. shape : list, optional The shape of each per-atom property. If not given, will be inferred from the length of each table_name value. unit : list, optional Lists the units for each prop_name as stored in the table. For a value of None, no conversion will be performed for that property. For a value of 'scaled', the corresponding table values will be taken in box-scaled units. If not given, unit values will be taken based on lammps_units if prop_name corresponds to a standard LAMMPS property, otherwise will be set to None (no conversion). dtype : list, optional Allows for the data type of each property to be explicitly given. Values of None will infer the data type from the corresponding property values. If not given, all values will be None. prop_info : list of dict, optional Structured form of property conversion parameters, in which each dictionary in the list corresponds to a single atoms property. Each dictionary must have a 'prop_name' field, and can optionally have 'table_name', 'shape', 'unit', and 'dtype' fields. float_format : str, optional c-style formatting string for floating point values. Default value is '%.13f'. return_prop_info : bool, optional Flag indicating if the filled-in prop_info is to be returned. Having this allows for 1:1 load/dump conversions. Default value is False (prop_info is not returned). Returns ------- content : str The generated atom_data content. Only returned if f is None. prop_info : list of dict The filled-in prop_info structure. Only returned if return_prop_info is True. """ lammps_unit = style.unit(lammps_units) # Set default values if prop_info is None: if prop_name is None: atoms_props = system.atoms_prop() try: atoms_props.pop(atoms_props.index('atom_id')) except: pass prop_name = ['atom_id'] + atoms_props if shape is None and table_name is None: shape = [] for name in prop_name: if name == 'atom_id': shape.append(()) elif name in ['spos', 'upos', 'supos']: shape.append((3,)) else: shape.append(system.atoms.view[name].shape[1:]) # Process conversion parameters prop_info = process_prop_info(prop_name=prop_name, table_name=table_name, shape=shape, unit=unit, dtype=dtype, prop_info=prop_info, lammps_units=lammps_units) # Write timestep info content = 'ITEM: TIMESTEP\n' try: content += '%i\n' % system.timestep except: content += '0\n' # Write number of atoms content += 'ITEM: NUMBER OF ATOMS\n' content += '%i\n' % (system.natoms) # Extract and convert box values xlo = uc.get_in_units(system.box.xlo, lammps_unit['length']) xhi = uc.get_in_units(system.box.xhi, lammps_unit['length']) ylo = uc.get_in_units(system.box.ylo, lammps_unit['length']) yhi = uc.get_in_units(system.box.yhi, lammps_unit['length']) zlo = uc.get_in_units(system.box.zlo, lammps_unit['length']) zhi = uc.get_in_units(system.box.zhi, lammps_unit['length']) xy = uc.get_in_units(system.box.xy, lammps_unit['length']) xz = uc.get_in_units(system.box.xz, lammps_unit['length']) yz = uc.get_in_units(system.box.yz, lammps_unit['length']) # Compute absolute box bounds xlo_bound = xlo + min((0.0, xy, xz, xy + xz)) xhi_bound = xhi + max((0.0, xy, xz, xy + xz)) ylo_bound = ylo + min((0.0, yz)) yhi_bound = yhi + max((0.0, yz)) zlo_bound = zlo zhi_bound = zhi is_orthogonal = (xy == 0.0 and xz == 0.0 and yz == 0.0) # Write system boundary info if is_orthogonal: content += 'ITEM: BOX BOUNDS' else: content += 'ITEM: BOX BOUNDS xy xz yz' # Write pbc info for i in range(3): if system.pbc[i]: content += ' pp' else: content += ' fm' content += '\n' # Write system boundary info if is_orthogonal: xf2 = float_format + ' ' + float_format + '\n' content += xf2 % (xlo_bound, xhi_bound) content += xf2 % (ylo_bound, yhi_bound) content += xf2 % (zlo_bound, zhi_bound) else: xf3 = float_format + ' ' + float_format + ' ' + float_format + '\n' content += xf3 % (xlo_bound, xhi_bound, xy) content += xf3 % (ylo_bound, yhi_bound, xz) content += xf3 % (zlo_bound, zhi_bound, yz) # Write atomic header info and prepare outarray for writing header = 'ITEM: ATOMS' for prop in prop_info: header += ' ' + ' '.join(prop['table_name']) header += '\n' content += header content += table_dump(system, prop_info=prop_info, float_format=float_format) returns = [] # Save to the file-like object if hasattr(f, 'write'): f.write(content) # Save to the file name elif f is not None: with open(f, 'w') as fp: fp.write(content) # Return as a string else: returns.append(content) if return_prop_info is True: returns.append(prop_info) if len(returns) == 1: return returns[0] elif len(returns) > 1: return tuple(returns)
def dump(system, fname, prop_info=None, xf='%.13e'): """ Write a LAMMPS-style dump file from a System. Arguments: system -- System to write to the dump file. fname -- name (and location) of file to save data to. Keyword Arguments: prop_info -- DataModelDict for relating the per-atom properties to/from the dump file and the System. Will create a default json instance <fname>.json if prop_info is not given and <fname>.json doesn't already exist. xf -- c-style format for printing the floating point numbers. Default is '%.13e'. """ #create or read prop_info Data Model if prop_info is None: try: with open(fname+'.json') as fj: prop_info = DataModelDict(fj) except: prop_info = __prop_info_default_dump(system) with open(fname+'.json', 'w') as fj: prop_info.json(fp=fj, indent=4) else: if os.path.isfile(prop_info): with open(prop_info) as f: prop_info = f.read() prop_info = DataModelDict(prop_info) #read box_unit if specified in prop_info prop_info = prop_info.find('LAMMPS-dump-atoms_prop-relate') box_unit = prop_info['box_prop'].get('unit', None) #open fname with open(fname, 'w') as f: #write timestep info f.write('ITEM: TIMESTEP\n') try: f.write('%i\n'%system.prop['timestep']) except: f.write('0\n') #write number of atoms f.write('ITEM: NUMBER OF ATOMS\n') f.write('%i\n' % ( system.natoms )) #write system boundary info for an orthogonal box if system.box.xy == 0.0 and system.box.xz == 0.0 and system.box.yz == 0.0: f.write('ITEM: BOX BOUNDS') for i in xrange(3): if system.pbc[i]: f.write(' pp') else: f.write(' fm') f.write('\n') f.write('%f %f\n' % ( uc.get_in_units(system.box.xlo, box_unit), uc.get_in_units(system.box.xhi, box_unit) )) f.write('%f %f\n' % ( uc.get_in_units(system.box.ylo, box_unit), uc.get_in_units(system.box.yhi, box_unit) )) f.write('%f %f\n' % ( uc.get_in_units(system.box.zlo, box_unit), uc.get_in_units(system.box.zhi, box_unit) )) #write system boundary info for a triclinic box else: f.write('ITEM: BOX BOUNDS xy xz yz') for i in xrange(3): if system.pbc[i]: f.write(' pp') else: f.write(' fm') f.write('\n') xlo_bound = uc.get_in_units(system.box.xlo, box_unit) + uc.get_in_units(min(( 0.0, system.box.xy, system.box.xz, system.box.xy + system.box.xz)), box_unit) xhi_bound = uc.get_in_units(system.box.xhi, box_unit) + uc.get_in_units(max(( 0.0, system.box.xy, system.box.xz, system.box.xy + system.box.xz)), box_unit) ylo_bound = uc.get_in_units(system.box.ylo, box_unit) + uc.get_in_units(min(( 0.0, system.box.yz )), box_unit) yhi_bound = uc.get_in_units(system.box.yhi, box_unit) + uc.get_in_units(max(( 0.0, system.box.yz )), box_unit) zlo_bound = uc.get_in_units(system.box.zlo, box_unit) zhi_bound = uc.get_in_units(system.box.zhi, box_unit) f.write('%f %f %f\n' % ( xlo_bound, xhi_bound, uc.get_in_units(system.box.xy, box_unit) )) f.write('%f %f %f\n' % ( ylo_bound, yhi_bound, uc.get_in_units(system.box.xz, box_unit) )) f.write('%f %f %f\n' % ( zlo_bound, zhi_bound, uc.get_in_units(system.box.yz, box_unit) )) #write atomic header info and prepare outarray for writing header = 'ITEM: ATOMS id' print_string = '%i' outarray = np.empty((system.natoms, len(prop_info['LAMMPS-attribute']))) start = 0 for attr, a_keys in prop_info['LAMMPS-attribute'].iteritems(): #get first prop relation for attr relation = a_keys.aslist('relation')[0] prop = relation.get('prop') index = (Ellipsis, ) + tuple(relation.aslist('index')) unit = relation.get('unit', None) if unit == 'scaled': unit = None scale = True else: scale = False #pass values to outarray outarray[:,start] = uc.get_in_units(system.atoms_prop(key=prop, scale=scale), unit)[index].reshape((system.natoms)) start += 1 #prepare header and print_string header += ' %s' % attr if am.tools.is_dtype_int(system.atoms.dtype[prop]): print_string += ' %i' else: print_string += ' ' + xf f.write(header + '\n') print_string += '\n' #iterate over all atoms for i in xrange(system.natoms): vals = (i+1, ) + tuple(outarray[i]) f.write(print_string % vals)
def model(self, **kwargs): """ Return a DataModelDict 'cell' representation of the system Keyword Arguments: box_unit -- length unit to use for the box. Default is angstrom. symbols -- list of atom-model symbols corresponding to the atom types. elements -- list of element tags corresponding to the atom types. prop_units -- dictionary where the keys are the property keys to include, and the values are units to use. If not given, only the positions in scaled units are included. """ box_unit = kwargs.get('box_unit', 'angstrom') symbols = kwargs.get('symbols', [None for i in xrange(self.natypes)]) if not isinstance(symbols, list): symbols = [symbols] assert len(symbols) == self.natypes, 'Number of symbols does not match number of atom types' elements = kwargs.get('elements', [None for i in xrange(self.natypes)]) if not isinstance(elements, list): elements = [elements] assert len(elements) == self.natypes, 'Number of elements does not match number of atom types' prop_units = kwargs.get('prop_units', {}) if 'pos' not in prop_units: prop_units['pos'] = 'scaled' a = uc.get_in_units(self.box.a, box_unit) b = uc.get_in_units(self.box.b, box_unit) c = uc.get_in_units(self.box.c, box_unit) alpha = self.box.alpha beta = self.box.beta gamma = self.box.gamma model = DM() model['cell'] = cell = DM() if np.allclose([alpha, beta, gamma], [90.0, 90.0, 90.0]): if np.isclose(b/a, 1.): if np.isclose(c/a, 1.): c_family = 'cubic' cell[c_family] = DM() cell[c_family]['a'] = DM([('value', (a+b+c)/3), ('unit', box_unit)]) else: c_family = 'tetragonal' cell[c_family] = DM() cell[c_family]['a'] = DM([('value', (a+b)/2), ('unit', box_unit)]) cell[c_family]['c'] = DM([('value', c), ('unit', box_unit)]) else: #if np.isclose(b/a, 3.0**0.5): # c_family = 'hexagonal' # cell[c_family] = DM() # a_av = (a + b/(3.0**0.5))/2. # cell[c_family]['a'] = DM([('value', a_av), ('unit', box_unit)]) # cell[c_family]['c'] = DM([('value', c), ('unit', box_unit)]) #else: c_family = 'orthorhombic' cell[c_family] = DM() cell[c_family]['a'] = DM([('value', a), ('unit', box_unit)]) cell[c_family]['b'] = DM([('value', b), ('unit', box_unit)]) cell[c_family]['c'] = DM([('value', c), ('unit', box_unit)]) else: raise ValueError('Non-orthogonal boxes comming') for i in xrange(self.natoms): atom = DM() atom['component'] = int(self.atoms_prop(a_id=i, key='atype')) symbol = symbols[self.atoms_prop(a_id=i, key='atype')-1] if symbol is not None: atom['symbol'] = symbol element = elements[self.atoms_prop(a_id=i, key='atype')-1] if element is not None: atom['element'] = element atom['position'] = DM() if prop_units['pos'] == 'scaled': atom['position']['value'] = list(self.atoms_prop(a_id=i, key='pos', scale=True)) else: atom['position']['value'] = list(uc.get_in_units(self.atoms_prop(a_id=i, key='pos'), prop_units['pos'])) atom['position']['unit'] = prop_units['pos'] for key, unit in prop_units.iteritems(): if key != 'pos' and key != 'atype': value = uc.get_in_units(self.atoms_prop(a_id=i, key=key), unit) try: value = list(value) except: pass prop = DM([('name', key), ('value', value), ('unit', unit)]) atom.append('property', prop) model.append('atom', atom) return DM([('atomic-system', model)])
def dump(system, **kwargs): """ Return a DataModelDict 'cell' representation of the system. Parameters ---------- system : atomman.System The system to generate the data model for. f : str or file-like object, optional File path or file-like object to write the content to. If not given, then the content is returned as a DataModelDict. format : str, optional File format 'xml' or 'json' to save the content as if f is given. If f is a filename, then the format will be automatically inferred from f's extension. If format is not given and cannot be inferred, then it will be set to 'json'. indent : int or None, optional Indentation option to use for XML/JSON content if f is given. A value of None (default) will add no line separatations or indentations. box_unit : str, optional Length unit to use for the box. Default value is 'angstrom'. symbols : list, optional list of atom-model symbols corresponding to the atom types. If not given, will use system.symbols. elements : list, optional list of element tags corresponding to the atom types. prop_units : dict, optional dictionary where the keys are the property keys to include, and the values are units to use. If not given, only the positions in scaled units are included. a_std : float, optional Standard deviation of a lattice constant to include if available. b_std : float, optional Standard deviation of b lattice constant to include if available. c_std : float, optional Standard deviation of c lattice constant to include if available. Returns ------- DataModelDict A 'cell' data model of the system. """ # Set default values box_unit = kwargs.get('box_unit', 'angstrom') indent = kwargs.get('indent', None) symbols = kwargs.get('symbols', system.symbols) if isinstance(symbols, stringtype): symbols = [symbols] assert len(symbols) == system.natypes, 'Number of symbols does not match number of atom types' elements = kwargs.get('elements', [None for i in range(system.natypes)]) if not isinstance(elements, list): elements = [elements] assert len(elements) == system.natypes, 'Number of elements does not match number of atom types' prop_units = kwargs.get('prop_units', {}) if 'pos' not in prop_units: prop_units['pos'] = 'scaled' # Extract system values a = system.box.a b = system.box.b c = system.box.c alpha = system.box.alpha beta = system.box.beta gamma = system.box.gamma # Check for box standard deviations if 'a_std' in kwargs and 'b_std' in kwargs and 'c_std' in kwargs: errors = True a_std = kwargs['a_std'] b_std = kwargs['b_std'] c_std = kwargs['c_std'] else: errors = False a_std = None b_std = None c_std = None # Initialize DataModelDict model = DM() model['cell'] = cell = DM() # Test crystal family c_family = identifyfamily(system.box) if c_family is None: c_family = 'triclinic' cell[c_family] = DM() if c_family == 'cubic': a_ave = (a + b + c) / 3 if errors is True: a_std_ave = (a_std + b_std + c_std) / 3 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) elif c_family == 'tetragonal': a_ave = (a + b) / 2 if errors is True: a_std_ave = (a_std + b_std) / 2 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) elif c_family == 'orthorhombic': cell[c_family]['a'] = uc.model(a, box_unit, error=a_std) cell[c_family]['b'] = uc.model(b, box_unit, error=b_std) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) elif c_family == 'hexagonal': a_ave = (a + b) / 2 if errors is True: a_std_ave = (a_std + b_std) / 2 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) elif c_family == 'rhombohedral': a_ave = (a + b + c) / 3 alpha_ave = (alpha + beta + gamma) / 3 if errors is True: a_std_ave = (a_std + b_std + c_std) / 3 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) cell[c_family]['alpha'] = alpha_ave elif c_family == 'monoclinic': cell[c_family]['a'] = uc.model(a, box_unit, error=a_std) cell[c_family]['b'] = uc.model(b, box_unit, error=b_std) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) cell[c_family]['beta'] = beta elif c_family == 'triclinic': cell[c_family]['a'] = uc.model(a, box_unit, error=a_std) cell[c_family]['b'] = uc.model(b, box_unit, error=b_std) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) cell[c_family]['alpha'] = alpha cell[c_family]['beta'] = beta cell[c_family]['gamma'] = gamma else: raise ValueError('Unknown crystal family') atype = system.atoms.atype aindex = atype - 1 # Build list of atoms and per-atom properties for i in range(system.natoms): atom = DM() atom['component'] = int(atype[i]) symbol = symbols[aindex[i]] if symbol is not None: atom['symbol'] = symbol element = elements[aindex[i]] if element is not None: atom['element'] = element atom['position'] = DM() if prop_units['pos'] == 'scaled': atom['position']['value'] = list(system.atoms_prop(a_id=i, key='pos', scale=True)) else: atom['position']['value'] = list(uc.get_in_units(system.atoms.pos[i], prop_units['pos'])) atom['position']['unit'] = prop_units['pos'] for key, unit in iteritems(prop_units): if key != 'pos' and key != 'atype': value = system.atoms.view[key][i] prop = DM() prop['name'] = key prop.update(uc.model(value, unit)) atom.append('property', prop) model.append('atom', atom) model = DM([('atomic-system', model)]) # Return DataModelDict or str if 'f' not in kwargs: if 'format' not in kwargs: return model elif kwargs['format'].lower() == 'xml': return model.xml(indent=indent) elif kwargs['format'].lower() == 'json': return model.json(indent=indent) # Write to file else: f = kwargs['f'] if 'format' not in kwargs: try: format = os.path.splitext(f)[1][1:] except: format = 'json' else: format = kwargs['format'] if hasattr(f, 'write'): if format.lower() == 'xml': return model.xml(fp=f, indent=indent) elif format.lower() == 'json': return model.json(fp=f, indent=indent) else: with open(f, 'w') as fp: if format.lower() == 'xml': return model.xml(fp=fp, indent=indent) elif format.lower() == 'json': return model.json(fp=fp, indent=indent) return
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 E_gsf_surface_plot(self, x=None, disregistry=None, fmt='ro-', normalize=False, smooth=True, a1vect=None, a2vect=None, xvect=None, length_unit='Å', energyperarea_unit='eV/Å^2', numx=100, numy=100, figsize=None, **kwargs): """ Extends the GammaSurface.E_gsf_surface_plot() method to plot the disregistry path on top of it. Parameters ---------- x : numpy.ndarray, optional x-coordinates. Default value is the stored x-coordinates. If x or disregistry are not set/given, then the disregistry path will not be added. disregistry : numpy.ndarray, optional (N, 3) shaped array of disregistry vectors at each x-coordinate. Default value is the stored disregistry values. If x or disregistry are not set/given, then the disregistry path will not be added. fmt : str, optional The matplotlib.pyplot.plot fmt parameter for the disregistry path line, i.e. color, marker and line style options. Default value is 'ro-': red with circle markers and solid line. normalize : bool, optional Flag indicating if axes are Cartesian (False, default) or normalized by a1, a2 vectors (True). smooth : bool, optional If True (default), then plot shows smooth interpolated values. If False, plot shows nearest raw data values. a1vect : np.array, optional Crystal vector for the a1 vector to use for plotting. Default value of None uses the saved a1vect. a2vect : np.array, optional Crystal vector for the a2 vector to use for plotting. Default value of None uses the saved a2vect. xvect : numpy.array, optional Crystal vector to align with the plotting x-axis for non-normalized plots. If not given, this is taken as the Cartesian of a1vect. length_unit : str, optional The unit of length to display non-normalized axes values in. Default value is 'Å'. energyperarea_unit : str, optional The unit of energy per area to display the stacking fault energies in. Default value is 'eV/Å^2'. numx : int, optional The number of plotting points to use along the x-axis. Default value is 100. numy : int, optional The number of plotting points to use along the y-axis. Default value is 100. figsize : tuple or None, optional The figure's x,y dimensions. If None (default), the values are scaled such that the x,y spacings are approximately equal, and the larger of the two values is set to 10. **kwargs : dict, optional Additional keywords are passed into the underlying matplotlib.pyplot.pcolormesh(). This allows control of such things like the colormap (cmap). Returns ------- matplotlib.pyplot.figure The generated figure allowing users to perform additional modifications. """ # Generate the surface plot fig = self.gamma.E_gsf_surface_plot( normalize=normalize, smooth=smooth, a1vect=a1vect, a2vect=a2vect, xvect=xvect, length_unit=length_unit, energyperarea_unit=energyperarea_unit, numx=numx, numy=numy, figsize=figsize, **kwargs) hasvals = True # Default values are class properties if x is None: try: x = self.x except: hasvals = False if disregistry is None: try: disregistry = self.disregistry except: hasvals = False if hasvals: # Get xvect direction if xvect is None: if a1vect is None: a1vect = self.gamma.a1vect xvect = np.dot(a1vect, self.gamma.box.vects) # Transform disregistry to gamma surface pos pos = disregistry.dot(self.transform) # Transform to x, y plotting coordinates and plot x, y = self.gamma.pos_to_xy(pos, xvect=xvect) x = uc.get_in_units(x, length_unit) y = uc.get_in_units(y, length_unit) plt.plot(x, y, fmt) return fig
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 model(self, **kwargs): """ Return a DataModelDict 'cell' representation of the system Keyword Arguments: box_unit -- length unit to use for the box. Default is angstrom. symbols -- list of atom-model symbols corresponding to the atom types. elements -- list of element tags corresponding to the atom types. prop_units -- dictionary where the keys are the property keys to include, and the values are units to use. If not given, only the positions in scaled units are included. a_std, b_std, c_std -- standard deviation of lattice constants values to include as value errors. """ box_unit = kwargs.get('box_unit', 'angstrom') symbols = kwargs.get('symbols', [None for i in xrange(self.natypes)]) if not isinstance(symbols, list): symbols = [symbols] assert len(symbols) == self.natypes, 'Number of symbols does not match number of atom types' elements = kwargs.get('elements', [None for i in xrange(self.natypes)]) if not isinstance(elements, list): elements = [elements] assert len(elements) == self.natypes, 'Number of elements does not match number of atom types' prop_units = kwargs.get('prop_units', {}) if 'pos' not in prop_units: prop_units['pos'] = 'scaled' a = self.box.a b = self.box.b c = self.box.c alpha = self.box.alpha beta = self.box.beta gamma = self.box.gamma if 'a_std' in kwargs and 'b_std' in kwargs and 'c_std' in kwargs: errors = True a_std = kwargs['a_std'] b_std = kwargs['b_std'] c_std = kwargs['c_std'] else: errors = False model = DM() model['cell'] = cell = DM() # Test for orthorhombic angles if np.allclose([alpha, beta, gamma], [90.0, 90.0, 90.0]): if np.isclose(b/a, 1.): if np.isclose(c/a, 1.): # For cubic (a = b = c) c_family = 'cubic' cell[c_family] = DM() cell[c_family]['a'] = DM() a_ave = (a + b + c) / 3 cell[c_family]['a']['value'] = uc.get_in_units(a_ave, box_unit) if errors is True: a_std_ave = (a_std + b_std + c_std) / 3 cell[c_family]['a']['error'] = uc.get_in_units(a_std_ave, box_unit) cell[c_family]['a']['unit'] = box_unit else: # For tetrahedral (a = b != c) c_family = 'tetragonal' cell[c_family] = DM() cell[c_family]['a'] = DM() cell[c_family]['c'] = DM() a_ave = (a + b) / 2 cell[c_family]['a']['value'] = uc.get_in_units(a_ave, box_unit) cell[c_family]['c']['value'] = uc.get_in_units(c, box_unit) if errors is True: a_std_ave = (a_std + b_std) / 2 cell[c_family]['a']['error'] = uc.get_in_units(a_std_ave, box_unit) cell[c_family]['c']['error'] = uc.get_in_units(c_std, box_unit) cell[c_family]['a']['unit'] = box_unit cell[c_family]['c']['unit'] = box_unit else: # For orthorhombic (a != b != c) c_family = 'orthorhombic' cell[c_family] = DM() cell[c_family]['a'] = DM() cell[c_family]['b'] = DM() cell[c_family]['c'] = DM() cell[c_family]['a']['value'] = uc.get_in_units(a, box_unit) cell[c_family]['b']['value'] = uc.get_in_units(b, box_unit) cell[c_family]['c']['value'] = uc.get_in_units(c, box_unit) if errors is True: cell[c_family]['a']['error'] = uc.get_in_units(a_std, box_unit) cell[c_family]['b']['error'] = uc.get_in_units(b_std, box_unit) cell[c_family]['c']['error'] = uc.get_in_units(c_std, box_unit) cell[c_family]['a']['unit'] = box_unit cell[c_family]['b']['unit'] = box_unit cell[c_family]['c']['unit'] = box_unit else: raise ValueError('Non-orthogonal boxes comming') for i in xrange(self.natoms): atom = DM() atom['component'] = int(self.atoms_prop(a_id=i, key='atype')) symbol = symbols[self.atoms_prop(a_id=i, key='atype')-1] if symbol is not None: atom['symbol'] = symbol element = elements[self.atoms_prop(a_id=i, key='atype')-1] if element is not None: atom['element'] = element atom['position'] = DM() if prop_units['pos'] == 'scaled': atom['position']['value'] = list(self.atoms_prop(a_id=i, key='pos', scale=True)) else: atom['position']['value'] = list(uc.get_in_units(self.atoms_prop(a_id=i, key='pos'), prop_units['pos'])) atom['position']['unit'] = prop_units['pos'] for key, unit in prop_units.iteritems(): if key != 'pos' and key != 'atype': value = uc.get_in_units(self.atoms_prop(a_id=i, key=key), unit) try: value = list(value) except: pass prop = DM([('name', key), ('value', value), ('unit', unit)]) atom.append('property', prop) model.append('atom', atom) return DM([('atomic-system', model)])
def interpolate_contour(system, name, property=None, index=None, magnitude=False, plotxaxis='x', plotyaxis='y', xlim=None, ylim=None, zlim=None, xbins=200, ybins=200, dots=True, czero=True, save=False, show=True, length_unit='angstrom', property_unit=None, cmap='jet'): """ Creates a contour plot of a system's per-atom properties by interpolating properties between atoms. Parameters ---------- system : atomman.System The system with the per-atom property that you want to plot. name : str The name of the per-atom property that you want to plot. property : array-like object, optional Values for the per-atom property to plot. If not given, values will be taken as the "name" property of system. index : int or tuple, optional Specifies which component of a multidimensional property to plot. Not needed if the property is scalar. magnitude : bool, optional If True, plots the per-atom magnitude of a vector property. Cannot be combined with index. Default value is False. plotxaxis : str or array-like object, optional Indicates the Cartesian direction associated with the system's atomic coordinates to align with the plotting x-axis. Values are either 3D unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes directions. plotxaxis and plotyaxis must be orthogonal. Default value is 'x' = [1, 0, 0]. plotyaxis : str or array-like object, optional Indicates the Cartesian direction associated with the system's atomic coordinates to align with the plotting y-axis. Values are either 3D unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes directions. plotxaxis and plotyaxis must be orthogonal. Default value is 'y' = [0, 1, 0]. xlim : tuple, optional The minimum and maximum coordinates along the plotting x-axis to include in the fit. Values are taken in the specified length_unit. If not given, then the limits are set based on min and max atomic coordinates along the plotting axis. ylim : tuple, optional The minimum and maximum coordinates along the plotting y-axis to include in the fit. Values are taken in the specified length_unit. If not given, then the limits are set based on min and max atomic coordinates along the plotting axis. zlim : tuple, optional The minimum and maximum coordinates normal to the plotting axes (i.e. plotxaxis X plotyaxis) to include in the fit. Values are taken in the specified length_unit. If not given, then the limits are set based on min and max atomic coordinates along the axis. xbins : int, optional Specifies the number of interpolation bins to use along the plotting x-axis. Default value is 200. ybins : int, optional Specifies the number of interpolation bins to use along the plotting y-axis. Default value is 200. dots : bool, optional If True, then the positions of the atoms are shown as circles. Default value is True. czero : bool, optional If True, the range of property values will be centered around zero, i.e. cmax = -cmin. If False, cmax and cmin will be independently selected using the property values. Default value is True. save : bool, optional If True, the generated plot will be saved to "name.png". Default value is False. show : bool, optional If True, matplotlib.pyplot.show() is called. Default value is True. length_unit : str, optional The units of length to use for the plotting x- and y-axes. Default value is 'angstrom'. property_unit : str or None, optional The units to use for the property value being plotted. Default value is None, in which no unit conversion is applied. cmap : str, optional The name of the matplotlib colormap to use. Default value is 'jet'. Returns ------- intsum : float The area integrated sum of the property over the plotted region. avsum : float The average property value taken across all plotting bins. """ # Give numeric values for str plot axis terms if plotxaxis == 'x': plotxaxis = [1.0,0.0,0.0] elif plotxaxis == 'y': plotxaxis = [0.0,1.0,0.0] elif plotxaxis == 'z': plotxaxis = [0.0,0.0,1.0] if plotyaxis == 'x': plotyaxis = [1.0,0.0,0.0] elif plotyaxis == 'y': plotyaxis = [0.0,1.0,0.0] elif plotyaxis == 'z': plotyaxis = [0.0,0.0,1.0] # Build transformation matrix, T, from plot axes. plotxaxis = np.asarray(plotxaxis, dtype='float64') plotyaxis = np.asarray(plotyaxis, dtype='float64') T = axes_check([plotxaxis, plotyaxis, np.cross(plotxaxis, plotyaxis)]) # Extract positions and transform using T pos = uc.get_in_units(np.inner(system.atoms.pos, T), length_unit) # Set plot limits if xlim is None: xlim = (pos[:, 0].min(), pos[:, 0].max()) if ylim is None: ylim = (pos[:, 1].min(), pos[:, 1].max()) if zlim is None: zlim = (pos[:, 2].min(), pos[:, 2].max()) # Extract property values if property is None: property = system.atoms.view[name] # Handle index if index is not None: assert magnitude is False, 'index and magnitude cannot be combined' if isinstance(index, inttype): index = [index] else: index = list(index) for i in index: name += '[' + str(i+1) + ']' property = property[tuple([Ellipsis] + index)] # Handle magnitude elif magnitude is True: property = np.linalg.norm(property, axis=1) name += '_mag' assert property.shape == (system.natoms, ), 'property to plot must be a per-atom scalar' # Identify only atoms in xlim, ylim, zlim in_bounds = ((pos[:,0] > xlim[0] - 5.) & (pos[:,0] < xlim[1] + 5.) & (pos[:,1] > ylim[0] - 5.) & (pos[:,1] < ylim[1] + 5.) & (pos[:,2] > zlim[0]) & (pos[:,2] < zlim[1])) # Extract plotting coordinates and values x = pos[in_bounds, 0] y = pos[in_bounds, 1] v = uc.get_in_units(property[in_bounds], property_unit) # Generate interpolation grid grid, xedges, yedges = grid_interpolate_2d(x, y, v, xbins=xbins, ybins=ybins, range=[xlim, ylim]) # Compute intsum and avsum values intsum = np.sum(grid) avsum = intsum / (xbins-1) / (ybins-1) intsum = intsum * (xlim[1] - xlim[0]) / (xbins-1) * (ylim[1] - ylim[0]) / (ybins-1) # Generate a pretty figure of the grid fig = prettygrid(grid, xedges, yedges, cmap=cmap, propname=name, czero=czero) # Add dots if dots: adddots(x, y, xedges, yedges) # Save, show and close figure if save is True: plt.savefig(name + '.png', dpi=800) if show is True: plt.show() plt.close(fig) return intsum, avsum
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 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 data_model(input_dict, results_dict=None): """Creates a DataModelDict containing the input and results data""" #Create the root of the DataModelDict output = DM() output['calculation-system-relax'] = calc = DM() #Assign uuid calc['key'] = input_dict['calc_key'] calc['calculation'] = DM() calc['calculation']['script'] = __calc_name__ calc['calculation']['run-parameter'] = run_params = DM() run_params['size-multipliers'] = DM() run_params['size-multipliers']['a'] = list(input_dict['sizemults'][0]) run_params['size-multipliers']['b'] = list(input_dict['sizemults'][1]) run_params['size-multipliers']['c'] = list(input_dict['sizemults'][2]) run_params['strain-range'] = input_dict['strainrange'] run_params['load_options'] = input_dict['load_options'] #Copy over potential data model info calc['potential'] = DM() calc['potential']['key'] = input_dict['potential'].key calc['potential']['id'] = input_dict['potential'].id #Save info on system file loaded system_load = input_dict['load'].split(' ') calc['system-info'] = DM() calc['system-info']['artifact'] = DM() calc['system-info']['artifact']['file'] = os.path.basename(' '.join( system_load[1:])) calc['system-info']['artifact']['format'] = system_load[0] calc['system-info']['artifact']['family'] = input_dict['system_family'] calc['system-info']['symbols'] = input_dict['symbols'] #Save phase-state info calc['phase-state'] = DM() calc['phase-state']['temperature'] = DM([('value', 0.0), ('unit', 'K')]) calc['phase-state']['pressure-xx'] = DM([ ('value', uc.get_in_units(input_dict['pressure_xx'], input_dict['pressure_unit'])), ('unit', input_dict['pressure_unit']) ]) calc['phase-state']['pressure-yy'] = DM([ ('value', uc.get_in_units(input_dict['pressure_yy'], input_dict['pressure_unit'])), ('unit', input_dict['pressure_unit']) ]) calc['phase-state']['pressure-zz'] = DM([ ('value', uc.get_in_units(input_dict['pressure_zz'], input_dict['pressure_unit'])), ('unit', input_dict['pressure_unit']) ]) if results_dict is None: calc['status'] = 'not calculated' else: #Save data model of the initial ucell calc['as-constructed-atomic-system'] = input_dict['ucell'].model( symbols=input_dict['symbols'], box_unit=input_dict['length_unit'])['atomic-system'] #Update ucell to relaxed lattice parameters a_mult = input_dict['sizemults'][0][1] - input_dict['sizemults'][0][0] b_mult = input_dict['sizemults'][1][1] - input_dict['sizemults'][1][0] c_mult = input_dict['sizemults'][2][1] - input_dict['sizemults'][2][0] relaxed_ucell = deepcopy(input_dict['ucell']) relaxed_ucell.box_set(a=results_dict['system_new'].box.a / a_mult, b=results_dict['system_new'].box.b / b_mult, c=results_dict['system_new'].box.c / c_mult, scale=True) #Save data model of the relaxed ucell calc['relaxed-atomic-system'] = relaxed_ucell.model( symbols=input_dict['symbols'], box_unit=input_dict['length_unit'])['atomic-system'] #Save the final cohesive energy calc['cohesive-energy'] = DM([ ('value', uc.get_in_units(results_dict['ecoh'], input_dict['energy_unit'])), ('unit', input_dict['energy_unit']) ]) #Save the final elastic constants c_family = calc['relaxed-atomic-system']['cell'].keys()[0] calc['elastic-constants'] = results_dict['C'].model( unit=input_dict['pressure_unit'], crystal_system=c_family)['elastic-constants'] return output
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 dump(system, **kwargs): """ Return a DataModelDict 'cell' representation of the system. Parameters ---------- system : atomman.System The system to generate the data model for. f : str or file-like object, optional File path or file-like object to write the content to. If not given, then the content is returned as a DataModelDict. format : str, optional File format 'xml' or 'json' to save if f is given. If not given, will be inferred from f if f is a filename, or taken as 'json'. indent : int, optional Indentation option to use for XML/JSON content if f is given. box_unit : str, optional Length unit to use for the box. Default value is 'angstrom'. symbols : list, optional list of atom-model symbols corresponding to the atom types. If not given, will use system.symbols. elements : list, optional list of element tags corresponding to the atom types. prop_units : dict, optional dictionary where the keys are the property keys to include, and the values are units to use. If not given, only the positions in scaled units are included. a_std : float, optional Standard deviation of a lattice constant to include if available. b_std : float, optional Standard deviation of b lattice constant to include if available. c_std : float, optional Standard deviation of c lattice constant to include if available. Returns ------- DataModelDict A 'cell' data model of the system. """ # Set default values box_unit = kwargs.get('box_unit', 'angstrom') symbols = kwargs.get('symbols', system.symbols) if isinstance(symbols, stringtype): symbols = [symbols] assert len(symbols) == system.natypes, 'Number of symbols does not match number of atom types' elements = kwargs.get('elements', [None for i in range(system.natypes)]) if not isinstance(elements, list): elements = [elements] assert len(elements) == system.natypes, 'Number of elements does not match number of atom types' prop_units = kwargs.get('prop_units', {}) if 'pos' not in prop_units: prop_units['pos'] = 'scaled' # Extract system values a = system.box.a b = system.box.b c = system.box.c alpha = system.box.alpha beta = system.box.beta gamma = system.box.gamma # Check for box standard deviations if 'a_std' in kwargs and 'b_std' in kwargs and 'c_std' in kwargs: errors = True a_std = kwargs['a_std'] b_std = kwargs['b_std'] c_std = kwargs['c_std'] else: errors = False a_std = None b_std = None c_std = None # Initialize DataModelDict model = DM() model['cell'] = cell = DM() # Test crystal family c_family = identifyfamily(system.box) if c_family is None: c_family = 'triclinic' cell[c_family] = DM() if c_family == 'cubic': a_ave = (a + b + c) / 3 if errors is True: a_std_ave = (a_std + b_std + c_std) / 3 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) elif c_family == 'tetragonal': a_ave = (a + b) / 2 if errors is True: a_std_ave = (a_std + b_std) / 2 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) elif c_family == 'orthorhombic': cell[c_family]['a'] = uc.model(a, box_unit, error=a_std) cell[c_family]['b'] = uc.model(b, box_unit, error=b_std) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) elif c_family == 'hexagonal': a_ave = (a + b) / 2 if errors is True: a_std_ave = (a_std + b_std) / 2 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) elif c_family == 'rhombohedral': a_ave = (a + b + c) / 3 alpha_ave = (alpha + beta + gamma) / 3 if errors is True: a_std_ave = (a_std + b_std + c_std) / 3 else: a_std_ave = None cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave) cell[c_family]['alpha'] = alpha_ave elif c_family == 'monoclinic': cell[c_family]['a'] = uc.model(a, box_unit, error=a_std) cell[c_family]['b'] = uc.model(b, box_unit, error=b_std) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) cell[c_family]['beta'] = beta elif c_family == 'triclinic': cell[c_family]['a'] = uc.model(a, box_unit, error=a_std) cell[c_family]['b'] = uc.model(b, box_unit, error=b_std) cell[c_family]['c'] = uc.model(c, box_unit, error=c_std) cell[c_family]['alpha'] = alpha cell[c_family]['beta'] = beta cell[c_family]['gamma'] = gamma else: raise ValueError('Unknown crystal family') atype = system.atoms.atype aindex = atype - 1 # Build list of atoms and per-atom properties for i in range(system.natoms): atom = DM() atom['component'] = int(atype[i]) symbol = symbols[aindex[i]] if symbol is not None: atom['symbol'] = symbol element = elements[aindex[i]] if element is not None: atom['element'] = element atom['position'] = DM() if prop_units['pos'] == 'scaled': atom['position']['value'] = list(system.atoms_prop(a_id=i, key='pos', scale=True)) else: atom['position']['value'] = list(uc.get_in_units(system.atoms.pos[i], prop_units['pos'])) atom['position']['unit'] = prop_units['pos'] for key, unit in iteritems(prop_units): if key != 'pos' and key != 'atype': value = system.atoms.view[key][i] prop = DM() prop['name'] = key prop.update(uc.model(value, unit)) atom.append('property', prop) model.append('atom', atom) model = DM([('atomic-system', model)]) # Return DataModelDict or str if 'f' not in kwargs: if 'format' not in kwargs: return model elif format.lower() == 'xml': return model.xml(indent=indent) elif format.lower() == 'json': return model.json(indent=indent) # Write to file else: f = kwargs['f'] if 'format' not in kwargs: try: format = os.path.splitext(f)[1][1:] except: format = 'json' else: format = kwargs['format'] if hasattr(f, 'write'): if format.lower() == 'xml': return model.xml(fp=f, indent=indent) elif format.lower() == 'json': return model.json(fp=f, indent=indent) else: with open(f, 'w') as fp: if format.lower() == 'xml': return model.xml(fp=fp, indent=indent) elif format.lower() == 'json': return model.json(fp=fp, indent=indent) return
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 dump(system, f=None, atom_style='atomic', units='metal', float_format='%.13f', return_info=True): """ Write a LAMMPS-style atom data file from a System. Parameters ---------- system : atomman.System The system to write to the atom data file. f : str or file-like object, optional File path or file-like object to write the content to. If not given, then the content is returned as a str. atom_style : str, optional The LAMMPS atom_style option associated with the data file. Default value is 'atomic'. units : str, optional The LAMMPS units option associated with the data file. Default value is 'metal'. float_format : str, optional c-style formatting string for floating point values. Default value is '%.13f'. return_info : bool, optional Indicates if the LAMMPS command lines associated with reading in the file are to be returned as a str. Default value is True. Returns ------- content : str The data file contents (returned if f is not given). read_info : str The LAMMPS input command lines to read the created data file in (returned if return_info is True). """ # Wrap atoms because LAMMPS hates atoms out of bounds in atom data files system.wrap() # Get unit information according to the units style units_dict = style.unit(units) # Generate header info content = '\n%i atoms\n' % system.natoms content += '%i atom types\n' % system.natypes # Extract and convert box values xlo = uc.get_in_units(system.box.xlo, units_dict['length']) xhi = uc.get_in_units(system.box.xhi, units_dict['length']) ylo = uc.get_in_units(system.box.ylo, units_dict['length']) yhi = uc.get_in_units(system.box.yhi, units_dict['length']) zlo = uc.get_in_units(system.box.zlo, units_dict['length']) zhi = uc.get_in_units(system.box.zhi, units_dict['length']) xy = system.box.xy xz = system.box.xz yz = system.box.yz # Write box values xf2 = float_format + ' ' + float_format content += xf2 % (xlo, xhi) +' xlo xhi\n' content += xf2 % (ylo, yhi) +' ylo yhi\n' content += xf2 % (zlo, zhi) +' zlo zhi\n' if xy != 0.0 or xz != 0.0 or yz != 0.0: xf3 = float_format + ' ' + float_format + ' ' + float_format content += xf3 % (xy, xz, yz) + ' xy xz yz\n' # Write atom info content += '\nAtoms\n\n' prop_info = atoms_prop_info(atom_style, units) content += dump_table(system, prop_info=prop_info, float_format=float_format) # Handle velocity information if included if 'velocity' in system.atoms_prop(): # Write velocity info content += '\nVelocities\n\n' prop_info = velocities_prop_info(atom_style, units) content += dump_table(system, prop_info=prop_info, float_format=float_format) returns = [] # Save to the file-like object if hasattr(f, 'write'): f.write(content) # Save to the file name elif f is not None: with open(f, 'w') as fp: fp.write(content) # Return as a string else: returns.append(content) if return_info is True: # Return appropriate units, atom_style, boundary, and read_data LAMMPS commands boundary = '' for i in range(3): if system.pbc[i]: boundary += 'p ' else: boundary += 'm ' if isinstance(f, stringtype): read_data = 'read_data ' + f else: read_data = '' newline = '\n' read_info = newline.join(['# Script and atom data file prepared by atomman package', '', 'units ' + units, 'atom_style ' + atom_style, '' 'boundary ' + boundary, read_data]) returns.append(read_info) if len(returns) == 1: return returns[0] elif len(returns) > 1: return tuple(returns)
def model(self, **kwargs): """ Return or set DataModelDict representation of the elastic constants. Keyword Arguments: model -- string or file-like object of json/xml model or DataModelDict. unit -- units to give values in. Default is None. crystal_system -- crystal system representation. Default is triclinic. If model is given, then model is converted into a DataModelDict and the elastic constants are read in if the model contains exactly one 'elastic-constants' branch. If model is not given, then a DataModelDict for the elastic constants is constructed. The values included will depend on the crystal system, and will be converted to the specified units. """ #Set values if model given if 'model' in kwargs: assert len(kwargs) == 1, 'no keyword arguments supported with model reading' model = DM(kwargs['model']).find('elastic-constants') c_dict = {} for C in model['C']: key = 'C' + C['ij'][0] + C['ij'][2] c_dict[key] = uc.value_unit(C['stiffness']) self.Cij = ElasticConstants(**c_dict).Cij #Return DataModelDict if model not given else: unit = kwargs.pop('unit', None) crystal_system = kwargs.pop('crystal_system', 'triclinic') assert len(kwargs) == 0, 'Invalid arguments' model = DM() model['elastic-constants'] = DM() model['elastic-constants']['C'] = C = [] c = uc.get_in_units(self.Cij, unit) c_dict = DM() if crystal_system == 'cubic': c_dict['1 1'] = (c[0,0] + c[1,1] + c[2,2]) / 3 c_dict['1 2'] = (c[0,1] + c[0,2] + c[1,2]) / 3 c_dict['4 4'] = (c[3,3] + c[4,4] + c[5,5]) / 3 elif crystal_system == 'hexagonal': c_dict['1 1'] = (c[0,0] + c[1,1]) / 2 c_dict['3 3'] = c[2,2] c_dict['1 2'] = (c[0,1] + (c[0,0] - 2*c[5,5])) / 2 c_dict['1 3'] = (c[0,2] + c[1,2]) / 2 c_dict['4 4'] = (c[3,3] + c[4,4]) / 2 elif crystal_system == 'tetragonal': c_dict['1 1'] = (c[0,0] + c[1,1]) / 2 c_dict['3 3'] = c[2,2] c_dict['1 2'] = c[0,1] c_dict['1 3'] = (c[0,2] + c[1,2]) / 2 c_dict['1 6'] = (c[0,5] - c[1,5]) / 2 c_dict['4 4'] = (c[3,3] + c[4,4]) / 2 c_dict['6 6'] = c[5,5] elif crystal_system == 'orthorhombic': c_dict['1 1'] = c[0,0] c_dict['2 2'] = c[1,1] c_dict['3 3'] = c[2,2] c_dict['1 2'] = c[0,1] c_dict['1 3'] = c[0,2] c_dict['2 3'] = c[1,2] c_dict['4 4'] = c[3,3] c_dict['5 5'] = c[4,4] c_dict['6 6'] = c[5,5] else: c_dict['1 1'] = c[0,0] c_dict['1 2'] = c[0,1] c_dict['1 3'] = c[0,2] c_dict['1 4'] = c[0,3] c_dict['1 5'] = c[0,4] c_dict['1 6'] = c[0,5] c_dict['2 2'] = c[1,1] c_dict['2 3'] = c[1,2] c_dict['2 4'] = c[1,3] c_dict['2 5'] = c[1,4] c_dict['2 6'] = c[1,5] c_dict['3 3'] = c[2,2] c_dict['3 4'] = c[2,3] c_dict['3 5'] = c[2,4] c_dict['3 6'] = c[2,5] c_dict['4 4'] = c[3,3] c_dict['4 5'] = c[3,4] c_dict['4 6'] = c[3,5] c_dict['5 5'] = c[4,4] c_dict['5 6'] = c[4,5] c_dict['6 6'] = c[5,5] for ij, value in c_dict.iteritems(): C.append(DM([('stiffness', DM([ ('value', value), ('unit', unit) ])), ('ij', ij) ]) ) return model
def integrator_info(integrator=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, temperature=0.0, randomseed=None, units='metal'): """ Generates LAMMPS commands for velocity creation and fix integrators. Keyword Arguments: integrator -- string giving the integration method to use. Options are 'npt', 'nvt', 'nph', 'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat. Default value is 'nph+l' for temperature = 0, and 'npt' otherwise. p_xx, p_yy, p_zz -- tensile pressures to equilibriate to. Default value is 0.0 for all. temperature -- temperature to relax at. Default value is 0. randomseed -- random number seed used by LAMMPS for velocity creation and Langevin thermostat. Default value generates a new random integer every time. units = LAMMPS units style to use. """ #Get lammps units lammps_units = lmp.style.unit(units) Px = uc.get_in_units(p_xx, lammps_units['pressure']) Py = uc.get_in_units(p_yy, lammps_units['pressure']) Pz = uc.get_in_units(p_zz, lammps_units['pressure']) T = temperature #Check temperature and set default integrator if temperature == 0.0: if integrator is None: integrator = 'nph+l' assert integrator not in ['npt', 'nvt'], 'npt and nvt cannot run at 0 K' elif temperature > 0: if integrator is None: integrator = 'npt' else: raise ValueError('Temperature must be positive') #Set default randomseed if randomseed is None: randomseed = random.randint(1, 900000000) if integrator == 'npt': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) Pdamp = 1000 * lmp.style.timestep(units) int_info = '\n'.join(['velocity all create %f %i' % (start_temp, randomseed), 'fix npt all npt temp %f %f %f &' % (T, T, Tdamp), ' x %f %f %f &' % (Px, Px, Pdamp), ' y %f %f %f &' % (Py, Py, Pdamp), ' z %f %f %f' % (Pz, Pz, Pdamp)]) elif integrator == 'nvt': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) int_info = '\n'.join(['velocity all create %f %i' % (start_temp, randomseed), 'fix nvt all nvt temp %f %f %f' % (T, T, Tdamp)]) elif integrator == 'nph': Pdamp = 1000 * lmp.style.timestep(units) int_info = '\n'.join(['fix nph all nph x %f %f %f &' % (Px, Px, Pdamp), ' y %f %f %f &' % (Py, Py, Pdamp), ' z %f %f %f' % (Pz, Pz, Pdamp)]) elif integrator == 'nve': int_info = 'fix nve all nve' elif integrator == 'nve+l': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) int_info = '\n'.join(['velocity all create %f %i' % (start_temp, randomseed), 'fix nve all nve', 'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp, randomseed)]) elif integrator == 'nph+l': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) Pdamp = 1000 * lmp.style.timestep(units) int_info = '\n'.join([#'velocity all create %f %i' % (start_temp, randomseed), 'fix nph all nph x %f %f %f &' % (Px, Px, Pdamp), ' y %f %f %f &' % (Py, Py, Pdamp), ' z %f %f %f' % (Pz, Pz, Pdamp), 'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp, randomseed)]) else: raise ValueError('Invalid integrator style') return int_info
def strain_system(lammps_command, system, potential, symbols, mpi_command=None, delta_strain=np.zeros(6), initial_strain=np.zeros(6), hold_atypes=[], number_of_steps=0, etol=0.0, ftol=1e-5, maxiter=10000, maxeval=100000): """ Applies strains to an atomic system in LAMMPS and relaxes the energy. Arguments: lammps_command -- command for running LAMMPS. system -- atomman.System to add the point defect to. potential -- atomman.lammps.Potential representation of a LAMMPS implemented potential. symbols -- list of element-model symbols for the Potential that correspond to system's atypes. Keyword Arguments: mpi_command -- MPI command for running LAMMPS in parallel. Default value is None (serial run). delta_strain -- strain state in 6 term Voigt to apply incrementally. Default value is all zeros. initial_strain -- strain state in 6 term Voigt to apply to the system prior to the incremental delta_strain. Default value is all zeros. number_of_steps -- number of times to apply the incremental delta_strain to the system. Default value is 0. hold_atypes -- list of atom types that should be held fixed (not allowed to relax). Default value is []. etol -- energy tolerance to use for the LAMMPS minimization. Default value is 0.0 (i.e. only uses ftol). ftol -- force tolerance to use for the LAMMPS minimization. Default value is 1e-6. maxiter -- the maximum number of iterations for the LAMMPS minimization. Default value is 100000. maxeval -- the maximum number of evaluations for the LAMMPS minimization. Default value is 100000. """ if am.tools.is_int(hold_atypes): hold_atypes = np.array([hold_atypes]) else: hold_atypes = np.asarray(hold_atypes) if len(hold_atypes) == 0: hold_info = '' else: move_atypes = np.setdiff1d(np.arange(1, system.natypes+1), hold_atypes) hold_info = '\n'.join(['', '#Fix boundary region atoms', 'group move type ' + ' '.join(np.char.mod('%d', move_atypes)), 'group hold subtract all move', 'fix nomove hold setforce 0.0 0.0 0.0', '']) #Get lammps units lammps_units = lmp.style.unit(potential.units) #Read LAMMPS input template with open('strain_system.template') as f: template = f.read() #Define lammps variables lammps_variables = {} lammps_variables['atomman_system_info'] = lmp.atom_data.dump(system, 'initial.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_pair_info'] = potential.pair_info(symbols) lammps_variables['hold_info'] = hold_info lammps_variables['energy_tolerance'] = etol lammps_variables['force_tolerance'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maximum_iterations'] = maxiter lammps_variables['maximum_evaluations'] = maxeval lammps_variables['number_of_steps'] = number_of_steps + 1 lammps_variables['delta_strain_xx'] = delta_strain[0] lammps_variables['delta_strain_yy'] = delta_strain[1] lammps_variables['delta_strain_zz'] = delta_strain[2] lammps_variables['delta_strain_xy'] = delta_strain[5] lammps_variables['delta_strain_xz'] = delta_strain[4] lammps_variables['delta_strain_yz'] = delta_strain[3] lammps_variables['initial_strain_xx'] = initial_strain[0] lammps_variables['initial_strain_yy'] = initial_strain[1] lammps_variables['initial_strain_zz'] = initial_strain[2] lammps_variables['initial_strain_xy'] = initial_strain[5] lammps_variables['initial_strain_xz'] = initial_strain[4] lammps_variables['initial_strain_yz'] = initial_strain[3] #Write lammps input script with open('strain_system.in', 'w') as f: f.write('\n'.join(iprPy.tools.fill_template(template, lammps_variables, '<', '>'))) #run lammps to relax perfect.dat output = lmp.run(lammps_command, 'strain_system.in', mpi_command) #Extract LAMMPS thermo data. lammps_step = np.asarray(output.finds('Step'), dtype=int)[1::2] E_total = uc.set_in_units(output.finds('PotEng'), lammps_units['energy'] )[1::2] p_xx = uc.set_in_units(output.finds('Pxx'), lammps_units['pressure'])[1::2] p_yy = uc.set_in_units(output.finds('Pyy'), lammps_units['pressure'])[1::2] p_zz = uc.set_in_units(output.finds('Pzz'), lammps_units['pressure'])[1::2] p_xy = uc.set_in_units(output.finds('Pxy'), lammps_units['pressure'])[1::2] p_xz = uc.set_in_units(output.finds('Pxz'), lammps_units['pressure'])[1::2] p_yz = uc.set_in_units(output.finds('Pyz'), lammps_units['pressure'])[1::2] strain_step = np.asarray(output.finds('s_step'), dtype=int)[1::2] return {'lammps_step':lammps_step, 'E_total':E_total, 'p_xx':p_xx, 'p_yy':p_yy, 'p_zz':p_zz, 'p_xy':p_xy, 'p_xz':p_xz, 'p_yz':p_yz, 'strain_step':strain_step}
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 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 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 data_model(input_dict, results_dict=None): """Creates a DataModelDict containing the input and results data""" #Create the root of the DataModelDict output = DM() output['calculation-dislocation-monopole'] = calc = DM() #Assign uuid calc['calculation'] = DM() calc['calculation']['id'] = input_dict['uuid'] calc['calculation']['script'] = __calc_name__ calc['calculation']['run-parameter'] = run_params = DM() run_params['a-multiplyer'] = input_dict['a_mult'] run_params['b-multiplyer'] = input_dict['b_mult'] run_params['c-multiplyer'] = input_dict['c_mult'] run_params['anneal_temperature'] = input_dict['anneal_temperature'] run_params['boundary_width'] = input_dict['boundary_width'] run_params['boundary_shape'] = input_dict['boundary_shape'] run_params['energy_tolerance'] = input_dict['energy_tolerance'] run_params['force_tolerance'] = input_dict['force_tolerance'] run_params['maximum_iterations'] = input_dict['maximum_iterations'] run_params['maximum_evaluations'] = input_dict['maximum_evaluations'] #Copy over potential data model info calc['potential'] = input_dict['potential']['LAMMPS-potential'][ 'potential'] #Save info on system file loaded system_load = input_dict['load'].split(' ') calc['system-info'] = DM() calc['system-info']['artifact'] = DM() calc['system-info']['artifact']['file'] = os.path.basename(' '.join( system_load[1:])) calc['system-info']['artifact']['format'] = system_load[0] calc['system-info']['artifact']['family'] = input_dict['system_family'] calc['system-info']['symbols'] = input_dict['symbols'] calc['elastic-constants'] = input_dict['elastic_constants_model'].find( 'elastic-constants') #Save data model of the initial ucell calc['dislocation-monopole-parameters'] = input_dict['dislocation_model'][ 'dislocation-monopole-parameters'] if results_dict is None: calc['status'] = 'not calculated' else: calc['defect-free-system'] = DM() calc['defect-free-system']['artifact'] = DM() calc['defect-free-system']['artifact']['file'] = 'base.dat' calc['defect-free-system']['artifact']['format'] = 'atom_data' old_symbols = input_dict['symbols'][:len(input_dict['symbols']) / 2] if len(old_symbols) == 1: old_symbols = old_symbols[0] calc['defect-free-system']['symbols'] = old_symbols calc['defect-system'] = DM() calc['defect-system']['artifact'] = DM() calc['defect-system']['artifact']['file'] = 'disl.dump' calc['defect-system']['artifact']['format'] = 'atom_dump' calc['defect-system']['symbols'] = input_dict['symbols'] #Save the final cohesive energy calc['potential-energy'] = DM([ ('value', uc.get_in_units(results_dict['potential_energy'], input_dict['energy_unit'])), ('unit', input_dict['energy_unit']) ]) calc['pre-ln-factor'] = DM([ ('value', uc.get_in_units( results_dict['pre-ln_factor'], input_dict['energy_unit'] + '/' + input_dict['length_unit'])), ('unit', input_dict['energy_unit'] + '/' + input_dict['length_unit']) ]) return output
def main(args): """Main function for running calc_struct_static.py""" try: infile = args[0] try: UUID = args[1] except: UUID = str(uuid.uuid4()) except: raise ValueError('Input file not given') #Read in parameters from input file input_dict = read_input(infile) #Initial parameter setup lammps_exe = input_dict.get('lammps_exe') pot_dir = input_dict.get('potential_dir', '') symbols = input_dict.get('symbols') u_length = input_dict.get('length_display_units', 'angstrom') u_press = input_dict.get('pressure_display_units', 'GPa') u_energy = input_dict.get('energy_display_units', 'eV') r_min = input_dict.get('r_min', None) r_max = input_dict.get('r_max', None) if r_min is None: r_min = uc.get_in_units(2.0, 'angstrom') else: r_min = uc.get_in_units(float(r_min), u_length) if r_max is None: r_max = uc.get_in_units(5.0, 'angstrom') else: r_max = uc.get_in_units(float(r_max), u_length) steps = int(input_dict.get('steps', 200)) #read in potential_file with open(input_dict['potential_file']) as f: potential = lmp.Potential(f, pot_dir) #read in prototype_file with open(input_dict['crystal_file']) as f: try: ucell = am.models.crystal(f)[0] except: f.seek(0) ucell = am.models.cif_cell(f)[0] #Run ecoh_vs_r rvals, avals, evals = ecoh_vs_r(lammps_exe, deepcopy(ucell), potential, symbols, rmin=r_min, rmax=r_max, rsteps=steps) #Use plot to get rough lattice parameter guess, a0, and build ucell a0 = avals[np.argmin(evals)] cell_0 = ucell.model(symbols=symbols, box_unit='scaled') ucell.box_set(a = a0, b = a0 * ucell.box.b / ucell.box.a, c = a0 * ucell.box.c / ucell.box.a, scale=True) #Run quick_aCij to refine values results = quick_a_Cij(lammps_exe, ucell, potential, symbols) #Plot Ecoh vs. r plt.title('Cohesive Energy vs. Interatomic Spacing') plt.xlabel('r (' + u_length + ')') plt.ylabel('Cohesive Energy (' + u_energy + '/atom)') plt.plot(uc.get_in_units(rvals, u_length), uc.get_in_units(evals, u_energy)) plt.savefig('Ecoh_vs_r.png') plt.close() ucell_new = results['ucell_new'] cell_1 = ucell_new.model(symbols=symbols, box_unit=u_length) ecoh = uc.get_in_units(results['ecoh'], u_energy) C = results['C'] output = DataModelDict() output['calculation-crystal-phase'] = calc = DataModelDict() calc['calculation-id'] = UUID with open(input_dict['potential_file']) as f: potdict = DataModelDict(f) calc['potential'] = potdict['LAMMPS-potential']['potential'] calc['crystal-info'] = DataModelDict() calc['crystal-info']['artifact'] = input_dict['crystal_file'] calc['crystal-info']['symbols'] = symbols calc['phase-state'] = DataModelDict() calc['phase-state']['temperature'] = DataModelDict([('value', 0.0), ('unit', 'K')]) calc['phase-state']['pressure'] = DataModelDict([('value', 0.0), ('unit', u_press)]) calc['as-constructed-atomic-system'] = cell_0['atomic-system'] calc['relaxed-atomic-system'] = cell_1['atomic-system'] c_family = cell_1['atomic-system']['cell'].keys()[0] calc['cohesive-energy'] = DataModelDict([('value', ecoh), ('unit', u_energy)]) calc['elastic-constants'] = C.model(unit=u_press, crystal_system=c_family)['elastic-constants'] calc['cohesive-energy-relation'] = DataModelDict() calc['cohesive-energy-relation']['r'] = DataModelDict([('value', list(uc.get_in_units(rvals, u_length))), ('unit', u_length)]) calc['cohesive-energy-relation']['a'] = DataModelDict([('value', list(uc.get_in_units(avals, u_length))), ('unit', u_length)]) calc['cohesive-energy-relation']['cohesive-energy'] = DataModelDict([('value', list(uc.get_in_units(evals, u_length))), ('unit', u_energy)]) with open('results.json', 'w') as f: output.json(fp=f, indent=4)
def dump(system, fname, units='metal', atom_style='atomic'): """ Write a LAMMPS-style atom data file from a System. Argument: system -- System to write to the atom data file. fname -- name (and location) of file to save data to. Keyword Arguments: atom_style -- LAMMPS atom_style option associated with the data file. Default is 'atomic'. units -- LAMMPS units option associated with the data file. Default is 'metal'. When the file is written, the units of all property values are automatically converted from atomman's working units to the LAMMPS style units. """ #wrap atoms because LAMMPS hates atoms out of bounds in atom data files system.wrap() #get unit information according to the units style units_dict = style.unit(units) length = units_dict['length'] #open file with open(fname,'w') as f: #header info f.write('\n%i atoms\n' % system.natoms) f.write('%i atom types\n' % system.natypes) #f.write('%f %f xlo xhi\n' % (uc.get_in_units(system.box.xlo, length), uc.get_in_units(system.box.xhi, length))) #f.write('%f %f ylo yhi\n' % (uc.get_in_units(system.box.ylo, length), uc.get_in_units(system.box.yhi, length))) #f.write('%f %f zlo zhi\n' % (uc.get_in_units(system.box.zlo, length), uc.get_in_units(system.box.zhi, length))) f.write('%s %s xlo xhi\n' % (repr(uc.get_in_units(system.box.xlo, length)), repr(uc.get_in_units(system.box.xhi, length)))) f.write('%s %s ylo yhi\n' % (repr(uc.get_in_units(system.box.ylo, length)), repr(uc.get_in_units(system.box.yhi, length)))) f.write('%s %s zlo zhi\n' % (repr(uc.get_in_units(system.box.zlo, length)), repr(uc.get_in_units(system.box.zhi, length)))) #add tilts if tilt values not equal to zero if system.box.xy == 0.0 and system.box.xz == 0.0 and system.box.yz == 0.0: pass else: #f.write('%f %f %f xy xz yz\n' % (uc.get_in_units(system.box.xy, length), uc.get_in_units(system.box.xz, length), uc.get_in_units(system.box.yz, length))) f.write('%s %s %s xy xz yz\n' % (repr(uc.get_in_units(system.box.xy, length)), repr(uc.get_in_units(system.box.xz, length)), repr(uc.get_in_units(system.box.yz, length)))) #Write atom info f.write('\nAtoms\n\n') props = style.atom(atom_style) #Count how many terms are being printed all_size = 0 for v in props.itervalues(): all_size += v[0] outarray = np.empty((system.natoms, all_size)) start = 0 print_string = '' #iterate over all properties and set values for printing for name, v in props.iteritems(): size, dim, dtype = v try: unit = units_dict[dim] except: unit = None if np.issubdtype(dtype, 'int64'): for i in xrange(size): print_string += ' %i' else: for i in xrange(size): print_string += ' %.13e' if name == 'a_id': outarray[:, start:start+size] = np.arange(1, system.natoms+1).reshape((system.natoms, size)) else: outarray[:, start:start+size] = uc.get_in_units(system.atoms_prop(key=name), unit).reshape((system.natoms, size)) start += size print_string = print_string.strip() + '\n' #iterate over all atoms for i in xrange(system.natoms): f.write(print_string % tuple(outarray[i])) #Test for velocity info v_test = system.atoms_prop(a_id=0, key='velocity') if v_test is not None: #Write velocity info f.write('\nVelocities\n\n') props = style.velocity(atom_style) #Count how many terms are being printed all_size = 0 for v in props.itervalues(): all_size += v[0] outarray = np.empty((system.natoms, all_size)) start = 0 print_string = '' #iterate over all properties and set values for printing for name, v in props.iteritems(): size, dim, dtype = v try: unit = units_dict[dim] except: unit = None if np.issubdtype(dtype, 'int64'): for i in xrange(size): print_string += ' %i' else: for i in xrange(size): print_string += ' %.13e' if name == 'a_id': outarray[:, start:start+size] = np.arange(1, system.natoms+1).reshape((system.natoms, size)) else: outarray[:, start:start+size] = uc.get_in_units(system.atoms_prop(key=name), unit).reshape((system.natoms, size)) start += size print_string = print_string.strip() + '\n' #iterate over all atoms for i in xrange(system.natoms): f.write(print_string % tuple(outarray[i])) #return appropriate unts, atom_style, boundary, and read_data LAMMPS commands boundary = '' for i in xrange(3): if system.pbc[i]: boundary += 'p ' else: boundary += 'm ' newline = '\n' script = newline.join(['#Script and atom data file prepared by AtomMan package', '', 'units ' + units, 'atom_style ' + atom_style, '' 'boundary ' + boundary, 'read_data ' + fname]) return script
def E_gsf_vs_x_plot(self, x=None, disregistry=None, figsize=None, length_unit='Å', energyperarea_unit='eV/Å^2'): """ Generates a plot of the stacking fault energy, i.e. misfit energy, associated with the disregistry values for each x coordinate. Parameters ---------- x : numpy.ndarray, optional x-coordinates. Default value is the stored x-coordinates. If x or disregistry are not set/given, then the disregistry path will not be added. disregistry : numpy.ndarray, optional (N, 3) shaped array of disregistry vectors at each x-coordinate. Default value is the stored disregistry values. If x or disregistry are not set/given, then the disregistry path will not be added. figsize : tuple or None, optional The figure's x,y dimensions. If None (default), then a figure size of (10, 6) will be generated. length_unit : str, optional The unit of length to display x coordinates in. Default value is 'Å'. energyperarea_unit : str, optional The unit of energy per area to display the stacking fault energies in. Default value is 'eV/Å^2'. Returns ------- matplotlib.pyplot.figure The generated figure allowing users to perform additional modifications. """ hasvals = True # Default values are class properties if x is None: try: x = self.x except: hasvals = False if disregistry is None: try: disregistry = self.disregistry except: hasvals = False if hasvals: if figsize is None: figsize = (10, 6) gsf = self.gamma.E_gsf(pos=disregistry.dot(self.transform)) fig = plt.figure(figsize=figsize) plt.plot(uc.get_in_units(x, length_unit), uc.get_in_units(gsf, energyperarea_unit)) plt.xlabel(f'x-coordinate (${length_unit}$)', size='xx-large') plt.ylabel(f'Stacking fault energy (${energyperarea_unit}$)', size='xx-large') return fig else: print( 'x and disregistry must be set/given to check stacking fault energies' )
def interpolate_contour(system, name, property=None, index=None, magnitude=False, plotxaxis='x', plotyaxis='y', xlim=None, ylim=None, zlim=None, xbins=200, ybins=200, dots=True, czero=True, save=False, show=True, length_unit='angstrom', property_unit=None, cmap='jet'): """ Creates a contour plot of a system's per-atom properties by interpolating properties between atoms. Parameters ---------- system : atomman.System The system with the per-atom property that you want to plot. name : str The name of the per-atom property that you want to plot. property : array-like object, optional Values for the per-atom property to plot. If not given, values will be taken as the "name" property of system. index : int or tuple, optional Specifies which component of a multidimensional property to plot. Not needed if the property is scalar. magnitude : bool, optional If True, plots the per-atom magnitude of a vector property. Cannot be combined with index. Default value is False. plotxaxis : str or array-like object, optional Indicates the Cartesian direction associated with the system's atomic coordinates to align with the plotting x-axis. Values are either 3D unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes directions. plotxaxis and plotyaxis must be orthogonal. Default value is 'x' = [1, 0, 0]. plotyaxis : str or array-like object, optional Indicates the Cartesian direction associated with the system's atomic coordinates to align with the plotting y-axis. Values are either 3D unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes directions. plotxaxis and plotyaxis must be orthogonal. Default value is 'y' = [0, 1, 0]. xlim : tuple, optional The minimum and maximum coordinates along the plotting x-axis to include in the fit. Values are taken in the specified length_unit. If not given, then the limits are set based on min and max atomic coordinates along the plotting axis. ylim : tuple, optional The minimum and maximum coordinates along the plotting y-axis to include in the fit. Values are taken in the specified length_unit. If not given, then the limits are set based on min and max atomic coordinates along the plotting axis. zlim : tuple, optional The minimum and maximum coordinates normal to the plotting axes (i.e. plotxaxis X plotyaxis) to include in the fit. Values are taken in the specified length_unit. If not given, then the limits are set based on min and max atomic coordinates along the axis. xbins : int, optional Specifies the number of interpolation bins to use along the plotting x-axis. Default value is 200. ybins : int, optional Specifies the number of interpolation bins to use along the plotting y-axis. Default value is 200. dots : bool, optional If True, then the positions of the atoms are shown as circles. Default value is True. czero : bool, optional If True, the range of property values will be centered around zero, i.e. cmax = -cmin. If False, cmax and cmin will be independently selected using the property values. Default value is True. save : bool, optional If True, the generated plot will be saved to "name.png". Default value is False. show : bool, optional If True, matplotlib.pyplot.show() is called. Default value is True. length_unit : str, optional The units of length to use for the plotting x- and y-axes. Default value is 'angstrom'. property_unit : str or None, optional The units to use for the property value being plotted. Default value is None, in which no unit conversion is applied. cmap : str, optional The name of the matplotlib colormap to use. Default value is 'jet'. Returns ------- intsum : float The area integrated sum of the property over the plotted region. avsum : float The average property value taken across all plotting bins. """ # Give numeric values for str plot axis terms if plotxaxis == 'x': plotxaxis = [1.0, 0.0, 0.0] elif plotxaxis == 'y': plotxaxis = [0.0, 1.0, 0.0] elif plotxaxis == 'z': plotxaxis = [0.0, 0.0, 1.0] if plotyaxis == 'x': plotyaxis = [1.0, 0.0, 0.0] elif plotyaxis == 'y': plotyaxis = [0.0, 1.0, 0.0] elif plotyaxis == 'z': plotyaxis = [0.0, 0.0, 1.0] # Build transformation matrix, T, from plot axes. plotxaxis = np.asarray(plotxaxis, dtype='float64') plotyaxis = np.asarray(plotyaxis, dtype='float64') T = axes_check([plotxaxis, plotyaxis, np.cross(plotxaxis, plotyaxis)]) # Extract positions and transform using T pos = uc.get_in_units(np.inner(system.atoms.pos, T), length_unit) # Set plot limits if xlim is None: xlim = (pos[:, 0].min(), pos[:, 0].max()) if ylim is None: ylim = (pos[:, 1].min(), pos[:, 1].max()) if zlim is None: zlim = (pos[:, 2].min(), pos[:, 2].max()) # Extract property values if property is None: property = system.atoms.view[name] # Handle index if index is not None: assert magnitude is False, 'index and magnitude cannot be combined' if isinstance(index, inttype): index = [index] else: index = list(index) for i in index: name += '[' + str(i + 1) + ']' property = property[tuple([Ellipsis] + index)] # Handle magnitude elif magnitude is True: property = np.linalg.norm(property, axis=1) name += '_mag' assert property.shape == ( system.natoms, ), 'property to plot must be a per-atom scalar' # Identify only atoms in xlim, ylim, zlim in_bounds = ((pos[:, 0] > xlim[0] - 5.) & (pos[:, 0] < xlim[1] + 5.) & (pos[:, 1] > ylim[0] - 5.) & (pos[:, 1] < ylim[1] + 5.) & (pos[:, 2] > zlim[0]) & (pos[:, 2] < zlim[1])) # Extract plotting coordinates and values x = pos[in_bounds, 0] y = pos[in_bounds, 1] v = uc.get_in_units(property[in_bounds], property_unit) # Generate interpolation grid grid, xedges, yedges = grid_interpolate_2d(x, y, v, xbins=xbins, ybins=ybins, range=[xlim, ylim]) # Compute intsum and avsum values intsum = np.sum(grid) avsum = intsum / (xbins - 1) / (ybins - 1) intsum = intsum * (xlim[1] - xlim[0]) / (xbins - 1) * ( ylim[1] - ylim[0]) / (ybins - 1) # Generate a pretty figure of the grid fig = prettygrid(grid, xedges, yedges, cmap=cmap, propname=name, czero=czero) # Add dots if dots: adddots(x, y, xedges, yedges) # Save, show and close figure if save is True: plt.savefig(name + '.png', dpi=800) if show is True: plt.show() plt.close(fig) return intsum, avsum
def interpolate_contour(system, property, index=None, plotbounds=None, bins=200, dots=True, czero=True, save=False, show=True, length_unit='angstrom', property_unit=None, cmap='jet'): """Creates a contour plot of a system's per-atom properties by interpolating between all of the atoms.""" if save is False and show is False: print 'Figure not saved or displayed!' values = system.atoms_prop(key=property) assert values is not None, 'atomic property not found!' name = str(property) if index is not None: if isinstance(index, (int, long)): index = [index] else: index = list(index) for i in index: name += '[' + str(i + 1) + ']' values = values[[Ellipsis] + index] assert values.shape == ( system.natoms, ), 'Identified property[index] must be a per-atom scalar' if plotbounds is None: plotbounds = np.array([[system.box.xlo, system.box.xhi], [system.box.ylo, system.box.yhi], [system.box.zlo, system.box.zhi]]) plotbounds = uc.get_in_units(plotbounds, length_unit) #Build arrays containing atomic information pos = uc.get_in_units(system.atoms_prop(key='pos'), length_unit) #identify only atoms in plotbounds in_bounds = ((pos[:, 0] > plotbounds[0, 0] - 5.) & (pos[:, 0] < plotbounds[0, 1] + 5.) & (pos[:, 1] > plotbounds[1, 0] - 5.) & (pos[:, 1] < plotbounds[1, 1] + 5.) & (pos[:, 2] > plotbounds[2, 0]) & (pos[:, 2] < plotbounds[2, 1])) x = pos[in_bounds, 0] y = pos[in_bounds, 1] v = values[in_bounds] box = plotbounds[:2, :2] grid, xedges, yedges = grid_interpolate_2d(x, y, v, bins=bins, range=box) intsum = np.sum(grid) avsum = intsum / (bins - 1) / (bins - 1) intsum = intsum * (plotbounds[0, 1] - plotbounds[0, 0]) / (bins - 1) * ( plotbounds[1, 1] - plotbounds[1, 0]) / (bins - 1) fig = prettygrid(grid, xedges, yedges, cmap=cmap, propname=name, czero=czero) if dots: adddots(x, y, xedges, yedges) if save: plt.savefig(name + '.png', dpi=800) if show == False: plt.close(fig) plt.show() return intsum, avsum
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 integrator_info(integrator=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, randomseed=None, units='metal'): """ Generates LAMMPS commands for velocity creation and fix integrators. Parameters ---------- 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.) 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). temperature : float, optional The temperature to relax at (default is 0.0). 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.) units : str, optional The LAMMPS units style to use (default is 'metal'). Returns ------- str The generated LAMMPS input lines for velocity create and fix integration commands. """ # Get lammps units lammps_units = lmp.style.unit(units) Px = uc.get_in_units(p_xx, lammps_units['pressure']) Py = uc.get_in_units(p_yy, lammps_units['pressure']) Pz = uc.get_in_units(p_zz, lammps_units['pressure']) Pxy = uc.get_in_units(p_xy, lammps_units['pressure']) Pxz = uc.get_in_units(p_xz, lammps_units['pressure']) Pyz = uc.get_in_units(p_yz, lammps_units['pressure']) T = temperature # Check temperature and set default integrator if temperature == 0.0: if integrator is None: integrator = 'nph+l' assert integrator not in ['npt', 'nvt'], 'npt and nvt cannot run at 0 K' elif temperature > 0: if integrator is None: integrator = 'npt' else: raise ValueError('Temperature must be positive') # Set default randomseed if randomseed is None: randomseed = random.randint(1, 900000000) if integrator == 'npt': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) Pdamp = 1000 * lmp.style.timestep(units) int_info = '\n'.join([ 'velocity all create %f %i' % (start_temp, randomseed), 'fix npt all npt temp %f %f %f &' % (T, T, Tdamp), ' x %f %f %f &' % (Px, Px, Pdamp), ' y %f %f %f &' % (Py, Py, Pdamp), ' z %f %f %f &' % (Pz, Pz, Pdamp), ' xy %f %f %f &' % (Pxy, Pxy, Pdamp), ' xz %f %f %f &' % (Pxz, Pxz, Pdamp), ' yz %f %f %f' % (Pyz, Pyz, Pdamp), ]) elif integrator == 'nvt': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) int_info = '\n'.join([ 'velocity all create %f %i' % (start_temp, randomseed), 'fix nvt all nvt temp %f %f %f' % (T, T, Tdamp), ]) elif integrator == 'nph': Pdamp = 1000 * lmp.style.timestep(units) int_info = '\n'.join([ 'fix nph all nph x %f %f %f &' % (Px, Px, Pdamp), ' y %f %f %f &' % (Py, Py, Pdamp), ' z %f %f %f &' % (Pz, Pz, Pdamp), ' xy %f %f %f &' % (Pxy, Pxy, Pdamp), ' xz %f %f %f &' % (Pxz, Pxz, Pdamp), ' yz %f %f %f' % (Pyz, Pyz, Pdamp), ]) elif integrator == 'nve': int_info = 'fix nve all nve' elif integrator == 'nve+l': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) int_info = '\n'.join([ 'velocity all create %f %i' % (start_temp, randomseed), 'fix nve all nve', 'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp, randomseed), ]) elif integrator == 'nph+l': start_temp = T*2.+1 Tdamp = 100 * lmp.style.timestep(units) Pdamp = 1000 * lmp.style.timestep(units) int_info = '\n'.join([ 'fix nph all nph x %f %f %f &' % (Px, Px, Pdamp), ' y %f %f %f &' % (Py, Py, Pdamp), ' z %f %f %f &' % (Pz, Pz, Pdamp), ' xy %f %f %f &' % (Pxy, Pxy, Pdamp), ' xz %f %f %f &' % (Pxz, Pxz, Pdamp), ' yz %f %f %f' % (Pyz, Pyz, Pdamp), 'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp, randomseed), ]) else: raise ValueError('Invalid integrator style') return int_info