Пример #1
0
    def plot_force(self, force_unit=None, length_unit=None, ax=None, **kwargs):
        """
        Creates a plot of the force to move along the path as a function of the
        arc coordinates.

        Parameters
        ----------
        force_unit : str or None, optional
            If given, the force values will be converted from atomman's
            working units to the specified units.  Default value of None will
            do no conversions, which is useful if the energyfxn is not reporting
            in atomman's working units.
        length_unit : str or None, optional
            If given, the arc coordinates will be converted from atomman's
            working units to the specified units.  Default value of None will
            do no conversions, which is useful if the coords are not given
            in atomman's working units.
        ax : matplotlib.pyplot.axis
            A pre-existing plotting axis.  Allows for more control and the
            use of subplots.
        **kwargs : any, optional
            All additional keyword arguments will be passed to 
            matplotlib.pyplot.figure().

        Returns
        -------
        matplotlib.pyplot.figure
            The generated figure allowing for further modifications.  Returned
            if ax is None.
        """
        if ax is None:
            fig = plt.figure(**kwargs)
            ax = fig.add_subplot(111)
            returnfig = True
        else:
            assert len(kwargs) == 0, 'ax and extra kwargs cannot both be given'
            returnfig = False

        # Handle force units
        force = self.force
        if force_unit is None:
            ax.set_ylabel('Force', size='x-large')
        else:
            ax.set_ylabel(f'Force ({force_unit})', size='x-large')
            force = uc.get_in_units(force, force_unit)

        # Handle length unit
        arccoord = self.arccoord
        if length_unit is None:
            ax.set_xlabel('Arc coordinate', size='x-large')
        else:
            ax.set_xlabel(f'Arc coordinate ({length_unit})', size='x-large')
            arccoord = uc.get_in_units(arccoord, length_unit)
        ax.set_xlim(0, arccoord[-1])

        ax.plot(arccoord, force, lw=3)

        if returnfig:
            return fig
Пример #2
0
def interpolate_contour(system, property, index=None, plotbounds=None, bins=200, dots=True, czero=True, save=False, show=True, length_unit='angstrom', property_unit=None, cmap='jet'):
    """Creates a contour plot of a system's per-atom properties by interpolating between all of the atoms."""
    
    if save is False and show is False:
        print 'Figure not saved or displayed!'
    
    values = system.atoms_prop(key=property)
    assert values is not None, 'atomic property not found!'
    
    name = str(property)
    if index is not None:
        if isinstance(index, (int, long)):
            index = [index]
        else:
            index = list(index)
        for i in index:
            name += '[' + str(i+1) + ']'
        values = values[[Ellipsis] + index]
    
    assert values.shape == (system.natoms, ), 'Identified property[index] must be a per-atom scalar' 
    
    if plotbounds is None:
        plotbounds = np.array([[system.box.xlo, system.box.xhi],
                               [system.box.ylo, system.box.yhi],
                               [system.box.zlo, system.box.zhi]])
        plotbounds = uc.get_in_units(plotbounds, length_unit)
        
    #Build arrays containing atomic information
    pos = uc.get_in_units(system.atoms_prop(key='pos'), length_unit)
    
    #identify only atoms in plotbounds
    in_bounds = ((pos[:,0] > plotbounds[0,0] - 5.) &
                 (pos[:,0] < plotbounds[0,1] + 5.) &
                 (pos[:,1] > plotbounds[1,0] - 5.) &
                 (pos[:,1] < plotbounds[1,1] + 5.) &
                 (pos[:,2] > plotbounds[2,0]) &
                 (pos[:,2] < plotbounds[2,1]))
    
    x = pos[in_bounds, 0]
    y = pos[in_bounds, 1]
    v = values[in_bounds]

    box = plotbounds[:2,:2]
    grid, xedges, yedges = grid_interpolate_2d(x, y, v, bins=bins, range=box)

    intsum = np.sum(grid)
    avsum = intsum / (bins-1) / (bins-1)
    intsum = intsum * (plotbounds[0,1]-plotbounds[0,0]) / (bins-1) * (plotbounds[1,1]-plotbounds[1,0]) / (bins-1)
        
    fig = prettygrid(grid, xedges, yedges, cmap=cmap, propname=name, czero=czero)
    if dots:
        adddots(x, y, xedges, yedges)
    if save:
        plt.savefig(name + '.png', dpi=800)
    if show==False:
        plt.close(fig)
    plt.show() 
 
    return intsum, avsum
Пример #3
0
def lammps_ELASTIC(lammps_command, system, potential, symbols, mpi_command=None, 
                   strainrange=1e-6, etol=0.0, ftol=0.0, maxiter=100, maxeval=1000, 
                   dmax=0.01, pressure_unit='GPa'):
    """Sets up and runs the ELASTIC example distributed with LAMMPS"""

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

    #Define lammps variables
    lammps_variables = {}
    
    lammps_variables['atomman_system_info'] = lmp.atom_data.dump(system, 'init.dat', units=potential.units, atom_style=potential.atom_style)
    lammps_variables['atomman_pair_info'] = potential.pair_info(symbols)
    lammps_variables['strainrange'] = strainrange
    lammps_variables['pressure_unit_scaling'] = uc.get_in_units(uc.set_in_units(1.0, lammps_units['pressure']), pressure_unit)
    lammps_variables['pressure_unit'] = pressure_unit
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    #Fill in mod.template files
    with open('init.mod.template') as template_file:
        template = template_file.read()
    with open('init.mod', 'w') as in_file:
        in_file.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    with open('potential.mod.template') as template_file:
        template = template_file.read()
    with open('potential.mod', 'w') as in_file:
        in_file.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))    
    
    output = lmp.run(lammps_command, 'in.elastic', mpi_command)
    
    #Extract output values
    relaxed = output['LAMMPS-log-thermo-data']['simulation'][0]
    
    results = {}
    results['E_coh'] = uc.set_in_units(relaxed['thermo']['PotEng'][-1], lammps_units['energy']) / system.natoms
    results['lx'] =    uc.set_in_units(relaxed['thermo']['Lx'][-1],     lammps_units['length'])
    results['ly'] =    uc.set_in_units(relaxed['thermo']['Ly'][-1],     lammps_units['length'])
    results['lz'] =    uc.set_in_units(relaxed['thermo']['Lz'][-1],     lammps_units['length'])
    
    with open('log.lammps') as log_file:
        log = log_file.read()
    
    start = log.find('print "Elastic Constant C11all = ${C11all} ${cunits}"')
    lines = log[start+54:].split('\n')

    for line in lines:
        terms = line.split()
        if len(terms) > 0 and terms[0] == 'Elastic':
            c_term = terms[2][:3]
            c_value = terms[4]
            results['c_unit'] = terms[5]
            results[c_term] = c_value
                
    return results    
Пример #4
0
    def disregistry_plot(self,
                         x=None,
                         disregistry=None,
                         figsize=None,
                         length_unit='Å'):
        """
        Creates a simple matplotlib figure showing the disregistry profiles.

        Parameters
        ----------
        x : numpy.ndarray, optional
            x-coordinates.  Default value is the stored x-coordinates.
        disregistry : numpy.ndarray, optional
            (N, 3) shaped array of disregistry vectors at each x-coordinate.
            Default value is the stored disregistry values.
        figsize : tuple, optional
            matplotlib figure figsize parameter.  Default value is (10, 6).
        length_unit : str, optional
            The unit of length to display positions and disregistry values in.
            Default value is 'Å'.
            
        Returns
        -------
        matplotlib.pyplot.figure
            The generated figure allowing users to perform additional
            modifications.
        """
        hasvals = True
        # Default values are class properties
        if x is None:
            try:
                x = self.x
            except:
                hasvals = False
        if disregistry is None:
            try:
                disregistry = self.disregistry
            except:
                hasvals = False
        if hasvals:

            if figsize is None:
                figsize = (10, 6)

            fig = plt.figure(figsize=figsize)

            x = uc.get_in_units(x, length_unit)
            disregistry = uc.get_in_units(disregistry, length_unit)
            plt.plot(x, disregistry[:, 0], label='edge disregistry')
            plt.plot(x, disregistry[:, 1], label='normal disregistry')
            plt.plot(x, disregistry[:, 2], label='screw disregistry')
            plt.legend(fontsize='xx-large')
            plt.xlabel(f'x (${length_unit}$)', size='xx-large')
            plt.ylabel(f'disregistry (${length_unit}$', size='xx-large')
            return fig

        else:
            print('x and disregistry must be set/given to plot')
Пример #5
0
    def fcc_edge(self):
        axes = np.array([[1, 0, -1], [1, 1, 1], [1, -2, 1]])

        alat = uc.set_in_units(4.0248, 'angstrom')

        C11 = uc.set_in_units(113.76, 'GPa')
        C12 = uc.set_in_units(61.71, 'GPa')
        C44 = uc.set_in_units(31.25, 'GPa')

        c = am.ElasticConstants(C11=C11, C12=C12, C44=C44)
        burgers = alat / 2 * np.array([1., 0., -1.])

        # initializing a new Stroh object using the data
        stroh = am.defect.Stroh(c, burgers, axes=axes)

        pos_test = uc.set_in_units(np.array([12.4, 13.5, -10.6]), 'angstrom')

        disp = stroh.displacement(pos_test)

        print("displacement =", uc.get_in_units(disp, 'angstrom'), 'angstrom')

        # monopole system
        box = am.Box(a=alat, b=alat, c=alat)
        atoms = am.Atoms(natoms=4,
                         prop={
                             'atype':
                             1,
                             'pos': [[0.0, 0.0, 0.0], [0.5, 0.5, 0.0],
                                     [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]]
                         })
        ucell = am.System(atoms=atoms, box=box, scale=True)
        system = am.rotate_cubic(ucell, axes)

        shift = np.array(
            [0.12500000000000, 0.50000000000000, 0.00000000000000])
        new_pos = system.atoms_prop(key='pos', scale=True) + shift
        system.atoms_prop(key='pos', value=new_pos, scale=True)

        system.supersize((-7, 7), (-6, 6), (0, 1))
        disp = stroh.displacement(system.atoms_prop(key='pos'))

        system.atoms_prop(key='pos', value=system.atoms_prop(key='pos') + disp)

        system.pbc = (False, False, True)
        system.wrap()

        pos = system.atoms_prop(key='pos')
        x = uc.get_in_units(pos[:, 0], 'angstrom')
        y = uc.get_in_units(pos[:, 1], 'angstrom')

        plt.figure(figsize=(8, 8))
        plt.scatter(x, y, s=30)
        plt.xlim(min(x), max(x))
        plt.ylim(min(y), max(y))
        plt.xlabel('x-position (Angstrom)', fontsize='large')
        plt.ylabel('y-position (Angstrom)', fontsize='large')
        plt.show()
Пример #6
0
def data_model(input_dict, results_dict=None):
    """Creates a DataModelDict containing the input and results data"""

    #Create the root of the DataModelDict
    output = DM()
    output['calculation-bain-transformation'] = calc = DM()

    #Assign uuid
    calc['calculation'] = DM()
    calc['calculation']['id'] = input_dict['uuid']
    calc['calculation']['script'] = __calc_name__

    calc['calculation']['run-parameter'] = run_params = DM()
    run_params['size-multipliers'] = DM()
    run_params['size-multipliers']['a'] = list(input_dict['size_mults'][0])
    run_params['size-multipliers']['b'] = list(input_dict['size_mults'][1])
    run_params['size-multipliers']['c'] = list(input_dict['size_mults'][2])
    run_params['energy_tolerance'] = input_dict['energy_tolerance']
    run_params['force_tolerance'] = input_dict['force_tolerance']
    run_params['maximum_iterations'] = input_dict['maximum_iterations']
    run_params['maximum_evaluations'] = input_dict['maximum_evaluations']

    #Copy over potential data model info
    calc['potential'] = input_dict['potential']['LAMMPS-potential'][
        'potential']

    #Save info on system file loaded
    system_load = input_dict['load'].split(' ')
    calc['system-info'] = DM()
    calc['system-info']['artifact'] = DM()
    calc['system-info']['artifact']['file'] = os.path.basename(' '.join(
        system_load[1:]))
    calc['system-info']['artifact']['format'] = system_load[0]
    calc['system-info']['artifact']['family'] = input_dict['system_family']
    calc['system-info']['symbols'] = input_dict['symbols']

    calc['bain-a-scale'] = input_dict['bain_a_scale']
    calc['bain-c-scale'] = input_dict['bain_c_scale']

    if results_dict is None:
        calc['status'] = 'not calculated'
    else:
        #print results_dict
        #Save the cohesive energy for the initial system
        calc['initial'] = DM([('value',
                               uc.get_in_units(results_dict['initial'],
                                               input_dict['energy_unit'])),
                              ('unit', input_dict['energy_unit'])])

        #Save the cohesive energy for the bain shifted system
        calc['bain'] = DM([('value',
                            uc.get_in_units(results_dict['bain'],
                                            input_dict['energy_unit'])),
                           ('unit', input_dict['energy_unit'])])

    return output
Пример #7
0
def gb_energy(lammps_command, potential, symbols, alat, axes_1, axes_2, E_coh,
              mpi_command=None, xshift=0.0, zshift=0.0):
    """Computes the grain boundary energy using the grain_boundary.in LAMMPS script"""
    
    axes_1 = np.asarray(axes_1, dtype=int)
    axes_2 = np.asarray(axes_2, dtype=int)
    lx = alat * np.linalg.norm(axes_1[0])
    lz = 2 * alat * np.linalg.norm(axes_1[2])
    mesh_dir = 'mesh-%.8f-%.8f' %(xshift, zshift) 
    if not os.path.isdir(mesh_dir):
        os.makedirs(mesh_dir)   
    
    #Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Define lammps variables
    lammps_variables = {}
    
    lammps_variables['units'] =             potential.units
    lammps_variables['atom_style'] =        potential.atom_style
    lammps_variables['atomman_pair_info'] = potential.pair_info(symbols)
    lammps_variables['alat'] =              uc.get_in_units(alat,   lammps_units['length'])
    lammps_variables['xsize'] =             uc.get_in_units(lx,     lammps_units['length'])
    lammps_variables['zsize'] =             uc.get_in_units(lz,     lammps_units['length'])
    lammps_variables['xshift'] =            uc.get_in_units(xshift, lammps_units['length'])
    lammps_variables['zshift'] =            uc.get_in_units(zshift, lammps_units['length'])
    lammps_variables['x_axis_1'] =          str(axes_1[0]).strip('[] ')
    lammps_variables['y_axis_1'] =          str(axes_1[1]).strip('[] ')
    lammps_variables['z_axis_1'] =          str(axes_1[2]).strip('[] ')
    lammps_variables['x_axis_2'] =          str(axes_2[0]).strip('[] ')
    lammps_variables['y_axis_2'] =          str(axes_2[1]).strip('[] ')
    lammps_variables['z_axis_2'] =          str(axes_2[2]).strip('[] ')
    lammps_variables['mesh_dir'] =          'mesh-%.8f-%.8f' %(xshift, zshift) 
    
    #Fill in mod.template files
    with open('grain_boundary.template') as template_file:
        template = template_file.read()
    lammps_input = os.path.join(mesh_dir, 'grain_boundary.in')
    with open(lammps_input, 'w') as in_file:
        in_file.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
        
    output = lmp.run(lammps_command, lammps_input, mpi_command)
    
    #Extract output values
    try:
        E_total = uc.set_in_units(output.finds('c_eatoms')[-1], lammps_units['energy'])
        natoms = output.finds('v_natoms')[-1]
    except:
        E_total = uc.set_in_units(output.finds('eatoms')[-1], lammps_units['energy'])
        natoms = output.finds('natoms')[-1]
        
    #Compute grain boundary energy
    E_gb = (E_total - E_coh*natoms) / (lx * lz)
    
    return E_gb
Пример #8
0
def box_content(system, units, float_format):
    """
    Generates the data file lines associated with box dimensions.

    Parameters
    ----------
    system : atomman.System
    units : str
    float_format : str
    """

    # Get unit information according to the units style
    units_dict = style.unit(units)
    length_unit = units_dict['length']

    # Define line format strings
    xf2 = float_format + ' ' + float_format
    xf3 = float_format + ' ' + float_format + ' ' + float_format

    # Extract and convert box values
    xlo = uc.get_in_units(system.box.xlo, length_unit)
    xhi = uc.get_in_units(system.box.xhi, length_unit)
    ylo = uc.get_in_units(system.box.ylo, length_unit)
    yhi = uc.get_in_units(system.box.yhi, length_unit)
    zlo = uc.get_in_units(system.box.zlo, length_unit)
    zhi = uc.get_in_units(system.box.zhi, length_unit)
    xy = uc.get_in_units(system.box.xy, length_unit)
    xz = uc.get_in_units(system.box.xz, length_unit)
    yz = uc.get_in_units(system.box.yz, length_unit)

    # Write box values
    content = ''
    content += xf2 % (xlo, xhi) + ' xlo xhi\n'
    content += xf2 % (ylo, yhi) + ' ylo yhi\n'
    content += xf2 % (zlo, zhi) + ' zlo zhi\n'
    if xy != 0.0 or xz != 0.0 or yz != 0.0:
        content += xf3 % (xy, xz, yz) + ' xy xz yz\n'

    return content
Пример #9
0
 def test_newton(self):
     newton = uc.set_in_units(1e5, 'dyn')
     assert pytest.approx(uc.get_in_units(newton, 'kg*m/s^2'), 1.0)
Пример #10
0
def data_model(input_dict, results_dict=None):
    """Creates a DataModelDict containing the input and results data""" 
    
    #Create the root of the DataModelDict
    output = DM()
    output['calculation-dynamic-elastic'] = calc = DM()
    
    #Assign uuid
    calc['key'] = input_dict['calc_key']
    calc['calculation'] = DM()
    calc['calculation']['script'] = __calc_name__
    
    calc['calculation']['run-parameter'] = run_params = DM()
    
    run_params['size-multipliers'] = DM()
    run_params['size-multipliers']['a'] = list(input_dict['sizemults'][0])
    run_params['size-multipliers']['b'] = list(input_dict['sizemults'][1])
    run_params['size-multipliers']['c'] = list(input_dict['sizemults'][2])
    
    run_params['load_options'] = input_dict['load_options']
    run_params['thermosteps'] = input_dict['thermosteps']
    run_params['runsteps'] = input_dict['runsteps']
    run_params['randomseed'] = input_dict['randomseed']
    run_params['integrator'] = input_dict['integrator']
    
    #Copy over potential data model info
    calc['potential'] = DM()
    calc['potential']['key'] = input_dict['potential'].key
    calc['potential']['id'] = input_dict['potential'].id
    
    #Save info on system file loaded
    system_load = input_dict['load'].split(' ')    
    calc['system-info'] = DM()
    calc['system-info']['artifact'] = DM()
    calc['system-info']['artifact']['file'] = os.path.basename(' '.join(system_load[1:]))
    calc['system-info']['artifact']['format'] = system_load[0]
    calc['system-info']['artifact']['family'] = input_dict['system_family']
    calc['system-info']['symbols'] = input_dict['symbols']
    
    
    #Save phase-state info
    calc['phase-state'] = DM()
    calc['phase-state']['temperature'] = DM()
    calc['phase-state']['temperature']['value'] = input_dict['temperature']
    calc['phase-state']['temperature']['unit'] = 'K'
    
    calc['phase-state']['pressure-xx'] = DM()
    calc['phase-state']['pressure-xx']['value'] = uc.get_in_units(input_dict['pressure_xx'],
                                                                  input_dict['pressure_unit'])
    calc['phase-state']['pressure-xx']['unit'] = input_dict['pressure_unit']
    
    calc['phase-state']['pressure-yy'] = DM()
    calc['phase-state']['pressure-yy']['value'] = uc.get_in_units(input_dict['pressure_yy'],
                                                                  input_dict['pressure_unit'])
    calc['phase-state']['pressure-yy']['unit'] = input_dict['pressure_unit']
    
    calc['phase-state']['pressure-zz'] = DM()
    calc['phase-state']['pressure-zz']['value'] = uc.get_in_units(input_dict['pressure_zz'],
                                                                  input_dict['pressure_unit'])
    calc['phase-state']['pressure-zz']['unit'] = input_dict['pressure_unit']

    if results_dict is None:
        calc['status'] = 'not calculated'
    else:
        
        calc['equilibrium-averages'] = avgs = DM()
        avgs['temperature'] = DM()
        avgs['temperature']['value'] = results_dict['temp'][0]
        avgs['temperature']['error'] = results_dict['temp'][1]
        avgs['temperature']['unit'] =  'K'
        
        avgs['pressure-xx'] = DM()
        avgs['pressure-xx']['value'] = uc.get_in_units(results_dict['pxx'][0], input_dict['pressure_unit'])
        avgs['pressure-xx']['error'] = uc.get_in_units(results_dict['pxx'][1], input_dict['pressure_unit'])
        avgs['pressure-xx']['unit'] =  input_dict['pressure_unit']
        
        avgs['pressure-yy'] = DM()
        avgs['pressure-yy']['value'] = uc.get_in_units(results_dict['pyy'][0], input_dict['pressure_unit'])
        avgs['pressure-yy']['error'] = uc.get_in_units(results_dict['pyy'][1], input_dict['pressure_unit'])
        avgs['pressure-yy']['unit'] =  input_dict['pressure_unit']

        avgs['pressure-zz'] = DM()
        avgs['pressure-zz']['value'] = uc.get_in_units(results_dict['pzz'][0], input_dict['pressure_unit'])
        avgs['pressure-zz']['error'] = uc.get_in_units(results_dict['pzz'][1], input_dict['pressure_unit'])
        avgs['pressure-zz']['unit'] =  input_dict['pressure_unit']

        avgs['cohesive-energy'] = DM()
        avgs['cohesive-energy']['value'] = uc.get_in_units(results_dict['E_coh'][0], input_dict['energy_unit'])
        avgs['cohesive-energy']['error'] = uc.get_in_units(results_dict['E_coh'][1], input_dict['energy_unit'])
        avgs['cohesive-energy']['unit'] =  input_dict['energy_unit']

        avgs['a'] = DM()
        avgs['a']['value'] = uc.get_in_units(results_dict['a'][0], input_dict['length_unit'])
        avgs['a']['error'] = uc.get_in_units(results_dict['a'][1], input_dict['length_unit'])
        avgs['a']['unit'] =  input_dict['length_unit']

        avgs['b'] = DM()
        avgs['b']['value'] = uc.get_in_units(results_dict['b'][0], input_dict['length_unit'])
        avgs['b']['error'] = uc.get_in_units(results_dict['b'][1], input_dict['length_unit'])
        avgs['b']['unit'] =  input_dict['length_unit']

        avgs['c'] = DM()
        avgs['c']['value'] = uc.get_in_units(results_dict['c'][0], input_dict['length_unit'])
        avgs['c']['error'] = uc.get_in_units(results_dict['c'][1], input_dict['length_unit'])
        avgs['c']['unit'] =  input_dict['length_unit']
        
    return output
