Пример #1
0
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    
Пример #2
0
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
Пример #3
0
    def run(self):
        self.update()
        self.system.update()
        dL = dLmps.DataLammps(self.system)
        dL.genFile()
        self.create_in()

        import atomman.lammps as lmp
        output = lmp.run(self.lammps_exe, self.in_lmp, return_style='object')

        self.parse_log()
Пример #4
0
    def run(self):
        self.calls += 1

        dL = dLmps.DataLammps(self.settings)
        dL.genFile()  # create data.lamp
        self.scriptLammps.create_in()  # create in.min

        import atomman.lammps as lmp
        output = lmp.run(self.lammps_exe, self.in_lmp, return_style='object')

        self.parse_log()
Пример #5
0
def energy_check(lammps_command: str,
                 system: am.System,
                 potential: lmp.Potential,
                 mpi_command: Optional[str] = None) -> dict:
    """
    Performs a quick run 0 calculation to evaluate the potential energy of a
    configuration.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The atomic configuration to evaluate.
    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.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        - **'E_pot'** (*float*) - The per-atom potential energy of the system.
    """
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='init.dat',
                              potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info

    # Fill in lammps input script
    template = read_calc_file('iprPy.calculation.energy_check', 'run0.template')
    script = filltemplate(template, lammps_variables, '<', '>')
    
    # Run LAMMPS
    output = lmp.run(lammps_command, script=script,
                     mpi_command=mpi_command, logfile=None)
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    results = {}
    results['E_pot'] = uc.set_in_units(thermo.v_peatom.values[-1],
                                       lammps_units['energy'])
    
    return results
    
Пример #6
0
def ecoh_vs_r(lammps_exe, ucell, potential, symbols, rmin=2.0, rmax=5.0, rsteps=200):
    """Measure cohesive energy of a crystal as a function of nearest neighbor distance, r0"""
    
    #Initial size setup
    r_a = r_a_ratio(ucell)
    amin = rmin / r_a
    amax = rmax / r_a
    alat0 = (amax + amin) / 2.
    delta = (amax-amin)/alat0
    
    #Create unit cell with a = alat0
    ucell.box_set(a = alat0, b = alat0 * ucell.box.b / ucell.box.a, c = alat0 * ucell.box.c / ucell.box.a, scale=True)

    #LAMMPS script setup
    pair_info = potential.pair_info(symbols)
    
    system_info = lmp.sys_gen(units =       potential.units,
                              atom_style =  potential.atom_style,
                              ucell =       ucell,
                              size =        np.array([[0,3], [0,3], [0,3]], dtype=np.int))

    #Write the LAMMPS input file
    with open('alat.in','w') as script:
        script.write(alat_script(system_info, pair_info, delta=delta, steps=rsteps))
    
    #Run LAMMPS
    data = lmp.run(lammps_exe, 'alat.in')
 
    #extract thermo data from log output 
    avalues = np.array(data.finds('Lx')) / 3.
    rvalues = avalues * r_a
    evalues = np.array(data.finds('peatom'))  
        
    #Use potential units and atom_style terms to convert values to appropriate length and energy units
    lmp_units = lmp.style.unit(potential.units)
    avalues = uc.set_in_units(avalues, lmp_units['length'])
    rvalues = uc.set_in_units(rvalues, lmp_units['length'])
    evalues = uc.set_in_units(evalues, lmp_units['energy'])
        
    return rvalues, avalues, evalues    
Пример #7
0
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
Пример #8
0
def diatom_scan(lammps_command: str,
                potential: am.lammps.Potential,
                symbols: list,
                mpi_command: Optional[str] = None,
                rmin: float = uc.set_in_units(0.02, 'angstrom'),
                rmax: float = uc.set_in_units(6.0, 'angstrom'),
                rsteps: int = 300) -> dict:
    """
    Performs a diatom energy scan over a range of interatomic spaces, r.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    symbols : list
        The potential symbols associated with the two atoms in the diatom.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    rmin : float, optional
        The minimum r spacing to use (default value is 0.02 angstroms).
    rmax : float, optional
        The maximum r spacing to use (default value is 6.0 angstroms).
    rsteps : int, optional
        The number of r spacing steps to evaluate (default value is 300).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'r_values'** (*numpy.array of float*) - All interatomic spacings,
          r, explored.
        - **'energy_values'** (*numpy.array of float*) - The computed potential
          energies for each r value.
    """

    # Build lists of values
    r_values = np.linspace(rmin, rmax, rsteps)
    energy_values = np.empty(rsteps)

    # Define atype based on symbols
    symbols = aslist(symbols)
    if len(symbols) == 1:
        atype = [1, 1]
    elif len(symbols) == 2:
        atype = [1, 2]
    else:
        raise ValueError('symbols must have one or two values')

    # Initialize system (will shift second atom's position later...)
    box = am.Box.cubic(a=rmax + 1)
    atoms = am.Atoms(atype=atype, pos=[[0.1, 0.1, 0.1], [0.1, 0.1, 0.1]])
    system = am.System(atoms=atoms,
                       box=box,
                       pbc=[False, False, False],
                       symbols=symbols)

    # Add charges if required
    if potential.atom_style == 'charge':
        system.atoms.prop_atype('charge', potential.charges(system.symbols))

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Define lammps variables
    lammps_variables = {}

    # Loop over values
    for i in range(rsteps):

        # Shift second atom's x position
        system.atoms.pos[1] = np.array([0.1 + r_values[i], 0.1, 0.1])

        # Save configuration
        system_info = system.dump('atom_data',
                                  f='diatom.dat',
                                  potential=potential)
        lammps_variables['atomman_system_pair_info'] = system_info

        # Write lammps input script
        lammps_script = 'run0.in'
        template = read_calc_file('iprPy.calculation.diatom_scan',
                                  'run0.template')
        with open(lammps_script, 'w') as f:
            f.write(filltemplate(template, lammps_variables, '<', '>'))

        # Run lammps and extract data
        try:
            output = lmp.run(lammps_command,
                             script_name=lammps_script,
                             mpi_command=mpi_command)
        except:
            energy_values[i] = np.nan
        else:
            energy = output.simulations[0]['thermo'].PotEng.values[-1]
            energy_values[i] = uc.set_in_units(energy, lammps_units['energy'])

    if len(energy_values[np.isfinite(energy_values)]) == 0:
        raise ValueError(
            'All LAMMPS runs failed. Potential likely invalid or incompatible.'
        )

    # Collect results
    results_dict = {}
    results_dict['r_values'] = r_values
    results_dict['energy_values'] = energy_values

    return results_dict
Пример #9
0
def relax_dynamic(lammps_command, system, potential, mpi_command=None,
                  p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0, p_xz=0.0, p_yz=0.0,
                  temperature=0.0, integrator=None, runsteps=220000,
                  thermosteps=100, dumpsteps=None, equilsteps=20000,
                  randomseed=None):
    """
    Performs a full dynamic relax on a given system at the given temperature
    to the specified pressure state.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    symbols : list of str
        The list of element-model symbols for the Potential that correspond to
        system's atypes.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    temperature : float, optional
        The temperature to relax at (default is 0.0).
    runsteps : int, optional
        The number of integration steps to perform (default is 220000).
    integrator : str or None, optional
        The integration method to use. Options are 'npt', 'nvt', 'nph',
        'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat.
        (Default is None, which will use 'nph+l' for temperature == 0, and
        'npt' otherwise.)
    thermosteps : int, optional
        Thermo values will be reported every this many steps (default is
        100).
    dumpsteps : int or None, optional
        Dump files will be saved every this many steps (default is None,
        which sets dumpsteps equal to runsteps).
    equilsteps : int, optional
        The number of timesteps at the beginning of the simulation to
        exclude when computing average values (default is 20000).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'relaxed_system'** (*float*) - The relaxed system.
        - **'E_coh'** (*float*) - The mean measured cohesive energy.
        - **'measured_pxx'** (*float*) - The measured x tensile pressure of the
          relaxed system.
        - **'measured_pyy'** (*float*) - The measured y tensile pressure of the
          relaxed system.
        - **'measured_pzz'** (*float*) - The measured z tensile pressure of the
          relaxed system.
        - **'measured_pxy'** (*float*) - The measured xy shear pressure of the
          relaxed system.
        - **'measured_pxz'** (*float*) - The measured xz shear pressure of the
          relaxed system.
        - **'measured_pyz'** (*float*) - The measured yz shear pressure of the
          relaxed system.
        - **'temp'** (*float*) - The mean measured temperature.
        - **'E_coh_std'** (*float*) - The standard deviation in the measured
          cohesive energy values.
        - **'measured_pxx_std'** (*float*) - The standard deviation in the
          measured x tensile pressure of the relaxed system.
        - **'measured_pyy_std'** (*float*) - The standard deviation in the
          measured y tensile pressure of the relaxed system.
        - **'measured_pzz_std'** (*float*) - The standard deviation in the
          measured z tensile pressure of the relaxed system.
        - **'measured_pxy_std'** (*float*) - The standard deviation in the
          measured xy shear pressure of the relaxed system.
        - **'measured_pxz_std'** (*float*) - The standard deviation in the
          measured xz shear pressure of the relaxed system.
        - **'measured_pyz_std'** (*float*) - The standard deviation in the
          measured yz shear pressure of the relaxed system.
        - **'temp_std'** (*float*) - The standard deviation in the measured
          temperature values.
    """
    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Handle default values
    if dumpsteps is None:
        dumpsteps = runsteps
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='init.dat',
                              potential=potential,
                              return_pair_info=True)
    lammps_variables['atomman_system_pair_info'] = system_info
    
    integ_info = integrator_info(integrator=integrator,
                                 p_xx=p_xx, p_yy=p_yy, p_zz=p_zz,
                                 p_xy=p_xy, p_xz=p_xz, p_yz=p_yz,
                                 temperature=temperature,
                                 randomseed=randomseed,
                                 units=potential.units)
    lammps_variables['integrator_info'] = integ_info
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['runsteps'] = runsteps
    lammps_variables['dumpsteps'] = dumpsteps
    
    # Set compute stress/atom based on LAMMPS version
    if lammps_date < datetime.date(2014, 2, 12):
        lammps_variables['stressterm'] = ''
    else:
        lammps_variables['stressterm'] = 'NULL'
    
    # Set dump_keys based on atom_style
    if potential.atom_style in ['charge']:
        lammps_variables['dump_keys'] = 'id type q xu yu zu c_pe c_ke &\n'
        lammps_variables['dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]'
    else:
        lammps_variables['dump_keys'] = 'id type xu yu zu c_pe c_ke &\n'
        lammps_variables['dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]'

    
    # Set dump_modify_format based on lammps_date
    if lammps_date < datetime.date(2016, 8, 3):
        if potential.atom_style in ['charge']:
            lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = Path(script_dir, 'full_relax.template')
    lammps_script = 'full_relax.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    # Run lammps 
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract LAMMPS thermo data. 
    results = {}
    thermo = output.simulations[0]['thermo']
    
    results['dumpfile_initial'] = '0.dump'
    results['symbols_initial'] = system.symbols
    
    # Load relaxed system from dump file
    last_dump_file = str(thermo.Step.values[-1])+'.dump'
    results['dumpfile_final'] = last_dump_file
    system = am.load('atom_dump', last_dump_file, symbols=system.symbols)
    results['symbols_final'] = system.symbols
    
    # Only consider values where Step >= equilsteps
    thermo = thermo[thermo.Step >= equilsteps]
    results['nsamples'] = len(thermo)
    
    # Get cohesive energy estimates
    natoms = system.natoms
    results['E_coh'] = uc.set_in_units(thermo.PotEng.mean() / natoms, lammps_units['energy'])
    results['E_coh_std'] = uc.set_in_units(thermo.PotEng.std() / natoms, lammps_units['energy'])
    
    results['lx'] = uc.set_in_units(thermo.Lx.mean(), lammps_units['length'])
    results['lx_std'] = uc.set_in_units(thermo.Lx.std(), lammps_units['length'])
    results['ly'] = uc.set_in_units(thermo.Ly.mean(), lammps_units['length'])
    results['ly_std'] = uc.set_in_units(thermo.Ly.std(), lammps_units['length'])
    results['lz'] = uc.set_in_units(thermo.Lz.mean(), lammps_units['length'])
    results['lz_std'] = uc.set_in_units(thermo.Lz.std(), lammps_units['length'])
    results['xy'] = uc.set_in_units(thermo.Xy.mean(), lammps_units['length'])
    results['xy_std'] = uc.set_in_units(thermo.Xy.std(), lammps_units['length'])
    results['xz'] = uc.set_in_units(thermo.Xz.mean(), lammps_units['length'])
    results['xz_std'] = uc.set_in_units(thermo.Xz.std(), lammps_units['length'])
    results['yz'] = uc.set_in_units(thermo.Yz.mean(), lammps_units['length'])
    results['yz_std'] = uc.set_in_units(thermo.Yz.std(), lammps_units['length'])
    
    results['measured_pxx'] = uc.set_in_units(thermo.Pxx.mean(), lammps_units['pressure'])
    results['measured_pxx_std'] = uc.set_in_units(thermo.Pxx.std(), lammps_units['pressure'])
    results['measured_pyy'] = uc.set_in_units(thermo.Pyy.mean(), lammps_units['pressure'])
    results['measured_pyy_std'] = uc.set_in_units(thermo.Pyy.std(), lammps_units['pressure'])
    results['measured_pzz'] = uc.set_in_units(thermo.Pzz.mean(), lammps_units['pressure'])
    results['measured_pzz_std'] = uc.set_in_units(thermo.Pzz.std(), lammps_units['pressure'])
    results['measured_pxy'] = uc.set_in_units(thermo.Pxy.mean(), lammps_units['pressure'])
    results['measured_pxy_std'] = uc.set_in_units(thermo.Pxy.std(), lammps_units['pressure'])
    results['measured_pxz'] = uc.set_in_units(thermo.Pxz.mean(), lammps_units['pressure'])
    results['measured_pxz_std'] = uc.set_in_units(thermo.Pxz.std(), lammps_units['pressure'])
    results['measured_pyz'] = uc.set_in_units(thermo.Pyz.mean(), lammps_units['pressure'])
    results['measured_pyz_std'] = uc.set_in_units(thermo.Pyz.std(), lammps_units['pressure'])
    results['temp'] = thermo.Temp.mean()
    results['temp_std'] = thermo.Temp.std()
    
    return results
Пример #10
0
def calc_cij(lammps_command,
             system,
             potential,
             mpi_command=None,
             p_xx=0.0,
             p_yy=0.0,
             p_zz=0.0,
             strainrange=1e-6,
             cycle=0):
    """
    Runs cij.in LAMMPS script to evaluate Cij, and E_coh of the current system,
    and define a new system with updated box dimensions to test.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    strainrange : float, optional
        The small strain value to apply when calculating the elastic
        constants (default is 1e-6).
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    cycle : int, optional
        Indicates the iteration cycle of quick_a_Cij().  This is used to
        uniquely save the LAMMPS input and output files.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'E_coh'** (*float*) - The cohesive energy of the supplied system.
        - **'stress'** (*numpy.array*) - The measured stress state of the
          supplied system.
        - **'C_elastic'** (*atomman.ElasticConstants*) - The supplied system's
          elastic constants.
        - **'system_new'** (*atomman.System*) - System with updated box
          dimensions.
    
    Raises
    ------
    RuntimeError
        If any of the new box dimensions are less than zero.
    """
    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data',
                              f='init' + str(cycle) + '.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['delta'] = strainrange
    lammps_variables['steps'] = 2

    # Write lammps input script
    template_file = Path(script_dir, 'cij.template')
    lammps_script = 'cij.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

    # Run lammps
    output = lmp.run(lammps_command,
                     lammps_script,
                     mpi_command=mpi_command,
                     return_style='model')
    shutil.move('log.lammps', 'cij-' + str(cycle) + '-log.lammps')

    # Extract LAMMPS thermo data. Each term ranges i=0-12 where i=0 is undeformed
    # The remaining values are for -/+ strain pairs in the six unique directions
    lx = uc.set_in_units(np.array(output.finds('Lx')), lammps_units['length'])
    ly = uc.set_in_units(np.array(output.finds('Ly')), lammps_units['length'])
    lz = uc.set_in_units(np.array(output.finds('Lz')), lammps_units['length'])
    xy = uc.set_in_units(np.array(output.finds('Xy')), lammps_units['length'])
    xz = uc.set_in_units(np.array(output.finds('Xz')), lammps_units['length'])
    yz = uc.set_in_units(np.array(output.finds('Yz')), lammps_units['length'])

    pxx = uc.set_in_units(np.array(output.finds('Pxx')),
                          lammps_units['pressure'])
    pyy = uc.set_in_units(np.array(output.finds('Pyy')),
                          lammps_units['pressure'])
    pzz = uc.set_in_units(np.array(output.finds('Pzz')),
                          lammps_units['pressure'])
    pxy = uc.set_in_units(np.array(output.finds('Pxy')),
                          lammps_units['pressure'])
    pxz = uc.set_in_units(np.array(output.finds('Pxz')),
                          lammps_units['pressure'])
    pyz = uc.set_in_units(np.array(output.finds('Pyz')),
                          lammps_units['pressure'])

    pe = uc.set_in_units(
        np.array(output.finds('PotEng')) / system.natoms,
        lammps_units['energy'])

    # Set the six non-zero strain values
    strains = np.array([(lx[2] - lx[1]) / lx[0], (ly[4] - ly[3]) / ly[0],
                        (lz[6] - lz[5]) / lz[0], (yz[8] - yz[7]) / lz[0],
                        (xz[10] - xz[9]) / lz[0], (xy[12] - xy[11]) / ly[0]])

    # Calculate cij using stress changes associated with each non-zero strain
    cij = np.empty((6, 6))
    for i in range(6):
        delta_stress = np.array([
            pxx[2 * i + 1] - pxx[2 * i + 2], pyy[2 * i + 1] - pyy[2 * i + 2],
            pzz[2 * i + 1] - pzz[2 * i + 2], pyz[2 * i + 1] - pyz[2 * i + 2],
            pxz[2 * i + 1] - pxz[2 * i + 2], pxy[2 * i + 1] - pxy[2 * i + 2]
        ])

        cij[i] = delta_stress / strains[i]

    for i in range(6):
        for j in range(i):
            cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2

    C = am.ElasticConstants(Cij=cij)

    S = C.Sij

    # Extract the current stress state
    stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]], [pxy[0], pyy[0], pyz[0]],
                            [pxz[0], pyz[0], pzz[0]]])

    s_xx = stress[0, 0] + p_xx
    s_yy = stress[1, 1] + p_yy
    s_zz = stress[2, 2] + p_zz

    new_a = system.box.a / (S[0, 0] * s_xx + S[0, 1] * s_yy + S[0, 2] * s_zz +
                            1)
    new_b = system.box.b / (S[1, 0] * s_xx + S[1, 1] * s_yy + S[1, 2] * s_zz +
                            1)
    new_c = system.box.c / (S[2, 0] * s_xx + S[2, 1] * s_yy + S[2, 2] * s_zz +
                            1)

    if new_a <= 0 or new_b <= 0 or new_c <= 0:
        raise RuntimeError('Divergence of box dimensions to <= 0')

    system_new = deepcopy(system)
    system_new.box_set(a=new_a, b=new_b, c=new_c, scale=True)

    results_dict = {}
    results_dict['E_coh'] = pe[0]
    results_dict['system_new'] = system_new
    results_dict['measured_pxx'] = pxx[0]
    results_dict['measured_pyy'] = pyy[0]
    results_dict['measured_pzz'] = pzz[0]
    results_dict['measured_pxy'] = pxy[0]
    results_dict['measured_pxz'] = pxz[0]
    results_dict['measured_pyz'] = pyz[0]
    return results_dict
