Ejemplo n.º 1
0
def test_filltemplate():
    template1 = 'this is my {val} to test'
    template2 = 'another <val> to test'
    template3 = 'yet another !@#$val?@ to test'

    var = {'val': 'template', 'notused':'STEVE!'}

    assert (filltemplate(template1, var, '{', '}')
            == 'this is my template to test')

    assert (filltemplate(template2, var, '<', '>')
            == 'another template to test')

    assert (filltemplate(template3, var, '!@#$', '?@')
            == 'yet another template to test')
Ejemplo n.º 2
0
def test_filltemplate():
    template1 = 'this is my {val} to test'
    template2 = 'another <val> to test'
    template3 = 'yet another !@#$val?@ to test'

    var = {'val': 'template', 'notused': 'STEVE!'}

    assert (filltemplate(template1, var, '{',
                         '}') == 'this is my template to test')

    assert (filltemplate(template2, var, '<',
                         '>') == 'another template to test')

    assert (filltemplate(template3, var, '!@#$',
                         '?@') == 'yet another template to test')
Ejemplo n.º 3
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
    
Ejemplo n.º 4
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
Ejemplo n.º 5
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
Ejemplo n.º 6
0
def disl_relax(
    lammps_command: str,
    system: am.System,
    potential: lmp.Potential,
    mpi_command: Optional[str] = None,
    annealtemp: float = 0.0,
    annealsteps: Optional[int] = None,
    randomseed: Optional[int] = None,
    etol: float = 0.0,
    ftol: float = 1e-6,
    maxiter: int = 10000,
    maxeval: int = 100000,
    dmax: float = uc.set_in_units(0.01, 'angstrom')
) -> dict:
    """
    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.
    annealsteps : int, optional
        The number of time steps to run the dynamic relaxation for.  Default
        is None, which will run for 10000 steps if annealtemp is not 0.0.        
    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', potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info
    lammps_variables['anneal_info'] = anneal_info(annealtemp, annealsteps,
                                                  randomseed, potential.units)
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = dmax
    lammps_variables['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
    lammps_script = 'disl_relax.in'
    template = read_calc_file('iprPy.calculation.dislocation_monopole',
                              'disl_relax.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)
    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
Ejemplo n.º 7
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
Ejemplo n.º 8
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
Ejemplo n.º 9
0
def relax_static(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,
                 dispmult: float = 0.0,
                 etol: float = 0.0,
                 ftol: float = 0.0,
                 maxiter: int = 100000,
                 maxeval: int = 1000000,
                 dmax: float = uc.set_in_units(0.01, 'angstrom'),
                 maxcycles: int = 100,
                 ctol: float = 1e-10) -> dict:
    """
    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:
        
        - **'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.
        - **'lx'** (*float*) - The relaxed lx box length.
        - **'ly'** (*float*) - The relaxed ly box length.
        - **'lz'** (*float*) - The relaxed lz box length.
        - **'xy'** (*float*) - The relaxed xy box tilt.
        - **'xz'** (*float*) - The relaxed xz box tilt.
        - **'yz'** (*float*) - The relaxed yz box tilt.
        - **'E_pot'** (*float*) - The potential energy per atom for the final
          configuration.
        - **'measured_pxx'** (*float*) - The measured x tensile pressure
          component for the final configuration.
        - **'measured_pyy'** (*float*) - The measured y tensile pressure
          component for the final configuration.
        - **'measured_pzz'** (*float*) - The measured z tensile pressure
          component for the final configuration.
        - **'measured_pxy'** (*float*) - The measured xy shear pressure
          component for the final configuration.
        - **'measured_pxz'** (*float*) - The measured xz shear pressure
          component for the final configuration.
        - **'measured_pyz'** (*float*) - The measured yz shear pressure
          component for the final configuration.
    """

    # 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):

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

        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_keys based on atom_style
        if potential.atom_style in ['charge']:
            lammps_variables['dump_keys'] = 'id type q x y z c_peatom'
        else:
            lammps_variables['dump_keys'] = 'id type x y z c_peatom'

        # 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"'
            else:
                lammps_variables[
                    'dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'

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

        # Run LAMMPS and extract thermo data
        logfile = 'log-' + str(cycle) + '.lammps'
        output = lmp.run(lammps_command,
                         script_name=lammps_script,
                         mpi_command=mpi_command,
                         logfile=logfile)
        thermo = output.simulations[0]['thermo']

        # Clean up dump files
        Path('0.dump').unlink()
        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_pot'] = 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
Ejemplo n.º 10
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
Ejemplo n.º 11
0
def elastic_constants_static(
    lammps_command: str,
    system: am.System,
    potential: lmp.Potential,
    mpi_command: Optional[str] = None,
    strainrange: float = 1e-6,
    etol: float = 0.0,
    ftol: float = 0.0,
    maxiter: int = 10000,
    maxeval: int = 100000,
    dmax: float = uc.set_in_units(0.01, 'angstrom')
) -> dict:
    """
    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:
        
        - **'raw_Cij_negative'** (*numpy.ndarray*) - The values of Cij obtained
          from only the negative strains.
        - **'raw_Cij_positive'** (*numpy.ndarray*) - The values of Cij obtained
          from only the positive strains.
        - **'C'** (*atomman.ElasticConstants*) - The computed elastic constants
          obtained from averaging the negative and positive strain values.
    """

    # 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', potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info
    lammps_variables['restart_commands'] = restart_commands(
        potential, 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
    lammps_script = 'cij.in'
    template = read_calc_file('iprPy.calculation.elastic_constants_static',
                              'cij.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)

    # 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
Ejemplo n.º 12
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
Ejemplo n.º 13
0
def stackingfaultrelax(lammps_command: str,
                       system: am.System,
                       potential: lmp.Potential,
                       mpi_command: Optional[str] = None,
                       sim_directory: Optional[str] = None,
                       cutboxvector: str = 'c',
                       etol: float = 0.0,
                       ftol: float = 0.0,
                       maxiter: int = 10000,
                       maxeval: int = 100000,
                       dmax: float = uc.set_in_units(0.01, 'angstrom'),
                       lammps_date: Optional[datetime.date] = None) -> dict:
    """
    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.
    """

    # 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(),
                              potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info
    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
    lammps_script = Path(sim_directory, 'sfmin.in')
    template = read_calc_file('iprPy.calculation.stacking_fault_map_2D',
                              'sfmin.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.as_posix(),
                     mpi_command=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
Ejemplo n.º 14
0
def relax_system(lammps_command: str,
                 system: am.System,
                 potential: lmp.Potential,
                 mpi_command: Optional[str] = None,
                 etol: float = 0.0,
                 ftol: float = 0.0,
                 maxiter: int = 10000,
                 maxeval: int = 100000,
                 dmax: float = uc.set_in_units(0.01, 'angstrom'),
                 lammps_date: Optional[datetime.date] = None) -> dict:
    """
    Sets up and runs the min.in LAMMPS script for performing an energy/force
    minimization to relax a system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    lammps_date : datetime.date or None, optional
        The date version of the LAMMPS executable.  If None, will be identified
        from the lammps_command (default is None).

    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The name of the LAMMPS log file.
        - **'initialdatafile'** (*str*) - The name of the LAMMPS data file
          used to import an inital configuration.
        - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the inital configuration.
        - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the relaxed configuration.
        - **'potentialenergy'** (*float*) - The total potential energy of
          the relaxed system.
    """

    # Ensure all atoms are within the system's box
    system.wrap()

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

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

    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='system.dat', potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])

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

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

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

    # Extract output values
    thermo = output.simulations[-1]['thermo']
    results = {}
    results['logfile'] = 'log.lammps'
    results['initialdatafile'] = 'system.dat'
    results['initialdumpfile'] = 'atom.0'
    results['finaldumpfile'] = 'atom.%i' % thermo.Step.values[-1]
    results['potentialenergy'] = uc.set_in_units(thermo.PotEng.values[-1],
                                                 lammps_units['energy'])

    return results
Ejemplo n.º 15
0
def bond_angle_scan(lammps_command: str,
                    potential: am.lammps.Potential,
                    symbols: list,
                    mpi_command: Optional[str] = None,
                    rmin: float = uc.set_in_units(0.5, 'angstrom'),
                    rmax: float = uc.set_in_units(6.0, 'angstrom'),
                    rnum: int = 100,
                    thetamin: float = 1.0,
                    thetamax: float = 180,
                    thetanum: int = 100) -> dict:
    """
    Performs a three-body bond angle energy scan over a range of interatomic
    spaces, r, and angles, theta.
    
    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 three atoms in the cluster.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    rmin : float, optional
        The minimum value for the r_ij and r_ik spacings. Default value is 0.5.
    rmax : float, optional
        The maximum value for the r_ij and r_ik spacings. Default value is 5.5.
    rnum : int, optional
        The number of r_ij and r_ik spacings to evaluate. Default value is 100.
    thetamin : float, optional
        The minimum value for the theta angle. Default value is 1.0.
    thetamax : float, optional
        The maximum value for the theta angle. Default value is 180.0.
    thetanum : int, optional
        The number of theta angles to evaluate. Default value is 100.
        
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'cluster'** (*atomman.cluster.BondAngleMap*) - Object that maps
          measured energies to r, theta coordinates, and contains built-in
          analysis tools.
        - **results_file'** (*str*) - File name containing the raw energy
          scan results.
        - **'length_unit'** (*str*) - Unit of length used in the results_file.
        - **'energy_unit'** (*str*) - Unit of energy used in the results_file.
    """

    # Create cluster object
    cluster = am.cluster.BondAngleMap(rmin=rmin,
                                      rmax=rmax,
                                      rnum=rnum,
                                      thetamin=thetamin,
                                      thetamax=thetamax,
                                      thetanum=thetanum,
                                      symbols=symbols)

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

    # Define lammps variables
    lammps_variables = {}

    # Add range parameters
    lammps_variables['rmin'] = uc.get_in_units(rmin, lammps_units['length'])
    lammps_variables['rmax'] = uc.get_in_units(rmax, lammps_units['length'])
    lammps_variables['rnum'] = uc.get_in_units(rnum, lammps_units['length'])
    lammps_variables['thetamin'] = thetamin
    lammps_variables['thetamax'] = thetamax
    lammps_variables['thetanum'] = thetanum

    # Add atomic types
    if len(cluster.symbols) == 1:
        natypes = 1
        atype = np.array([1, 1, 1])
        symbols = cluster.symbols
    elif len(cluster.symbols) == 3:
        symbols, atype = np.unique(cluster.symbols, return_inverse=True)
        atype += 1
        natypes = len(symbols)
    lammps_variables['natypes'] = natypes
    lammps_variables['atype1'] = atype[0]
    lammps_variables['atype2'] = atype[1]
    lammps_variables['atype3'] = atype[2]

    # Add potential information
    lammps_variables['atomman_pair_info'] = potential.pair_info(symbols)
    lammps_variables['atom_style'] = potential.atom_style
    lammps_variables['units'] = potential.units

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

    # Run lammps and extract data
    lmp.run(lammps_command,
            script_name=lammps_script,
            mpi_command=mpi_command,
            logfile=None,
            screen=False)
    cluster.load_table('3_body_scan.txt',
                       length_unit=lammps_units['length'],
                       energy_unit=lammps_units['energy'])

    # Collect results
    results_dict = {}
    results_dict['cluster'] = cluster
    results_dict['results_file'] = '3_body_scan.txt'
    results_dict['length_unit'] = lammps_units['length']
    results_dict['energy_unit'] = lammps_units['energy']

    return results_dict
Ejemplo n.º 16
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
Ejemplo n.º 17
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
Ejemplo n.º 18
0
def pointdiffusion(lammps_command: str,
                   system: am.System,
                   potential: lmp.Potential,
                   point_kwargs: Union[list, dict],
                   mpi_command: Optional[str] = None,
                   temperature: float = 300.0,
                   runsteps: int = 200000,
                   thermosteps: Optional[int] = None,
                   dumpsteps: int = 0,
                   equilsteps: int = 20000,
                   randomseed: Optional[int] = None) -> dict:
                   
    """
    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:
        #print(pkwargs)
        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='perfect.dat',
                              potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info
    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
    lammps_script = 'diffusion.in'
    template = read_calc_file('iprPy.calculation.point_defect_diffusion',
                              'diffusion.template')
    with open(lammps_script, 'w') as f:
        f.write(filltemplate(template, lammps_variables, '<', '>'))
    
    # Run lammps
    output = lmp.run(lammps_command, script_name=lammps_script,
                     mpi_command=mpi_command)
    
    # Extract 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'])
    E_pots = uc.set_in_units(thermo.PotEng.values, lammps_units['energy'])
    E_totals = uc.set_in_units(thermo.TotEng.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
    results['nsamples'] = len(thermo)
    
    # Get mean and std for temperature, pressure, and 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['E_pot'] = np.mean(E_pots)
    results['E_pot_std'] = np.std(E_pots)
    results['E_total'] = np.mean(E_totals)
    results['E_total_std'] = np.std(E_totals)
    
    # 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