Пример #11
0
def integrator_info(integrator: Optional[str] = None,
                    p_xx: float = 0.0,
                    p_yy: float = 0.0,
                    p_zz: float = 0.0,
                    p_xy: float = 0.0,
                    p_xz: float = 0.0,
                    p_yz: float = 0.0,
                    temperature: float = 0.0,
                    velocity_temperature: Optional[float] = None,
                    randomseed: Optional[int] = None,
                    units: str = 'metal',
                    lammps_date=None) -> str:
    """
    Generates LAMMPS commands for velocity creation and fix integrators. 
    
    Parameters
    ----------
    integrator : str or None, optional
        The integration method to use. Options are 'npt', 'nvt', 'nph',
        'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat.
        (Default is None, which will use 'nph+l' for temperature == 0, and
        'npt' otherwise.)
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    p_xy : float, optional
        The value to relax the xy shear pressure component to (default is
        0.0).
    p_xz : float, optional
        The value to relax the xz shear pressure component to (default is
        0.0).
    p_yz : float, optional
        The value to relax the yz shear pressure component to (default is
        0.0).
    temperature : float, optional
        The temperature to relax at (default is 0.0).
    velocity_temperature : float or None, optional
        The temperature to use for generating initial atomic velocities with
        integrators containing thermostats.  If None (default) then it will
        be set to 2 * temperature + 1.  If 0.0 then the velocities will not be
        (re)set.
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    units : str, optional
        The LAMMPS units style to use (default is 'metal').
    
    Returns
    -------
    str
        The generated LAMMPS input lines for velocity create and fix
        integration commands.
    """

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

    # Convert pressures to lammps units
    p_xx = uc.get_in_units(p_xx, lammps_units['pressure'])
    p_yy = uc.get_in_units(p_yy, lammps_units['pressure'])
    p_zz = uc.get_in_units(p_zz, lammps_units['pressure'])
    p_xy = uc.get_in_units(p_xy, lammps_units['pressure'])
    p_xz = uc.get_in_units(p_xz, lammps_units['pressure'])
    p_yz = uc.get_in_units(p_yz, lammps_units['pressure'])

    # Check temperature and set default integrator
    if temperature == 0.0:
        if integrator is None: integrator = 'nph+l'
        assert integrator not in ['npt',
                                  'nvt'], 'npt and nvt cannot run at 0 K'
    elif temperature > 0:
        if integrator is None: integrator = 'npt'
    else:
        raise ValueError('Temperature must be positive')

    # Set dampening parameters based on timestep values
    temperature_damp = 100 * lmp.style.timestep(units)
    pressure_damp = 1000 * lmp.style.timestep(units)

    # Set default randomseed
    if randomseed is None:
        randomseed = random.randint(1, 900000000)

    # Set default velocity_temperature
    if velocity_temperature is None:
        velocity_temperature = 2.0 * temperature + 1

    # Build info_lines
    info_lines = []

    if integrator == 'npt':

        if velocity_temperature > 0.0:
            info_lines.append(
                f'velocity all create {velocity_temperature} {randomseed}')

        info_lines.extend([
            f'fix npt all npt temp {temperature} {temperature} {temperature_damp} &',
            f'                x {p_xx} {p_xx} {pressure_damp} &',
            f'                y {p_yy} {p_yy} {pressure_damp} &',
            f'                z {p_zz} {p_zz} {pressure_damp} &',
            f'                xy {p_xy} {p_xy} {pressure_damp} &',
            f'                xz {p_xz} {p_xz} {pressure_damp} &',
            f'                yz {p_yz} {p_yz} {pressure_damp}'
        ])

    elif integrator == 'nvt':

        if velocity_temperature > 0.0:
            info_lines.append(
                f'velocity all create {velocity_temperature} {randomseed}')

        info_lines.extend([
            f'fix nvt all nvt temp {temperature} {temperature} {temperature_damp}'
        ])

    elif integrator == 'nph':

        info_lines.extend([
            f'fix nph all nph x {p_xx} {p_xx} {pressure_damp} &',
            f'                y {p_yy} {p_yy} {pressure_damp} &',
            f'                z {p_zz} {p_zz} {pressure_damp} &',
            f'                xy {p_xy} {p_xy} {pressure_damp} &',
            f'                xz {p_xz} {p_xz} {pressure_damp} &',
            f'                yz {p_yz} {p_yz} {pressure_damp}'
        ])

    elif integrator == 'nve':

        info_lines.extend(['fix nve all nve'])

    elif integrator == 'nve+l':

        if velocity_temperature > 0.0:
            info_lines.append(
                f'velocity all create {velocity_temperature} {randomseed}')

        info_lines.extend([
            'fix nve all nve',
            f'fix langevin all langevin {temperature} {temperature} {temperature_damp} {randomseed}'
        ])

    elif integrator == 'nph+l':

        info_lines.extend([
            f'fix nph all nph x {p_xx} {p_xx} {pressure_damp} &',
            f'                y {p_yy} {p_yy} {pressure_damp} &',
            f'                z {p_zz} {p_zz} {pressure_damp} &',
            f'                xy {p_xy} {p_xy} {pressure_damp} &',
            f'                xz {p_xz} {p_xz} {pressure_damp} &',
            f'                yz {p_yz} {p_yz} {pressure_damp}',
        ])

        # Add ptemp if LAMMPS is newer than June 2020 and temperature is zero
        if np.isclose(temperature,
                      0.0) and lammps_date >= datetime.date(2020, 6, 9):
            info_lines[-1] += ' &'
            info_lines.extend(['                ptemp 1.0'])

        info_lines.extend([
            f'fix langevin all langevin {temperature} {temperature} {temperature_damp} {randomseed}'
        ])

    else:
        raise ValueError('Invalid integrator style')

    return '\n'.join(info_lines)
Пример #12
0
def stackingfaultpoint(lammps_command,
                       system,
                       potential,
                       mpi_command=None,
                       sim_directory=None,
                       cutboxvector='c',
                       faultpos=0.5,
                       faultshift=[0.0, 0.0, 0.0],
                       etol=0.0,
                       ftol=0.0,
                       maxiter=10000,
                       maxeval=100000,
                       dmax=uc.set_in_units(0.01, 'angstrom'),
                       lammps_date=None):
    """
    Perform a stacking fault relaxation simulation for a single faultshift.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simuation in.  If not
        given, will use the current working directory.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    cutboxvector : str, optional
        Indicates which of the three system box vectors, 'a', 'b', or 'c', to
        cut with a non-periodic boundary (default is 'c').
    faultpos : float, optional
        The fractional position along the cutboxvector where the stacking
        fault plane will be placed (default is 0.5).
    faultshift : list of float, optional
        The vector shift to apply to all atoms above the fault plane defined
        by faultpos (default is [0,0,0], i.e. no shift applied).
    lammps_date : datetime.date or None, optional
        The date version of the LAMMPS executable.  If None, will be identified from the lammps_command (default is None).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The filename of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed system.
        - **'system'** (*atomman.System*) - The relaxed system.
        - **'A_fault'** (*float*) - The area of the fault surface.
        - **'E_total'** (*float*) - The total potential energy of the relaxed
          system.
        - **'disp'** (*float*) - The center of mass difference between atoms
          above and below the fault plane in the cutboxvector direction.
    
    Raises
    ------
    ValueError
        For invalid cutboxvectors.
    """

    # Set options based on cutboxvector
    if cutboxvector == 'a':
        # Assert system is compatible with planeaxis value
        if system.box.xy != 0.0 or system.box.xz != 0.0:
            raise ValueError(
                "box tilts xy and xz must be 0 for cutboxvector='a'")

        # Specify cutindex
        cutindex = 0

        # Identify atoms above fault
        faultpos = system.box.xlo + system.box.lx * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)

        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.bvect,
                                            system.box.cvect))

        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce NULL 0 0'

    elif cutboxvector == 'b':
        # Assert system is compatible with planeaxis value
        if system.box.yz != 0.0:
            raise ValueError("box tilt yz must be 0 for cutboxvector='b'")

        # Specify cutindex
        cutindex = 1

        # Identify atoms above fault
        faultpos = system.box.ylo + system.box.ly * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)

        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.cvect))

        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 NULL 0'

    elif cutboxvector == 'c':
        # Specify cutindex
        cutindex = 2

        # Identify atoms above fault
        faultpos = system.box.zlo + system.box.lz * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)

        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.bvect))

        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 0 NULL'

    else:
        raise ValueError('Invalid cutboxvector')

    # Assert faultshift is in cut plane
    if faultshift[cutindex] != 0.0:
        raise ValueError('faultshift must be in cut plane')

    # Generate stacking fault system by shifting atoms above the fault
    sfsystem = deepcopy(system)
    sfsystem.pbc = [True, True, True]
    sfsystem.pbc[cutindex] = False
    sfsystem.atoms.pos[abovefault] += faultshift
    sfsystem.wrap()

    if sim_directory is not None:
        # Create sim_directory if it doesn't exist
        if not os.path.isdir(sim_directory):
            os.mkdir(sim_directory)

        # Add '/' to end of sim_directory string if needed
        if sim_directory[-1] != '/':
            sim_directory = sim_directory + '/'
    else:
        # Set sim_directory if is None
        sim_directory = ''

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

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

    # Define lammps variables
    lammps_variables = {}
    system_info = sfsystem.dump('atom_data',
                                f=os.path.join(sim_directory, 'system.dat'),
                                units=potential.units,
                                atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(
        sfsystem.symbols)
    lammps_variables['fix_cut_setforce'] = fix_cut_setforce
    lammps_variables['sim_directory'] = sim_directory
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])

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

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

    # Run LAMMPS
    output = lmp.run(lammps_command,
                     lammps_script,
                     mpi_command,
                     logfile=os.path.join(sim_directory, 'log.lammps'))

    # Extract output values
    thermo = output.simulations[-1]['thermo']
    logfile = os.path.join(sim_directory, 'log.lammps')
    dumpfile = os.path.join(sim_directory, '%i.dump' % thermo.Step.values[-1])
    E_total = uc.set_in_units(thermo.PotEng.values[-1], lammps_units['energy'])

    # Load relaxed system
    sfsystem = am.load('atom_dump', dumpfile, symbols=sfsystem.symbols)

    # Find center of mass difference in top/bottom planes
    disp = (sfsystem.atoms.pos[abovefault, cutindex].mean() -
            sfsystem.atoms.pos[~abovefault, cutindex].mean())

    # Return results
    results_dict = {}
    results_dict['logfile'] = logfile
    results_dict['dumpfile'] = dumpfile
    results_dict['system'] = sfsystem
    results_dict['A_fault'] = faultarea
    results_dict['E_total'] = E_total
    results_dict['disp'] = disp

    return results_dict