Пример #11
0
def pointdiffusion(lammps_command,
                   system,
                   potential,
                   point_kwargs,
                   mpi_command=None,
                   temperature=300,
                   runsteps=200000,
                   thermosteps=None,
                   dumpsteps=0,
                   equilsteps=20000,
                   randomseed=None):
    """
    Evaluates the diffusion rate of a point defect at a given temperature. This
    method will run two simulations: an NVT run at the specified temperature to 
    equilibrate the system, then an NVE run to measure the defect's diffusion 
    rate. The diffusion rate is evaluated using the mean squared displacement of
    all atoms in the system, and using the assumption that diffusion is only due
    to the added defect(s).
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    point_kwargs : dict or list of dict
        One or more dictionaries containing the keyword arguments for
        the atomman.defect.point() function to generate specific point
        defect configuration(s).
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    temperature : float, optional
        The temperature to run at (default is 300.0).
    runsteps : int, optional
        The number of integration steps to perform (default is 200000).
    thermosteps : int, optional
        Thermo values will be reported every this many steps (default is
        100).
    dumpsteps : int or None, optional
        Dump files will be saved every this many steps (default is 0,
        which does not output dump files).
    equilsteps : int, optional
        The number of timesteps at the beginning of the simulation to
        exclude when computing average values (default is 20000).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'natoms'** (*int*) - The number of atoms in the system.
        - **'temp'** (*float*) - The mean measured temperature.
        - **'pxx'** (*float*) - The mean measured normal xx pressure.
        - **'pyy'** (*float*) - The mean measured normal yy pressure.
        - **'pzz'** (*float*) - The mean measured normal zz pressure.
        - **'Epot'** (*numpy.array*) - The mean measured total potential 
          energy.
        - **'temp_std'** (*float*) - The standard deviation in the measured
          temperature values.
        - **'pxx_std'** (*float*) - The standard deviation in the measured
          normal xx pressure values.
        - **'pyy_std'** (*float*) - The standard deviation in the measured
          normal yy pressure values.
        - **'pzz_std'** (*float*) - The standard deviation in the measured
          normal zz pressure values.
        - **'Epot_std'** (*float*) - The standard deviation in the measured
          total potential energy values.
        - **'dx'** (*float*) - The computed diffusion constant along the 
          x-direction.
        - **'dy'** (*float*) - The computed diffusion constant along the 
          y-direction.
        - **'dz'** (*float*) - The computed diffusion constant along the 
          y-direction.
        - **'d'** (*float*) - The total computed diffusion constant.
    """
    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()

    # Add defect(s) to the initially perfect system
    if not isinstance(point_kwargs, (list, tuple)):
        point_kwargs = [point_kwargs]
    for pkwargs in point_kwargs:
        system = am.defect.point(system, **pkwargs)

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']

    # Check that temperature is greater than zero
    if temperature <= 0.0:
        raise ValueError('Temperature must be greater than zero')

    # Handle default values
    if thermosteps is None:
        thermosteps = runsteps // 1000
        if thermosteps == 0:
            thermosteps = 1
    if dumpsteps is None:
        dumpsteps = runsteps
    if randomseed is None:
        randomseed = random.randint(1, 900000000)

    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data',
                              f='initial.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['temperature'] = temperature
    lammps_variables['runsteps'] = runsteps
    lammps_variables['equilsteps'] = equilsteps
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['dumpsteps'] = dumpsteps
    lammps_variables['randomseed'] = randomseed
    lammps_variables['timestep'] = lmp.style.timestep(potential.units)

    # Set dump_info
    if dumpsteps == 0:
        lammps_variables['dump_info'] = ''
    else:
        lammps_variables['dump_info'] = '\n'.join([
            '',
            '# Define dump files',
            'dump dumpit all custom ${dumpsteps} *.dump id type x y z c_peatom',
            'dump_modify dumpit format <dump_modify_format>',
            '',
        ])

        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables[
                'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'

    # Write lammps input script
    template_file = Path(script_dir, 'diffusion.template')
    lammps_script = 'diffusion.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

    # Run lammps
    output = lmp.run(lammps_command, 'diffusion.in', mpi_command)

    # Extract LAMMPS thermo data.
    thermo = output.simulations[1]['thermo']
    temps = thermo.Temp.values
    pxxs = uc.set_in_units(thermo.Pxx.values, lammps_units['pressure'])
    pyys = uc.set_in_units(thermo.Pyy.values, lammps_units['pressure'])
    pzzs = uc.set_in_units(thermo.Pzz.values, lammps_units['pressure'])
    potengs = uc.set_in_units(thermo.PotEng.values, lammps_units['energy'])
    steps = thermo.Step.values

    # Read user-defined thermo data
    if output.lammps_date < datetime.date(2016, 8, 1):
        msd_x = uc.set_in_units(thermo['msd[1]'].values,
                                lammps_units['length'] + '^2')
        msd_y = uc.set_in_units(thermo['msd[2]'].values,
                                lammps_units['length'] + '^2')
        msd_z = uc.set_in_units(thermo['msd[3]'].values,
                                lammps_units['length'] + '^2')
        msd = uc.set_in_units(thermo['msd[4]'].values,
                              lammps_units['length'] + '^2')
    else:
        msd_x = uc.set_in_units(thermo['c_msd[1]'].values,
                                lammps_units['length'] + '^2')
        msd_y = uc.set_in_units(thermo['c_msd[2]'].values,
                                lammps_units['length'] + '^2')
        msd_z = uc.set_in_units(thermo['c_msd[3]'].values,
                                lammps_units['length'] + '^2')
        msd = uc.set_in_units(thermo['c_msd[4]'].values,
                              lammps_units['length'] + '^2')

    # Initialize results dict
    results = {}
    results['natoms'] = system.natoms

    # Get mean and std for temperature, pressure, and potential energy
    results['temp'] = np.mean(temps)
    results['temp_std'] = np.std(temps)
    results['pxx'] = np.mean(pxxs)
    results['pxx_std'] = np.std(pxxs)
    results['pyy'] = np.mean(pyys)
    results['pyy_std'] = np.std(pyys)
    results['pzz'] = np.mean(pzzs)
    results['pzz_std'] = np.std(pzzs)
    results['Epot'] = np.mean(potengs)
    results['Epot_std'] = np.std(potengs)

    # Convert steps to times
    times = steps * uc.set_in_units(lammps_variables['timestep'],
                                    lammps_units['time'])

    # Estimate diffusion rates
    # MSD_ptd = natoms * MSD_atoms (if one defect in system)
    # MSD = 2 * ndim * D * t  -->  D = MSD/t / (2 * ndim)
    mx = np.polyfit(times, system.natoms * msd_x, 1)[0]
    my = np.polyfit(times, system.natoms * msd_y, 1)[0]
    mz = np.polyfit(times, system.natoms * msd_z, 1)[0]
    m = np.polyfit(times, system.natoms * msd, 1)[0]

    results['dx'] = mx / 2
    results['dy'] = my / 2
    results['dz'] = mz / 2
    results['d'] = m / 6

    return results
Пример #12
0
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
Пример #13
0
def calc_cij(lammps_command,
             system,
             potential,
             symbols,
             p_xx=0.0,
             p_yy=0.0,
             p_zz=0.0,
             delta=1e-5,
             cycle=0):
    """Runs cij_script and returns current Cij, stress, Ecoh, and new system guess."""

    #Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    #Define lammps variables
    lammps_variables = {}
    lammps_variables['atomman_system_info'] = lmp.sys_gen(
        units=potential.units,
        atom_style=potential.atom_style,
        ucell=system,
        size=np.array([[0, 3], [0, 3], [0, 3]]))
    lammps_variables['atomman_pair_info'] = potential.pair_info(symbols)
    lammps_variables['delta'] = delta
    lammps_variables['steps'] = 2

    #Write lammps input script
    template_file = 'cij.template'
    lammps_script = 'cij.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

    #Run lammps
    output = lmp.run(lammps_command, lammps_script)
    shutil.move('log.lammps', 'cij-' + str(cycle) + '-log.lammps')

    #Extract LAMMPS thermo data. Each term ranges i=0-12 where i=0 is undeformed
    #The remaining values are for -/+ strain pairs in the six unique directions
    lx = uc.set_in_units(np.array(output.finds('Lx')), lammps_units['length'])
    ly = uc.set_in_units(np.array(output.finds('Ly')), lammps_units['length'])
    lz = uc.set_in_units(np.array(output.finds('Lz')), lammps_units['length'])
    xy = uc.set_in_units(np.array(output.finds('Xy')), lammps_units['length'])
    xz = uc.set_in_units(np.array(output.finds('Xz')), lammps_units['length'])
    yz = uc.set_in_units(np.array(output.finds('Yz')), lammps_units['length'])

    pxx = uc.set_in_units(np.array(output.finds('Pxx')),
                          lammps_units['pressure'])
    pyy = uc.set_in_units(np.array(output.finds('Pyy')),
                          lammps_units['pressure'])
    pzz = uc.set_in_units(np.array(output.finds('Pzz')),
                          lammps_units['pressure'])
    pxy = uc.set_in_units(np.array(output.finds('Pxy')),
                          lammps_units['pressure'])
    pxz = uc.set_in_units(np.array(output.finds('Pxz')),
                          lammps_units['pressure'])
    pyz = uc.set_in_units(np.array(output.finds('Pyz')),
                          lammps_units['pressure'])
    try:
        pe = uc.set_in_units(np.array(output.finds('v_peatom')),
                             lammps_units['energy'])
        assert len(pe) > 0
    except:
        pe = uc.set_in_units(np.array(output.finds('peatom')),
                             lammps_units['energy'])

    #Set the six non-zero strain values
    strains = np.array([(lx[2] - lx[1]) / lx[0], (ly[4] - ly[3]) / ly[0],
                        (lz[6] - lz[5]) / lz[0], (yz[8] - yz[7]) / lz[0],
                        (xz[10] - xz[9]) / lz[0], (xy[12] - xy[11]) / ly[0]])

    #calculate cij using stress changes associated with each non-zero strain
    cij = np.empty((6, 6))
    for i in xrange(6):
        delta_stress = np.array([
            pxx[2 * i + 1] - pxx[2 * i + 2], pyy[2 * i + 1] - pyy[2 * i + 2],
            pzz[2 * i + 1] - pzz[2 * i + 2], pyz[2 * i + 1] - pyz[2 * i + 2],
            pxz[2 * i + 1] - pxz[2 * i + 2], pxy[2 * i + 1] - pxy[2 * i + 2]
        ])

        cij[i] = delta_stress / strains[i]

    for i in xrange(6):
        for j in xrange(i):
            cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2

    C = am.ElasticConstants(Cij=cij)

    if np.allclose(C.Cij, 0.0):
        raise RuntimeError('Divergence of elastic constants to <= 0')
    try:
        S = C.Sij
    except:
        raise RuntimeError('singular C:\n' + str(C.Cij))

    #extract the current stress state
    stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]], [pxy[0], pyy[0], pyz[0]],
                            [pxz[0], pyz[0], pzz[0]]])

    s_xx = stress[0, 0] + p_xx
    s_yy = stress[1, 1] + p_yy
    s_zz = stress[2, 2] + p_zz

    new_a = system.box.a / (S[0, 0] * s_xx + S[0, 1] * s_yy + S[0, 2] * s_zz +
                            1)
    new_b = system.box.b / (S[1, 0] * s_xx + S[1, 1] * s_yy + S[1, 2] * s_zz +
                            1)
    new_c = system.box.c / (S[2, 0] * s_xx + S[2, 1] * s_yy + S[2, 2] * s_zz +
                            1)

    if new_a <= 0 or new_b <= 0 or new_c <= 0:
        raise RuntimeError('Divergence of box dimensions to <= 0')

    newbox = am.Box(a=new_a, b=new_b, c=new_c)
    system_new = deepcopy(system)
    system_new.box_set(vects=newbox.vects, scale=True)

    return {'C': C, 'stress': stress, 'ecoh': pe[0], 'system_new': system_new}