Пример #13
0
def pointdefect(lammps_command, system, potential, point_kwargs,
                mpi_command=None, etol=0.0, ftol=0.0, maxiter=10000,
                maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Adds one or more point defects to a system and evaluates the defect 
    formation energy.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    point_kwargs : dict or list of dict
        One or more dictionaries containing the keyword arguments for
        the atomman.defect.point() function to generate specific point
        defect configuration(s).
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simuation in.  If not
        given, will use the current working directory.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'E_coh'** (*float*) - The cohesive energy of the bulk system.
        - **'E_ptd_f'** (*float*) - The point.defect formation energy.
        - **'E_total_base'** (*float*) - The total potential energy of the
          relaxed bulk system.
        - **'E_total_ptd'** (*float*) - The total potential energy of the
          relaxed defect system.
        - **'system_base'** (*atomman.System*) - The relaxed bulk system.
        - **'system_ptd'** (*atomman.System*) - The relaxed defect system.
        - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file
          for the relaxed bulk system.
        - **'dumpfile_ptd'** (*str*) - The filename of the LAMMPS dump file
          for the relaxed defect system.
    """
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='perfect.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = dmax
    
    # Set dump_modify_format based on lammps_date
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'min.template'
    lammps_script = 'min.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

    # Run lammps to relax perfect.dat
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract LAMMPS thermo data.
    thermo = output.simulations[0]['thermo']
    E_total_base = uc.set_in_units(thermo.PotEng.values[-1],
                                   lammps_units['energy'])
    E_coh = E_total_base / system.natoms
    
    # Rename log file
    shutil.move('log.lammps', 'min-perfect-log.lammps')
    
    # Load relaxed system from dump file and copy old box vectors because 
    # dump files crop the values.
    last_dump_file = 'atom.' + str(thermo.Step.values[-1])
    system_base = am.load('atom_dump', last_dump_file, symbols=system.symbols)
    system_base.box_set(vects=system.box.vects)
    system_base.dump('atom_dump', f='perfect.dump')
    
    # Add defect(s)
    system_ptd = deepcopy(system_base)
    if not isinstance(point_kwargs, (list, tuple)):
        point_kwargs = [point_kwargs]
    for pkwargs in point_kwargs:
        system_ptd = am.defect.point(system_ptd, **pkwargs)
    
    # Update lammps variables
    system_info = system_ptd.dump('atom_data', f='defect.dat',
                                  units = potential.units, 
                                  atom_style = potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    
    # Write lammps input script
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run lammps
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract lammps thermo data
    thermo = output.simulations[0]['thermo']
    E_total_ptd = uc.set_in_units(thermo.PotEng.values[-1],
                                  lammps_units['energy'])
    
    # Rename log file
    shutil.move('log.lammps', 'min-defect-log.lammps')
    
    # Load relaxed system from dump file and copy old vects as 
    # the dump files crop the values
    last_dump_file = 'atom.'+str(thermo.Step.values[-1])
    system_ptd = am.load('atom_dump', last_dump_file, symbols=system_ptd.symbols)
    system_ptd.box_set(vects=system.box.vects)
    system_ptd.dump('atom_dump', f='defect.dump')
    
    # Compute defect formation energy
    E_ptd_f = E_total_ptd - E_coh * system_ptd.natoms
    
    # Cleanup files
    for fname in glob.iglob('atom.*'):
        os.remove(fname)
    for dumpjsonfile in glob.iglob('*.dump.json'):
        os.remove(dumpjsonfile)
    
    # Return results
    results_dict = {}
    results_dict['E_coh'] = E_coh
    results_dict['E_ptd_f'] = E_ptd_f
    results_dict['E_total_base'] = E_total_base
    results_dict['E_total_ptd'] = E_total_ptd
    results_dict['system_base'] = system_base
    results_dict['system_ptd'] = system_ptd
    results_dict['dumpfile_base'] = 'perfect.dump'
    results_dict['dumpfile_ptd'] = 'defect.dump'
    
    return results_dict
Пример #14
0
    def check_energies(self,
                       x=None,
                       disregistry=None,
                       energyperlength_unit='eV/Å'):
        """
        Prints a summary string of all computed energy components.

        Parameters
        ----------
        x : numpy.ndarray, optional
            x-coordinates.  Default value is the stored x-coordinates.
        disregistry : numpy.ndarray, optional
            (N, 3) shaped array of disregistry vectors at each x-coordinate.
            Default value is the stored disregistry values.
        energyperlength_unit : str, optional
            The units of energy per length to report the dislocation line
            energies in.  Default value is 'eV/Å'.
        """
        hasvals = True
        # Default values are class properties
        if x is None:
            try:
                x = self.x
            except:
                hasvals = False
        if disregistry is None:
            try:
                disregistry = self.disregistry
            except:
                hasvals = False

        if hasvals:
            print(f'Dislocation energy terms in {energyperlength_unit}:')
            print(
                'Misfit energy =    ',
                uc.get_in_units(self.misfit_energy(x, disregistry),
                                energyperlength_unit))
            print(
                'Elastic energy =   ',
                uc.get_in_units(self.elastic_energy(x, disregistry),
                                energyperlength_unit))
            print(
                'Long-range energy =',
                uc.get_in_units(self.longrange_energy(), energyperlength_unit))
            print(
                'Stress energy =    ',
                uc.get_in_units(self.stress_energy(x, disregistry),
                                energyperlength_unit))
            print(
                'Surface energy =   ',
                uc.get_in_units(self.surface_energy(x, disregistry),
                                energyperlength_unit))
            print(
                'Nonlocal energy =  ',
                uc.get_in_units(self.nonlocal_energy(x, disregistry),
                                energyperlength_unit))
            print(
                'Total energy =     ',
                uc.get_in_units(self.total_energy(x, disregistry),
                                energyperlength_unit))
        else:
            print('x and disregistry must be set/given to check energies')
Пример #15
0
def disl_relax(lammps_command, system, potential,
               mpi_command=None, annealtemp=0.0, randomseed=None,
               etol=0.0, ftol=1e-6, maxiter=10000, maxeval=100000,
               dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Sets up and runs the disl_relax.in LAMMPS script for relaxing a
    dislocation monopole system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    annealtemp : float, optional
        The temperature to perform a dynamic relaxation at. (Default is 0.0,
        which will skip the dynamic relaxation.)
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
        
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The name of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The name of the LAMMPS dump file
          for the relaxed system.
        - **'E_total'** (*float*) - The total potential energy for the
          relaxed system.
    """
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='system.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['anneal_info'] = anneal_info(annealtemp, randomseed,
                                                  potential.units)
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = dmax
    lammps_variables['group_move'] = ' '.join(np.array(range(1, system.natypes // 2 + 1), dtype=str))
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'disl_relax.template'
    lammps_script = 'disl_relax.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    thermo = output.simulations[-1]['thermo']
    
    # Extract output values
    results = {}
    results['logfile'] = 'log.lammps'
    results['dumpfile'] = '%i.dump' % thermo.Step.values[-1] 
    results['E_total'] = uc.set_in_units(thermo.PotEng.values[-1],
                                         lammps_units['energy'])
    
    return results
Пример #16
0
def table_dump(system, f=None, prop_info=None, float_format ='%.13f'):
    """
    Converts a system's atoms' values to a string table.  Modified from
    table.dump to handle alternate pos fields that dump files can use.
    
    Parameters
    ----------
    system : atomman.System
        An atomman representation of a system.
    f : str or file-like object, optional
        File path or file-like object to write the content to.  If not given,
        then the content is returned as a str.
    prop_info : list of dict, optional
        Structured form of property conversion parameters, in which each
        dictionary in the list corresponds to a single atoms property.  Each
        dictionary must have a 'prop_name' field, and can optionally have
        'table_name', 'shape', 'unit', and 'dtype' fields.
    float_format : str, optional
        c-style formatting string for floating point values.  Default value is
        '%.13f'.
    
    Returns
    -------
    str
        The generated data table.  Only returned if fp is None.
    """
    # Set parameters
    natoms = system.natoms
    key_rename = OrderedDict()
    
    # Build list of properties to scale and alternate pos
    scale = []
    altpos = []
    for prop in prop_info:
        if prop['prop_name'] in ['spos', 'upos', 'supos']:
            altpos.append(prop['prop_name'])
        if prop['unit'] == 'scaled':
            scale.append(prop['prop_name'])
            prop['unit'] = None
    
    # Transform to dataframe
    df = system.atoms_df(scale)
    
    # Check atom_id values
    if 'atom_id' not in df:
        df['atom_id'] = range(1, natoms+1)
    assert len(df.atom_id) == len(set(df.atom_id)), 'atom_id is not unique for all atoms'    
    
    # Add alternate pos terms
    if 'upos' in altpos:
        df['upos[0]'] = df['pos[0]']
        df['upos[1]'] = df['pos[1]']
        df['upos[2]'] = df['pos[2]']
    if 'spos' in altpos or 'supos' in altpos:
        spos = system.atoms_prop(key='pos', scale=True)
        if 'spos' in altpos:
            df['spos[0]'] = spos[:,0]
            df['spos[1]'] = spos[:,1]
            df['spos[2]'] = spos[:,2]
        if 'supos' in altpos:
            df['supos[0]'] = spos[:,0]
            df['supos[1]'] = spos[:,1]
            df['supos[2]'] = spos[:,2]
    
    # Loop over all properties
    for prop in prop_info:
        pname = prop['prop_name']
        
        # loop over all table names and property indexes
        for tname, (index, istr) in zip(prop['table_name'],
                                        indexstr(prop['shape'])):
            
            # Build name change dict
            key_rename[pname + istr] = tname
            
            # Convert units if needed
            if prop['unit'] is not None:
                df[pname + istr] = uc.get_in_units(df[pname + istr], prop['unit'])
    
    # Rename and reorganize
    df = df.rename(columns=key_rename)[list(key_rename.values())]
  
    # Generate table
    sep = ' '
    if ispython2:
        sep = sep.encode('utf-8')
    return df.to_csv(path_or_buf=f, sep=sep, index=None, header=False,
                     float_format=float_format)
Пример #17
0
def dump(system, f=None, lammps_units='metal', scale=False, prop_name=None,
         table_name=None, shape=None, unit=None, dtype=None,
         prop_info=None, float_format ='%.13f', return_prop_info=False):
    """
    Write a LAMMPS-style atom data file from a System.
    
    Parameters
    ----------
    system : atomman.System 
        The system to write to the atom data file.
    f : str or file-like object, optional
        File path or file-like object to write the content to.  If not given,
        then the content is returned as a str.
    lammps_units : str, optional
        The LAMMPS units option associated with the table values.  This is used
        for the box dimensions and default units for standard dump properties 
        (not compute/fix definitions). 
    scale : bool, optional
        Flag indicating if atom positions are to be scaled relative to the box
        (True) or given in absolute Cartesian values (False, default).
    prop_name : list, optional
        The Atoms properties to include.  If neither prop_name or prop_info are
        given, all system properties will be included.
    table_name : list, optional
        The dump table column name(s) that correspond to each prop_name.  If not
        given, the table_name values will be based on the prop_name and shape
        values.
    shape : list, optional
        The shape of each per-atom property.  If not given, will be inferred
        from the length of each table_name value.
    unit : list, optional
        Lists the units for each prop_name as stored in the table.  For a
        value of None, no conversion will be performed for that property.  For
        a value of 'scaled', the corresponding table values will be taken in
        box-scaled units.  If not given, unit values will be taken based on
        lammps_units if prop_name corresponds to a standard LAMMPS property, 
        otherwise will be set to None (no conversion).
    dtype : list, optional
        Allows for the data type of each property to be explicitly given.
        Values of None will infer the data type from the corresponding
        property values.  If not given, all values will be None.
    prop_info : list of dict, optional
        Structured form of property conversion parameters, in which each
        dictionary in the list corresponds to a single atoms property.  Each
        dictionary must have a 'prop_name' field, and can optionally have
        'table_name', 'shape', 'unit', and 'dtype' fields.
    float_format : str, optional
        c-style formatting string for floating point values.  Default value is
        '%.13f'.
    return_prop_info : bool, optional
        Flag indicating if the filled-in prop_info is to be returned.  Having
        this allows for 1:1 load/dump conversions.  Default value is False
        (prop_info is not returned).
        
    Returns
    -------
    content : str
        The generated atom_data content.  Only returned if f is None.
    
    prop_info : list of dict
        The filled-in prop_info structure. Only returned if
        return_prop_info is True.
    """
    lammps_unit = style.unit(lammps_units)
    
    # Set default values
    if prop_info is None:
        if prop_name is None:
            atoms_props = system.atoms_prop()
            try:
                atoms_props.pop(atoms_props.index('atom_id'))
            except:
                pass
            prop_name = ['atom_id'] + atoms_props
        
        if shape is None and table_name is None:
            shape = []
            for name in prop_name:
                if name == 'atom_id':
                    shape.append(())
                elif name in ['spos', 'upos', 'supos']:
                    shape.append((3,))
                else:
                    shape.append(system.atoms.view[name].shape[1:])
    
    # Process conversion parameters
    prop_info = process_prop_info(prop_name=prop_name, table_name=table_name,
                                  shape=shape, unit=unit, dtype=dtype,
                                  prop_info=prop_info,
                                  lammps_units=lammps_units)
    
    # Write timestep info
    content = 'ITEM: TIMESTEP\n'
    try:
        content += '%i\n' % system.timestep
    except:
        content += '0\n'
    
    # Write number of atoms
    content += 'ITEM: NUMBER OF ATOMS\n'
    content += '%i\n' % (system.natoms)
    
    # Extract and convert box values
    xlo = uc.get_in_units(system.box.xlo, lammps_unit['length'])
    xhi = uc.get_in_units(system.box.xhi, lammps_unit['length'])
    ylo = uc.get_in_units(system.box.ylo, lammps_unit['length'])
    yhi = uc.get_in_units(system.box.yhi, lammps_unit['length'])
    zlo = uc.get_in_units(system.box.zlo, lammps_unit['length'])
    zhi = uc.get_in_units(system.box.zhi, lammps_unit['length'])
    xy = uc.get_in_units(system.box.xy, lammps_unit['length'])
    xz = uc.get_in_units(system.box.xz, lammps_unit['length'])
    yz = uc.get_in_units(system.box.yz, lammps_unit['length'])
    
    # Compute absolute box bounds
    xlo_bound = xlo + min((0.0, xy, xz, xy + xz))
    xhi_bound = xhi + max((0.0, xy, xz, xy + xz))
    ylo_bound = ylo + min((0.0, yz))
    yhi_bound = yhi + max((0.0, yz))
    zlo_bound = zlo
    zhi_bound = zhi
    
    is_orthogonal = (xy == 0.0 and xz == 0.0 and yz == 0.0)
    
    # Write system boundary info
    if is_orthogonal:
        content += 'ITEM: BOX BOUNDS'
    else:
        content += 'ITEM: BOX BOUNDS xy xz yz'
        
    # Write pbc info
    for i in range(3):
        if system.pbc[i]:
            content += ' pp'
        else:
            content += ' fm'
    content += '\n'
        
    # Write system boundary info
    if is_orthogonal:
        xf2 = float_format + ' ' + float_format + '\n'
        content += xf2 % (xlo_bound, xhi_bound)
        content += xf2 % (ylo_bound, yhi_bound)
        content += xf2 % (zlo_bound, zhi_bound)
    else:
        xf3 = float_format + ' ' + float_format + ' ' + float_format + '\n'
        content += xf3 % (xlo_bound, xhi_bound, xy)
        content += xf3 % (ylo_bound, yhi_bound, xz)
        content += xf3 % (zlo_bound, zhi_bound, yz)
    
    # Write atomic header info and prepare outarray for writing
    header = 'ITEM: ATOMS'
    for prop in prop_info:
        header += ' ' + ' '.join(prop['table_name'])
    header += '\n'
    content += header
    
    content += table_dump(system, prop_info=prop_info, float_format=float_format)
    
    returns = []
    
    # Save to the file-like object
    if hasattr(f, 'write'):
        f.write(content)
    
    # Save to the file name
    elif f is not None:
        with open(f, 'w') as fp:
            fp.write(content)
    
    # Return as a string
    else:
        returns.append(content)
    
    if return_prop_info is True:
        returns.append(prop_info)
        
    if len(returns) == 1:
        return returns[0]
    elif len(returns) > 1:
        return tuple(returns)
Пример #18
0
def dump(system, fname, prop_info=None, xf='%.13e'):
    """
    Write a LAMMPS-style dump file from a System.
    
    Arguments:
    system -- System to write to the dump file.
    fname -- name (and location) of file to save data to.    
    
    Keyword Arguments:
    prop_info -- DataModelDict for relating the per-atom properties to/from the dump file and the System. Will create a default json instance <fname>.json if prop_info is not given and <fname>.json doesn't already exist.
    xf -- c-style format for printing the floating point numbers. Default is '%.13e'.
    """  

    #create or read prop_info Data Model
    if prop_info is None:
        try:
            with open(fname+'.json') as fj:
                prop_info = DataModelDict(fj)
        except:                            
            prop_info = __prop_info_default_dump(system)
            with open(fname+'.json', 'w') as fj:
                prop_info.json(fp=fj, indent=4)
    else:
        if os.path.isfile(prop_info):
            with open(prop_info) as f:
                prop_info = f.read()
        prop_info = DataModelDict(prop_info)
    
    #read box_unit if specified in prop_info
    prop_info = prop_info.find('LAMMPS-dump-atoms_prop-relate')
    box_unit = prop_info['box_prop'].get('unit', None)
        
    #open fname
    with open(fname, 'w') as f:

        #write timestep info
        f.write('ITEM: TIMESTEP\n')
        try:
            f.write('%i\n'%system.prop['timestep'])
        except:
            f.write('0\n')
        
        #write number of atoms
        f.write('ITEM: NUMBER OF ATOMS\n')
        f.write('%i\n' % ( system.natoms ))
        
        #write system boundary info for an orthogonal box
        if system.box.xy == 0.0 and system.box.xz == 0.0 and system.box.yz == 0.0:
            f.write('ITEM: BOX BOUNDS')
            for i in xrange(3):
                if system.pbc[i]:
                    f.write(' pp')
                else:
                    f.write(' fm')
            f.write('\n')
            
            f.write('%f %f\n' % ( uc.get_in_units(system.box.xlo, box_unit), uc.get_in_units(system.box.xhi, box_unit) )) 
            f.write('%f %f\n' % ( uc.get_in_units(system.box.ylo, box_unit), uc.get_in_units(system.box.yhi, box_unit) ))
            f.write('%f %f\n' % ( uc.get_in_units(system.box.zlo, box_unit), uc.get_in_units(system.box.zhi, box_unit) ))
        
        #write system boundary info for a triclinic box
        else:
            f.write('ITEM: BOX BOUNDS xy xz yz')
            for i in xrange(3):
                if system.pbc[i]:
                    f.write(' pp')
                else:
                    f.write(' fm')
            f.write('\n')

            xlo_bound = uc.get_in_units(system.box.xlo, box_unit) + uc.get_in_units(min(( 0.0, system.box.xy, system.box.xz, system.box.xy + system.box.xz)), box_unit)
            xhi_bound = uc.get_in_units(system.box.xhi, box_unit) + uc.get_in_units(max(( 0.0, system.box.xy, system.box.xz, system.box.xy + system.box.xz)), box_unit)
            ylo_bound = uc.get_in_units(system.box.ylo, box_unit) + uc.get_in_units(min(( 0.0, system.box.yz )), box_unit) 
            yhi_bound = uc.get_in_units(system.box.yhi, box_unit) + uc.get_in_units(max(( 0.0, system.box.yz )), box_unit)
            zlo_bound = uc.get_in_units(system.box.zlo, box_unit)
            zhi_bound = uc.get_in_units(system.box.zhi, box_unit)
            
            f.write('%f %f %f\n' % ( xlo_bound, xhi_bound, uc.get_in_units(system.box.xy, box_unit) )) 
            f.write('%f %f %f\n' % ( ylo_bound, yhi_bound, uc.get_in_units(system.box.xz, box_unit) ))
            f.write('%f %f %f\n' % ( zlo_bound, zhi_bound, uc.get_in_units(system.box.yz, box_unit) ))

        #write atomic header info and prepare outarray for writing
        header = 'ITEM: ATOMS id'
        print_string = '%i'
        outarray = np.empty((system.natoms, len(prop_info['LAMMPS-attribute'])))
        start = 0
        for attr, a_keys in prop_info['LAMMPS-attribute'].iteritems():
            
            #get first prop relation for attr
            relation = a_keys.aslist('relation')[0]
            prop =   relation.get('prop')
            index = (Ellipsis, ) + tuple(relation.aslist('index'))
            unit =  relation.get('unit', None)
            if unit == 'scaled':
                unit = None
                scale = True
            else:
                scale = False            
            
            #pass values to outarray
            outarray[:,start] = uc.get_in_units(system.atoms_prop(key=prop, scale=scale), unit)[index].reshape((system.natoms))
            start += 1
            
            #prepare header and print_string
            header += ' %s' % attr
            if am.tools.is_dtype_int(system.atoms.dtype[prop]):
                print_string += ' %i'
            else:
                print_string += ' ' + xf
            
        f.write(header + '\n')
        print_string += '\n'
        
        #iterate over all atoms
        for i in xrange(system.natoms):
            vals = (i+1, ) + tuple(outarray[i])
            f.write(print_string % vals)
Пример #19
0
 def model(self, **kwargs):
     """
     Return a DataModelDict 'cell' representation of the system
     
     Keyword Arguments:
     box_unit -- length unit to use for the box. Default is angstrom.
     symbols -- list of atom-model symbols corresponding to the atom types. 
     elements -- list of element tags corresponding to the atom types. 
     prop_units -- dictionary where the keys are the property keys to 
                   include, and the values are units to use. If not given, 
                   only the positions in scaled units are included.
     """
     
     box_unit = kwargs.get('box_unit', 'angstrom')
     
     symbols = kwargs.get('symbols', [None for i in xrange(self.natypes)])
     if not isinstance(symbols, list):
         symbols = [symbols]
     assert len(symbols) == self.natypes, 'Number of symbols does not match number of atom types'
     
     elements = kwargs.get('elements', [None for i in xrange(self.natypes)])
     if not isinstance(elements, list):
         elements = [elements]
     assert len(elements) == self.natypes, 'Number of elements does not match number of atom types'
     
     prop_units = kwargs.get('prop_units', {})
     if 'pos' not in prop_units:
         prop_units['pos'] = 'scaled'        
     
     a = uc.get_in_units(self.box.a, box_unit)
     b = uc.get_in_units(self.box.b, box_unit)
     c = uc.get_in_units(self.box.c, box_unit)
     alpha = self.box.alpha
     beta =  self.box.beta
     gamma = self.box.gamma
 
     model = DM()
     model['cell'] = cell = DM()
     if np.allclose([alpha, beta, gamma], [90.0, 90.0, 90.0]):
         if np.isclose(b/a, 1.):
             if np.isclose(c/a, 1.):
                 c_family = 'cubic'
                 cell[c_family] = DM()
                 cell[c_family]['a'] = DM([('value', (a+b+c)/3), ('unit', box_unit)])
             else:
                 c_family = 'tetragonal'
                 cell[c_family] = DM()
                 cell[c_family]['a'] = DM([('value', (a+b)/2), ('unit', box_unit)])
                 cell[c_family]['c'] = DM([('value', c), ('unit', box_unit)])
         else:
             #if np.isclose(b/a, 3.0**0.5):
             #    c_family = 'hexagonal'
             #    cell[c_family] = DM()
             #    a_av = (a + b/(3.0**0.5))/2.
             #    cell[c_family]['a'] = DM([('value', a_av), ('unit', box_unit)])
             #    cell[c_family]['c'] = DM([('value', c), ('unit', box_unit)])
             
             #else:
             c_family = 'orthorhombic'
             cell[c_family] = DM()
             cell[c_family]['a'] = DM([('value', a), ('unit', box_unit)])
             cell[c_family]['b'] = DM([('value', b), ('unit', box_unit)])
             cell[c_family]['c'] = DM([('value', c), ('unit', box_unit)])
             
     else:
         raise ValueError('Non-orthogonal boxes comming')
     
     for i in xrange(self.natoms):
         atom = DM()
         
         atom['component'] = int(self.atoms_prop(a_id=i, key='atype'))
         
         symbol = symbols[self.atoms_prop(a_id=i, key='atype')-1]
         if symbol is not None:
             atom['symbol'] = symbol
             
         element = elements[self.atoms_prop(a_id=i, key='atype')-1]
         if element is not None:
             atom['element'] = element
         
         atom['position'] = DM()
         if prop_units['pos'] == 'scaled':
             atom['position']['value'] = list(self.atoms_prop(a_id=i, key='pos', scale=True))
         else:
             atom['position']['value'] = list(uc.get_in_units(self.atoms_prop(a_id=i, key='pos'), prop_units['pos']))
         atom['position']['unit'] = prop_units['pos']
             
         for key, unit in prop_units.iteritems():
             if key != 'pos' and key != 'atype':
                 value = uc.get_in_units(self.atoms_prop(a_id=i, key=key), unit)
                 try:
                     value = list(value)
                 except:
                     pass
                 prop = DM([('name',  key), 
                                       ('value', value),
                                       ('unit',  unit)])
                 
                 atom.append('property', prop)
                 
         
         model.append('atom', atom)
         
     return DM([('atomic-system', model)])
Пример #20
0
def dump(system, **kwargs):
    """
    Return a DataModelDict 'cell' representation of the system.
    
    Parameters
    ----------
    system : atomman.System
        The system to generate the data model for.
    f : str or file-like object, optional
        File path or file-like object to write the content to.  If not given,
        then the content is returned as a DataModelDict.
    format : str, optional
        File format 'xml' or 'json' to save the content as if f is given.  If
        f is a filename, then the format will be automatically inferred from
        f's extension.  If format is not given and cannot be inferred, then it
        will be set to 'json'.
    indent : int or None, optional
        Indentation option to use for XML/JSON content if f is given.  A value
        of None (default) will add no line separatations or indentations.
    box_unit : str, optional
        Length unit to use for the box. Default value is 'angstrom'.
    symbols : list, optional
        list of atom-model symbols corresponding to the atom types.  If not
        given, will use system.symbols.
    elements : list, optional
        list of element tags corresponding to the atom types.
    prop_units : dict, optional
        dictionary where the keys are the property keys to include, and
        the values are units to use. If not given, only the positions in
        scaled units are included.
    a_std : float, optional
        Standard deviation of a lattice constant to include if available.
    b_std : float, optional
        Standard deviation of b lattice constant to include if available.
    c_std : float, optional
        Standard deviation of c lattice constant to include if available.
    
    Returns
    -------
    DataModelDict
        A 'cell' data model of the system.
    """
    
    # Set default values
    box_unit = kwargs.get('box_unit', 'angstrom')
    indent = kwargs.get('indent', None)
    symbols = kwargs.get('symbols', system.symbols)
    if isinstance(symbols, stringtype):
        symbols = [symbols]
    assert len(symbols) == system.natypes, 'Number of symbols does not match number of atom types'
    
    elements = kwargs.get('elements', [None for i in range(system.natypes)])
    if not isinstance(elements, list):
        elements = [elements]
    assert len(elements) == system.natypes, 'Number of elements does not match number of atom types'
    
    prop_units = kwargs.get('prop_units', {})
    if 'pos' not in prop_units:
        prop_units['pos'] = 'scaled'
    
    # Extract system values
    a = system.box.a
    b = system.box.b
    c = system.box.c
    alpha = system.box.alpha
    beta =  system.box.beta
    gamma = system.box.gamma
    
    # Check for box standard deviations
    if 'a_std' in kwargs and 'b_std' in kwargs and 'c_std' in kwargs:
        errors = True
        a_std = kwargs['a_std']
        b_std = kwargs['b_std']
        c_std = kwargs['c_std']
    else:
        errors = False
        a_std = None
        b_std = None
        c_std = None
    
    # Initialize DataModelDict
    model = DM()
    model['cell'] = cell = DM()
    
    # Test crystal family
    c_family = identifyfamily(system.box)
    if c_family is None:
        c_family = 'triclinic'
    cell[c_family] = DM()
    
    if c_family == 'cubic':
        a_ave = (a + b + c) / 3
        if errors is True:
            a_std_ave = (a_std + b_std + c_std) / 3
        else:
            a_std_ave = None
        
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
    
    elif c_family == 'tetragonal':
        a_ave = (a + b) / 2
        if errors is True:
            a_std_ave = (a_std + b_std) / 2
        else:
            a_std_ave = None
        
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
    
    elif c_family == 'orthorhombic':
        cell[c_family]['a'] = uc.model(a, box_unit, error=a_std)
        cell[c_family]['b'] = uc.model(b, box_unit, error=b_std)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
    
    elif c_family == 'hexagonal':
        a_ave = (a + b) / 2
        if errors is True:
            a_std_ave = (a_std + b_std) / 2
        else:
            a_std_ave = None
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
    
    elif c_family == 'rhombohedral':
        a_ave = (a + b + c) / 3
        alpha_ave = (alpha + beta + gamma) / 3
        if errors is True:
            a_std_ave = (a_std + b_std + c_std) / 3
        else:
            a_std_ave = None
        
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
        cell[c_family]['alpha'] = alpha_ave
    
    elif c_family == 'monoclinic':
        cell[c_family]['a'] = uc.model(a, box_unit, error=a_std)
        cell[c_family]['b'] = uc.model(b, box_unit, error=b_std)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
        cell[c_family]['beta'] = beta
    
    elif c_family == 'triclinic':
        cell[c_family]['a'] = uc.model(a, box_unit, error=a_std)
        cell[c_family]['b'] = uc.model(b, box_unit, error=b_std)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
        cell[c_family]['alpha'] = alpha
        cell[c_family]['beta'] = beta
        cell[c_family]['gamma'] = gamma
    else:
        raise ValueError('Unknown crystal family')
    
    atype = system.atoms.atype
    aindex = atype - 1
    
    # Build list of atoms and per-atom properties
    for i in range(system.natoms):
        atom = DM()
        
        atom['component'] = int(atype[i])
        
        symbol = symbols[aindex[i]]
        if symbol is not None:
            atom['symbol'] = symbol
        
        element = elements[aindex[i]]
        if element is not None:
            atom['element'] = element
        
        atom['position'] = DM()
        if prop_units['pos'] == 'scaled':
            atom['position']['value'] = list(system.atoms_prop(a_id=i, key='pos', scale=True))
        else:
            atom['position']['value'] = list(uc.get_in_units(system.atoms.pos[i], prop_units['pos']))
        atom['position']['unit'] = prop_units['pos']
        
        for key, unit in iteritems(prop_units):
            if key != 'pos' and key != 'atype':
                value = system.atoms.view[key][i]
                prop = DM()
                prop['name'] = key 
                prop.update(uc.model(value, unit))
                atom.append('property', prop)
        
        model.append('atom', atom)
    
    model = DM([('atomic-system', model)])
    
    # Return DataModelDict or str
    if 'f' not in kwargs:
        if 'format' not in kwargs:
            return model
        elif kwargs['format'].lower() == 'xml':
            return model.xml(indent=indent)
        elif kwargs['format'].lower() == 'json':
            return model.json(indent=indent)
    
    # Write to file
    else:
        f = kwargs['f']
        if 'format' not in kwargs:
            try:
                format = os.path.splitext(f)[1][1:]
            except:
                format = 'json'
        else:
            format = kwargs['format']
        
        if hasattr(f, 'write'):
            if format.lower() == 'xml':
                return model.xml(fp=f, indent=indent)
            elif format.lower() == 'json':
                return model.json(fp=f, indent=indent)
        
        else:
            with open(f, 'w') as fp:
                if format.lower() == 'xml':
                    return model.xml(fp=fp, indent=indent)
                elif format.lower() == 'json':
                    return model.json(fp=fp, indent=indent)
        
    
    return 
Пример #21
0
def relax_system(lammps_command, system, potential,
                 mpi_command=None, etol=0.0, ftol=0.0, maxiter=10000,
                 maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Sets up and runs the min.in LAMMPS script for performing an energy/force
    minimization to relax a system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The name of the LAMMPS log file.
        - **'initialdatafile'** (*str*) - The name of the LAMMPS data file
          used to import an inital configuration.
        - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the inital configuration.
        - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the relaxed configuration.
        - **'potentialenergy'** (*float*) - The total potential energy of
          the relaxed system.
    """
    
    # Ensure all atoms are within the system's box
    system.wrap()
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
      
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    
    # Generate system and pair info
    system_info = system.dump('atom_data', f='system.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    
    # Pass in run parameters
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'min.template'
    lammps_script = 'min.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    results = {}
    results['logfile'] = 'log.lammps'
    results['initialdatafile'] = 'system.dat'
    results['initialdumpfile'] = 'atom.0'
    results['finaldumpfile'] = 'atom.%i' % thermo.Step.values[-1]
    results['potentialenergy'] = uc.set_in_units(thermo.PotEng.values[-1],
                                                 lammps_units['energy'])
    
    return results
Пример #22
0
 def test_newton(self):
     newton = uc.set_in_units(1e5, 'dyn')
     assert pytest.approx(uc.get_in_units(newton, 'kg*m/s^2'), 1.0)
Пример #23
0
    def E_gsf_surface_plot(self,
                           x=None,
                           disregistry=None,
                           fmt='ro-',
                           normalize=False,
                           smooth=True,
                           a1vect=None,
                           a2vect=None,
                           xvect=None,
                           length_unit='Å',
                           energyperarea_unit='eV/Å^2',
                           numx=100,
                           numy=100,
                           figsize=None,
                           **kwargs):
        """
        Extends the GammaSurface.E_gsf_surface_plot() method to plot the
        disregistry path on top of it.
        
        Parameters
        ----------
        x : numpy.ndarray, optional
            x-coordinates.  Default value is the stored x-coordinates. If x
            or disregistry are not set/given, then the disregistry path will
            not be added.
        disregistry : numpy.ndarray, optional
            (N, 3) shaped array of disregistry vectors at each x-coordinate.
            Default value is the stored disregistry values.  If x
            or disregistry are not set/given, then the disregistry path will
            not be added.
        fmt : str, optional
            The matplotlib.pyplot.plot fmt parameter for the disregistry path
            line, i.e. color, marker and line style options.  Default value is
            'ro-': red with circle markers and solid line.
        normalize : bool, optional
            Flag indicating if axes are Cartesian (False, default) or
            normalized by a1, a2 vectors (True).
        smooth : bool, optional
            If True (default), then plot shows smooth interpolated values.
            If False, plot shows nearest raw data values.
        a1vect : np.array, optional
            Crystal vector for the a1 vector to use for plotting.  Default
            value of None uses the saved a1vect.
        a2vect : np.array, optional
            Crystal vector for the a2 vector to use for plotting.  Default
            value of None uses the saved a2vect.
        xvect : numpy.array, optional
            Crystal vector to align with the plotting x-axis for 
            non-normalized plots.  If not given, this is taken as the Cartesian
            of a1vect.
        length_unit : str, optional
            The unit of length to display non-normalized axes values in.
            Default value is 'Å'.
        energyperarea_unit : str, optional
            The unit of energy per area to display the stacking fault energies
            in. Default value is 'eV/Å^2'.
        numx : int, optional
            The number of plotting points to use along the x-axis.  Default
            value is 100.
        numy : int, optional
            The number of plotting points to use along the y-axis.  Default
            value is 100.       
        figsize : tuple or None, optional
            The figure's x,y dimensions.  If None (default), the values are
            scaled such that the x,y spacings are approximately equal, and the
            larger of the two values is set to 10.
        **kwargs : dict, optional
            Additional keywords are passed into the underlying 
            matplotlib.pyplot.pcolormesh(). This allows control of such things
            like the colormap (cmap).
            
        Returns
        -------
        matplotlib.pyplot.figure
            The generated figure allowing users to perform additional
            modifications.
        """

        # Generate the surface plot
        fig = self.gamma.E_gsf_surface_plot(
            normalize=normalize,
            smooth=smooth,
            a1vect=a1vect,
            a2vect=a2vect,
            xvect=xvect,
            length_unit=length_unit,
            energyperarea_unit=energyperarea_unit,
            numx=numx,
            numy=numy,
            figsize=figsize,
            **kwargs)

        hasvals = True
        # Default values are class properties
        if x is None:
            try:
                x = self.x
            except:
                hasvals = False
        if disregistry is None:
            try:
                disregistry = self.disregistry
            except:
                hasvals = False
        if hasvals:

            # Get xvect direction
            if xvect is None:
                if a1vect is None:
                    a1vect = self.gamma.a1vect
                xvect = np.dot(a1vect, self.gamma.box.vects)

            # Transform disregistry to gamma surface pos
            pos = disregistry.dot(self.transform)

            # Transform to x, y plotting coordinates and plot
            x, y = self.gamma.pos_to_xy(pos, xvect=xvect)
            x = uc.get_in_units(x, length_unit)
            y = uc.get_in_units(y, length_unit)
            plt.plot(x, y, fmt)

        return fig
Пример #24
0
def relax_static(lammps_command,
                 system,
                 potential,
                 mpi_command=None,
                 p_xx=0.0,
                 p_yy=0.0,
                 p_zz=0.0,
                 p_xy=0.0,
                 p_xz=0.0,
                 p_yz=0.0,
                 dispmult=0.0,
                 etol=0.0,
                 ftol=0.0,
                 maxiter=10000,
                 maxeval=100000,
                 dmax=uc.set_in_units(0.01, 'angstrom'),
                 maxcycles=100,
                 ctol=1e-10):
    """
    Repeatedly runs the ELASTIC example distributed with LAMMPS until box
    dimensions converge within a tolerance.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    p_xy : float, optional
        The value to relax the xy shear pressure component to (default is
        0.0).
    p_xz : float, optional
        The value to relax the xz shear pressure component to (default is
        0.0).
    p_yz : float, optional
        The value to relax the yz shear pressure component to (default is
        0.0).
    dispmult : float, optional
        Multiplier for applying a random displacement to all atomic positions
        prior to relaxing. Default value is 0.0.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    pressure_unit : str, optional
        The unit of pressure to calculate the elastic constants in (default is
        'GPa').
    maxcycles : int, optional
        The maximum number of times the minimization algorithm is called.
        Default value is 100.
    ctol : float, optional
        The relative tolerance used to determine if the lattice constants have
        converged (default is 1e-10).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'relaxed_system'** (*float*) - The relaxed system.
        - **'E_coh'** (*float*) - The cohesive energy of the relaxed system.
        - **'measured_pxx'** (*float*) - The measured x tensile pressure of the
          relaxed system.
        - **'measured_pyy'** (*float*) - The measured y tensile pressure of the
          relaxed system.
        - **'measured_pzz'** (*float*) - The measured z tensile pressure of the
          relaxed system.
        - **'measured_pxy'** (*float*) - The measured xy shear pressure of the
          relaxed system.
        - **'measured_pxz'** (*float*) - The measured xz shear pressure of the
          relaxed system.
        - **'measured_pyz'** (*float*) - The measured yz shear pressure of the
          relaxed system.
    """

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

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

    # Save initial configuration as a dump file
    system.dump('atom_dump', f='initial.dump')

    # Apply small random distortions to atoms
    system.atoms.pos += dispmult * np.random.rand(
        *system.atoms.pos.shape) - dispmult / 2

    # Initialize parameters
    old_vects = system.box.vects
    converged = False

    # Run minimizations up to maxcycles times
    for cycle in range(maxcycles):
        old_system = deepcopy(system)

        # Define lammps variables
        lammps_variables = {}
        system_info = system.dump('atom_data',
                                  f='init.dat',
                                  units=potential.units,
                                  atom_style=potential.atom_style)
        lammps_variables['atomman_system_info'] = system_info
        lammps_variables['atomman_pair_info'] = potential.pair_info(
            system.symbols)
        lammps_variables['p_xx'] = uc.get_in_units(p_xx,
                                                   lammps_units['pressure'])
        lammps_variables['p_yy'] = uc.get_in_units(p_yy,
                                                   lammps_units['pressure'])
        lammps_variables['p_zz'] = uc.get_in_units(p_zz,
                                                   lammps_units['pressure'])
        lammps_variables['p_xy'] = uc.get_in_units(p_xy,
                                                   lammps_units['pressure'])
        lammps_variables['p_xz'] = uc.get_in_units(p_xz,
                                                   lammps_units['pressure'])
        lammps_variables['p_yz'] = uc.get_in_units(p_yz,
                                                   lammps_units['pressure'])
        lammps_variables['etol'] = etol
        lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
        lammps_variables['maxiter'] = maxiter
        lammps_variables['maxeval'] = maxeval
        lammps_variables['dmax'] = uc.get_in_units(dmax,
                                                   lammps_units['length'])

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

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

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

        # Clean up dump files
        os.remove('0.dump')
        last_dump_file = str(thermo.Step.values[-1]) + '.dump'
        renamed_dump_file = 'relax_static-' + str(cycle) + '.dump'
        shutil.move(last_dump_file, renamed_dump_file)

        # Load relaxed system
        system = am.load('atom_dump',
                         renamed_dump_file,
                         symbols=system.symbols)

        # Test if box dimensions have converged
        if np.allclose(old_vects, system.box.vects, rtol=ctol, atol=0):
            converged = True
            break
        else:
            old_vects = system.box.vects

    # Check for convergence
    if converged is False:
        raise RuntimeError('Failed to converge after ' + str(maxcycles) +
                           ' cycles')

    # Zero out near-zero tilt factors
    lx = system.box.lx
    ly = system.box.ly
    lz = system.box.lz
    xy = system.box.xy
    xz = system.box.xz
    yz = system.box.yz
    if np.isclose(xy / ly, 0.0, rtol=0.0, atol=1e-10):
        xy = 0.0
    if np.isclose(xz / lz, 0.0, rtol=0.0, atol=1e-10):
        xz = 0.0
    if np.isclose(yz / lz, 0.0, rtol=0.0, atol=1e-10):
        yz = 0.0
    system.box.set(lx=lx, ly=ly, lz=lz, xy=xy, xz=xz, yz=yz)
    system.wrap()

    # Build results_dict
    results_dict = {}
    results_dict['dumpfile_initial'] = 'initial.dump'
    results_dict['symbols_initial'] = system.symbols
    results_dict['dumpfile_final'] = renamed_dump_file
    results_dict['symbols_final'] = system.symbols
    results_dict['E_coh'] = uc.set_in_units(
        thermo.PotEng.values[-1] / system.natoms, lammps_units['energy'])

    results_dict['lx'] = uc.set_in_units(lx, lammps_units['length'])
    results_dict['ly'] = uc.set_in_units(ly, lammps_units['length'])
    results_dict['lz'] = uc.set_in_units(lz, lammps_units['length'])
    results_dict['xy'] = uc.set_in_units(xy, lammps_units['length'])
    results_dict['xz'] = uc.set_in_units(xz, lammps_units['length'])
    results_dict['yz'] = uc.set_in_units(yz, lammps_units['length'])

    results_dict['measured_pxx'] = uc.set_in_units(thermo.Pxx.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pyy'] = uc.set_in_units(thermo.Pyy.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pzz'] = uc.set_in_units(thermo.Pzz.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pxy'] = uc.set_in_units(thermo.Pxy.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pxz'] = uc.set_in_units(thermo.Pxz.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pyz'] = uc.set_in_units(thermo.Pyz.values[-1],
                                                   lammps_units['pressure'])

    return results_dict
Пример #25
0
    def model(self, **kwargs):
        """
        Return a DataModelDict 'cell' representation of the system
        
        Keyword Arguments:
        box_unit -- length unit to use for the box. Default is angstrom.
        symbols -- list of atom-model symbols corresponding to the atom types. 
        elements -- list of element tags corresponding to the atom types. 
        prop_units -- dictionary where the keys are the property keys to 
                      include, and the values are units to use. If not given, 
                      only the positions in scaled units are included.
        a_std, b_std, c_std -- standard deviation of lattice constants values to
                               include as value errors.
        """
        
        box_unit = kwargs.get('box_unit', 'angstrom')
        
        symbols = kwargs.get('symbols', [None for i in xrange(self.natypes)])
        if not isinstance(symbols, list):
            symbols = [symbols]
        assert len(symbols) == self.natypes, 'Number of symbols does not match number of atom types'
        
        elements = kwargs.get('elements', [None for i in xrange(self.natypes)])
        if not isinstance(elements, list):
            elements = [elements]
        assert len(elements) == self.natypes, 'Number of elements does not match number of atom types'
        
        prop_units = kwargs.get('prop_units', {})
        if 'pos' not in prop_units:
            prop_units['pos'] = 'scaled'
        
        a = self.box.a
        b = self.box.b
        c = self.box.c
        alpha = self.box.alpha
        beta =  self.box.beta
        gamma = self.box.gamma
        
        if 'a_std' in kwargs and 'b_std' in kwargs and 'c_std' in kwargs:
            errors = True
            a_std = kwargs['a_std']
            b_std = kwargs['b_std']
            c_std = kwargs['c_std']
        else:
            errors = False
        
    
        model = DM()
        model['cell'] = cell = DM()
        
        # Test for orthorhombic angles
        if np.allclose([alpha, beta, gamma], [90.0, 90.0, 90.0]):
            if np.isclose(b/a, 1.):
                if np.isclose(c/a, 1.):
                    
                    # For cubic (a = b = c)
                    c_family = 'cubic'
                    
                    cell[c_family] = DM()
                    cell[c_family]['a'] = DM()
                    
                    a_ave = (a + b + c) / 3
                    cell[c_family]['a']['value'] = uc.get_in_units(a_ave, box_unit)
                    
                    if errors is True:
                        a_std_ave = (a_std + b_std + c_std) / 3
                        cell[c_family]['a']['error'] = uc.get_in_units(a_std_ave, box_unit)
                    
                    cell[c_family]['a']['unit'] = box_unit
                
                else:
                    # For tetrahedral (a = b != c)
                    c_family = 'tetragonal'
                    
                    cell[c_family] = DM()
                    cell[c_family]['a'] = DM()
                    cell[c_family]['c'] = DM()
                    
                    a_ave = (a + b) / 2
                    cell[c_family]['a']['value'] = uc.get_in_units(a_ave, box_unit)
                    cell[c_family]['c']['value'] = uc.get_in_units(c, box_unit)
                    
                    if errors is True:
                        a_std_ave = (a_std + b_std) / 2
                        cell[c_family]['a']['error'] = uc.get_in_units(a_std_ave, box_unit)
                        cell[c_family]['c']['error'] = uc.get_in_units(c_std, box_unit)
                        
                    cell[c_family]['a']['unit'] = box_unit
                    cell[c_family]['c']['unit'] = box_unit

            else:
                # For orthorhombic (a != b != c)
                c_family = 'orthorhombic'
                
                cell[c_family] = DM()
                cell[c_family]['a'] = DM()
                cell[c_family]['b'] = DM()
                cell[c_family]['c'] = DM()
                
                cell[c_family]['a']['value'] = uc.get_in_units(a, box_unit)
                cell[c_family]['b']['value'] = uc.get_in_units(b, box_unit)
                cell[c_family]['c']['value'] = uc.get_in_units(c, box_unit)
                
                if errors is True:
                    cell[c_family]['a']['error'] = uc.get_in_units(a_std, box_unit)
                    cell[c_family]['b']['error'] = uc.get_in_units(b_std, box_unit)
                    cell[c_family]['c']['error'] = uc.get_in_units(c_std, box_unit)
                
                cell[c_family]['a']['unit'] = box_unit
                cell[c_family]['b']['unit'] = box_unit
                cell[c_family]['c']['unit'] = box_unit
        else:
            raise ValueError('Non-orthogonal boxes comming')
        
        for i in xrange(self.natoms):
            atom = DM()
            
            atom['component'] = int(self.atoms_prop(a_id=i, key='atype'))
            
            symbol = symbols[self.atoms_prop(a_id=i, key='atype')-1]
            if symbol is not None:
                atom['symbol'] = symbol
                
            element = elements[self.atoms_prop(a_id=i, key='atype')-1]
            if element is not None:
                atom['element'] = element
            
            atom['position'] = DM()
            if prop_units['pos'] == 'scaled':
                atom['position']['value'] = list(self.atoms_prop(a_id=i, key='pos', scale=True))
            else:
                atom['position']['value'] = list(uc.get_in_units(self.atoms_prop(a_id=i, key='pos'), prop_units['pos']))
            atom['position']['unit'] = prop_units['pos']
                
            for key, unit in prop_units.iteritems():
                if key != 'pos' and key != 'atype':
                    value = uc.get_in_units(self.atoms_prop(a_id=i, key=key), unit)
                    try:
                        value = list(value)
                    except:
                        pass
                    prop = DM([('name',  key), 
                                          ('value', value),
                                          ('unit',  unit)])
                    
                    atom.append('property', prop)
                    
            
            model.append('atom', atom)
            
        return DM([('atomic-system', model)])
Пример #26
0
def interpolate_contour(system, name, property=None, index=None, magnitude=False,
                        plotxaxis='x', plotyaxis='y', xlim=None, ylim=None,
                        zlim=None, xbins=200, ybins=200, dots=True, czero=True,
                        save=False, show=True, length_unit='angstrom',
                        property_unit=None, cmap='jet'):
    """
    Creates a contour plot of a system's per-atom properties by interpolating
    properties between atoms.
    
    Parameters
    ----------
    system : atomman.System
        The system with the per-atom property that you want to plot.
    name : str
        The name of the per-atom property that you want to plot.
    property : array-like object, optional
        Values for the per-atom property to plot.  If not given, values will
        be taken as the "name" property of system.
    index : int or tuple, optional
        Specifies which component of a multidimensional property to plot.  Not
        needed if the property is scalar.
    magnitude : bool, optional
        If True, plots the per-atom magnitude of a vector property.  Cannot be
        combined with index.  Default value is False.
    plotxaxis : str or array-like object, optional
        Indicates the Cartesian direction associated with the system's atomic
        coordinates to align with the plotting x-axis.  Values are either 3D
        unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes
        directions.  plotxaxis and plotyaxis must be orthogonal.  Default value
        is 'x' = [1, 0, 0].
    plotyaxis : str or array-like object, optional
        Indicates the Cartesian direction associated with the system's atomic
        coordinates to align with the plotting y-axis.  Values are either 3D
        unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes
        directions.  plotxaxis and plotyaxis must be orthogonal.  Default value
        is 'y' = [0, 1, 0].
    xlim : tuple, optional
        The minimum and maximum coordinates along the plotting x-axis to
        include in the fit.  Values are taken in the specified length_unit.
        If not given, then the limits are set based on min and max atomic
        coordinates along the plotting axis.
    ylim : tuple, optional
        The minimum and maximum coordinates along the plotting y-axis to
        include in the fit.  Values are taken in the specified length_unit.
        If not given, then the limits are set based on min and max atomic
        coordinates along the plotting axis.
    zlim : tuple, optional
        The minimum and maximum coordinates normal to the plotting axes
        (i.e. plotxaxis X plotyaxis) to include in the fit.  Values are taken
        in the specified length_unit.  If not given, then the limits are set
        based on min and max atomic coordinates along the axis.    
    xbins : int, optional
        Specifies the number of interpolation bins to use along the plotting 
        x-axis.  Default value is 200.
    ybins : int, optional
        Specifies the number of interpolation bins to use along the plotting
        y-axis.  Default value is 200.
    dots : bool, optional
        If True, then the positions of the atoms are shown as circles.
        Default value is True.
    czero : bool, optional
        If True, the range of property values will be centered around zero,
        i.e. cmax = -cmin.  If False, cmax and cmin will be independently
        selected using the property values.  Default value is True.
    save : bool, optional
        If True, the generated plot will be saved to "name.png".  Default
        value is False.
    show : bool, optional
        If True, matplotlib.pyplot.show() is called.  Default value is True.
    length_unit : str, optional
        The units of length to use for the plotting x- and y-axes.  Default
        value is 'angstrom'.
    property_unit : str or None, optional
        The units to use for the property value being plotted.  Default value
        is None, in which no unit conversion is applied.
    cmap : str, optional
        The name of the matplotlib colormap to use.  Default value is 'jet'.
    
    Returns
    -------
    intsum : float
        The area integrated sum of the property over the plotted region.
    avsum : float
        The average property value taken across all plotting bins.
    """
    
    # Give numeric values for str plot axis terms
    if plotxaxis == 'x':
        plotxaxis = [1.0,0.0,0.0]
    elif plotxaxis == 'y':
        plotxaxis = [0.0,1.0,0.0]
    elif plotxaxis == 'z':
        plotxaxis = [0.0,0.0,1.0]
    
    if plotyaxis == 'x':
        plotyaxis = [1.0,0.0,0.0]
    elif plotyaxis == 'y':
        plotyaxis = [0.0,1.0,0.0]
    elif plotyaxis == 'z':
        plotyaxis = [0.0,0.0,1.0]
    
    # Build transformation matrix, T, from plot axes.
    plotxaxis = np.asarray(plotxaxis, dtype='float64')
    plotyaxis = np.asarray(plotyaxis, dtype='float64')
    T = axes_check([plotxaxis, plotyaxis, np.cross(plotxaxis, plotyaxis)])
    
    # Extract positions and transform using T
    pos = uc.get_in_units(np.inner(system.atoms.pos, T), length_unit)
    
    # Set plot limits
    if xlim is None:
        xlim = (pos[:, 0].min(), pos[:, 0].max())
    if ylim is None:
        ylim = (pos[:, 1].min(), pos[:, 1].max())
    if zlim is None:
        zlim = (pos[:, 2].min(), pos[:, 2].max())
    
    # Extract property values
    if property is None:
        property = system.atoms.view[name]
    
    # Handle index
    if index is not None:
        assert magnitude is False, 'index and magnitude cannot be combined'
        if isinstance(index, inttype):
            index = [index]
        else:
            index = list(index)
        for i in index:
            name += '[' + str(i+1) + ']'
        property = property[tuple([Ellipsis] + index)]
    
    # Handle magnitude
    elif magnitude is True:
        property = np.linalg.norm(property, axis=1)
        name += '_mag'
    
    assert property.shape == (system.natoms, ), 'property to plot must be a per-atom scalar'
    
    # Identify only atoms in xlim, ylim, zlim
    in_bounds = ((pos[:,0] > xlim[0] - 5.) &
                 (pos[:,0] < xlim[1] + 5.) &
                 (pos[:,1] > ylim[0] - 5.) &
                 (pos[:,1] < ylim[1] + 5.) &
                 (pos[:,2] > zlim[0]) &
                 (pos[:,2] < zlim[1]))
    
    # Extract plotting coordinates and values
    x = pos[in_bounds, 0]
    y = pos[in_bounds, 1]
    v = uc.get_in_units(property[in_bounds], property_unit)
    
    # Generate interpolation grid
    grid, xedges, yedges = grid_interpolate_2d(x, y, v, xbins=xbins, ybins=ybins, range=[xlim, ylim])
    
    # Compute intsum and avsum values
    intsum = np.sum(grid)
    avsum = intsum / (xbins-1) / (ybins-1)
    intsum = intsum * (xlim[1] - xlim[0]) / (xbins-1) * (ylim[1] - ylim[0]) / (ybins-1)
    
    # Generate a pretty figure of the grid
    fig = prettygrid(grid, xedges, yedges, cmap=cmap, propname=name, czero=czero)
    
    # Add dots
    if dots:
        adddots(x, y, xedges, yedges)
    
    # Save, show and close figure
    if save is True:
        plt.savefig(name + '.png', dpi=800)
    if show is True:
        plt.show()
    plt.close(fig)
    
    return intsum, avsum
Пример #27
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
Пример #28
0
def stackingfaultpoint(lammps_command, system, potential,
                       mpi_command=None, sim_directory=None,
                       cutboxvector='c', faultpos=0.5,
                       faultshift=[0.0, 0.0, 0.0], etol=0.0, ftol=0.0,
                       maxiter=10000, maxeval=100000,
                       dmax=uc.set_in_units(0.01, 'angstrom'),
                       lammps_date=None):
    """
    Perform a stacking fault relaxation simulation for a single faultshift.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simuation in.  If not
        given, will use the current working directory.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    cutboxvector : str, optional
        Indicates which of the three system box vectors, 'a', 'b', or 'c', to
        cut with a non-periodic boundary (default is 'c').
    faultpos : float, optional
        The fractional position along the cutboxvector where the stacking
        fault plane will be placed (default is 0.5).
    faultshift : list of float, optional
        The vector shift to apply to all atoms above the fault plane defined
        by faultpos (default is [0,0,0], i.e. no shift applied).
    lammps_date : datetime.date or None, optional
        The date version of the LAMMPS executable.  If None, will be identified from the lammps_command (default is None).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The filename of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed system.
        - **'system'** (*atomman.System*) - The relaxed system.
        - **'A_fault'** (*float*) - The area of the fault surface.
        - **'E_total'** (*float*) - The total potential energy of the relaxed
          system.
        - **'disp'** (*float*) - The center of mass difference between atoms
          above and below the fault plane in the cutboxvector direction.
    
    Raises
    ------
    ValueError
        For invalid cutboxvectors.
    """
    
    # Set options based on cutboxvector
    if cutboxvector == 'a':
        # Assert system is compatible with planeaxis value
        if system.box.xy != 0.0 or system.box.xz != 0.0:
            raise ValueError("box tilts xy and xz must be 0 for cutboxvector='a'")
        
        # Specify cutindex
        cutindex = 0
        
        # Identify atoms above fault
        faultpos = system.box.xlo + system.box.lx * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.bvect,
                                            system.box.cvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce NULL 0 0'
    
    elif cutboxvector == 'b':
        # Assert system is compatible with planeaxis value
        if system.box.yz != 0.0:
            raise ValueError("box tilt yz must be 0 for cutboxvector='b'")
        
        # Specify cutindex
        cutindex = 1
        
        # Identify atoms above fault
        faultpos = system.box.ylo + system.box.ly * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.cvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 NULL 0'
        
    elif cutboxvector == 'c':
        # Specify cutindex
        cutindex = 2
        
        # Identify atoms above fault
        faultpos = system.box.zlo + system.box.lz * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.bvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 0 NULL'
        
    else: 
        raise ValueError('Invalid cutboxvector')
    
    # Assert faultshift is in cut plane
    if faultshift[cutindex] != 0.0:
        raise ValueError('faultshift must be in cut plane')
    
    # Generate stacking fault system by shifting atoms above the fault
    sfsystem = deepcopy(system)
    sfsystem.pbc = [True, True, True]
    sfsystem.pbc[cutindex] = False
    sfsystem.atoms.pos[abovefault] += faultshift
    sfsystem.wrap()
    
    if sim_directory is not None:
        # Create sim_directory if it doesn't exist
        if not os.path.isdir(sim_directory):
            os.mkdir(sim_directory)
        
        # Add '/' to end of sim_directory string if needed
        if sim_directory[-1] != '/': 
            sim_directory = sim_directory + '/'
    else:
        # Set sim_directory if is None
        sim_directory = ''
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
       
    #Get lammps version date
    if lammps_date is None:
        lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = sfsystem.dump('atom_data',
                                f=os.path.join(sim_directory, 'system.dat'),
                                units=potential.units,
                                atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(sfsystem.symbols)
    lammps_variables['fix_cut_setforce'] = fix_cut_setforce
    lammps_variables['sim_directory'] = sim_directory
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'sfmin.template'
    lammps_script = os.path.join(sim_directory, 'sfmin.in')
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command,
                     logfile=os.path.join(sim_directory, 'log.lammps'))
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    logfile = os.path.join(sim_directory, 'log.lammps')
    dumpfile = os.path.join(sim_directory, '%i.dump' % thermo.Step.values[-1])
    E_total = uc.set_in_units(thermo.PotEng.values[-1],
                              lammps_units['energy'])
    
    #Load relaxed system
    sfsystem = am.load('atom_dump', dumpfile, symbols=sfsystem.symbols)
    
    # Find center of mass difference in top/bottom planes
    disp = (sfsystem.atoms.pos[abovefault, cutindex].mean()
            - sfsystem.atoms.pos[~abovefault, cutindex].mean())
    
    # Return results
    results_dict = {}
    results_dict['logfile'] = logfile
    results_dict['dumpfile'] = dumpfile
    results_dict['system'] = sfsystem
    results_dict['A_fault'] = faultarea
    results_dict['E_total'] = E_total
    results_dict['disp'] = disp
    
    return results_dict
Пример #29
0
def data_model(input_dict, results_dict=None):
    """Creates a DataModelDict containing the input and results data"""

    #Create the root of the DataModelDict
    output = DM()
    output['calculation-system-relax'] = calc = DM()

    #Assign uuid
    calc['key'] = input_dict['calc_key']
    calc['calculation'] = DM()
    calc['calculation']['script'] = __calc_name__

    calc['calculation']['run-parameter'] = run_params = DM()

    run_params['size-multipliers'] = DM()
    run_params['size-multipliers']['a'] = list(input_dict['sizemults'][0])
    run_params['size-multipliers']['b'] = list(input_dict['sizemults'][1])
    run_params['size-multipliers']['c'] = list(input_dict['sizemults'][2])

    run_params['strain-range'] = input_dict['strainrange']
    run_params['load_options'] = input_dict['load_options']

    #Copy over potential data model info
    calc['potential'] = DM()
    calc['potential']['key'] = input_dict['potential'].key
    calc['potential']['id'] = input_dict['potential'].id

    #Save info on system file loaded
    system_load = input_dict['load'].split(' ')
    calc['system-info'] = DM()
    calc['system-info']['artifact'] = DM()
    calc['system-info']['artifact']['file'] = os.path.basename(' '.join(
        system_load[1:]))
    calc['system-info']['artifact']['format'] = system_load[0]
    calc['system-info']['artifact']['family'] = input_dict['system_family']
    calc['system-info']['symbols'] = input_dict['symbols']

    #Save phase-state info
    calc['phase-state'] = DM()
    calc['phase-state']['temperature'] = DM([('value', 0.0), ('unit', 'K')])
    calc['phase-state']['pressure-xx'] = DM([
        ('value',
         uc.get_in_units(input_dict['pressure_xx'],
                         input_dict['pressure_unit'])),
        ('unit', input_dict['pressure_unit'])
    ])
    calc['phase-state']['pressure-yy'] = DM([
        ('value',
         uc.get_in_units(input_dict['pressure_yy'],
                         input_dict['pressure_unit'])),
        ('unit', input_dict['pressure_unit'])
    ])
    calc['phase-state']['pressure-zz'] = DM([
        ('value',
         uc.get_in_units(input_dict['pressure_zz'],
                         input_dict['pressure_unit'])),
        ('unit', input_dict['pressure_unit'])
    ])

    if results_dict is None:
        calc['status'] = 'not calculated'
    else:
        #Save data model of the initial ucell
        calc['as-constructed-atomic-system'] = input_dict['ucell'].model(
            symbols=input_dict['symbols'],
            box_unit=input_dict['length_unit'])['atomic-system']

        #Update ucell to relaxed lattice parameters
        a_mult = input_dict['sizemults'][0][1] - input_dict['sizemults'][0][0]
        b_mult = input_dict['sizemults'][1][1] - input_dict['sizemults'][1][0]
        c_mult = input_dict['sizemults'][2][1] - input_dict['sizemults'][2][0]
        relaxed_ucell = deepcopy(input_dict['ucell'])
        relaxed_ucell.box_set(a=results_dict['system_new'].box.a / a_mult,
                              b=results_dict['system_new'].box.b / b_mult,
                              c=results_dict['system_new'].box.c / c_mult,
                              scale=True)

        #Save data model of the relaxed ucell
        calc['relaxed-atomic-system'] = relaxed_ucell.model(
            symbols=input_dict['symbols'],
            box_unit=input_dict['length_unit'])['atomic-system']

        #Save the final cohesive energy
        calc['cohesive-energy'] = DM([
            ('value',
             uc.get_in_units(results_dict['ecoh'], input_dict['energy_unit'])),
            ('unit', input_dict['energy_unit'])
        ])

        #Save the final elastic constants
        c_family = calc['relaxed-atomic-system']['cell'].keys()[0]
        calc['elastic-constants'] = results_dict['C'].model(
            unit=input_dict['pressure_unit'],
            crystal_system=c_family)['elastic-constants']

    return output
Пример #30
0
def relax_system(lammps_command,
                 system,
                 potential,
                 mpi_command=None,
                 etol=0.0,
                 ftol=0.0,
                 maxiter=10000,
                 maxeval=100000,
                 dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Sets up and runs the min.in LAMMPS script for performing an energy/force
    minimization to relax a system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The name of the LAMMPS log file.
        - **'initialdatafile'** (*str*) - The name of the LAMMPS data file
          used to import an inital configuration.
        - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the inital configuration.
        - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the relaxed configuration.
        - **'potentialenergy'** (*float*) - The total potential energy of
          the relaxed system.
    """
    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()

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

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

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

    # Define lammps variables
    lammps_variables = {}

    # Generate system and pair info
    system_info = system.dump('atom_data',
                              f='system.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)

    # Pass in run parameters
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])

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

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

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

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

    return results
Пример #31
0
def dump(system, **kwargs):
    """
    Return a DataModelDict 'cell' representation of the system.
    
    Parameters
    ----------
    system : atomman.System
        The system to generate the data model for.
    f : str or file-like object, optional
        File path or file-like object to write the content to.  If not given,
        then the content is returned as a DataModelDict.
    format : str, optional
        File format 'xml' or 'json' to save if f is given.  If not given, will
        be inferred from f if f is a filename, or taken as 'json'.
    indent : int, optional
        Indentation option to use for XML/JSON content if f is given.
    box_unit : str, optional
        Length unit to use for the box. Default value is 'angstrom'.
    symbols : list, optional
        list of atom-model symbols corresponding to the atom types.  If not
        given, will use system.symbols.
    elements : list, optional
        list of element tags corresponding to the atom types.
    prop_units : dict, optional
        dictionary where the keys are the property keys to include, and
        the values are units to use. If not given, only the positions in
        scaled units are included.
    a_std : float, optional
        Standard deviation of a lattice constant to include if available.
    b_std : float, optional
        Standard deviation of b lattice constant to include if available.
    c_std : float, optional
        Standard deviation of c lattice constant to include if available.
    
    Returns
    -------
    DataModelDict
        A 'cell' data model of the system.
    """
    
    # Set default values
    box_unit = kwargs.get('box_unit', 'angstrom')
    
    symbols = kwargs.get('symbols', system.symbols)
    if isinstance(symbols, stringtype):
        symbols = [symbols]
    assert len(symbols) == system.natypes, 'Number of symbols does not match number of atom types'
    
    elements = kwargs.get('elements', [None for i in range(system.natypes)])
    if not isinstance(elements, list):
        elements = [elements]
    assert len(elements) == system.natypes, 'Number of elements does not match number of atom types'
    
    prop_units = kwargs.get('prop_units', {})
    if 'pos' not in prop_units:
        prop_units['pos'] = 'scaled'
    
    # Extract system values
    a = system.box.a
    b = system.box.b
    c = system.box.c
    alpha = system.box.alpha
    beta =  system.box.beta
    gamma = system.box.gamma
    
    # Check for box standard deviations
    if 'a_std' in kwargs and 'b_std' in kwargs and 'c_std' in kwargs:
        errors = True
        a_std = kwargs['a_std']
        b_std = kwargs['b_std']
        c_std = kwargs['c_std']
    else:
        errors = False
        a_std = None
        b_std = None
        c_std = None
    
    # Initialize DataModelDict
    model = DM()
    model['cell'] = cell = DM()
    
    # Test crystal family
    c_family = identifyfamily(system.box)
    if c_family is None:
        c_family = 'triclinic'
    cell[c_family] = DM()
    
    if c_family == 'cubic':
        a_ave = (a + b + c) / 3
        if errors is True:
            a_std_ave = (a_std + b_std + c_std) / 3
        else:
            a_std_ave = None
        
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
    
    elif c_family == 'tetragonal':
        a_ave = (a + b) / 2
        if errors is True:
            a_std_ave = (a_std + b_std) / 2
        else:
            a_std_ave = None
        
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
    
    elif c_family == 'orthorhombic':
        cell[c_family]['a'] = uc.model(a, box_unit, error=a_std)
        cell[c_family]['b'] = uc.model(b, box_unit, error=b_std)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
    
    elif c_family == 'hexagonal':
        a_ave = (a + b) / 2
        if errors is True:
            a_std_ave = (a_std + b_std) / 2
        else:
            a_std_ave = None
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
    
    elif c_family == 'rhombohedral':
        a_ave = (a + b + c) / 3
        alpha_ave = (alpha + beta + gamma) / 3
        if errors is True:
            a_std_ave = (a_std + b_std + c_std) / 3
        else:
            a_std_ave = None
        
        cell[c_family]['a'] = uc.model(a_ave, box_unit, error=a_std_ave)
        cell[c_family]['alpha'] = alpha_ave
    
    elif c_family == 'monoclinic':
        cell[c_family]['a'] = uc.model(a, box_unit, error=a_std)
        cell[c_family]['b'] = uc.model(b, box_unit, error=b_std)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
        cell[c_family]['beta'] = beta
    
    elif c_family == 'triclinic':
        cell[c_family]['a'] = uc.model(a, box_unit, error=a_std)
        cell[c_family]['b'] = uc.model(b, box_unit, error=b_std)
        cell[c_family]['c'] = uc.model(c, box_unit, error=c_std)
        cell[c_family]['alpha'] = alpha
        cell[c_family]['beta'] = beta
        cell[c_family]['gamma'] = gamma
    else:
        raise ValueError('Unknown crystal family')
    
    atype = system.atoms.atype
    aindex = atype - 1
    
    # Build list of atoms and per-atom properties
    for i in range(system.natoms):
        atom = DM()
        
        atom['component'] = int(atype[i])
        
        symbol = symbols[aindex[i]]
        if symbol is not None:
            atom['symbol'] = symbol
        
        element = elements[aindex[i]]
        if element is not None:
            atom['element'] = element
        
        atom['position'] = DM()
        if prop_units['pos'] == 'scaled':
            atom['position']['value'] = list(system.atoms_prop(a_id=i, key='pos', scale=True))
        else:
            atom['position']['value'] = list(uc.get_in_units(system.atoms.pos[i], prop_units['pos']))
        atom['position']['unit'] = prop_units['pos']
        
        for key, unit in iteritems(prop_units):
            if key != 'pos' and key != 'atype':
                value = system.atoms.view[key][i]
                prop = DM()
                prop['name'] = key 
                prop.update(uc.model(value, unit))
                atom.append('property', prop)
        
        model.append('atom', atom)
    
    model = DM([('atomic-system', model)])
    
    # Return DataModelDict or str
    if 'f' not in kwargs:
        if 'format' not in kwargs:
            return model
        elif format.lower() == 'xml':
            return model.xml(indent=indent)
        elif format.lower() == 'json':
            return model.json(indent=indent)
    
    # Write to file
    else:
        f = kwargs['f']
        if 'format' not in kwargs:
            try:
                format = os.path.splitext(f)[1][1:]
            except:
                format = 'json'
        else:
            format = kwargs['format']
        
        if hasattr(f, 'write'):
            if format.lower() == 'xml':
                return model.xml(fp=f, indent=indent)
            elif format.lower() == 'json':
                return model.json(fp=f, indent=indent)
        
        else:
            with open(f, 'w') as fp:
                if format.lower() == 'xml':
                    return model.xml(fp=fp, indent=indent)
                elif format.lower() == 'json':
                    return model.json(fp=fp, indent=indent)
        
    
    return 
def elastic_constants_static(lammps_command, system, potential, mpi_command=None,
                             strainrange=1e-6, etol=0.0, ftol=0.0, maxiter=10000,
                             maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Repeatedly runs the ELASTIC example distributed with LAMMPS until box
    dimensions converge within a tolerance.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    strainrange : float, optional
        The small strain value to apply when calculating the elastic
        constants (default is 1e-6).
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'a_lat'** (*float*) - The relaxed a lattice constant.
        - **'b_lat'** (*float*) - The relaxed b lattice constant.
        - **'c_lat'** (*float*) - The relaxed c lattice constant.
        - **'alpha_lat'** (*float*) - The alpha lattice angle.
        - **'beta_lat'** (*float*) - The beta lattice angle.
        - **'gamma_lat'** (*float*) - The gamma lattice angle.
        - **'E_coh'** (*float*) - The cohesive energy of the relaxed system.
        - **'stress'** (*numpy.array*) - The measured stress state of the
          relaxed system.
        - **'C_elastic'** (*atomman.ElasticConstants*) - The relaxed system's
          elastic constants.
        - **'system_relaxed'** (*atomman.System*) - The relaxed system.
    """
    
    # Convert hexagonal cells to orthorhombic to avoid LAMMPS tilt issues
    if am.tools.ishexagonal(system.box):
        system = system.rotate([[2,-1,-1,0], [0, 1, -1, 0], [0,0,0,1]])
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    # Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='init.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['strainrange'] = strainrange
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Fill in template files
    template_file = 'cij.template'
    lammps_script = 'cij.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    template_file2 = 'potential.template'
    lammps_script2 = 'potential.in'
    with open(template_file2) as f:
        template = f.read()
    with open(lammps_script2, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Pull out initial state
    thermo = output.simulations[0]['thermo']
    pxx0 = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
    pyy0 = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
    pzz0 = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
    pyz0 = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
    pxz0 = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
    pxy0 = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])
    
    # Negative strains
    cij_n = np.empty((6,6))
    for i in range(6):
        j = 1 + i * 2
        # Pull out strained state
        thermo = output.simulations[j]['thermo']
        pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
        pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
        pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
        pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
        pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
        pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])
        
        # Calculate cij_n using stress changes
        cij_n[i] = np.array([pxx - pxx0, pyy - pyy0, pzz - pzz0,
                             pyz - pyz0, pxz - pxz0, pxy - pxy0])/ strainrange
    
    # Positive strains
    cij_p = np.empty((6,6))
    for i in range(6):
        j = 2 + i * 2
        # Pull out strained state
        thermo = output.simulations[j]['thermo']
        pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
        pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
        pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
        pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
        pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
        pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])
        
        # Calculate cij_p using stress changes
        cij_p[i] = -np.array([pxx - pxx0, pyy - pyy0, pzz - pzz0,
                              pyz - pyz0, pxz - pxz0, pxy - pxy0])/ strainrange
    
    # Average symmetric values
    cij = (cij_n + cij_p) / 2
    for i in range(6):
        for j in range(i):
            cij[i,j] = cij[j,i] = (cij[i,j] + cij[j,i]) / 2
    
    # Define results_dict
    results_dict = {}
    results_dict['raw_cij_negative'] = cij_n
    results_dict['raw_cij_positive'] = cij_p
    results_dict['C'] = am.ElasticConstants(Cij=cij)
    
    return results_dict
Пример #33
0
def dump(system, f=None, atom_style='atomic', units='metal',
         float_format='%.13f', return_info=True):
    """
    Write a LAMMPS-style atom data file from a System.
    
    Parameters
    ----------
    system : atomman.System 
        The system to write to the atom data file.
    f : str or file-like object, optional
        File path or file-like object to write the content to.  If not given,
        then the content is returned as a str.
    atom_style : str, optional
        The LAMMPS atom_style option associated with the data file.  Default
        value is 'atomic'.
    units : str, optional
        The LAMMPS units option associated with the data file. Default value
        is 'metal'.
    float_format : str, optional
        c-style formatting string for floating point values.  Default value is
        '%.13f'.
    return_info : bool, optional
        Indicates if the LAMMPS command lines associated with reading in the
        file are to be returned as a str.  Default value is True.
    
    Returns
    -------
    content : str
        The data file contents (returned if f is not given).
    read_info : str
        The LAMMPS input command lines to read the created data file in (returned if return_info is True).
    """
    # Wrap atoms because LAMMPS hates atoms out of bounds in atom data files
    system.wrap()
    
    # Get unit information according to the units style
    units_dict = style.unit(units)
    
    # Generate header info
    content = '\n%i atoms\n' % system.natoms
    content += '%i atom types\n' % system.natypes
    
    # Extract and convert box values
    xlo = uc.get_in_units(system.box.xlo, units_dict['length'])
    xhi = uc.get_in_units(system.box.xhi, units_dict['length'])
    ylo = uc.get_in_units(system.box.ylo, units_dict['length'])
    yhi = uc.get_in_units(system.box.yhi, units_dict['length'])
    zlo = uc.get_in_units(system.box.zlo, units_dict['length'])
    zhi = uc.get_in_units(system.box.zhi, units_dict['length'])
    xy = system.box.xy
    xz = system.box.xz
    yz = system.box.yz
    
    # Write box values
    xf2 = float_format + ' ' + float_format
    content += xf2 % (xlo, xhi) +' xlo xhi\n'
    content += xf2 % (ylo, yhi) +' ylo yhi\n'
    content += xf2 % (zlo, zhi) +' zlo zhi\n'
    if xy != 0.0 or xz != 0.0 or yz != 0.0:
        xf3 = float_format + ' ' + float_format + ' ' + float_format
        content += xf3 % (xy, xz, yz) + ' xy xz yz\n'
    
    # Write atom info
    content += '\nAtoms\n\n'
    prop_info = atoms_prop_info(atom_style, units)
    
    content += dump_table(system, prop_info=prop_info, float_format=float_format)
    
    # Handle velocity information if included
    if 'velocity' in system.atoms_prop():
        
        # Write velocity info
        content += '\nVelocities\n\n'
        prop_info = velocities_prop_info(atom_style, units)
        
        content += dump_table(system, prop_info=prop_info, float_format=float_format)
    
    returns = []
    
    # Save to the file-like object
    if hasattr(f, 'write'):
        f.write(content)
    
    # Save to the file name
    elif f is not None:
        with open(f, 'w') as fp:
            fp.write(content)
    
    # Return as a string
    else:
        returns.append(content)
    
    if return_info is True:
        # Return appropriate units, atom_style, boundary, and read_data LAMMPS commands
        boundary = ''
        for i in range(3):
            if system.pbc[i]:
                boundary += 'p '
            else:
                boundary += 'm '
        
        if isinstance(f, stringtype):
            read_data = 'read_data ' + f
        else:
            read_data = ''
        newline = '\n'
        read_info = newline.join(['# Script and atom data file prepared by atomman package',
                                  '',
                                  'units ' + units,
                                  'atom_style ' + atom_style,
                                  ''
                                  'boundary ' + boundary,
                                  read_data])
        returns.append(read_info)
    
    if len(returns) == 1:
        return returns[0]
    elif len(returns) > 1:
        return tuple(returns)
Пример #34
0
 def model(self, **kwargs):
     """
     Return or set DataModelDict representation of the elastic constants.
     
     Keyword Arguments:
     model -- string or file-like object of json/xml model or DataModelDict.
     unit -- units to give values in. Default is None.
     crystal_system -- crystal system representation. Default is triclinic.
                          
     If model is given, then model is converted into a DataModelDict and the elastic constants are read in if the model contains exactly one 'elastic-constants' branch.
     
     If model is not given, then a DataModelDict for the elastic constants is constructed. The values included will depend on the crystal system, and will be converted to the specified units.   
     """
     
     #Set values if model given
     if 'model' in kwargs:        
         assert len(kwargs) == 1, 'no keyword arguments supported with model reading' 
         model = DM(kwargs['model']).find('elastic-constants')
         
         c_dict = {}
         for C in model['C']:
             key = 'C' + C['ij'][0] + C['ij'][2]
             c_dict[key] = uc.value_unit(C['stiffness'])
         self.Cij = ElasticConstants(**c_dict).Cij 
     
     #Return DataModelDict if model not given
     else:    
         unit = kwargs.pop('unit', None)
         crystal_system = kwargs.pop('crystal_system', 'triclinic')
         assert len(kwargs) == 0, 'Invalid arguments'
         
         model = DM()
         model['elastic-constants'] = DM()
         model['elastic-constants']['C'] = C = []
         
         c = uc.get_in_units(self.Cij, unit)
         c_dict = DM()
         
         if crystal_system == 'cubic':
             c_dict['1 1'] = (c[0,0] + c[1,1] + c[2,2]) / 3
             c_dict['1 2'] = (c[0,1] + c[0,2] + c[1,2]) / 3
             c_dict['4 4'] = (c[3,3] + c[4,4] + c[5,5]) / 3
         
         elif crystal_system == 'hexagonal':
             c_dict['1 1'] = (c[0,0] + c[1,1]) / 2
             c_dict['3 3'] = c[2,2]
             c_dict['1 2'] = (c[0,1] + (c[0,0] - 2*c[5,5])) / 2
             c_dict['1 3'] = (c[0,2] + c[1,2]) / 2
             c_dict['4 4'] = (c[3,3] + c[4,4]) / 2   
                 
         elif crystal_system == 'tetragonal':
             c_dict['1 1'] = (c[0,0] + c[1,1]) / 2
             c_dict['3 3'] = c[2,2]
             c_dict['1 2'] = c[0,1]
             c_dict['1 3'] = (c[0,2] + c[1,2]) / 2
             c_dict['1 6'] = (c[0,5] - c[1,5]) / 2
             c_dict['4 4'] = (c[3,3] + c[4,4]) / 2
             c_dict['6 6'] = c[5,5]
             
         elif crystal_system == 'orthorhombic':
             c_dict['1 1'] = c[0,0]
             c_dict['2 2'] = c[1,1]
             c_dict['3 3'] = c[2,2]
             c_dict['1 2'] = c[0,1]
             c_dict['1 3'] = c[0,2]
             c_dict['2 3'] = c[1,2]
             c_dict['4 4'] = c[3,3]
             c_dict['5 5'] = c[4,4]
             c_dict['6 6'] = c[5,5]   
         
         else:
             c_dict['1 1'] = c[0,0]
             c_dict['1 2'] = c[0,1]
             c_dict['1 3'] = c[0,2]
             c_dict['1 4'] = c[0,3]
             c_dict['1 5'] = c[0,4]
             c_dict['1 6'] = c[0,5]
             c_dict['2 2'] = c[1,1]
             c_dict['2 3'] = c[1,2]
             c_dict['2 4'] = c[1,3]  
             c_dict['2 5'] = c[1,4]
             c_dict['2 6'] = c[1,5]
             c_dict['3 3'] = c[2,2]
             c_dict['3 4'] = c[2,3]
             c_dict['3 5'] = c[2,4]
             c_dict['3 6'] = c[2,5]
             c_dict['4 4'] = c[3,3]
             c_dict['4 5'] = c[3,4]
             c_dict['4 6'] = c[3,5]  
             c_dict['5 5'] = c[4,4]
             c_dict['5 6'] = c[4,5]
             c_dict['6 6'] = c[5,5]
             
         for ij, value in c_dict.iteritems():
             C.append(DM([('stiffness', DM([ ('value', value), 
                                             ('unit', unit)   ])),
                          ('ij', ij)                              ]) )
         return model
Пример #35
0
def integrator_info(integrator=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, 
                    temperature=0.0, randomseed=None, units='metal'):
    """
    Generates LAMMPS commands for velocity creation and fix integrators. 
    
    Keyword Arguments:
    integrator -- string giving the integration method to use. Options are 'npt', 'nvt',
                  'nph', 'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat.
                  Default value is 'nph+l' for temperature = 0, and 'npt' otherwise. 
    p_xx, p_yy, p_zz -- tensile pressures to equilibriate to.  Default value is 0.0 for all. 
    temperature -- temperature to relax at. Default value is 0. 
    randomseed -- random number seed used by LAMMPS for velocity creation and Langevin
                   thermostat. Default value generates a new random integer every time.
    units = LAMMPS units style to use.
    """
    
    #Get lammps units
    lammps_units = lmp.style.unit(units)
    Px = uc.get_in_units(p_xx, lammps_units['pressure'])
    Py = uc.get_in_units(p_yy, lammps_units['pressure'])
    Pz = uc.get_in_units(p_zz, lammps_units['pressure'])
    T = temperature
    
    #Check temperature and set default integrator
    if temperature == 0.0:
        if integrator is None: integrator = 'nph+l'
        assert integrator not in ['npt', 'nvt'], 'npt and nvt cannot run at 0 K'
    elif temperature > 0:
        if integrator is None: integrator = 'npt'
    else:
        raise ValueError('Temperature must be positive')
    
    #Set default randomseed
    if randomseed is None: randomseed = random.randint(1, 900000000)
    
    if   integrator == 'npt':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        Pdamp = 1000 * lmp.style.timestep(units)
        int_info = '\n'.join(['velocity all create %f %i' % (start_temp, randomseed),
                              'fix npt all npt temp %f %f %f &' % (T, T, Tdamp),
                              '                x %f %f %f &' % (Px, Px, Pdamp),
                              '                y %f %f %f &' % (Py, Py, Pdamp),
                              '                z %f %f %f' % (Pz, Pz, Pdamp)])
    
    elif integrator == 'nvt':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        int_info = '\n'.join(['velocity all create %f %i' % (start_temp, randomseed),
                              'fix nvt all nvt temp %f %f %f' % (T, T, Tdamp)])
    
    elif integrator == 'nph':
        Pdamp = 1000 * lmp.style.timestep(units)
        int_info = '\n'.join(['fix nph all nph x %f %f %f &' % (Px, Px, Pdamp),
                              '                y %f %f %f &' % (Py, Py, Pdamp),
                              '                z %f %f %f' % (Pz, Pz, Pdamp)])
    
    elif integrator == 'nve':
        int_info = 'fix nve all nve'
        
    elif integrator == 'nve+l':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        int_info = '\n'.join(['velocity all create %f %i' % (start_temp, randomseed),
                              'fix nve all nve',
                              'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp, randomseed)])
                              
    elif integrator == 'nph+l':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        Pdamp = 1000 * lmp.style.timestep(units)
        int_info = '\n'.join([#'velocity all create %f %i' % (start_temp, randomseed),
                              'fix nph all nph x %f %f %f &' % (Px, Px, Pdamp),
                              '                y %f %f %f &' % (Py, Py, Pdamp),
                              '                z %f %f %f' % (Pz, Pz, Pdamp),
                              'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp, randomseed)])                              
    
    else:
        raise ValueError('Invalid integrator style')
    
    return int_info
Пример #36
0
def strain_system(lammps_command, system, potential, symbols, mpi_command=None, 
                  delta_strain=np.zeros(6), initial_strain=np.zeros(6), 
                  hold_atypes=[], number_of_steps=0,
                  etol=0.0, ftol=1e-5, maxiter=10000, maxeval=100000):
    """
    Applies strains to an atomic system in LAMMPS and relaxes the energy.
    
    Arguments:
    lammps_command -- command for running LAMMPS.
    system -- atomman.System to add the point defect to.
    potential -- atomman.lammps.Potential representation of a LAMMPS implemented potential.
    symbols -- list of element-model symbols for the Potential that correspond to system's atypes.
    
    Keyword Arguments:
    mpi_command -- MPI command for running LAMMPS in parallel. Default value is None (serial run).  
    delta_strain -- strain state in 6 term Voigt to apply incrementally. Default value is all zeros.
    initial_strain -- strain state in 6 term Voigt to apply to the system prior to the incremental delta_strain. 
                      Default value is all zeros.
    number_of_steps -- number of times to apply the incremental delta_strain to the system. Default value is 0.    
    hold_atypes -- list of atom types that should be held fixed (not allowed to relax). Default value is [].    
    etol -- energy tolerance to use for the LAMMPS minimization. Default value is 0.0 (i.e. only uses ftol). 
    ftol -- force tolerance to use for the LAMMPS minimization. Default value is 1e-6.
    maxiter -- the maximum number of iterations for the LAMMPS minimization. Default value is 100000.
    maxeval -- the maximum number of evaluations for the LAMMPS minimization. Default value is 100000.    
    """    
    
    if am.tools.is_int(hold_atypes):
        hold_atypes = np.array([hold_atypes])
    else:
        hold_atypes = np.asarray(hold_atypes)
    
    if len(hold_atypes) == 0:
        hold_info = ''
    else:
        move_atypes = np.setdiff1d(np.arange(1, system.natypes+1), hold_atypes)
        hold_info = '\n'.join(['', '#Fix boundary region atoms',
                               'group           move type ' + ' '.join(np.char.mod('%d', move_atypes)),
                               'group           hold subtract all move',
                               'fix             nomove hold setforce 0.0 0.0 0.0', ''])

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

    #Read LAMMPS input template
    with open('strain_system.template') as f:
        template = f.read()
    
    #Define lammps variables
    lammps_variables = {}
    lammps_variables['atomman_system_info'] = lmp.atom_data.dump(system, 'initial.dat', 
                                                                 units=potential.units, 
                                                                 atom_style=potential.atom_style)
    lammps_variables['atomman_pair_info'] =   potential.pair_info(symbols)
    lammps_variables['hold_info'] =           hold_info
    lammps_variables['energy_tolerance'] =    etol
    lammps_variables['force_tolerance'] =     uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maximum_iterations'] =  maxiter
    lammps_variables['maximum_evaluations'] = maxeval
    lammps_variables['number_of_steps'] =     number_of_steps + 1   
    lammps_variables['delta_strain_xx'] =     delta_strain[0]
    lammps_variables['delta_strain_yy'] =     delta_strain[1]
    lammps_variables['delta_strain_zz'] =     delta_strain[2]
    lammps_variables['delta_strain_xy'] =     delta_strain[5]
    lammps_variables['delta_strain_xz'] =     delta_strain[4]
    lammps_variables['delta_strain_yz'] =     delta_strain[3]
    lammps_variables['initial_strain_xx'] =   initial_strain[0]
    lammps_variables['initial_strain_yy'] =   initial_strain[1]
    lammps_variables['initial_strain_zz'] =   initial_strain[2]
    lammps_variables['initial_strain_xy'] =   initial_strain[5]
    lammps_variables['initial_strain_xz'] =   initial_strain[4]
    lammps_variables['initial_strain_yz'] =   initial_strain[3]
    
    #Write lammps input script
    with open('strain_system.in', 'w') as f:
        f.write('\n'.join(iprPy.tools.fill_template(template, lammps_variables, '<', '>')))

    #run lammps to relax perfect.dat
    output = lmp.run(lammps_command, 'strain_system.in', mpi_command)
    
    #Extract LAMMPS thermo data.
    lammps_step = np.asarray(output.finds('Step'), dtype=int)[1::2]
    E_total =     uc.set_in_units(output.finds('PotEng'), lammps_units['energy']  )[1::2]
    p_xx =        uc.set_in_units(output.finds('Pxx'),    lammps_units['pressure'])[1::2]
    p_yy =        uc.set_in_units(output.finds('Pyy'),    lammps_units['pressure'])[1::2]
    p_zz =        uc.set_in_units(output.finds('Pzz'),    lammps_units['pressure'])[1::2]
    p_xy =        uc.set_in_units(output.finds('Pxy'),    lammps_units['pressure'])[1::2]
    p_xz =        uc.set_in_units(output.finds('Pxz'),    lammps_units['pressure'])[1::2]
    p_yz =        uc.set_in_units(output.finds('Pyz'),    lammps_units['pressure'])[1::2]
    strain_step = np.asarray(output.finds('s_step'), dtype=int)[1::2]
    
    return {'lammps_step':lammps_step, 'E_total':E_total, 'p_xx':p_xx, 'p_yy':p_yy, 'p_zz':p_zz, 
            'p_xy':p_xy, 'p_xz':p_xz, 'p_yz':p_yz, 'strain_step':strain_step}
Пример #37
0
def relax_static(lammps_command, system, potential, mpi_command=None,
                 p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0, p_xz=0.0, p_yz=0.0,
                 dispmult=0.0, etol=0.0, ftol=0.0,  maxiter=10000,
                 maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom'),
                 maxcycles=100, ctol=1e-10):
    """
    Repeatedly runs the ELASTIC example distributed with LAMMPS until box
    dimensions converge within a tolerance.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    p_xy : float, optional
        The value to relax the xy shear pressure component to (default is
        0.0).
    p_xz : float, optional
        The value to relax the xz shear pressure component to (default is
        0.0).
    p_yz : float, optional
        The value to relax the yz shear pressure component to (default is
        0.0).
    dispmult : float, optional
        Multiplier for applying a random displacement to all atomic positions
        prior to relaxing. Default value is 0.0.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    pressure_unit : str, optional
        The unit of pressure to calculate the elastic constants in (default is
        'GPa').
    maxcycles : int, optional
        The maximum number of times the minimization algorithm is called.
        Default value is 100.
    ctol : float, optional
        The relative tolerance used to determine if the lattice constants have
        converged (default is 1e-10).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'relaxed_system'** (*float*) - The relaxed system.
        - **'E_coh'** (*float*) - The cohesive energy of the relaxed system.
        - **'measured_pxx'** (*float*) - The measured x tensile pressure of the
          relaxed system.
        - **'measured_pyy'** (*float*) - The measured y tensile pressure of the
          relaxed system.
        - **'measured_pzz'** (*float*) - The measured z tensile pressure of the
          relaxed system.
        - **'measured_pxy'** (*float*) - The measured xy shear pressure of the
          relaxed system.
        - **'measured_pxz'** (*float*) - The measured xz shear pressure of the
          relaxed system.
        - **'measured_pyz'** (*float*) - The measured yz shear pressure of the
          relaxed system.
    """
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    # Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Save initial configuration as a dump file
    system.dump('atom_dump', f='initial.dump')
    
    # Apply small random distortions to atoms
    system.atoms.pos += dispmult * np.random.rand(*system.atoms.pos.shape) - dispmult / 2
    
    # Initialize parameters
    old_vects = system.box.vects
    converged = False
    
    # Run minimizations up to maxcycles times
    for cycle in range(maxcycles):
        old_system = deepcopy(system)
        
        # Define lammps variables
        lammps_variables = {}
        system_info = system.dump('atom_data', f='init.dat',
                                  units=potential.units,
                                  atom_style=potential.atom_style)
        lammps_variables['atomman_system_info'] = system_info
        lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
        lammps_variables['p_xx'] = uc.get_in_units(p_xx, lammps_units['pressure'])
        lammps_variables['p_yy'] = uc.get_in_units(p_yy, lammps_units['pressure'])
        lammps_variables['p_zz'] = uc.get_in_units(p_zz, lammps_units['pressure'])
        lammps_variables['p_xy'] = uc.get_in_units(p_xy, lammps_units['pressure'])
        lammps_variables['p_xz'] = uc.get_in_units(p_xz, lammps_units['pressure'])
        lammps_variables['p_yz'] = uc.get_in_units(p_yz, lammps_units['pressure'])
        lammps_variables['etol'] = etol
        lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
        lammps_variables['maxiter'] = maxiter
        lammps_variables['maxeval'] = maxeval
        lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
        
        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'
        
        # Write lammps input script
        template_file = 'minbox.template'
        lammps_script = 'minbox.in'
        with open(template_file) as f:
            template = f.read()
        with open(lammps_script, 'w') as f:
            f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
        
        # Run LAMMPS and extract thermo data
        logfile = 'log-' + str(cycle) + '.lammps'
        output = lmp.run(lammps_command, lammps_script, mpi_command, logfile=logfile)
        thermo = output.simulations[0]['thermo']
        
        # Clean up dump files
        os.remove('0.dump')
        last_dump_file = str(thermo.Step.values[-1]) + '.dump'
        renamed_dump_file = 'relax_static-' + str(cycle) + '.dump'
        shutil.move(last_dump_file, renamed_dump_file)
        
        # Load relaxed system
        system = am.load('atom_dump', renamed_dump_file, symbols=system.symbols)
        
        # Test if box dimensions have converged
        if np.allclose(old_vects, system.box.vects, rtol=ctol, atol=0):
            converged = True
            break
        else:
            old_vects = system.box.vects
    
    # Check for convergence
    if converged is False:
        raise RuntimeError('Failed to converge after ' + str(maxcycles) + ' cycles')
    
    # Zero out near-zero tilt factors
    lx = system.box.lx
    ly = system.box.ly
    lz = system.box.lz
    xy = system.box.xy
    xz = system.box.xz
    yz = system.box.yz
    if np.isclose(xy/ly, 0.0, rtol=0.0, atol=1e-10):
        xy = 0.0
    if np.isclose(xz/lz, 0.0, rtol=0.0, atol=1e-10):
        xz = 0.0
    if np.isclose(yz/lz, 0.0, rtol=0.0, atol=1e-10):
        yz = 0.0
    system.box.set(lx=lx, ly=ly, lz=lz, xy=xy, xz=xz, yz=yz)
    system.wrap()
    
    # Build results_dict
    results_dict = {}
    results_dict['dumpfile_initial'] = 'initial.dump'
    results_dict['symbols_initial'] = system.symbols
    results_dict['dumpfile_final'] = renamed_dump_file
    results_dict['symbols_final'] = system.symbols
    results_dict['E_coh'] = uc.set_in_units(thermo.PotEng.values[-1] / system.natoms,
                                       lammps_units['energy'])
                                       
    results_dict['lx'] = uc.set_in_units(lx, lammps_units['length'])
    results_dict['ly'] = uc.set_in_units(ly, lammps_units['length'])
    results_dict['lz'] = uc.set_in_units(lz, lammps_units['length'])
    results_dict['xy'] = uc.set_in_units(xy, lammps_units['length'])
    results_dict['xz'] = uc.set_in_units(xz, lammps_units['length'])
    results_dict['yz'] = uc.set_in_units(yz, lammps_units['length'])
    
    results_dict['measured_pxx'] = uc.set_in_units(thermo.Pxx.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pyy'] = uc.set_in_units(thermo.Pyy.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pzz'] = uc.set_in_units(thermo.Pzz.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pxy'] = uc.set_in_units(thermo.Pxy.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pxz'] = uc.set_in_units(thermo.Pxz.values[-1],
                                                   lammps_units['pressure'])
    results_dict['measured_pyz'] = uc.set_in_units(thermo.Pyz.values[-1],
                                                   lammps_units['pressure'])
    
    return results_dict
Пример #38
0
def dislocationarraystress(lammps_command,
                           system,
                           potential,
                           temperature,
                           mpi_command=None,
                           sigma_xz=0.0,
                           sigma_yz=0.0,
                           runsteps=100000,
                           thermosteps=100,
                           dumpsteps=None,
                           randomseed=None,
                           bwidth=None,
                           rigidbounds=False):
    """
    Applies a constant external shearing stress to a periodic array of
    dislocations atomic system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The bulk system to add the defect to.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    temperature : float
        The temperature to run the simulation at.
    mpi_command : str or None, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sigma_xz : float, optional
        The xz shear stress to apply to the system through the boundary atoms.
        Default value is 0.0.
    sigma_yz : float, optional
        The yz shear stress to apply to the system through the boundary atoms.
        Default value is 0.0.
    runsteps : int, optional
        The total number of steps to run the simulation for.  Default value
        is 100000.
    thermosteps : int, optional
        The system-wide thermo data will be outputted every this many steps.
        Default value is 100.
    dumpsteps : int, optional
        The atomic configurations will be saved to LAMMPS dump files every 
        this many steps.  Default value is equal to runsteps, which will only
        save the initial and final configurations.
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities. Default is
        None which will select a random int between 1 and 900000000.
    bwidth : float, optional
        The thickness of the boundary region. Default value is 10 Angstroms.
    rigidbounds : bool, optional
        If True, the atoms in the boundary regions will be treated as a rigid
        block such that they move as one and do not allow internal atomic
        relaxations.  Default value is False, in which case the boundaries will
        be free surfaces.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file
          for the relaxed base system.
        - **'symbols_base'** (*list of str*) - The list of element-model
          symbols for the Potential that correspond to the base system's
          atypes.
        - **'Stroh_preln'** (*float*) - The pre-logarithmic factor in the 
          dislocation's self-energy expression.
        - **'Stroh_K_tensor'** (*numpy.array of float*) - The energy
          coefficient tensor based on the dislocation's Stroh solution.
        - **'dumpfile_disl'** (*str*) - The filename of the LAMMPS dump file
          for the relaxed dislocation monopole system.
        - **'symbols_disl'** (*list of str*) - The list of element-model
          symbols for the Potential that correspond to the dislocation
          monopole system's atypes.
        - **'E_total_disl'** (*float*) - The total potential energy of the
          dislocation monopole system.
    """
    try:
        # Get script's location if __file__ exists
        script_dir = Path(__file__).parent
    except:
        # Use cwd otherwise
        script_dir = Path.cwd()

    # Set default values
    if bwidth is None:
        bwidth = uc.set_in_units(10, 'angstrom')
    if randomseed is None:
        randomseed = random.randint(1, 900000000)

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

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

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

    lammps_variables['temperature'] = temperature

    stress_unit = lammps_units['force'] + '/' + lammps_units['length'] + '^2'
    lammps_variables['sigma_xz'] = uc.get_in_units(sigma_xz, stress_unit)
    lammps_variables['sigma_yz'] = uc.get_in_units(sigma_yz, stress_unit)
    lammps_variables['dumpsteps'] = dumpsteps
    lammps_variables['runsteps'] = runsteps
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['randomseed'] = randomseed
    lammps_variables['bwidth'] = uc.get_in_units(bwidth,
                                                 lammps_units['length'])
    lammps_variables['tdamp'] = 100 * lmp.style.timestep(potential.units)
    lammps_variables['timestep'] = lmp.style.timestep(potential.units)

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

    # Write lammps input script
    if rigidbounds is True:
        template_file = os.path.join(script_dir,
                                     'dislarray_rigid_stress.template')
    else:
        template_file = os.path.join(script_dir,
                                     'dislarray_free_stress.template')
    lammps_script = 'dislarray_stress.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    thermo = output.simulations[-1]['thermo']
    steps = thermo.Step.values
    times = uc.set_in_units(steps * lmp.style.timestep(potential.units),
                            lammps_units['time'])

    # Read user-defined thermo data
    if output.lammps_date < datetime.date(2016, 8, 1):
        strains_xz = thermo['strain_xz'].values
        strains_yz = thermo['strain_yz'].values
    else:
        strains_xz = thermo['v_strain_xz'].values
        strains_yz = thermo['v_strain_yz'].values

    # Compute average strain rates
    strainrate_xz, b = np.polyfit(times, strains_xz, 1)
    strainrate_yz, b = np.polyfit(times, strains_yz, 1)

    # Extract output values
    results_dict = {}
    results_dict['times'] = times
    results_dict['strains_xz'] = strains_xz
    results_dict['strains_yz'] = strains_yz
    results_dict['strainrate_xz'] = strainrate_xz
    results_dict['strainrate_yz'] = strainrate_yz

    return results_dict
Пример #39
0
def elastic_constants_static(lammps_command,
                             system,
                             potential,
                             mpi_command=None,
                             strainrange=1e-6,
                             etol=0.0,
                             ftol=0.0,
                             maxiter=10000,
                             maxeval=100000,
                             dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Repeatedly runs the ELASTIC example distributed with LAMMPS until box
    dimensions converge within a tolerance.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    strainrange : float, optional
        The small strain value to apply when calculating the elastic
        constants (default is 1e-6).
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'a_lat'** (*float*) - The relaxed a lattice constant.
        - **'b_lat'** (*float*) - The relaxed b lattice constant.
        - **'c_lat'** (*float*) - The relaxed c lattice constant.
        - **'alpha_lat'** (*float*) - The alpha lattice angle.
        - **'beta_lat'** (*float*) - The beta lattice angle.
        - **'gamma_lat'** (*float*) - The gamma lattice angle.
        - **'E_coh'** (*float*) - The cohesive energy of the relaxed system.
        - **'stress'** (*numpy.array*) - The measured stress state of the
          relaxed system.
        - **'C_elastic'** (*atomman.ElasticConstants*) - The relaxed system's
          elastic constants.
        - **'system_relaxed'** (*atomman.System*) - The relaxed system.
    """

    # Convert hexagonal cells to orthorhombic to avoid LAMMPS tilt issues
    if am.tools.ishexagonal(system.box):
        system = system.rotate([[2, -1, -1, 0], [0, 1, -1, 0], [0, 0, 0, 1]])

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

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

    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data',
                              f='init.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['strainrange'] = strainrange
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])

    # Fill in template files
    template_file = 'cij.template'
    lammps_script = 'cij.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    template_file2 = 'potential.template'
    lammps_script2 = 'potential.in'
    with open(template_file2) as f:
        template = f.read()
    with open(lammps_script2, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))

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

    # Pull out initial state
    thermo = output.simulations[0]['thermo']
    pxx0 = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
    pyy0 = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
    pzz0 = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
    pyz0 = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
    pxz0 = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
    pxy0 = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])

    # Negative strains
    cij_n = np.empty((6, 6))
    for i in range(6):
        j = 1 + i * 2
        # Pull out strained state
        thermo = output.simulations[j]['thermo']
        pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
        pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
        pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
        pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
        pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
        pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])

        # Calculate cij_n using stress changes
        cij_n[i] = np.array([
            pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0,
            pxy - pxy0
        ]) / strainrange

    # Positive strains
    cij_p = np.empty((6, 6))
    for i in range(6):
        j = 2 + i * 2
        # Pull out strained state
        thermo = output.simulations[j]['thermo']
        pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
        pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
        pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
        pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
        pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
        pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])

        # Calculate cij_p using stress changes
        cij_p[i] = -np.array([
            pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0,
            pxy - pxy0
        ]) / strainrange

    # Average symmetric values
    cij = (cij_n + cij_p) / 2
    for i in range(6):
        for j in range(i):
            cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2

    # Define results_dict
    results_dict = {}
    results_dict['raw_cij_negative'] = cij_n
    results_dict['raw_cij_positive'] = cij_p
    results_dict['C'] = am.ElasticConstants(Cij=cij)

    return results_dict
Пример #40
0
def data_model(input_dict, results_dict=None):
    """Creates a DataModelDict containing the input and results data"""

    #Create the root of the DataModelDict
    output = DM()
    output['calculation-dislocation-monopole'] = calc = DM()

    #Assign uuid
    calc['calculation'] = DM()
    calc['calculation']['id'] = input_dict['uuid']
    calc['calculation']['script'] = __calc_name__

    calc['calculation']['run-parameter'] = run_params = DM()
    run_params['a-multiplyer'] = input_dict['a_mult']
    run_params['b-multiplyer'] = input_dict['b_mult']
    run_params['c-multiplyer'] = input_dict['c_mult']
    run_params['anneal_temperature'] = input_dict['anneal_temperature']
    run_params['boundary_width'] = input_dict['boundary_width']
    run_params['boundary_shape'] = input_dict['boundary_shape']
    run_params['energy_tolerance'] = input_dict['energy_tolerance']
    run_params['force_tolerance'] = input_dict['force_tolerance']
    run_params['maximum_iterations'] = input_dict['maximum_iterations']
    run_params['maximum_evaluations'] = input_dict['maximum_evaluations']

    #Copy over potential data model info
    calc['potential'] = input_dict['potential']['LAMMPS-potential'][
        'potential']

    #Save info on system file loaded
    system_load = input_dict['load'].split(' ')
    calc['system-info'] = DM()
    calc['system-info']['artifact'] = DM()
    calc['system-info']['artifact']['file'] = os.path.basename(' '.join(
        system_load[1:]))
    calc['system-info']['artifact']['format'] = system_load[0]
    calc['system-info']['artifact']['family'] = input_dict['system_family']
    calc['system-info']['symbols'] = input_dict['symbols']

    calc['elastic-constants'] = input_dict['elastic_constants_model'].find(
        'elastic-constants')

    #Save data model of the initial ucell
    calc['dislocation-monopole-parameters'] = input_dict['dislocation_model'][
        'dislocation-monopole-parameters']

    if results_dict is None:
        calc['status'] = 'not calculated'
    else:
        calc['defect-free-system'] = DM()
        calc['defect-free-system']['artifact'] = DM()
        calc['defect-free-system']['artifact']['file'] = 'base.dat'
        calc['defect-free-system']['artifact']['format'] = 'atom_data'
        old_symbols = input_dict['symbols'][:len(input_dict['symbols']) / 2]
        if len(old_symbols) == 1: old_symbols = old_symbols[0]
        calc['defect-free-system']['symbols'] = old_symbols

        calc['defect-system'] = DM()
        calc['defect-system']['artifact'] = DM()
        calc['defect-system']['artifact']['file'] = 'disl.dump'
        calc['defect-system']['artifact']['format'] = 'atom_dump'
        calc['defect-system']['symbols'] = input_dict['symbols']

        #Save the final cohesive energy
        calc['potential-energy'] = DM([
            ('value',
             uc.get_in_units(results_dict['potential_energy'],
                             input_dict['energy_unit'])),
            ('unit', input_dict['energy_unit'])
        ])
        calc['pre-ln-factor'] = DM([
            ('value',
             uc.get_in_units(
                 results_dict['pre-ln_factor'],
                 input_dict['energy_unit'] + '/' + input_dict['length_unit'])),
            ('unit',
             input_dict['energy_unit'] + '/' + input_dict['length_unit'])
        ])

    return output