Пример #14
0
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
Пример #15
0
def stackingfaultrelax(lammps_command, system, potential,
                       mpi_command=None, sim_directory=None,
                       cutboxvector='c',
                       etol=0.0, ftol=0.0,
                       maxiter=10000, maxeval=100000,
                       dmax=uc.set_in_units(0.01, 'angstrom'),
                       lammps_date=None):
    """
    Perform a stacking fault relaxation simulation for a single faultshift.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system containing a stacking fault.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simulation in.  If not
        given, will use the current working directory.
    cutboxvector : str, optional
        Indicates which of the three system box vectors, 'a', 'b', or 'c', has
        the non-periodic boundary (default is 'c').  Fault plane normal is
        defined by the cross of the other two box vectors.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    lammps_date : datetime.date or None, optional
        The date version of the LAMMPS executable.  If None, will be identified
        from the lammps_command (default is None).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The filename of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed system.
        - **'system'** (*atomman.System*) - The relaxed system.
        - **'E_total'** (*float*) - The total potential energy of the relaxed
          system.
    
    Raises
    ------
    ValueError
        For invalid cutboxvectors.
    """
    # Get script's location
    script_dir = Path(__file__).parent

    # Give correct LAMMPS fix setforce command
    if cutboxvector == 'a':
        fix_cut_setforce = 'fix cut all setforce NULL 0 0'    
    elif cutboxvector == 'b':
        fix_cut_setforce = 'fix cut all setforce 0 NULL 0'
    elif cutboxvector == 'c':
        fix_cut_setforce = 'fix cut all setforce 0 0 NULL'    
    else: 
        raise ValueError('Invalid cutboxvector')
    
    if sim_directory is not None:
        # Create sim_directory if it doesn't exist
        sim_directory = Path(sim_directory)
        if not sim_directory.is_dir():
            sim_directory.mkdir()
        sim_directory = sim_directory.as_posix()+'/'
    else:
        # Set sim_directory if is None
        sim_directory = ''
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    if lammps_date is None:
        lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data',
                              f=Path(sim_directory, 'system.dat').as_posix(),
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['fix_cut_setforce'] = fix_cut_setforce
    lammps_variables['sim_directory'] = sim_directory
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = Path(script_dir, 'sfmin.template')
    lammps_script = Path(sim_directory, 'sfmin.in')
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script.as_posix(), mpi_command,
                     logfile=Path(sim_directory, 'log.lammps').as_posix())
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    logfile = Path(sim_directory, 'log.lammps').as_posix()
    dumpfile = Path(sim_directory, '%i.dump' % thermo.Step.values[-1]).as_posix()
    E_total = uc.set_in_units(thermo.PotEng.values[-1],
                              lammps_units['energy'])
    
    # Load relaxed system
    system = am.load('atom_dump', dumpfile, symbols=system.symbols)
    
    # Return results
    results_dict = {}
    results_dict['logfile'] = logfile
    results_dict['dumpfile'] = dumpfile
    results_dict['system'] = system
    results_dict['E_total'] = E_total
    
    return results_dict
Пример #16
0
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
Пример #17
0
def pointdiffusion(lammps_command, system, potential, point_kwargs,
                   mpi_command=None, temperature=300,
                   runsteps=200000, thermosteps=None, dumpsteps=0,
                   equilsteps=20000, randomseed=None):
                   
    """
    Evaluates the diffusion rate of a point defect at a given temperature. This
    method will run two simulations: an NVT run at the specified temperature to 
    equilibrate the system, then an NVE run to measure the defect's diffusion 
    rate. The diffusion rate is evaluated using the mean squared displacement of
    all atoms in the system, and using the assumption that diffusion is only due
    to the added defect(s).
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    point_kwargs : dict or list of dict
        One or more dictionaries containing the keyword arguments for
        the atomman.defect.point() function to generate specific point
        defect configuration(s).
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    temperature : float, optional
        The temperature to run at (default is 300.0).
    runsteps : int, optional
        The number of integration steps to perform (default is 200000).
    thermosteps : int, optional
        Thermo values will be reported every this many steps (default is
        100).
    dumpsteps : int or None, optional
        Dump files will be saved every this many steps (default is 0,
        which does not output dump files).
    equilsteps : int, optional
        The number of timesteps at the beginning of the simulation to
        exclude when computing average values (default is 20000).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'natoms'** (*int*) - The number of atoms in the system.
        - **'temp'** (*float*) - The mean measured temperature.
        - **'pxx'** (*float*) - The mean measured normal xx pressure.
        - **'pyy'** (*float*) - The mean measured normal yy pressure.
        - **'pzz'** (*float*) - The mean measured normal zz pressure.
        - **'Epot'** (*numpy.array*) - The mean measured total potential 
          energy.
        - **'temp_std'** (*float*) - The standard deviation in the measured
          temperature values.
        - **'pxx_std'** (*float*) - The standard deviation in the measured
          normal xx pressure values.
        - **'pyy_std'** (*float*) - The standard deviation in the measured
          normal yy pressure values.
        - **'pzz_std'** (*float*) - The standard deviation in the measured
          normal zz pressure values.
        - **'Epot_std'** (*float*) - The standard deviation in the measured
          total potential energy values.
        - **'dx'** (*float*) - The computed diffusion constant along the 
          x-direction.
        - **'dy'** (*float*) - The computed diffusion constant along the 
          y-direction.
        - **'dz'** (*float*) - The computed diffusion constant along the 
          y-direction.
        - **'d'** (*float*) - The total computed diffusion constant.
    """
    
    # Add defect(s) to the initially perfect system
    if not isinstance(point_kwargs, (list, tuple)):
        point_kwargs = [point_kwargs]
    for pkwargs in point_kwargs:
        system = am.defect.point(system, **pkwargs)
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Check that temperature is greater than zero
    if temperature <= 0.0:
        raise ValueError('Temperature must be greater than zero')
    
    # Handle default values
    if thermosteps is None: 
        thermosteps = runsteps // 1000
        if thermosteps == 0:
            thermosteps = 1
    if dumpsteps is None:
        dumpsteps = runsteps
    if randomseed is None:
        randomseed = random.randint(1, 900000000)
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='initial.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['temperature'] = temperature
    lammps_variables['runsteps'] = runsteps
    lammps_variables['equilsteps'] = equilsteps
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['dumpsteps'] = dumpsteps
    lammps_variables['randomseed'] = randomseed
    lammps_variables['timestep'] = lmp.style.timestep(potential.units)
    
    # Set dump_info
    if dumpsteps == 0:
        lammps_variables['dump_info'] = ''
    else:
        lammps_variables['dump_info'] = '\n'.join([
            '',
            '# Define dump files',
            'dump dumpit all custom ${dumpsteps} *.dump id type x y z c_peatom',
            'dump_modify dumpit format <dump_modify_format>',
            '',
        ])
        
        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'diffusion.template'
    lammps_script = 'diffusion.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    # Run lammps
    output = lmp.run(lammps_command, 'diffusion.in', mpi_command)
    
    # Extract LAMMPS thermo data.
    thermo = output.simulations[1]['thermo']
    temps = thermo.Temp.values
    pxxs = uc.set_in_units(thermo.Pxx.values, lammps_units['pressure'])
    pyys = uc.set_in_units(thermo.Pyy.values, lammps_units['pressure'])
    pzzs = uc.set_in_units(thermo.Pzz.values, lammps_units['pressure'])
    potengs = uc.set_in_units(thermo.PotEng.values, lammps_units['energy'])
    steps = thermo.Step.values
    
    # Read user-defined thermo data
    if output.lammps_date < datetime.date(2016, 8, 1):
        msd_x = uc.set_in_units(thermo['msd[1]'].values,
                                lammps_units['length']+'^2')
        msd_y = uc.set_in_units(thermo['msd[2]'].values,
                                lammps_units['length']+'^2')
        msd_z = uc.set_in_units(thermo['msd[3]'].values,
                                lammps_units['length']+'^2')
        msd = uc.set_in_units(thermo['msd[4]'].values,
                              lammps_units['length']+'^2')
    else:
        msd_x = uc.set_in_units(thermo['c_msd[1]'].values,
                                lammps_units['length']+'^2')
        msd_y = uc.set_in_units(thermo['c_msd[2]'].values,
                                lammps_units['length']+'^2')
        msd_z = uc.set_in_units(thermo['c_msd[3]'].values,
                                lammps_units['length']+'^2')
        msd = uc.set_in_units(thermo['c_msd[4]'].values,
                              lammps_units['length']+'^2')
        
    # Initialize results dict
    results = {}
    results['natoms'] = system.natoms
    
    # Get mean and std for temperature, pressure, and potential energy
    results['temp'] = np.mean(temps)
    results['temp_std'] = np.std(temps)
    results['pxx'] = np.mean(pxxs)
    results['pxx_std'] = np.std(pxxs)
    results['pyy'] = np.mean(pyys)
    results['pyy_std'] = np.std(pyys)
    results['pzz'] = np.mean(pzzs)
    results['pzz_std'] = np.std(pzzs)
    results['Epot'] = np.mean(potengs)
    results['Epot_std'] = np.std(potengs)
    
    # Convert steps to times
    times = steps * uc.set_in_units(lammps_variables['timestep'], lammps_units['time'])
    
    # Estimate diffusion rates
    # MSD_ptd = natoms * MSD_atoms (if one defect in system)
    # MSD = 2 * ndim * D * t  -->  D = MSD/t / (2 * ndim)
    mx = np.polyfit(times, system.natoms * msd_x, 1)[0]
    my = np.polyfit(times, system.natoms * msd_y, 1)[0]
    mz = np.polyfit(times, system.natoms * msd_z, 1)[0]
    m = np.polyfit(times, system.natoms * msd, 1)[0]
    
    results['dx'] = mx / 2
    results['dy'] = my / 2
    results['dz'] = mz / 2
    results['d'] = m / 6
    
    return results