Пример #41
0
def main(args):    
    """Main function for running calc_struct_static.py"""

    try:
        infile = args[0]
        try:
            UUID = args[1]
        except:
            UUID = str(uuid.uuid4())
    except:
        raise ValueError('Input file not given')

    #Read in parameters from input file
    input_dict = read_input(infile)

    #Initial parameter setup
    lammps_exe =   input_dict.get('lammps_exe')
    pot_dir =      input_dict.get('potential_dir', '')
    
    symbols =      input_dict.get('symbols') 
    
    u_length =     input_dict.get('length_display_units',   'angstrom')
    u_press =      input_dict.get('pressure_display_units', 'GPa')
    u_energy =     input_dict.get('energy_display_units',   'eV')
    r_min =        input_dict.get('r_min', None)
    r_max =        input_dict.get('r_max', None)
    
    if r_min is None:
        r_min = uc.get_in_units(2.0, 'angstrom')
    else:
        r_min = uc.get_in_units(float(r_min), u_length)
    if r_max is None:
        r_max = uc.get_in_units(5.0, 'angstrom')
    else:
        r_max = uc.get_in_units(float(r_max), u_length)
    steps = int(input_dict.get('steps', 200))

    #read in potential_file
    with open(input_dict['potential_file']) as f:
        potential = lmp.Potential(f, pot_dir)        
        
    #read in prototype_file
    with open(input_dict['crystal_file']) as f:
        try:
            ucell = am.models.crystal(f)[0]
        except:
            f.seek(0)
            ucell = am.models.cif_cell(f)[0]
    
    #Run ecoh_vs_r
    rvals, avals, evals = ecoh_vs_r(lammps_exe, deepcopy(ucell), potential, symbols, rmin=r_min, rmax=r_max, rsteps=steps)
    
    #Use plot to get rough lattice parameter guess, a0, and build ucell
    a0 = avals[np.argmin(evals)]
    cell_0 = ucell.model(symbols=symbols, box_unit='scaled')

    ucell.box_set(a = a0, b = a0 * ucell.box.b / ucell.box.a, c = a0 * ucell.box.c / ucell.box.a, scale=True)

    #Run quick_aCij to refine values
    results = quick_a_Cij(lammps_exe, ucell, potential, symbols)
    
    #Plot Ecoh vs. r
    plt.title('Cohesive Energy vs. Interatomic Spacing')
    plt.xlabel('r (' + u_length + ')')
    plt.ylabel('Cohesive Energy (' + u_energy + '/atom)')
    plt.plot(uc.get_in_units(rvals, u_length), uc.get_in_units(evals, u_energy))
    plt.savefig('Ecoh_vs_r.png')
    plt.close()

    ucell_new = results['ucell_new']
    cell_1 = ucell_new.model(symbols=symbols, box_unit=u_length)
    ecoh = uc.get_in_units(results['ecoh'], u_energy)
    C = results['C']    
    
    output = DataModelDict()
    output['calculation-crystal-phase'] = calc = DataModelDict()
    calc['calculation-id'] = UUID
    
    with open(input_dict['potential_file']) as f:
        potdict = DataModelDict(f) 
    calc['potential'] = potdict['LAMMPS-potential']['potential']
    
    calc['crystal-info'] = DataModelDict()
    calc['crystal-info']['artifact'] = input_dict['crystal_file']
    calc['crystal-info']['symbols'] = symbols
    
    calc['phase-state'] = DataModelDict()
    calc['phase-state']['temperature'] = DataModelDict([('value', 0.0), ('unit', 'K')])
    calc['phase-state']['pressure'] = DataModelDict([('value', 0.0), ('unit', u_press)])
    
    calc['as-constructed-atomic-system'] = cell_0['atomic-system']
    
    calc['relaxed-atomic-system'] = cell_1['atomic-system']
    c_family = cell_1['atomic-system']['cell'].keys()[0]
    
    calc['cohesive-energy'] = DataModelDict([('value', ecoh), ('unit', u_energy)])
    calc['elastic-constants'] = C.model(unit=u_press, crystal_system=c_family)['elastic-constants']

    calc['cohesive-energy-relation'] = DataModelDict()
    calc['cohesive-energy-relation']['r'] = DataModelDict([('value', list(uc.get_in_units(rvals, u_length))), ('unit', u_length)])
    calc['cohesive-energy-relation']['a'] = DataModelDict([('value', list(uc.get_in_units(avals, u_length))), ('unit', u_length)])
    calc['cohesive-energy-relation']['cohesive-energy'] = DataModelDict([('value', list(uc.get_in_units(evals, u_length))), ('unit', u_energy)])
    
    with open('results.json', 'w') as f:
        output.json(fp=f, indent=4)