Пример #18
0
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
Пример #19
0
def e_vs_r(lammps_command,
           system,
           potential,
           symbols,
           mpi_command=None,
           ucell=None,
           rmin=2.0,
           rmax=6.0,
           rsteps=200):
    """
    Performs a cohesive energy scan over a range of interatomic spaces, r.
    
    Arguments:
    lammps_command -- command for running LAMMPS.
    system -- atomman.System to perform the scan on.
    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).  
    ucell -- an atomman.System representing a fundamental unit cell of the 
             system. If not given, ucell will be taken as system. 
    rmin -- the minimum r spacing to use. Default value is 2.0.
    rmax -- the minimum r spacing to use. Default value is 6.0.
    rsteps -- the number of r spacing steps to evaluate. Default value is 200.    
    """

    #Make system a deepcopy of itself (protect original from changes)
    system = deepcopy(system)

    #Set ucell = system if ucell not given
    if ucell is None:
        ucell = system

    #Calculate the r/a ratio for the unit cell
    r_a = r_a_ratio(ucell)

    #Get ratios of lx, ly, and lz of system relative to a of ucell
    lx_a = system.box.a / ucell.box.a
    ly_a = system.box.b / ucell.box.a
    lz_a = system.box.c / ucell.box.a
    alpha = system.box.alpha
    beta = system.box.beta
    gamma = system.box.gamma

    #build lists of values
    r_values = np.linspace(rmin, rmax, rsteps)
    a_values = r_values / r_a
    Ecoh_values = np.empty(rsteps)

    #Loop over values
    for i in xrange(rsteps):

        #Rescale system's box
        a = a_values[i]
        system.box_set(a=a * lx_a,
                       b=a * ly_a,
                       c=a * lz_a,
                       alpha=alpha,
                       beta=beta,
                       gamma=gamma,
                       scale=True)

        #Get lammps units
        lammps_units = lmp.style.unit(potential.units)

        #Define lammps variables
        lammps_variables = {}
        lammps_variables['atomman_system_info'] = lmp.atom_data.dump(
            system,
            'atom.dat',
            units=potential.units,
            atom_style=potential.atom_style)
        lammps_variables['atomman_pair_info'] = potential.pair_info(symbols)

        #Write lammps input script
        template_file = 'run0.template'
        lammps_script = 'run0.in'
        with open(template_file) as f:
            template = f.read()
        with open(lammps_script, 'w') as f:
            f.write(
                iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

        #Run lammps and extract data
        output = lmp.run(lammps_command, lammps_script, mpi_command)
        try:
            Ecoh_values[i] = uc.set_in_units(
                output.finds('v_peatom')[-1], lammps_units['energy'])
        except:
            Ecoh_values[i] = uc.set_in_units(
                output.finds('peatom')[-1], lammps_units['energy'])
        shutil.move('log.lammps', 'run0-' + str(i) + '-log.lammps')

    #Find unit cell systems at the energy minimums
    min_cells = []
    for i in xrange(1, rsteps - 1):
        if Ecoh_values[i] < Ecoh_values[
                i - 1] and Ecoh_values[i] < Ecoh_values[i + 1]:
            a = a_values[i]
            cell = deepcopy(ucell)
            cell.box_set(a=a,
                         b=a * ucell.box.b / ucell.box.a,
                         c=a * ucell.box.c / ucell.box.a,
                         alpha=alpha,
                         beta=beta,
                         gamma=gamma,
                         scale=True)
            min_cells.append(cell)

    return {
        'r_values': r_values,
        'a_values': a_values,
        'Ecoh_values': Ecoh_values,
        'min_cell': min_cells
    }
Пример #20
0
def e_vs_r(lammps_command, system, potential,
           mpi_command=None, ucell=None, 
           rmin=uc.set_in_units(2.0, 'angstrom'), 
           rmax=uc.set_in_units(6.0, 'angstrom'), rsteps=200):
    """
    Performs a cohesive energy scan over a range of interatomic spaces, r.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    ucell : atomman.System, optional
        The fundamental unit cell correspodning to system.  This is used to
        convert system dimensions to cell dimensions. If not given, ucell will
        be taken as system.
    rmin : float, optional
        The minimum r spacing to use (default value is 2.0 angstroms).
    rmax : float, optional
        The maximum r spacing to use (default value is 6.0 angstroms).
    rsteps : int, optional
        The number of r spacing steps to evaluate (default value is 200).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'r_values'** (*numpy.array of float*) - All interatomic spacings,
          r, explored.
        - **'a_values'** (*numpy.array of float*) - All unit cell a lattice
          constants corresponding to the values explored.
        - **'Ecoh_values'** (*numpy.array of float*) - The computed cohesive
          energies for each r value.
        - **'min_cell'** (*list of atomman.System*) - Systems corresponding to
          the minima identified in the Ecoh_values.
    """
    
    # Make system a deepcopy of itself (protect original from changes)
    system = deepcopy(system)
    
    # Set ucell = system if ucell not given
    if ucell is None:
        ucell = system
    
    # Calculate the r/a ratio for the unit cell
    r_a = r_a_ratio(ucell)
    
    # Get ratios of lx, ly, and lz of system relative to a of ucell
    lx_a = system.box.a / ucell.box.a
    ly_a = system.box.b / ucell.box.a
    lz_a = system.box.c / ucell.box.a
    alpha = system.box.alpha
    beta =  system.box.beta
    gamma = system.box.gamma
 
    # Build lists of values
    r_values = np.linspace(rmin, rmax, rsteps)
    a_values = r_values / r_a
    Ecoh_values = np.empty(rsteps)
    
    # Loop over values
    for i in range(rsteps):
        
        # Rescale system's box
        a = a_values[i]
        system.box_set(a = a * lx_a, 
                       b = a * ly_a, 
                       c = a * lz_a, 
                       alpha=alpha, beta=beta, gamma=gamma, scale=True)
        
        # Get lammps units
        lammps_units = lmp.style.unit(potential.units)
        
        # Define lammps variables
        lammps_variables = {}
        system_info = system.dump('atom_data', f='atom.dat',
                                  units=potential.units,
                                  atom_style=potential.atom_style)
        lammps_variables['atomman_system_info'] = system_info
        lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
        
        # Write lammps input script
        template_file = 'run0.template'
        lammps_script = 'run0.in'
        with open(template_file) as f:
            template = f.read()
        with open(lammps_script, 'w') as f:
            f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                             '<', '>'))
        
        # Run lammps and extract data
        output = lmp.run(lammps_command, lammps_script, mpi_command)
        
        thermo = output.simulations[0]['thermo']
        
        if output.lammps_date < datetime.date(2016, 8, 1):
            Ecoh_values[i] = uc.set_in_units(thermo.peatom.values[-1],
                                             lammps_units['energy'])
        else:
            Ecoh_values[i] = uc.set_in_units(thermo.v_peatom.values[-1],
                                             lammps_units['energy'])
        
        # Rename log.lammps
        shutil.move('log.lammps', 'run0-'+str(i)+'-log.lammps')
    
    # Find unit cell systems at the energy minimums
    min_cells = []
    for i in range(1, rsteps - 1):
        if (Ecoh_values[i] < Ecoh_values[i-1]
            and Ecoh_values[i] < Ecoh_values[i+1]):
            a = a_values[i]
            cell = deepcopy(ucell)
            cell.box_set(a = a,
                         b = a * ucell.box.b / ucell.box.a,
                         c = a * ucell.box.c / ucell.box.a, 
                         alpha=alpha, beta=beta, gamma=gamma, scale=True)
            min_cells.append(cell)
    
    # Collect results
    results_dict = {}
    results_dict['r_values'] = r_values
    results_dict['a_values'] = a_values
    results_dict['Ecoh_values'] = Ecoh_values
    results_dict['min_cell'] = min_cells
    
    return results_dict
Пример #21
0
def full_relax(lammps_command, system, potential, symbols, 
               mpi_command=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, temperature=0.0,
               runsteps=100000, integrator=None, thermosteps=None, dumpsteps=None, 
               randomseed=None):
    """
    Performs a full dynamic relax on a given system at the given temperature 
    to the specified pressure state. 
    
    Arguments:
    lammps_command -- directory location for lammps executable
    system -- atomman.System to dynamically relax
    potential -- atomman.lammps.Potential representation of a LAMMPS implemented potential
    symbols -- list of element-model symbols for the Potential that correspond to the System's atypes
    
    Keyword Arguments:
    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.
    runsteps -- number of integration steps to perform. Default value is 100000.
    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.    
    thermosteps -- output thermo values every this many steps. Default value is 
                    runsteps/1000.
    dumpsteps -- output dump files every this many steps. Default value is runsteps
                  (only first and last steps are outputted as dump files).
    randomseed -- random number seed used by LAMMPS for velocity creation and Langevin
                   thermostat. Default value generates a new random integer every time.
    """
        
    #Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Handle default values
    if thermosteps is None: 
        if thermosteps >= 1000: thermosteps = runsteps/1000
        else:                    thermosteps = 1
    if dumpsteps is None:       dumpsteps = runsteps
    
    #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['integrator_info'] = integrator_info(integrator=integrator, 
                                                          p_xx=p_xx, p_yy=p_yy, p_zz=p_zz, 
                                                          temperature=temperature, 
                                                          randomseed=randomseed, 
                                                          units=potential.units)
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['runsteps'] = runsteps
    lammps_variables['dumpsteps'] = dumpsteps
    
    #Write lammps input script
    template_file = 'full_relax.template'
    lammps_script = 'full_relax.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    #Run lammps 
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    #Extract LAMMPS thermo data. 
    thermo = output.find('thermo')
    lx = uc.set_in_units(np.array(thermo['Lx']), lammps_units['length'])
    ly = uc.set_in_units(np.array(thermo['Ly']), lammps_units['length'])
    lz = uc.set_in_units(np.array(thermo['Lz']), lammps_units['length'])
    #xy = uc.set_in_units(np.array(thermo['Xy']), lammps_units['length'])
    #xz = uc.set_in_units(np.array(thermo['Xz']), lammps_units['length'])
    #yz = uc.set_in_units(np.array(thermo['Yz']), lammps_units['length'])
    
    pxx = uc.set_in_units(np.array(thermo['Pxx']), lammps_units['pressure'])
    pyy = uc.set_in_units(np.array(thermo['Pyy']), lammps_units['pressure'])
    pzz = uc.set_in_units(np.array(thermo['Pzz']), lammps_units['pressure'])
    pxy = uc.set_in_units(np.array(thermo['Pxy']), lammps_units['pressure'])
    pxz = uc.set_in_units(np.array(thermo['Pxz']), lammps_units['pressure'])
    pyz = uc.set_in_units(np.array(thermo['Pyz']), lammps_units['pressure'])
    
    pe = uc.set_in_units(np.array(thermo['PotEng']), lammps_units['energy'])
    #ke = uc.set_in_units(np.array(thermo['KinEng']), lammps_units['energy'])
    
    temp = np.array(thermo['Temp'])
    step = np.array(thermo['Step'], dtype=int)
    
    return {'step':step, 'lx':lx, 'ly':ly, 'lz':lz, 'pe':pe, 'temp':temp,
            'pxx':pxx, 'pyy':pyy, 'pzz':pzz, 'pxy':pxy, 'pxz':pxz, 'pyz':pyz}