Пример #42
0
def dump(system, fname, units='metal', atom_style='atomic'):
    """
    Write a LAMMPS-style atom data file from a System.
    
    Argument:
    system -- System to write to the atom data file.
    fname -- name (and location) of file to save data to.
    
    Keyword Arguments:
    atom_style -- LAMMPS atom_style option associated with the data file.  Default is 'atomic'.
    units -- LAMMPS units option associated with the data file. Default is 'metal'.
    
    When the file is written, the units of all property values are automatically converted from atomman's working units to the LAMMPS style units.
    """
    #wrap atoms because LAMMPS hates atoms out of bounds in atom data files
    system.wrap()
    
    #get unit information according to the units style
    units_dict = style.unit(units)
    length = units_dict['length']
    
    #open file
    with open(fname,'w') as f:
    
        #header info
        f.write('\n%i atoms\n' % system.natoms)
        f.write('%i atom types\n' % system.natypes)
        #f.write('%f %f xlo xhi\n' % (uc.get_in_units(system.box.xlo, length), uc.get_in_units(system.box.xhi, length)))
        #f.write('%f %f ylo yhi\n' % (uc.get_in_units(system.box.ylo, length), uc.get_in_units(system.box.yhi, length)))
        #f.write('%f %f zlo zhi\n' % (uc.get_in_units(system.box.zlo, length), uc.get_in_units(system.box.zhi, length)))
        f.write('%s %s xlo xhi\n' % (repr(uc.get_in_units(system.box.xlo, length)), repr(uc.get_in_units(system.box.xhi, length))))
        f.write('%s %s ylo yhi\n' % (repr(uc.get_in_units(system.box.ylo, length)), repr(uc.get_in_units(system.box.yhi, length))))
        f.write('%s %s zlo zhi\n' % (repr(uc.get_in_units(system.box.zlo, length)), repr(uc.get_in_units(system.box.zhi, length))))
        
        #add tilts if tilt values not equal to zero
        if system.box.xy == 0.0 and system.box.xz == 0.0 and system.box.yz == 0.0:
            pass
        else:
            #f.write('%f %f %f xy xz yz\n' % (uc.get_in_units(system.box.xy, length), uc.get_in_units(system.box.xz, length), uc.get_in_units(system.box.yz, length)))
            f.write('%s %s %s xy xz yz\n' % (repr(uc.get_in_units(system.box.xy, length)), repr(uc.get_in_units(system.box.xz, length)), repr(uc.get_in_units(system.box.yz, length))))
        
        #Write atom info
        f.write('\nAtoms\n\n')
        props = style.atom(atom_style)
        
        #Count how many terms are being printed
        all_size = 0
        for v in props.itervalues():
            all_size += v[0]
        
        outarray = np.empty((system.natoms, all_size))
        start = 0
        print_string = ''
        
        #iterate over all properties and set values for printing
        for name, v in props.iteritems():
            size, dim, dtype = v
            try:
                unit = units_dict[dim]
            except:
                unit = None
            
            if np.issubdtype(dtype, 'int64'):
                for i in xrange(size):
                    print_string += ' %i'
            else:
                for i in xrange(size):  
                    print_string += ' %.13e'
            
            if name == 'a_id':
                outarray[:, start:start+size] = np.arange(1, system.natoms+1).reshape((system.natoms, size))
            else:
                outarray[:, start:start+size] = uc.get_in_units(system.atoms_prop(key=name), unit).reshape((system.natoms, size))
            start += size
        print_string = print_string.strip() + '\n'
        
        #iterate over all atoms
        for i in xrange(system.natoms):
            f.write(print_string % tuple(outarray[i]))
        
            
        #Test for velocity info
        v_test = system.atoms_prop(a_id=0, key='velocity')
        if v_test is not None:
            
            #Write velocity info
            f.write('\nVelocities\n\n')
            props = style.velocity(atom_style) 
        
            #Count how many terms are being printed
            all_size = 0
            for v in props.itervalues():
                all_size += v[0]
            
            outarray = np.empty((system.natoms, all_size))
            start = 0
            print_string = ''
            
            #iterate over all properties and set values for printing
            for name, v in props.iteritems():
                size, dim, dtype = v
                try:
                    unit = units_dict[dim]
                except:
                    unit = None

                if np.issubdtype(dtype, 'int64'):
                    for i in xrange(size):
                        print_string += ' %i'
                else:
                    for i in xrange(size):  
                        print_string += ' %.13e'
                
                if name == 'a_id':
                    outarray[:, start:start+size] = np.arange(1, system.natoms+1).reshape((system.natoms, size))
                else:
                    outarray[:, start:start+size] = uc.get_in_units(system.atoms_prop(key=name), unit).reshape((system.natoms, size))
                start += size
            print_string = print_string.strip() + '\n'
            
            #iterate over all atoms
            for i in xrange(system.natoms):
                f.write(print_string % tuple(outarray[i]))
    
    #return appropriate unts, atom_style, boundary, and read_data LAMMPS commands    
    boundary = ''
    for i in xrange(3):
        if system.pbc[i]:
            boundary += 'p '
        else:
            boundary += 'm '    
    
    newline = '\n'
    script = newline.join(['#Script and atom data file prepared by AtomMan package',
                           '',
                           'units ' + units,
                           'atom_style ' + atom_style,
                           ''
                           'boundary ' + boundary,
                           'read_data ' + fname])
    return script 