Пример #22
0
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 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
Пример #24
0
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
Пример #25
0
def dynamic_stress(lammps_command, system, potential, symbols, 
                   mpi_command=None, temperature=0.0, runsteps=100000, 
                   integrator=None, thermosteps=None, dumpsteps=None, 
                   restartsteps=None, randomseed=None):
    """
    Performs a dynamic run on a given system at the given temperature to 
    measure stress state. 
    
    Arguments:
    lammps_command -- directory location for lammps executable
    system -- atomman.System to dynamically relax
    potential -- atomman.lammps.Potential representation of a LAMMPS 
                 implemented potential
    symbols -- list of element-model symbols for the Potential that correspond
               to the System's atypes
    
    Keyword Arguments:
    mpi_command -- MPI command to use when running LAMMPS.
    temperature -- temperature to relax at. Default value is 0.
    runsteps -- number of integration steps to perform. Default value is 100000.
    integrator -- string giving the integration method to use. Options are limited to 
                  'nvt', 'nve' and 'nve+l'. The +l options use Langevin thermostat.
                  Default value is 'nve+l' for temperature = 0, and 'nvt' otherwise.    
    thermosteps -- output thermo values every this many steps. Default value is 
                    runsteps/1000.
    dumpsteps -- output dump files every this many steps. Default value is runsteps
                  (only first and last steps are outputted as dump files).
    randomseed -- random number seed used by LAMMPS for velocity creation and Langevin
                   thermostat. Default value generates a new random integer every time.
    """
    
    assert integrator in ['nvt', 'nve', 'nve+l'], 'Only nvt, nve, and nve+l integrators allowed'
    
    #Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Handle default values
    if thermosteps is None: 
        if thermosteps >= 1000: thermosteps = runsteps/1000
        else:                    thermosteps = 1
    if dumpsteps is None:       dumpsteps = runsteps
    if restartsteps is None:    restartsteps = dumpsteps
    
    #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['integrator_info'] = integrator_info(integrator=integrator, 
                                                          temperature=temperature, 
                                                          randomseed=randomseed, 
                                                          units=potential.units)
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['runsteps'] = runsteps
    lammps_variables['restartsteps'] = restartsteps
    lammps_variables['dumpsteps'] = dumpsteps
    
    #Check LAMMPS compute stress version
    compute_stress_version = iprPy.tools.lammps_version.compute_stress(lammps_command)
    if compute_stress_version == 0:
        lammps_variables['stressterm'] = 'NULL'
    elif compute_stress_version == 1:
        lammps_variables['stressterm'] = ''
    
    #Write lammps input script
    template_file = 'full_relax.template'
    lammps_script = 'full_relax.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    #Write restart input script
    lammps_restart = 'full_relax_restart.in'
    lammps_variables['atomman_system_info'] = 'read_restart *.restart'
    with open(lammps_restart, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    #Run lammps 
    output = lmp.run(lammps_command, lammps_script, mpi_command, restart_script_name=lammps_restart, flatten_thermo='last')
    
    #Extract LAMMPS thermo data. 
    thermo = output.find('thermo')
    #lx = uc.set_in_units(np.array(thermo['Lx']), lammps_units['length'])
    #ly = uc.set_in_units(np.array(thermo['Ly']), lammps_units['length'])
    #lz = uc.set_in_units(np.array(thermo['Lz']), lammps_units['length'])
    #xy = uc.set_in_units(np.array(thermo['Xy']), lammps_units['length'])
    #xz = uc.set_in_units(np.array(thermo['Xz']), lammps_units['length'])
    #yz = uc.set_in_units(np.array(thermo['Yz']), lammps_units['length'])
    
    pxx = uc.set_in_units(np.array(thermo['Pxx']), lammps_units['pressure'])
    pyy = uc.set_in_units(np.array(thermo['Pyy']), lammps_units['pressure'])
    pzz = uc.set_in_units(np.array(thermo['Pzz']), lammps_units['pressure'])
    pxy = uc.set_in_units(np.array(thermo['Pxy']), lammps_units['pressure'])
    pxz = uc.set_in_units(np.array(thermo['Pxz']), lammps_units['pressure'])
    pyz = uc.set_in_units(np.array(thermo['Pyz']), lammps_units['pressure'])
    
    pe = uc.set_in_units(np.array(thermo['PotEng']), lammps_units['energy'])
    #ke = uc.set_in_units(np.array(thermo['KinEng']), lammps_units['energy'])
    
    temp = np.array(thermo['Temp'])
    step = np.array(thermo['Step'], dtype=int)
    
    return {'step':step, 'pe':pe, 'temp':temp,
            'pxx':pxx, 'pyy':pyy, 'pzz':pzz, 'pxy':pxy, 'pxz':pxz, 'pyz':pyz}
Пример #26
0
def isolated_atom(lammps_command: str,
                  potential: am.lammps.Potential,
                  mpi_command: Optional[str] = None) -> dict:
    """
    Evaluates the isolated atom energy for each elemental model of a potential.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    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.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'energy'** (*dict*) - The computed potential energies for each
          symbol.
    """
    # Initialize dictionary
    energydict = {}

    # Initialize single atom system
    box = am.Box.cubic(a=1)
    atoms = am.Atoms(atype=1, pos=[[0.5, 0.5, 0.5]])
    system = am.System(atoms=atoms, box=box, pbc=[False, False, False])

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Define lammps variables
    lammps_variables = {}

    # Loop over symbols
    for symbol in potential.symbols:
        system.symbols = symbol

        # Add charges if required
        if potential.atom_style == 'charge':
            system.atoms.prop_atype('charge',
                                    potential.charges(system.symbols))

        # Save configuration
        system_info = system.dump('atom_data',
                                  f='isolated.dat',
                                  potential=potential)
        lammps_variables['atomman_system_pair_info'] = system_info

        # Write lammps input script
        lammps_script = 'run0.in'
        template = read_calc_file('iprPy.calculation.isolated_atom',
                                  'run0.template')
        with open(lammps_script, 'w') as f:
            f.write(filltemplate(template, lammps_variables, '<', '>'))

        # Run lammps and extract data
        output = lmp.run(lammps_command,
                         script_name=lammps_script,
                         mpi_command=mpi_command)
        energy = output.simulations[0]['thermo'].PotEng.values[-1]
        energydict[symbol] = uc.set_in_units(energy, lammps_units['energy'])

    # Collect results
    results_dict = {}
    results_dict['energy'] = energydict

    return results_dict
Пример #27
0
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 bain_run_calcs(input_dict, __calc_type__, step):

    #-------------------SETS UP THE SYSTEM--------------------

    #Read in potential
    potential = lmp.Potential(
        input_dict['potential'],
        input_dict['potential_dir'])  #reads the potential filename
    system = input_dict['initial_system']

    #if initial step, set up system to find difference in cohesive energy. this step is skipped when a and c values change.
    if step == 'initial':
        a_scale = 1
        c_scale = 1
    elif step == 'bain':
        a_scale = input_dict['bain_a_scale']
        c_scale = input_dict['bain_c_scale']

    #scale box and atoms simultaneously
    system.box_set(a=a_scale * system.box.a,
                   b=a_scale * system.box.b,
                   c=c_scale * system.box.c,
                   scale=True)

    #ensure all atoms are within the box
    system.wrap()

    #-------------------RUN LAMMPS AND SAVE RESULTS------------------
    #use above information to generate system_info and pair_info for LAMMPS
    system_info = am.lammps.atom_data.dump('bain.dat',
                                           system,
                                           units=potential.units,
                                           atom_style=potential.atom_style)
    pair_info = potential.pair_info(
        input_dict['symbols']
    )  #pulls the potential info when given which element the calculation is running on

    #write the LAMMPS input script
    with open('bain.in', 'w') as f:
        f.write(
            bain_relax_script('min.template',
                              system_info,
                              pair_info,
                              etol=input_dict['energy_tolerance'],
                              ftol=input_dict['force_tolerance'],
                              maxiter=input_dict['maximum_iterations'],
                              maxeval=input_dict['maximum_evaluations']))
    #run LAMMPS
    output = lmp.run(input_dict['lammps_command'], 'bain.in',
                     input_dict['mpi_command'])

    atom_last = 'atom.%i' % output.finds('Step')[
        -1]  #prints number of iterations (?)

    #save results into results_dict
    if step == 'initial':
        try:
            os.rename(atom_last, 'initial.dump')
        except:
            os.remove('initial.dump')
            os.rename(atom_last, 'initial.dump')
        os.remove('atom.0')
        d_system = lmp.atom_dump.load('initial.dump')

    elif step == 'bain':
        try:
            os.rename(atom_last, 'bain.dump')
        except:
            os.remove('bain.dump')
            os.rename(atom_last, 'bain.dump')
        os.remove('atom.0')
        d_system = lmp.atom_dump.load('bain.dump')

    results_dict = {}
    results_dict['potential_energy'] = float(output.finds('PotEng')[-1])

    #return values
    return results_dict['potential_energy']
Пример #29
0
def phonon(lammps_command,
           ucell,
           potential,
           mpi_command=None,
           a_mult=3,
           b_mult=3,
           c_mult=3,
           distance=0.01,
           symprec=1e-5):

    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']

    # Generate pair_info
    pair_info = potential.pair_info(ucell.symbols)

    # Use spglib to find primitive unit cell of ucell
    convcell = ucell.dump('spglib_cell')
    primcell = spglib.find_primitive(convcell, symprec=symprec)
    primucell = am.load('spglib_cell', primcell,
                        symbols=ucell.symbols).normalize()

    # Initialize Phonopy object
    phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms'),
                             [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]])
    phonon.generate_displacements(distance=distance)

    # Loop over displaced supercells to compute forces
    forcearrays = []
    for supercell in phonon.supercells_with_displacements:

        # Save to LAMMPS data file
        system = am.load('phonopy_Atoms', supercell)
        system_info = system.dump('atom_data', f='disp.dat')

        # Define lammps variables
        lammps_variables = {}
        lammps_variables['atomman_system_info'] = system_info
        lammps_variables['atomman_pair_info'] = pair_info

        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables[
                'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'

        # Write lammps input script
        template_file = Path(script_dir, 'phonon.template')
        lammps_script = 'phonon.in'
        with open(template_file) as f:
            template = f.read()
        with open(lammps_script, 'w') as f:
            f.write(
                iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

        # Run LAMMPS
        lmp.run(lammps_command, 'phonon.in', mpi_command=mpi_command)

        # Extract forces from dump file
        results = am.load('atom_dump', 'forces.dump')
        forces = uc.set_in_units(results.atoms.force, lammps_units['force'])
        forcearrays.append(forces)

    # Set computed forces
    phonon.set_forces(forcearrays)

    # Save to yaml file
    phonon.save('phonopy_params.yaml')

    # Compute band structure
    phonon.produce_force_constants()
    phonon.auto_band_structure(plot=True)

    plt.savefig(Path('.', 'band.png'), dpi=400)
    plt.close()

    # Compute total density of states
    phonon.auto_total_dos(plot=True)
    plt.savefig('total_dos.png', dpi=400)
    plt.close()

    # Compute partial density of states
    phonon.auto_projected_dos(plot=True)
    plt.savefig('projected_dos.png', dpi=400)
    plt.close()

    # Compute thermal properties
    phonon.run_thermal_properties()
    phonon.plot_thermal_properties()
    plt.savefig('thermal.png', dpi=400)
    plt.close()

    return {}
Пример #30
0
def relax_dynamic(lammps_command: str,
                  system: am.System,
                  potential: lmp.Potential,
                  mpi_command: Optional[str] = None,
                  p_xx: float = 0.0,
                  p_yy: float = 0.0,
                  p_zz: float = 0.0,
                  p_xy: float = 0.0,
                  p_xz: float = 0.0,
                  p_yz: float = 0.0,
                  temperature: float = 0.0,
                  integrator: Optional[str] = None,
                  runsteps: int = 220000,
                  thermosteps: int = 100,
                  dumpsteps: Optional[int] = None,
                  restartsteps: Optional[int] = None,
                  equilsteps: int = 20000,
                  randomseed: Optional[int] = None) -> dict:
    """
    Performs a full dynamic relax on a given system at the given temperature
    to the specified pressure state.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    symbols : list of str
        The list of element-model symbols for the Potential that correspond to
        system's atypes.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    temperature : float, optional
        The temperature to relax at (default is 0.0).
    runsteps : int, optional
        The number of integration steps to perform (default is 220000).
    integrator : str or None, optional
        The integration method to use. Options are 'npt', 'nvt', 'nph',
        'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat.
        (Default is None, which will use 'nph+l' for temperature == 0, and
        'npt' otherwise.)
    thermosteps : int, optional
        Thermo values will be reported every this many steps (default is
        100).
    dumpsteps : int or None, optional
        Dump files will be saved every this many steps (default is None,
        which sets dumpsteps equal to runsteps).
    restartsteps : int or None, optional
        Restart files will be saved every this many steps (default is None,
        which sets restartsteps equal to runsteps).
    equilsteps : int, optional
        The number of timesteps at the beginning of the simulation to
        exclude when computing average values (default is 20000).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'dumpfile_initial'** (*str*) - The name of the initial dump file
          created.
        - **'symbols_initial'** (*list*) - The symbols associated with the
          initial dump file.
        - **'dumpfile_final'** (*str*) - The name of the final dump file
          created.
        - **'symbols_final'** (*list*) - The symbols associated with the final
          dump file.
        - **'nsamples'** (*int*) - The number of thermodynamic samples included
          in the mean and standard deviation estimates.  Can also be used to
          estimate standard error values assuming that the thermo step size is
          large enough (typically >= 100) to assume the samples to be
          independent.
        - **'E_pot'** (*float*) - The mean measured potential energy.
        - **'measured_pxx'** (*float*) - The measured x tensile pressure of the
          relaxed system.
        - **'measured_pyy'** (*float*) - The measured y tensile pressure of the
          relaxed system.
        - **'measured_pzz'** (*float*) - The measured z tensile pressure of the
          relaxed system.
        - **'measured_pxy'** (*float*) - The measured xy shear pressure of the
          relaxed system.
        - **'measured_pxz'** (*float*) - The measured xz shear pressure of the
          relaxed system.
        - **'measured_pyz'** (*float*) - The measured yz shear pressure of the
          relaxed system.
        - **'temp'** (*float*) - The mean measured temperature.
        - **'E_pot_std'** (*float*) - The standard deviation in the measured
          potential energy values.
        - **'measured_pxx_std'** (*float*) - The standard deviation in the
          measured x tensile pressure of the relaxed system.
        - **'measured_pyy_std'** (*float*) - The standard deviation in the
          measured y tensile pressure of the relaxed system.
        - **'measured_pzz_std'** (*float*) - The standard deviation in the
          measured z tensile pressure of the relaxed system.
        - **'measured_pxy_std'** (*float*) - The standard deviation in the
          measured xy shear pressure of the relaxed system.
        - **'measured_pxz_std'** (*float*) - The standard deviation in the
          measured xz shear pressure of the relaxed system.
        - **'measured_pyz_std'** (*float*) - The standard deviation in the
          measured yz shear pressure of the relaxed system.
        - **'temp_std'** (*float*) - The standard deviation in the measured
          temperature values.
    """

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']

    # Handle default values
    if dumpsteps is None:
        dumpsteps = runsteps
    if restartsteps is None:
        restartsteps = runsteps

    # Define lammps variables
    lammps_variables = {}

    # Dump initial system as data and build LAMMPS inputs
    system_info = system.dump('atom_data', f='init.dat', potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info

    # Generate LAMMPS inputs for restarting
    system_info2 = potential.pair_restart_info('*.restart', system.symbols)
    lammps_variables['atomman_pair_restart_info'] = system_info2

    # Integrator lines for main run
    integ_info = integrator_info(integrator=integrator,
                                 p_xx=p_xx,
                                 p_yy=p_yy,
                                 p_zz=p_zz,
                                 p_xy=p_xy,
                                 p_xz=p_xz,
                                 p_yz=p_yz,
                                 temperature=temperature,
                                 randomseed=randomseed,
                                 units=potential.units,
                                 lammps_date=lammps_date)
    lammps_variables['integrator_info'] = integ_info

    # Integrator lines for restarts
    integ_info2 = integrator_info(integrator=integrator,
                                  p_xx=p_xx,
                                  p_yy=p_yy,
                                  p_zz=p_zz,
                                  p_xy=p_xy,
                                  p_xz=p_xz,
                                  p_yz=p_yz,
                                  temperature=temperature,
                                  velocity_temperature=0.0,
                                  randomseed=randomseed,
                                  units=potential.units,
                                  lammps_date=lammps_date)
    lammps_variables['integrator_restart_info'] = integ_info2

    # Other run settings
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['runsteps'] = runsteps
    lammps_variables['dumpsteps'] = dumpsteps
    lammps_variables['restartsteps'] = restartsteps

    # Set compute stress/atom based on LAMMPS version
    if lammps_date < datetime.date(2014, 2, 12):
        lammps_variables['stressterm'] = ''
    else:
        lammps_variables['stressterm'] = 'NULL'

    # Set dump_keys based on atom_style
    if potential.atom_style in ['charge']:
        lammps_variables['dump_keys'] = 'id type q xu yu zu c_pe c_ke &\n'
        lammps_variables[
            'dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]'
    else:
        lammps_variables['dump_keys'] = 'id type xu yu zu c_pe c_ke &\n'
        lammps_variables[
            'dump_keys'] += 'c_stress[1] c_stress[2] c_stress[3] c_stress[4] c_stress[5] c_stress[6]'

    # Set dump_modify_format based on lammps_date
    if lammps_date < datetime.date(2016, 8, 3):
        if potential.atom_style in ['charge']:
            lammps_variables['dump_modify_format'] = f'"%d %d{12 * " %.13e"}"'
        else:
            lammps_variables['dump_modify_format'] = f'"%d %d{11 * " %.13e"}"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'

    # Write lammps input script
    lammps_script = 'full_relax.in'
    template = read_calc_file('iprPy.calculation.relax_dynamic',
                              'full_relax.template')
    with open(lammps_script, 'w') as f:
        f.write(filltemplate(template, lammps_variables, '<', '>'))

    # Write lammps restart input script
    restart_script = 'full_relax_restart.in'
    template = read_calc_file('iprPy.calculation.relax_dynamic',
                              'full_relax_restart.template')
    with open(restart_script, 'w') as f:
        f.write(filltemplate(template, lammps_variables, '<', '>'))

    # Run lammps
    output = lmp.run(lammps_command,
                     script_name=lammps_script,
                     restart_script_name=restart_script,
                     mpi_command=mpi_command,
                     screen=False)

    # Extract LAMMPS thermo data.
    results = {}
    thermo = output.flatten()['thermo']

    results['dumpfile_initial'] = '0.dump'
    results['symbols_initial'] = system.symbols

    # Load relaxed system from dump file
    last_dump_file = f'{thermo.Step.values[-1]}.dump'
    results['dumpfile_final'] = last_dump_file
    system = am.load('atom_dump', last_dump_file, symbols=system.symbols)
    results['symbols_final'] = system.symbols

    # Only consider values where Step >= equilsteps
    thermo = thermo[thermo.Step >= equilsteps]
    results['nsamples'] = len(thermo)

    # Get cohesive energy estimates
    natoms = system.natoms
    results['E_pot'] = uc.set_in_units(thermo.PotEng.mean() / natoms,
                                       lammps_units['energy'])
    results['E_pot_std'] = uc.set_in_units(thermo.PotEng.std() / natoms,
                                           lammps_units['energy'])

    results['E_total'] = uc.set_in_units(thermo.TotEng.mean() / natoms,
                                         lammps_units['energy'])
    results['E_total_std'] = uc.set_in_units(thermo.TotEng.std() / natoms,
                                             lammps_units['energy'])

    results['lx'] = uc.set_in_units(thermo.Lx.mean(), lammps_units['length'])
    results['lx_std'] = uc.set_in_units(thermo.Lx.std(),
                                        lammps_units['length'])
    results['ly'] = uc.set_in_units(thermo.Ly.mean(), lammps_units['length'])
    results['ly_std'] = uc.set_in_units(thermo.Ly.std(),
                                        lammps_units['length'])
    results['lz'] = uc.set_in_units(thermo.Lz.mean(), lammps_units['length'])
    results['lz_std'] = uc.set_in_units(thermo.Lz.std(),
                                        lammps_units['length'])
    results['xy'] = uc.set_in_units(thermo.Xy.mean(), lammps_units['length'])
    results['xy_std'] = uc.set_in_units(thermo.Xy.std(),
                                        lammps_units['length'])
    results['xz'] = uc.set_in_units(thermo.Xz.mean(), lammps_units['length'])
    results['xz_std'] = uc.set_in_units(thermo.Xz.std(),
                                        lammps_units['length'])
    results['yz'] = uc.set_in_units(thermo.Yz.mean(), lammps_units['length'])
    results['yz_std'] = uc.set_in_units(thermo.Yz.std(),
                                        lammps_units['length'])

    results['measured_pxx'] = uc.set_in_units(thermo.Pxx.mean(),
                                              lammps_units['pressure'])
    results['measured_pxx_std'] = uc.set_in_units(thermo.Pxx.std(),
                                                  lammps_units['pressure'])
    results['measured_pyy'] = uc.set_in_units(thermo.Pyy.mean(),
                                              lammps_units['pressure'])
    results['measured_pyy_std'] = uc.set_in_units(thermo.Pyy.std(),
                                                  lammps_units['pressure'])
    results['measured_pzz'] = uc.set_in_units(thermo.Pzz.mean(),
                                              lammps_units['pressure'])
    results['measured_pzz_std'] = uc.set_in_units(thermo.Pzz.std(),
                                                  lammps_units['pressure'])
    results['measured_pxy'] = uc.set_in_units(thermo.Pxy.mean(),
                                              lammps_units['pressure'])
    results['measured_pxy_std'] = uc.set_in_units(thermo.Pxy.std(),
                                                  lammps_units['pressure'])
    results['measured_pxz'] = uc.set_in_units(thermo.Pxz.mean(),
                                              lammps_units['pressure'])
    results['measured_pxz_std'] = uc.set_in_units(thermo.Pxz.std(),
                                                  lammps_units['pressure'])
    results['measured_pyz'] = uc.set_in_units(thermo.Pyz.mean(),
                                              lammps_units['pressure'])
    results['measured_pyz_std'] = uc.set_in_units(thermo.Pyz.std(),
                                                  lammps_units['pressure'])
    results['temp'] = thermo.Temp.mean()
    results['temp_std'] = thermo.Temp.std()

    return results
Пример #31
0
def cij_run0(lammps_command: str,
             system: am.System,
             potential: lmp.Potential,
             mpi_command: Optional[str] = None,
             strainrange: float = 1e-6,
             cycle: int = 0) -> dict:
    """
    Runs cij_run0.in LAMMPS script to evaluate the elastic constants,
    pressure and potential energy of the current 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.
    strainrange : float, optional
        The small strain value to apply when calculating the elastic
        constants (default is 1e-6).
    cycle : int, optional
        Indicates the iteration cycle of quick_a_Cij().  This is used to
        uniquely save the LAMMPS input and output files.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'E_pot'** (*float*) - The potential energy per atom for the
          supplied system.
        - **'pressure'** (*numpy.array*) - The measured pressure state of the
          supplied system.
        - **'C_elastic'** (*atomman.ElasticConstants*) - The supplied system's
          elastic constants.
    """

    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='init.dat', potential=potential)
    restart_info = potential.pair_restart_info('initial.restart',
                                               system.symbols)
    lammps_variables['pair_data_info'] = system_info
    lammps_variables['pair_restart_info'] = restart_info
    lammps_variables['strainrange'] = strainrange

    # Write lammps input script
    lammps_script = 'cij_run0.in'
    template = read_calc_file('iprPy.calculation.relax_box',
                              'cij_run0.template')
    with open(lammps_script, 'w') as f:
        f.write(filltemplate(template, lammps_variables, '<', '>'))

    # Run lammps
    output = lmp.run(lammps_command,
                     script_name=lammps_script,
                     mpi_command=mpi_command,
                     logfile=f'cij-{cycle}-log.lammps')
    thermo = output.flatten('all').thermo

    # Extract LAMMPS thermo data. Each term ranges i=0-12 where i=0 is undeformed
    # The remaining values are for -/+ strain pairs in the six unique directions
    lx = uc.set_in_units(thermo.Lx, lammps_units['length'])
    ly = uc.set_in_units(thermo.Ly, lammps_units['length'])
    lz = uc.set_in_units(thermo.Lz, lammps_units['length'])
    xy = uc.set_in_units(thermo.Xy, lammps_units['length'])
    xz = uc.set_in_units(thermo.Xz, lammps_units['length'])
    yz = uc.set_in_units(thermo.Yz, lammps_units['length'])

    pxx = uc.set_in_units(thermo.Pxx, lammps_units['pressure'])
    pyy = uc.set_in_units(thermo.Pyy, lammps_units['pressure'])
    pzz = uc.set_in_units(thermo.Pzz, lammps_units['pressure'])
    pxy = uc.set_in_units(thermo.Pxy, lammps_units['pressure'])
    pxz = uc.set_in_units(thermo.Pxz, lammps_units['pressure'])
    pyz = uc.set_in_units(thermo.Pyz, lammps_units['pressure'])

    pe = uc.set_in_units(thermo.PotEng / system.natoms, lammps_units['energy'])

    # Extract the pressure tensor
    pij = np.array([[pxx[0], pxy[0], pxz[0]], [pxy[0], pyy[0], pyz[0]],
                    [pxz[0], pyz[0], pzz[0]]])

    # Set the six non-zero strain values
    strains = np.array([(lx[2] - lx[1]) / lx[0], (ly[4] - ly[3]) / ly[0],
                        (lz[6] - lz[5]) / lz[0], (yz[8] - yz[7]) / lz[0],
                        (xz[10] - xz[9]) / lz[0], (xy[12] - xy[11]) / ly[0]])

    # Calculate cij using stress changes associated with each non-zero strain
    cij = np.empty((6, 6))
    for i in range(6):
        delta_stress = np.array([
            pxx[2 * i + 1] - pxx[2 * i + 2], pyy[2 * i + 1] - pyy[2 * i + 2],
            pzz[2 * i + 1] - pzz[2 * i + 2], pyz[2 * i + 1] - pyz[2 * i + 2],
            pxz[2 * i + 1] - pxz[2 * i + 2], pxy[2 * i + 1] - pxy[2 * i + 2]
        ])

        cij[i] = delta_stress / strains[i]

    for i in range(6):
        for j in range(i):
            cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2

    C = am.ElasticConstants(Cij=cij)

    results = {}
    results['E_pot'] = pe[0]
    results['pij'] = pij
    results['C'] = C

    return results
Пример #32
0
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
Пример #33
0
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}
Пример #34
0
def e_vs_r_scan(lammps_command: str,
                system: am.System,
                potential: am.lammps.Potential,
                mpi_command: Optional[str] = None,
                ucell: Optional[am.System] = None,
                rmin: float = uc.set_in_units(2.0, 'angstrom'),
                rmax: float = uc.set_in_units(6.0, 'angstrom'),
                rsteps: int = 200) -> dict:
    """
    Performs a cohesive energy scan over a range of interatomic spaces, r.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    ucell : atomman.System, optional
        The fundamental unit cell correspodning to system.  This is used to
        convert system dimensions to cell dimensions. If not given, ucell will
        be taken as system.
    rmin : float, optional
        The minimum r spacing to use (default value is 2.0 angstroms).
    rmax : float, optional
        The maximum r spacing to use (default value is 6.0 angstroms).
    rsteps : int, optional
        The number of r spacing steps to evaluate (default value is 200).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'r_values'** (*numpy.array of float*) - All interatomic spacings,
          r, explored.
        - **'a_values'** (*numpy.array of float*) - All unit cell a lattice
          constants corresponding to the values explored.
        - **'Ecoh_values'** (*numpy.array of float*) - The computed cohesive
          energies for each r value.
        - **'min_cell'** (*list of atomman.System*) - Systems corresponding to
          the minima identified in the Ecoh_values.
    """

    # Make system a deepcopy of itself (protect original from changes)
    system = deepcopy(system)

    # Set ucell = system if ucell not given
    if ucell is None:
        ucell = system

    # Calculate the r/a ratio for the unit cell
    r_a = ucell.r0() / ucell.box.a

    # Get ratios of lx, ly, and lz of system relative to a of ucell
    lx_a = system.box.a / ucell.box.a
    ly_a = system.box.b / ucell.box.a
    lz_a = system.box.c / ucell.box.a
    alpha = system.box.alpha
    beta = system.box.beta
    gamma = system.box.gamma

    # Build lists of values
    r_values = np.linspace(rmin, rmax, rsteps)
    a_values = r_values / r_a
    Ecoh_values = np.empty(rsteps)

    # Loop over values
    for i in range(rsteps):

        # Rescale system's box
        a = a_values[i]
        system.box_set(a=a * lx_a,
                       b=a * ly_a,
                       c=a * lz_a,
                       alpha=alpha,
                       beta=beta,
                       gamma=gamma,
                       scale=True)

        # Get lammps units
        lammps_units = lmp.style.unit(potential.units)

        # Define lammps variables
        lammps_variables = {}
        system_info = system.dump('atom_data',
                                  f='atom.dat',
                                  potential=potential)
        lammps_variables['atomman_system_pair_info'] = system_info

        # Write lammps input script
        lammps_script = 'run0.in'
        template = read_calc_file('iprPy.calculation.E_vs_r_scan',
                                  'run0.template')
        with open(lammps_script, 'w') as f:
            f.write(filltemplate(template, lammps_variables, '<', '>'))

        # Run lammps and extract data
        try:
            output = lmp.run(lammps_command,
                             script_name=lammps_script,
                             mpi_command=mpi_command)
        except:
            Ecoh_values[i] = np.nan
        else:
            thermo = output.simulations[0]['thermo']

            if output.lammps_date < datetime.date(2016, 8, 1):
                Ecoh_values[i] = uc.set_in_units(thermo.peatom.values[-1],
                                                 lammps_units['energy'])
            else:
                Ecoh_values[i] = uc.set_in_units(thermo.v_peatom.values[-1],
                                                 lammps_units['energy'])

        # Rename log.lammps
        try:
            shutil.move('log.lammps', 'run0-' + str(i) + '-log.lammps')
        except:
            pass

    if len(Ecoh_values[np.isfinite(Ecoh_values)]) == 0:
        raise ValueError(
            'All LAMMPS runs failed. Potential likely invalid or incompatible.'
        )

    # Find unit cell systems at the energy minimums
    min_cells = []
    for i in range(1, rsteps - 1):
        if (Ecoh_values[i] < Ecoh_values[i - 1]
                and Ecoh_values[i] < Ecoh_values[i + 1]):
            a = a_values[i]
            cell = deepcopy(ucell)
            cell.box_set(a=a,
                         b=a * ucell.box.b / ucell.box.a,
                         c=a * ucell.box.c / ucell.box.a,
                         alpha=alpha,
                         beta=beta,
                         gamma=gamma,
                         scale=True)
            min_cells.append(cell)

    # Collect results
    results_dict = {}
    results_dict['r_values'] = r_values
    results_dict['a_values'] = a_values
    results_dict['Ecoh_values'] = Ecoh_values
    results_dict['min_cell'] = min_cells

    return results_dict
Пример #35
0
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
Пример #36
0
def calc_cij(lammps_exe, ucell, potential, symbols, p_xx=0.0, p_yy=0.0, p_zz=0.0):
    """Runs cij_script and returns current Cij, stress, Ecoh, and new ucell guess."""
    
    #setup system and pair info
    system_info = lmp.sys_gen(units =       potential.units,
                              atom_style =  potential.atom_style,
                              ucell =       ucell,
                              size =        np.array([[0,3], [0,3], [0,3]]))

    pair_info = potential.pair_info(symbols)
    
    #create script and run
    with open('cij.in','w') as f:
        f.write(cij_script(system_info, pair_info))
    data = lmp.run(lammps_exe, 'cij.in')
    
    #get units for pressure and energy used by LAMMPS simulation
    lmp_units = lmp.style.unit(potential.units)
    p_unit = lmp_units['pressure']
    e_unit = lmp_units['energy']
    
    #Extract thermo values. Each term ranges i=0-12 where i=0 is undeformed
    #The remaining values are for -/+ strain pairs in the six unique directions
    lx = np.array(data.finds('Lx'))
    ly = np.array(data.finds('Ly'))
    lz = np.array(data.finds('Lz'))
    xy = np.array(data.finds('Xy'))
    xz = np.array(data.finds('Xz'))
    yz = np.array(data.finds('Yz'))
    
    pxx = uc.set_in_units(np.array(data.finds('Pxx')), p_unit)
    pyy = uc.set_in_units(np.array(data.finds('Pyy')), p_unit)
    pzz = uc.set_in_units(np.array(data.finds('Pzz')), p_unit)
    pxy = uc.set_in_units(np.array(data.finds('Pxy')), p_unit)
    pxz = uc.set_in_units(np.array(data.finds('Pxz')), p_unit)
    pyz = uc.set_in_units(np.array(data.finds('Pyz')), p_unit)
    
    pe = uc.set_in_units(np.array(data.finds('peatom')), e_unit)
    
    #Set the six non-zero strain values
    strains = np.array([ (lx[2] -  lx[1])  / lx[0],
                         (ly[4] -  ly[3])  / ly[0],
                         (lz[6] -  lz[5])  / lz[0],
                         (yz[8] -  yz[7])  / lz[0],
                         (xz[10] - xz[9])  / lz[0],
                         (xy[12] - xy[11]) / ly[0] ])

    #calculate cij using stress changes associated with each non-zero strain
    cij = np.empty((6,6))
    for i in xrange(6):
        delta_stress = np.array([ pxx[2*i+1]-pxx[2*i+2],
                                  pyy[2*i+1]-pyy[2*i+2],
                                  pzz[2*i+1]-pzz[2*i+2],
                                  pyz[2*i+1]-pyz[2*i+2],
                                  pxz[2*i+1]-pxz[2*i+2],
                                  pxy[2*i+1]-pxy[2*i+2] ])

        cij[i] = delta_stress / strains[i] 
        
    for i in xrange(6):
        for j in xrange(i):
            cij[i,j] = cij[j,i] = (cij[i,j] + cij[j,i]) / 2

    C = am.tools.ElasticConstants(Cij=cij)
    
    if np.allclose(C.Cij, 0.0):
        raise RuntimeError('Divergence of elastic constants to <= 0')
    try:
        S = C.Sij
    except:
        raise RuntimeError('singular C:\n'+str(C.Cij))

    
    #extract the current stress state
    stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]],
                            [pxy[0], pyy[0], pyz[0]],
                            [pxz[0], pyz[0], pzz[0]]])
    
    s_xx = stress[0,0] + p_xx
    s_yy = stress[1,1] + p_yy
    s_zz = stress[2,2] + p_zz
    
    new_a = ucell.box.a / (S[0,0]*s_xx + S[0,1]*s_yy + S[0,2]*s_zz + 1)
    new_b = ucell.box.b / (S[1,0]*s_xx + S[1,1]*s_yy + S[1,2]*s_zz + 1)
    new_c = ucell.box.c / (S[2,0]*s_xx + S[2,1]*s_yy + S[2,2]*s_zz + 1)
    
    if new_a <= 0 or new_b <= 0 or new_c <=0:
        raise RuntimeError('Divergence of box dimensions to <= 0')
    
    newbox = am.Box(a=new_a, b=new_b, c=new_c)
    ucell_new = deepcopy(ucell)
    ucell_new.box_set(vects=newbox.vects, scale=True)
    
    return {'C':C, 'stress':stress, 'ecoh':pe[0], 'ucell_new':ucell_new}