Пример #43
0
    def E_gsf_vs_x_plot(self,
                        x=None,
                        disregistry=None,
                        figsize=None,
                        length_unit='Å',
                        energyperarea_unit='eV/Å^2'):
        """
        Generates a plot of the stacking fault energy, i.e. misfit energy,
        associated with the disregistry values for each x coordinate.
        
        Parameters
        ----------
        x : numpy.ndarray, optional
            x-coordinates.  Default value is the stored x-coordinates. If x
            or disregistry are not set/given, then the disregistry path will
            not be added.
        disregistry : numpy.ndarray, optional
            (N, 3) shaped array of disregistry vectors at each x-coordinate.
            Default value is the stored disregistry values.  If x
            or disregistry are not set/given, then the disregistry path will
            not be added.
        figsize : tuple or None, optional
            The figure's x,y dimensions.  If None (default), then a figure
            size of (10, 6) will be generated.
        length_unit : str, optional
            The unit of length to display x coordinates in.  Default value is
            'Å'.
        energyperarea_unit : str, optional
            The unit of energy per area to display the stacking fault energies
            in. Default value is 'eV/Å^2'.
            
        Returns
        -------
        matplotlib.pyplot.figure
            The generated figure allowing users to perform additional
            modifications.
        """
        hasvals = True
        # Default values are class properties
        if x is None:
            try:
                x = self.x
            except:
                hasvals = False
        if disregistry is None:
            try:
                disregistry = self.disregistry
            except:
                hasvals = False
        if hasvals:

            if figsize is None:
                figsize = (10, 6)

            gsf = self.gamma.E_gsf(pos=disregistry.dot(self.transform))

            fig = plt.figure(figsize=figsize)
            plt.plot(uc.get_in_units(x, length_unit),
                     uc.get_in_units(gsf, energyperarea_unit))
            plt.xlabel(f'x-coordinate (${length_unit}$)', size='xx-large')
            plt.ylabel(f'Stacking fault energy (${energyperarea_unit}$)',
                       size='xx-large')
            return fig

        else:
            print(
                'x and disregistry must be set/given to check stacking fault energies'
            )