Пример #37
0
def phononcalc(lammps_command,
               ucell,
               potential,
               mpi_command=None,
               a_mult=3,
               b_mult=3,
               c_mult=3,
               distance=0.01,
               symprec=1e-5,
               savefile='phonopy_params.yaml',
               plot=True,
               lammps_date=None):
    """
    Uses phonopy to compute the phonons for a unit cell structure using a
    LAMMPS interatomic potential.

    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    ucell : atomman.System
        The unit cell system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    a_mult : int, optional
        The a size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    b_mult : int, optional
        The b size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    c_mult : int, optional
        The c size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    distance : float, optional
        The atomic displacement distance used for computing the phonons.
        Default value is 0.01.
    symprec : float, optional
        Absolute length tolerance to use in identifying symmetry of atomic
        sites and system boundaries. Default value is 1e-5.
    savefile: str, optional
        The name of the phonopy yaml backup file.  Default value is
        'phonopy_params.yaml'.
    plot : bool, optional
        Flag indicating if band structure and DOS figures are to be generated.
        Default value is True.
    lammps_date : datetime.date, optional
        The version date associated with lammps_command.  If not given, the
        version will be identified.
    """
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Get lammps version date
    if lammps_date is None:
        lammps_date = lmp.checkversion(lammps_command)['date']

    # Use spglib to find primitive unit cell of ucell
    convcell = ucell.dump('spglib_cell')
    primcell = spglib.find_primitive(convcell, symprec=symprec)
    primucell = am.load('spglib_cell', primcell,
                        symbols=ucell.symbols).normalize()

    # Initialize Phonopy object
    phonon = phonopy.Phonopy(primucell.dump('phonopy_Atoms',
                                            symbols=potential.elements(
                                                primucell.symbols)),
                             [[a_mult, 0, 0], [0, b_mult, 0], [0, 0, c_mult]],
                             factor=phonopy.units.VaspToTHz)
    phonon.generate_displacements(distance=distance)

    # Loop over displaced supercells to compute forces
    forcearrays = []
    for supercell in phonon.supercells_with_displacements:

        # Save to LAMMPS data file
        system = am.load('phonopy_Atoms', supercell, symbols=primucell.symbols)
        system_info = system.dump('atom_data',
                                  f='disp.dat',
                                  potential=potential)

        # Define lammps variables
        lammps_variables = {}
        lammps_variables['atomman_system_pair_info'] = system_info

        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables[
                'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'

        # Write lammps input script
        lammps_script = 'phonon.in'
        template = read_calc_file('iprPy.calculation.phonon',
                                  'phonon.template')
        with open(lammps_script, 'w') as f:
            f.write(filltemplate(template, lammps_variables, '<', '>'))

        # Run LAMMPS
        lmp.run(lammps_command,
                script_name=lammps_script,
                mpi_command=mpi_command)

        # Extract forces from dump file
        forcestructure = am.load('atom_dump', 'forces.dump')
        forces = uc.set_in_units(forcestructure.atoms.force,
                                 lammps_units['force'])
        forcearrays.append(forces)

    results = {}

    # Set computed forces
    phonon.set_forces(forcearrays)

    # Save to yaml file
    phonon.save(savefile)

    # Compute band structure
    phonon.produce_force_constants()
    phonon.auto_band_structure(plot=plot)
    results['band_structure'] = phonon.get_band_structure_dict()
    if plot:
        plt.ylabel('Frequency (THz)')
        plt.savefig(Path('.', 'band.png'), dpi=400)
        plt.close()

    # Compute density of states
    phonon.auto_total_dos(plot=False)
    phonon.auto_projected_dos(plot=False)
    dos = phonon.get_total_dos_dict()
    dos['frequency'] = uc.set_in_units(dos.pop('frequency_points'), 'THz')
    dos['projected_dos'] = phonon.get_projected_dos_dict()['projected_dos']
    results['dos'] = dos

    # Compute thermal properties
    phonon.run_thermal_properties()
    results['thermal_properties'] = phonon.get_thermal_properties_dict()

    results['phonon'] = phonon
    return results
Пример #38
0
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
Пример #39
0
def phonon_quasiharmonic(lammps_command: str,
                         ucell: am.System,
                         potential: lmp.Potential,
                         mpi_command: Optional[str] = None,
                         a_mult: int = 3,
                         b_mult: int = 3,
                         c_mult: int = 3,
                         distance: float = 0.01,
                         symprec: float = 1e-5,
                         strainrange: float = 0.01,
                         numstrains: int = 5) -> dict:
    """
    Function that performs phonon and quasiharmonic approximation calculations
    using phonopy and LAMMPS.

    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    ucell : atomman.System
        The unit cell system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    a_mult : int, optional
        The a size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    b_mult : int, optional
        The b size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    c_mult : int, optional
        The c size multiplier to use on ucell before running the phonon
        calculation.  Must be an int and not a tuple.  Default value is 3.
    distance : float, optional
        The atomic displacement distance used for computing the phonons.
        Default value is 0.01.
    symprec : float, optional
        Absolute length tolerance to use in identifying symmetry of atomic
        sites and system boundaries. Default value is 1e-5.
    strainrange : float, optional
        The range of strains to apply to the unit cell to use with the
        quasiharmonic calculations.  Default value is 0.01.
    numstrains : int, optional
        The number of strains to use for the quasiharmonic calculations.
        Must be an odd integer.  If 1, then the quasiharmonic calculations
        will not be performed.  Default value is 5.
    """
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

    # Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']

    # Get original box vectors
    vects = ucell.box.vects

    # Generate the range of strains
    if numstrains == 1:
        zerostrain = phononcalc(lammps_command,
                                ucell,
                                potential,
                                mpi_command=mpi_command,
                                a_mult=a_mult,
                                b_mult=b_mult,
                                c_mult=c_mult,
                                distance=distance,
                                symprec=symprec,
                                lammps_date=lammps_date)
        phonons = [zerostrain['phonon']]
        qha = None

    elif numstrains % 2 == 0 or numstrains < 5:
        raise ValueError(
            'Invalid number of strains: must be odd and 1 or >= 5')
    else:
        strains = np.linspace(-strainrange, strainrange, numstrains)
        istrains = np.linspace(-(numstrains - 1) / 2, (numstrains - 1) / 2,
                               numstrains,
                               dtype=int)

        volumes = []
        energies = []
        phonons = []
        temperatures = None
        free_energy = None
        heat_capacity = None
        entropy = None

        # Loop over all strains
        for istrain, strain in zip(istrains, strains):

            # Identify the zero strain run
            if istrain == 0:
                zerostrainrun = True
                savefile = 'phonopy_params.yaml'
            else:
                zerostrainrun = False
                savefile = f'phonopy_params_{istrain}.yaml'

            # Generate system at the strain
            newvects = vects * (1 + strain)
            ucell.box_set(vects=newvects, scale=True)
            volumes.append(ucell.box.volume)
            system = ucell.supersize(a_mult, b_mult, c_mult)

            # Define lammps variables
            lammps_variables = {}
            system_info = system.dump('atom_data',
                                      f='disp.dat',
                                      potential=potential)
            lammps_variables['atomman_system_pair_info'] = system_info

            # Set dump_modify_format based on lammps_date
            if lammps_date < datetime.date(2016, 8, 3):
                lammps_variables[
                    'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e %.13e %.13e"'
            else:
                lammps_variables['dump_modify_format'] = 'float %.13e'

            # Write lammps input script
            lammps_script = 'phonon.in'
            template = read_calc_file('iprPy.calculation.phonon',
                                      'phonon.template')
            with open(lammps_script, 'w') as f:
                f.write(filltemplate(template, lammps_variables, '<', '>'))

            # Run LAMMPS
            output = lmp.run(lammps_command,
                             script_name='phonon.in',
                             mpi_command=mpi_command)

            # Extract system energy
            thermo = output.simulations[0]['thermo']
            energy = uc.set_in_units(thermo.PotEng.values[-1],
                                     lammps_units['energy'])

            # Scale energy by sizemults and append to list
            energies.append(energy / (a_mult * b_mult * c_mult))

            # Compute phonon info for ucell
            phononinfo = phononcalc(lammps_command,
                                    ucell,
                                    potential,
                                    mpi_command=mpi_command,
                                    a_mult=a_mult,
                                    b_mult=b_mult,
                                    c_mult=c_mult,
                                    distance=distance,
                                    symprec=symprec,
                                    savefile=savefile,
                                    plot=zerostrainrun,
                                    lammps_date=lammps_date)
            phonons.append(phononinfo['phonon'])

            # Extract temperature values from the first run
            if temperatures is None:
                temperatures = phononinfo['thermal_properties']['temperatures']

                # Initialize QHA input arrays
                free_energy = np.empty((len(temperatures), len(strains)))
                heat_capacity = np.empty((len(temperatures), len(strains)))
                entropy = np.empty((len(temperatures), len(strains)))

            # Get values for zerostrainrun
            if zerostrainrun is True:
                zerostrain = phononinfo

            # Copy values to qha input arrays
            free_energy[:, istrain] = phononinfo['thermal_properties'][
                'free_energy']
            entropy[:, istrain] = phononinfo['thermal_properties']['entropy']
            heat_capacity[:, istrain] = phononinfo['thermal_properties'][
                'heat_capacity']

        # Compute qha
        try:
            qha = phonopy.PhonopyQHA(volumes=volumes,
                                     electronic_energies=energies,
                                     temperatures=temperatures,
                                     free_energy=free_energy,
                                     cv=heat_capacity,
                                     entropy=entropy)
        except:
            qha = None

    results = {}

    # Add phonopy objects
    results['phonon_objects'] = phonons
    results['qha_object'] = qha

    # Extract zerostrain properties
    results['band_structure'] = zerostrain['band_structure']
    results['density_of_states'] = zerostrain['dos']

    # Convert units on thermal properties
    results['thermal_properties'] = zerostrain['thermal_properties']
    results['thermal_properties']['temperature'] = results[
        'thermal_properties'].pop('temperatures')
    results['thermal_properties']['Helmholtz'] = uc.set_in_units(
        results['thermal_properties'].pop('free_energy'), 'kJ/mol')
    results['thermal_properties']['entropy'] = uc.set_in_units(
        results['thermal_properties'].pop('entropy'), 'J/K/mol')
    results['thermal_properties']['heat_capacity_v'] = uc.set_in_units(
        results['thermal_properties'].pop('heat_capacity'), 'J/K/mol')

    if qha is not None:

        # Create QHA plots
        qha.plot_bulk_modulus()
        plt.xlabel('Volume ($Å^3$)', size='large')
        plt.ylabel('Energy ($eV$)', size='large')
        plt.savefig('bulk_modulus.png', dpi=400, bbox_inches='tight')
        plt.close()

        qha.plot_helmholtz_volume()
        plt.savefig('helmholtz_volume.png', dpi=400)
        plt.close()

        # Package volume vs energy scans
        results['volume_scan'] = {}
        results['volume_scan']['volume'] = np.array(volumes)
        results['volume_scan']['strain'] = strains
        results['volume_scan']['energy'] = np.array(energies)

        # Compute and add QHA properties
        properties = qha.get_bulk_modulus_parameters()
        results['E0'] = uc.set_in_units(properties[0], 'eV')
        results['B0'] = uc.set_in_units(properties[1], 'eV/angstrom^3')
        results['B0prime'] = uc.set_in_units(properties[2], 'eV/angstrom^3')
        results['V0'] = uc.set_in_units(properties[3], 'angstrom^3')

        results['thermal_properties']['volume'] = uc.set_in_units(
            np.hstack([qha.volume_temperature, np.nan]), 'angstrom^3')
        results['thermal_properties']['thermal_expansion'] = np.hstack(
            [qha.thermal_expansion, np.nan])
        results['thermal_properties']['Gibbs'] = uc.set_in_units(
            np.hstack([qha.gibbs_temperature, np.nan]), 'eV')
        results['thermal_properties']['bulk_modulus'] = uc.set_in_units(
            np.hstack([qha.bulk_modulus_temperature, np.nan]), 'GPa')
        results['thermal_properties'][
            'heat_capacity_p_numerical'] = uc.set_in_units(
                np.hstack([qha.heat_capacity_P_numerical, np.nan]), 'J/K/mol')
        results['thermal_properties'][
            'heat_capacity_p_polyfit'] = uc.set_in_units(
                np.hstack([qha.heat_capacity_P_polyfit, np.nan]), 'J/K/mol')
        results['thermal_properties']['gruneisen'] = np.hstack(
            [qha.gruneisen_temperature, np.nan])

    return results
def 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
Пример #41
0
def relax_dynamic(lammps_command, system, potential, mpi_command=None,
                  p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0, p_xz=0.0, p_yz=0.0,
                  temperature=0.0, integrator=None, runsteps=220000,
                  thermosteps=100, dumpsteps=None, equilsteps=20000,
                  randomseed=None):
    """
    Performs a full dynamic relax on a given system at the given temperature
    to the specified pressure state.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    symbols : list of str
        The list of element-model symbols for the Potential that correspond to
        system's atypes.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    temperature : float, optional
        The temperature to relax at (default is 0.0).
    runsteps : int, optional
        The number of integration steps to perform (default is 220000).
    integrator : str or None, optional
        The integration method to use. Options are 'npt', 'nvt', 'nph',
        'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat.
        (Default is None, which will use 'nph+l' for temperature == 0, and
        'npt' otherwise.)
    thermosteps : int, optional
        Thermo values will be reported every this many steps (default is
        100).
    dumpsteps : int or None, optional
        Dump files will be saved every this many steps (default is None,
        which sets dumpsteps equal to runsteps).
    equilsteps : int, optional
        The number of timesteps at the beginning of the simulation to
        exclude when computing average values (default is 20000).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'relaxed_system'** (*float*) - The relaxed system.
        - **'E_coh'** (*float*) - The mean measured cohesive energy.
        - **'measured_pxx'** (*float*) - The measured x tensile pressure of the
          relaxed system.
        - **'measured_pyy'** (*float*) - The measured y tensile pressure of the
          relaxed system.
        - **'measured_pzz'** (*float*) - The measured z tensile pressure of the
          relaxed system.
        - **'measured_pxy'** (*float*) - The measured xy shear pressure of the
          relaxed system.
        - **'measured_pxz'** (*float*) - The measured xz shear pressure of the
          relaxed system.
        - **'measured_pyz'** (*float*) - The measured yz shear pressure of the
          relaxed system.
        - **'temp'** (*float*) - The mean measured temperature.
        - **'E_coh_std'** (*float*) - The standard deviation in the measured
          cohesive energy values.
        - **'measured_pxx_std'** (*float*) - The standard deviation in the
          measured x tensile pressure of the relaxed system.
        - **'measured_pyy_std'** (*float*) - The standard deviation in the
          measured y tensile pressure of the relaxed system.
        - **'measured_pzz_std'** (*float*) - The standard deviation in the
          measured z tensile pressure of the relaxed system.
        - **'measured_pxy_std'** (*float*) - The standard deviation in the
          measured xy shear pressure of the relaxed system.
        - **'measured_pxz_std'** (*float*) - The standard deviation in the
          measured xz shear pressure of the relaxed system.
        - **'measured_pyz_std'** (*float*) - The standard deviation in the
          measured yz shear pressure of the relaxed system.
        - **'temp_std'** (*float*) - The standard deviation in the measured
          temperature values.
    """
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Handle default values
    if dumpsteps is None:
        dumpsteps = runsteps
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='init.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    integ_info = integrator_info(integrator=integrator,
                                 p_xx=p_xx, p_yy=p_yy, p_zz=p_zz,
                                 p_xy=p_xy, p_xz=p_xz, p_yz=p_yz,
                                 temperature=temperature,
                                 randomseed=randomseed,
                                 units=potential.units)
    lammps_variables['integrator_info'] = integ_info
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['runsteps'] = runsteps
    lammps_variables['dumpsteps'] = dumpsteps
    
    # Set compute stress/atom based on LAMMPS version
    if lammps_date < datetime.date(2014, 2, 12):
        lammps_variables['stressterm'] = ''
    else:
        lammps_variables['stressterm'] = 'NULL'
    
    # Set dump_modify_format based on lammps_date
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'full_relax.template'
    lammps_script = 'full_relax.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    # Run lammps 
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract LAMMPS thermo data. 
    results = {}
    thermo = output.simulations[0]['thermo']
    
    results['dumpfile_initial'] = '0.dump'
    results['symbols_initial'] = system.symbols
    
    # Load relaxed system from dump file
    last_dump_file = str(thermo.Step.values[-1])+'.dump'
    results['dumpfile_final'] = last_dump_file
    system = am.load('atom_dump', last_dump_file, symbols=system.symbols)
    results['symbols_final'] = system.symbols
    
    # Only consider values where Step >= equilsteps
    thermo = thermo[thermo.Step >= equilsteps]
    results['nsamples'] = len(thermo)
    
    # Get cohesive energy estimates
    natoms = system.natoms
    results['E_coh'] = uc.set_in_units(thermo.PotEng.mean() / natoms, lammps_units['energy'])
    results['E_coh_std'] = uc.set_in_units(thermo.PotEng.std() / natoms, lammps_units['energy'])
    
    results['lx'] = uc.set_in_units(thermo.Lx.mean(), lammps_units['length'])
    results['lx_std'] = uc.set_in_units(thermo.Lx.std(), lammps_units['length'])
    results['ly'] = uc.set_in_units(thermo.Ly.mean(), lammps_units['length'])
    results['ly_std'] = uc.set_in_units(thermo.Ly.std(), lammps_units['length'])
    results['lz'] = uc.set_in_units(thermo.Lz.mean(), lammps_units['length'])
    results['lz_std'] = uc.set_in_units(thermo.Lz.std(), lammps_units['length'])
    results['xy'] = uc.set_in_units(thermo.Xy.mean(), lammps_units['length'])
    results['xy_std'] = uc.set_in_units(thermo.Xy.std(), lammps_units['length'])
    results['xz'] = uc.set_in_units(thermo.Xz.mean(), lammps_units['length'])
    results['xz_std'] = uc.set_in_units(thermo.Xz.std(), lammps_units['length'])
    results['yz'] = uc.set_in_units(thermo.Yz.mean(), lammps_units['length'])
    results['yz_std'] = uc.set_in_units(thermo.Yz.std(), lammps_units['length'])
    
    results['measured_pxx'] = uc.set_in_units(thermo.Pxx.mean(), lammps_units['pressure'])
    results['measured_pxx_std'] = uc.set_in_units(thermo.Pxx.std(), lammps_units['pressure'])
    results['measured_pyy'] = uc.set_in_units(thermo.Pyy.mean(), lammps_units['pressure'])
    results['measured_pyy_std'] = uc.set_in_units(thermo.Pyy.std(), lammps_units['pressure'])
    results['measured_pzz'] = uc.set_in_units(thermo.Pzz.mean(), lammps_units['pressure'])
    results['measured_pzz_std'] = uc.set_in_units(thermo.Pzz.std(), lammps_units['pressure'])
    results['measured_pxy'] = uc.set_in_units(thermo.Pxy.mean(), lammps_units['pressure'])
    results['measured_pxy_std'] = uc.set_in_units(thermo.Pxy.std(), lammps_units['pressure'])
    results['measured_pxz'] = uc.set_in_units(thermo.Pxz.mean(), lammps_units['pressure'])
    results['measured_pxz_std'] = uc.set_in_units(thermo.Pxz.std(), lammps_units['pressure'])
    results['measured_pyz'] = uc.set_in_units(thermo.Pyz.mean(), lammps_units['pressure'])
    results['measured_pyz_std'] = uc.set_in_units(thermo.Pyz.std(), lammps_units['pressure'])
    results['temp'] = thermo.Temp.mean()
    results['temp_std'] = thermo.Temp.std()
    
    return results