Пример #44
0
def interpolate_contour(system,
                        name,
                        property=None,
                        index=None,
                        magnitude=False,
                        plotxaxis='x',
                        plotyaxis='y',
                        xlim=None,
                        ylim=None,
                        zlim=None,
                        xbins=200,
                        ybins=200,
                        dots=True,
                        czero=True,
                        save=False,
                        show=True,
                        length_unit='angstrom',
                        property_unit=None,
                        cmap='jet'):
    """
    Creates a contour plot of a system's per-atom properties by interpolating
    properties between atoms.
    
    Parameters
    ----------
    system : atomman.System
        The system with the per-atom property that you want to plot.
    name : str
        The name of the per-atom property that you want to plot.
    property : array-like object, optional
        Values for the per-atom property to plot.  If not given, values will
        be taken as the "name" property of system.
    index : int or tuple, optional
        Specifies which component of a multidimensional property to plot.  Not
        needed if the property is scalar.
    magnitude : bool, optional
        If True, plots the per-atom magnitude of a vector property.  Cannot be
        combined with index.  Default value is False.
    plotxaxis : str or array-like object, optional
        Indicates the Cartesian direction associated with the system's atomic
        coordinates to align with the plotting x-axis.  Values are either 3D
        unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes
        directions.  plotxaxis and plotyaxis must be orthogonal.  Default value
        is 'x' = [1, 0, 0].
    plotyaxis : str or array-like object, optional
        Indicates the Cartesian direction associated with the system's atomic
        coordinates to align with the plotting y-axis.  Values are either 3D
        unit vectors, or strings 'x', 'y', or 'z' for the Cartesian axes
        directions.  plotxaxis and plotyaxis must be orthogonal.  Default value
        is 'y' = [0, 1, 0].
    xlim : tuple, optional
        The minimum and maximum coordinates along the plotting x-axis to
        include in the fit.  Values are taken in the specified length_unit.
        If not given, then the limits are set based on min and max atomic
        coordinates along the plotting axis.
    ylim : tuple, optional
        The minimum and maximum coordinates along the plotting y-axis to
        include in the fit.  Values are taken in the specified length_unit.
        If not given, then the limits are set based on min and max atomic
        coordinates along the plotting axis.
    zlim : tuple, optional
        The minimum and maximum coordinates normal to the plotting axes
        (i.e. plotxaxis X plotyaxis) to include in the fit.  Values are taken
        in the specified length_unit.  If not given, then the limits are set
        based on min and max atomic coordinates along the axis.    
    xbins : int, optional
        Specifies the number of interpolation bins to use along the plotting 
        x-axis.  Default value is 200.
    ybins : int, optional
        Specifies the number of interpolation bins to use along the plotting
        y-axis.  Default value is 200.
    dots : bool, optional
        If True, then the positions of the atoms are shown as circles.
        Default value is True.
    czero : bool, optional
        If True, the range of property values will be centered around zero,
        i.e. cmax = -cmin.  If False, cmax and cmin will be independently
        selected using the property values.  Default value is True.
    save : bool, optional
        If True, the generated plot will be saved to "name.png".  Default
        value is False.
    show : bool, optional
        If True, matplotlib.pyplot.show() is called.  Default value is True.
    length_unit : str, optional
        The units of length to use for the plotting x- and y-axes.  Default
        value is 'angstrom'.
    property_unit : str or None, optional
        The units to use for the property value being plotted.  Default value
        is None, in which no unit conversion is applied.
    cmap : str, optional
        The name of the matplotlib colormap to use.  Default value is 'jet'.
    
    Returns
    -------
    intsum : float
        The area integrated sum of the property over the plotted region.
    avsum : float
        The average property value taken across all plotting bins.
    """

    # Give numeric values for str plot axis terms
    if plotxaxis == 'x':
        plotxaxis = [1.0, 0.0, 0.0]
    elif plotxaxis == 'y':
        plotxaxis = [0.0, 1.0, 0.0]
    elif plotxaxis == 'z':
        plotxaxis = [0.0, 0.0, 1.0]

    if plotyaxis == 'x':
        plotyaxis = [1.0, 0.0, 0.0]
    elif plotyaxis == 'y':
        plotyaxis = [0.0, 1.0, 0.0]
    elif plotyaxis == 'z':
        plotyaxis = [0.0, 0.0, 1.0]

    # Build transformation matrix, T, from plot axes.
    plotxaxis = np.asarray(plotxaxis, dtype='float64')
    plotyaxis = np.asarray(plotyaxis, dtype='float64')
    T = axes_check([plotxaxis, plotyaxis, np.cross(plotxaxis, plotyaxis)])

    # Extract positions and transform using T
    pos = uc.get_in_units(np.inner(system.atoms.pos, T), length_unit)

    # Set plot limits
    if xlim is None:
        xlim = (pos[:, 0].min(), pos[:, 0].max())
    if ylim is None:
        ylim = (pos[:, 1].min(), pos[:, 1].max())
    if zlim is None:
        zlim = (pos[:, 2].min(), pos[:, 2].max())

    # Extract property values
    if property is None:
        property = system.atoms.view[name]

    # Handle index
    if index is not None:
        assert magnitude is False, 'index and magnitude cannot be combined'
        if isinstance(index, inttype):
            index = [index]
        else:
            index = list(index)
        for i in index:
            name += '[' + str(i + 1) + ']'
        property = property[tuple([Ellipsis] + index)]

    # Handle magnitude
    elif magnitude is True:
        property = np.linalg.norm(property, axis=1)
        name += '_mag'

    assert property.shape == (
        system.natoms, ), 'property to plot must be a per-atom scalar'

    # Identify only atoms in xlim, ylim, zlim
    in_bounds = ((pos[:, 0] > xlim[0] - 5.) & (pos[:, 0] < xlim[1] + 5.) &
                 (pos[:, 1] > ylim[0] - 5.) & (pos[:, 1] < ylim[1] + 5.) &
                 (pos[:, 2] > zlim[0]) & (pos[:, 2] < zlim[1]))

    # Extract plotting coordinates and values
    x = pos[in_bounds, 0]
    y = pos[in_bounds, 1]
    v = uc.get_in_units(property[in_bounds], property_unit)

    # Generate interpolation grid
    grid, xedges, yedges = grid_interpolate_2d(x,
                                               y,
                                               v,
                                               xbins=xbins,
                                               ybins=ybins,
                                               range=[xlim, ylim])

    # Compute intsum and avsum values
    intsum = np.sum(grid)
    avsum = intsum / (xbins - 1) / (ybins - 1)
    intsum = intsum * (xlim[1] - xlim[0]) / (xbins - 1) * (
        ylim[1] - ylim[0]) / (ybins - 1)

    # Generate a pretty figure of the grid
    fig = prettygrid(grid,
                     xedges,
                     yedges,
                     cmap=cmap,
                     propname=name,
                     czero=czero)

    # Add dots
    if dots:
        adddots(x, y, xedges, yedges)

    # Save, show and close figure
    if save is True:
        plt.savefig(name + '.png', dpi=800)
    if show is True:
        plt.show()
    plt.close(fig)

    return intsum, avsum
Пример #45
0
 def model(self, **kwargs):
     """
     Return or set DataModelDict representation of the elastic constants.
     
     Keyword Arguments:
     model -- string or file-like object of json/xml model or DataModelDict.
     unit -- units to give values in. Default is None.
     crystal_system -- crystal system representation. Default is triclinic.
                          
     If model is given, then model is converted into a DataModelDict and the elastic constants are read in if the model contains exactly one 'elastic-constants' branch.
     
     If model is not given, then a DataModelDict for the elastic constants is constructed. The values included will depend on the crystal system, and will be converted to the specified units.   
     """
     
     #Set values if model given
     if 'model' in kwargs:        
         assert len(kwargs) == 1, 'no keyword arguments supported with model reading' 
         model = DM(kwargs['model']).find('elastic-constants')
         
         c_dict = {}
         for C in model['C']:
             key = 'C' + C['ij'][0] + C['ij'][2]
             c_dict[key] = uc.value_unit(C['stiffness'])
         self.Cij = ElasticConstants(**c_dict).Cij 
     
     #Return DataModelDict if model not given
     else:    
         unit = kwargs.pop('unit', None)
         crystal_system = kwargs.pop('crystal_system', 'triclinic')
         assert len(kwargs) == 0, 'Invalid arguments'
         
         model = DM()
         model['elastic-constants'] = DM()
         model['elastic-constants']['C'] = C = []
         
         c = uc.get_in_units(self.Cij, unit)
         c_dict = DM()
         
         if crystal_system == 'cubic':
             c_dict['1 1'] = (c[0,0] + c[1,1] + c[2,2]) / 3
             c_dict['1 2'] = (c[0,1] + c[0,2] + c[1,2]) / 3
             c_dict['4 4'] = (c[3,3] + c[4,4] + c[5,5]) / 3
         
         elif crystal_system == 'hexagonal':
             c_dict['1 1'] = (c[0,0] + c[1,1]) / 2
             c_dict['3 3'] = c[2,2]
             c_dict['1 2'] = (c[0,1] + (c[0,0] - 2*c[5,5])) / 2
             c_dict['1 3'] = (c[0,2] + c[1,2]) / 2
             c_dict['4 4'] = (c[3,3] + c[4,4]) / 2   
                 
         elif crystal_system == 'tetragonal':
             c_dict['1 1'] = (c[0,0] + c[1,1]) / 2
             c_dict['3 3'] = c[2,2]
             c_dict['1 2'] = c[0,1]
             c_dict['1 3'] = (c[0,2] + c[1,2]) / 2
             c_dict['1 6'] = (c[0,5] - c[1,5]) / 2
             c_dict['4 4'] = (c[3,3] + c[4,4]) / 2
             c_dict['6 6'] = c[5,5]
             
         elif crystal_system == 'orthorhombic':
             c_dict['1 1'] = c[0,0]
             c_dict['2 2'] = c[1,1]
             c_dict['3 3'] = c[2,2]
             c_dict['1 2'] = c[0,1]
             c_dict['1 3'] = c[0,2]
             c_dict['2 3'] = c[1,2]
             c_dict['4 4'] = c[3,3]
             c_dict['5 5'] = c[4,4]
             c_dict['6 6'] = c[5,5]   
         
         else:
             c_dict['1 1'] = c[0,0]
             c_dict['1 2'] = c[0,1]
             c_dict['1 3'] = c[0,2]
             c_dict['1 4'] = c[0,3]
             c_dict['1 5'] = c[0,4]
             c_dict['1 6'] = c[0,5]
             c_dict['2 2'] = c[1,1]
             c_dict['2 3'] = c[1,2]
             c_dict['2 4'] = c[1,3]  
             c_dict['2 5'] = c[1,4]
             c_dict['2 6'] = c[1,5]
             c_dict['3 3'] = c[2,2]
             c_dict['3 4'] = c[2,3]
             c_dict['3 5'] = c[2,4]
             c_dict['3 6'] = c[2,5]
             c_dict['4 4'] = c[3,3]
             c_dict['4 5'] = c[3,4]
             c_dict['4 6'] = c[3,5]  
             c_dict['5 5'] = c[4,4]
             c_dict['5 6'] = c[4,5]
             c_dict['6 6'] = c[5,5]
             
         for ij, value in c_dict.iteritems():
             C.append(DM([('stiffness', DM([ ('value', value), 
                                             ('unit', unit)   ])),
                          ('ij', ij)                              ]) )
         return model
Пример #46
0
def interpolate_contour(system,
                        property,
                        index=None,
                        plotbounds=None,
                        bins=200,
                        dots=True,
                        czero=True,
                        save=False,
                        show=True,
                        length_unit='angstrom',
                        property_unit=None,
                        cmap='jet'):
    """Creates a contour plot of a system's per-atom properties by interpolating between all of the atoms."""

    if save is False and show is False:
        print 'Figure not saved or displayed!'

    values = system.atoms_prop(key=property)
    assert values is not None, 'atomic property not found!'

    name = str(property)
    if index is not None:
        if isinstance(index, (int, long)):
            index = [index]
        else:
            index = list(index)
        for i in index:
            name += '[' + str(i + 1) + ']'
        values = values[[Ellipsis] + index]

    assert values.shape == (
        system.natoms,
    ), 'Identified property[index] must be a per-atom scalar'

    if plotbounds is None:
        plotbounds = np.array([[system.box.xlo, system.box.xhi],
                               [system.box.ylo, system.box.yhi],
                               [system.box.zlo, system.box.zhi]])
        plotbounds = uc.get_in_units(plotbounds, length_unit)

    #Build arrays containing atomic information
    pos = uc.get_in_units(system.atoms_prop(key='pos'), length_unit)

    #identify only atoms in plotbounds
    in_bounds = ((pos[:, 0] > plotbounds[0, 0] - 5.) &
                 (pos[:, 0] < plotbounds[0, 1] + 5.) &
                 (pos[:, 1] > plotbounds[1, 0] - 5.) &
                 (pos[:, 1] < plotbounds[1, 1] + 5.) &
                 (pos[:, 2] > plotbounds[2, 0]) &
                 (pos[:, 2] < plotbounds[2, 1]))

    x = pos[in_bounds, 0]
    y = pos[in_bounds, 1]
    v = values[in_bounds]

    box = plotbounds[:2, :2]
    grid, xedges, yedges = grid_interpolate_2d(x, y, v, bins=bins, range=box)

    intsum = np.sum(grid)
    avsum = intsum / (bins - 1) / (bins - 1)
    intsum = intsum * (plotbounds[0, 1] - plotbounds[0, 0]) / (bins - 1) * (
        plotbounds[1, 1] - plotbounds[1, 0]) / (bins - 1)

    fig = prettygrid(grid,
                     xedges,
                     yedges,
                     cmap=cmap,
                     propname=name,
                     czero=czero)
    if dots:
        adddots(x, y, xedges, yedges)
    if save:
        plt.savefig(name + '.png', dpi=800)
    if show == False:
        plt.close(fig)
    plt.show()

    return intsum, avsum
def disl_relax(lammps_command,
               system,
               potential,
               mpi_command=None,
               annealtemp=0.0,
               randomseed=None,
               etol=0.0,
               ftol=1e-6,
               maxiter=10000,
               maxeval=100000,
               dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Sets up and runs the disl_relax.in LAMMPS script for relaxing a
    dislocation monopole system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    annealtemp : float, optional
        The temperature to perform a dynamic relaxation at. (Default is 0.0,
        which will skip the dynamic relaxation.)
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
        
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The name of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The name of the LAMMPS dump file
          for the relaxed system.
        - **'E_total'** (*float*) - The total potential energy for the
          relaxed system.
    """

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

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

    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data',
                              f='system.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['anneal_info'] = anneal_info(annealtemp, randomseed,
                                                  potential.units)
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = dmax
    lammps_variables['group_move'] = ' '.join(
        np.array(range(1, system.natypes // 2 + 1), dtype=str))

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

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

    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    thermo = output.simulations[-1]['thermo']

    # Extract output values
    results = {}
    results['logfile'] = 'log.lammps'
    results['dumpfile'] = '%i.dump' % thermo.Step.values[-1]
    results['E_total'] = uc.set_in_units(thermo.PotEng.values[-1],
                                         lammps_units['energy'])

    return results
Пример #48
0
def integrator_info(integrator=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, p_xy=0.0,
                    p_xz=0.0, p_yz=0.0, temperature=0.0, randomseed=None,
                    units='metal'):
    """
    Generates LAMMPS commands for velocity creation and fix integrators. 
    
    Parameters
    ----------
    integrator : str or None, optional
        The integration method to use. Options are 'npt', 'nvt', 'nph',
        'nve', 'nve+l', 'nph+l'. The +l options use Langevin thermostat.
        (Default is None, which will use 'nph+l' for temperature == 0, and
        'npt' otherwise.)
    p_xx : float, optional
        The value to relax the x tensile pressure component to (default is
        0.0).
    p_yy : float, optional
        The value to relax the y tensile pressure component to (default is
        0.0).
    p_zz : float, optional
        The value to relax the z tensile pressure component to (default is
        0.0).
    p_xy : float, optional
        The value to relax the xy shear pressure component to (default is
        0.0).
    p_xz : float, optional
        The value to relax the xz shear pressure component to (default is
        0.0).
    p_yz : float, optional
        The value to relax the yz shear pressure component to (default is
        0.0).
    temperature : float, optional
        The temperature to relax at (default is 0.0).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    units : str, optional
        The LAMMPS units style to use (default is 'metal').
    
    Returns
    -------
    str
        The generated LAMMPS input lines for velocity create and fix
        integration commands.
    """
    
    # Get lammps units
    lammps_units = lmp.style.unit(units)
    Px = uc.get_in_units(p_xx, lammps_units['pressure'])
    Py = uc.get_in_units(p_yy, lammps_units['pressure'])
    Pz = uc.get_in_units(p_zz, lammps_units['pressure'])
    Pxy = uc.get_in_units(p_xy, lammps_units['pressure'])
    Pxz = uc.get_in_units(p_xz, lammps_units['pressure'])
    Pyz = uc.get_in_units(p_yz, lammps_units['pressure'])
    T = temperature
    
    # Check temperature and set default integrator
    if temperature == 0.0:
        if integrator is None: integrator = 'nph+l'
        assert integrator not in ['npt', 'nvt'], 'npt and nvt cannot run at 0 K'
    elif temperature > 0:
        if integrator is None: integrator = 'npt'
    else:
        raise ValueError('Temperature must be positive')
    
    # Set default randomseed
    if randomseed is None: randomseed = random.randint(1, 900000000)
    
    if integrator == 'npt':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        Pdamp = 1000 * lmp.style.timestep(units)
        int_info = '\n'.join([
                'velocity all create %f %i' % (start_temp, randomseed),
                'fix npt all npt temp %f %f %f &' % (T, T, Tdamp),
                '                x %f %f %f &' % (Px, Px, Pdamp),
                '                y %f %f %f &' % (Py, Py, Pdamp),
                '                z %f %f %f &' % (Pz, Pz, Pdamp),
                '                xy %f %f %f &' % (Pxy, Pxy, Pdamp),
                '                xz %f %f %f &' % (Pxz, Pxz, Pdamp),
                '                yz %f %f %f' % (Pyz, Pyz, Pdamp),
                ])
    
    elif integrator == 'nvt':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        int_info = '\n'.join([
                'velocity all create %f %i' % (start_temp, randomseed),
                'fix nvt all nvt temp %f %f %f' % (T, T, Tdamp),
                ])
    
    elif integrator == 'nph':
        Pdamp = 1000 * lmp.style.timestep(units)
        int_info = '\n'.join([
                'fix nph all nph x %f %f %f &' % (Px, Px, Pdamp),
                '                y %f %f %f &' % (Py, Py, Pdamp),
                '                z %f %f %f &' % (Pz, Pz, Pdamp),
                '                xy %f %f %f &' % (Pxy, Pxy, Pdamp),
                '                xz %f %f %f &' % (Pxz, Pxz, Pdamp),
                '                yz %f %f %f' % (Pyz, Pyz, Pdamp),
                ])
    
    elif integrator == 'nve':
        int_info = 'fix nve all nve'
        
    elif integrator == 'nve+l':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        int_info = '\n'.join([
                'velocity all create %f %i' % (start_temp, randomseed),
                'fix nve all nve',
                'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp,
                                                           randomseed),
                ])

    elif integrator == 'nph+l':
        start_temp = T*2.+1
        Tdamp = 100 * lmp.style.timestep(units)
        Pdamp = 1000 * lmp.style.timestep(units)
        int_info = '\n'.join([
                'fix nph all nph x %f %f %f &' % (Px, Px, Pdamp),
                '                y %f %f %f &' % (Py, Py, Pdamp),
                '                z %f %f %f &' % (Pz, Pz, Pdamp),
                '                xy %f %f %f &' % (Pxy, Pxy, Pdamp),
                '                xz %f %f %f &' % (Pxz, Pxz, Pdamp),
                '                yz %f %f %f' % (Pyz, Pyz, Pdamp),
                'fix langevin all langevin %f %f %f %i' % (T, T, Tdamp,
                                                           randomseed),
                ])
    else:
        raise ValueError('Invalid integrator style')
    
    return int_info