示例#1
0
    def __init__(self, model=None, name=None, params=None, **kwargs):
        """Initializes a Calculation object for a given style."""
        
        # Initialize subsets used by the calculation
        self.__potential = LammpsPotential(self)
        self.__commands = LammpsCommands(self)
        self.__units = Units(self)
        subsets = (self.commands, self.potential, self.units)

        # Initialize unique calculation attributes
        self.symbols = None
        self.number_of_steps_r = 100
        self.minimum_r = uc.set_in_units(0.5, 'angstrom')
        self.maximum_r = uc.set_in_units(6.0, 'angstrom')
        self.number_of_steps_theta = 100
        self.minimum_theta = 1.0
        self.maximum_theta = 180.0
        
        self.cluster = None
        self.results_file = None
        self.results_length_unit = None
        self.results_energy_unit = None

        # Define calc shortcut
        self.calc = bond_angle_scan

        # Call parent constructor
        super().__init__(model=model, name=name, params=params,
                         subsets=subsets, **kwargs)
示例#2
0
    def __init__(self, model=None, name=None, params=None, **kwargs):
        """Initializes a Calculation object for a given style."""

        # Initialize subsets used by the calculation
        self.__potential = LammpsPotential(self)
        self.__commands = LammpsCommands(self)
        self.__units = Units(self)
        subsets = (self.commands, self.potential, self.units)

        # Initialize unique calculation attributes
        self.symbols = []
        self.number_of_steps_r = 300
        self.minimum_r = uc.set_in_units(0.02, 'angstrom')
        self.maximum_r = uc.set_in_units(6.0, 'angstrom')
        self.r_values = None
        self.energy_values = None

        # Define calc shortcut
        self.calc = diatom_scan

        # Call parent constructor
        super().__init__(model=model,
                         name=name,
                         params=params,
                         subsets=subsets,
                         **kwargs)
示例#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 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()
示例#5
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
示例#6
0
def peierlsnabarro(alat, C, axes, burgers, gamma,
                   cutofflongrange=uc.set_in_units(1000, 'angstrom'),
                   tau=np.zeros((3,3)), alpha=[0.0], beta=np.zeros((3,3)),
                   cdiffelastic=False, cdiffsurface=True, cdiffstress=False,
                   fullstress=True,
                   halfwidth=uc.set_in_units(1, 'angstrom'),
                   normalizedisreg=True,
                   xnum=None, xmax=None, xstep=None,
                   min_method='Powell', min_options={}):
    """
    Solves a Peierls-Nabarro dislocation model.
    """
    
    # Solve Stroh method for dislocation
    stroh = am.defect.Stroh(C, burgers, axes=axes)
    Kij = stroh.K_tensor
    
    # Transform burgers to axes
    T = am.tools.axes_check(axes)
    b = T.dot(burgers)
    
    # Scale xmax and xstep by alat
    if xmax is not None:
        xmax *= alat
    if xstep is not None:
        xstep *= alat
    
    # Generate initial disregistry guess
    x, idisreg = pn_arctan_disregistry(xmax=xmax, xstep=xstep, xnum=xnum,
                                       burgers=b, halfwidth=halfwidth,
                                       normalize=normalizedisreg)
    
    # Minimize disregistry
    pnsolution = SDVPN(x, idisreg, gamma, axes, Kij,
                       tau=tau, alpha=alpha, beta=beta,
                       cutofflongrange=cutofflongrange,
                       burgers=b,
                       fullstress=fullstress,
                       cdiffelastic=cdiffelastic,
                       cdiffsurface=cdiffsurface,
                       cdiffstress=cdiffstress,
                       min_method=min_method,
                       min_options=min_options)
    
    # Initialize results dict
    results_dict = {}
    results_dict['SDVPN_solution'] = pnsolution
    
    return results_dict
示例#7
0
    def cal_screw_const(self, tag='intro'):
        axes = np.array([[1, 1, -2], [-1, 1, 0], [1, 1, 1]])

        alat = uc.set_in_units(self.pot['lattice'], 'angstrom')
        C11 = uc.set_in_units(self.pot['c11'], 'GPa')
        C12 = uc.set_in_units(self.pot['c12'], 'GPa')
        C44 = uc.set_in_units(self.pot['c44'], 'GPa')

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

        stroh = am.defect.Stroh(c, burgers, axes=axes)
        print("K tensor", stroh.K_tensor)
        print("K (biKijbj)", stroh.K_coeff, "eV/A")
        print("pre-ln alpha = biKijbj/4pi", stroh.preln, "ev/A")
示例#8
0
文件: Phonon.py 项目: lmhale99/iprPy
    def __init__(self, model=None, name=None, params=None, **kwargs):
        """Initializes a Calculation object for a given style."""
        
        # Initialize subsets used by the calculation
        self.__potential = LammpsPotential(self)
        self.__commands = LammpsCommands(self)
        self.__units = Units(self)
        self.__system = AtommanSystemLoad(self)
        self.__system_mods = AtommanSystemManipulate(self)
        subsets = (self.commands, self.potential, self.system,
                   self.system_mods, self.units)

        # Initialize unique calculation attributes
        self.strainrange = 0.01
        self.displacementdistance = uc.set_in_units(0.01, 'angstrom')
        self.symmetryprecision = 1e-5         
        self.numstrains = 5 
        self.__bandstructure = None
        self.__dos = None
        self.__thermal = None
        self.__volumescan = None
        self.__E0 = None
        self.__B0 = None
        self.__B0prime = None
        self.__V0 = None
        self.__phonons = None
        self.__qha = None
        
        # Define calc shortcut
        self.calc = phonon_quasiharmonic

        # Call parent constructor
        super().__init__(model=model, name=name, params=params,
                         subsets=subsets, **kwargs)
示例#9
0
    def __init__(self, parent, prefix='', templateheader=None,
                 templatedescription=None):
        """
        Initializes a calculation record subset object.

        Parameters
        ----------
        parent : iprPy.calculation.Calculation
            The parent calculation object that the subset object is part of.
            This allows for the subset methods to access parameters set to the
            calculation itself or other subsets.
        prefix : str, optional
            An optional prefix to add to metadata field names to allow for
            differentiating between multiple subsets of the same style within
            a single record
        templateheader : str, optional
            An alternate header to use in the template file for the subset.
        templatedescription : str, optional
            An alternate description of the subset for the templatedoc.
        """
        super().__init__(parent, prefix=prefix, templateheader=templateheader,
                         templatedescription=templatedescription)

        self.energytolerance = 0.0
        self.forcetolerance = 0.0
        self.maxiterations = 100000
        self.maxevaluations = 1000000
        self.maxatommotion = uc.set_in_units(0.01, 'angstrom') 
示例#10
0
 def test_scalar_model(self):
     unit = 'mJ/s^2'
     v = 1234.214
     value = uc.set_in_units(v, unit)
     model = uc.model(value, unit)
     value2 = uc.value_unit(model)
     assert pytest.approx(value, value2)
示例#11
0
 def test_tensor_model(self):
     unit = 'mJ/s^2'
     v = np.array([[1234.214, 346.23], [109.124, 235.781]])
     value = uc.set_in_units(v, unit)
     model = uc.model(value, unit)
     value2 = uc.value_unit(model)
     assert np.allclose(value, value2)
示例#12
0
 def test_vector_model(self):
     unit = 'mJ/s^2'
     v = np.array([1234.214, 346.23])
     value = uc.set_in_units(v, unit)
     model = uc.model(value, unit)
     value2 = uc.value_unit(model)
     assert np.allclose(value, value2)
示例#13
0
def value(input_dict, key, default_unit=None, default_term=None):
    """
    Converts a string dictionary value into a float with proper unit conversion.
    
    The string can either be:
        a number
        a number and unit separated by a single space
    
    Keyword Arguments:
    input_dict -- a dictionary
    key -- the key for the value in input_dict
    default_unit -- unit for the value if not specified in the string.
    default_term -- string of the value (and unit) to use if key is not in input_dict.
    
    Note that the unit in default_term does not have to correspond to default_unit.
    This allows for default values to be constant regardless of preferred units.
    
    returns the value as a float in atomman's working units.
    """
    term = input_dict.get(key, default_term)

    try:
        i = term.strip().index(' ')
        value = float(term[:i])
        unit = term[i + 1:]
    except:
        value = float(term)
        unit = default_unit

    return uc.set_in_units(value, unit)
示例#14
0
    def __init__(self, model=None, name=None, params=None, **kwargs):
        """Initializes a Calculation object for a given style."""
        
        # Initialize subsets used by the calculation
        self.__units = Units(self)
        self.__system = AtommanSystemLoad(self)
        subsets = (self.system, self.units)

        # Initialize unique calculation attributes
        self.primitivecell = False
        self.idealcell = True
        self.symmetryprecision = uc.set_in_units(0.01, 'angstrom')
        self.__pearson = None
        self.__number = None
        self.__international = None
        self.__schoenflies = None
        self.__wyckoffs = None
        self.__wyckoff_fingerprint = None
        self.__spg_ucell = None

        # Define calc shortcut
        self.calc = crystal_space_group

        # Call parent constructor
        super().__init__(model=model, name=name, params=params,
                         subsets=subsets, **kwargs)
示例#15
0
def dumbbell(system, atype=None, pos=None, ptd_id=None, db_vect=None, scale=False, atol=None):
    """
    Returns a new System where a dumbbell interstitial point defect has been inserted.
    
    Keyword Arguments:
    system -- base System that the defect is added to.    
    atype -- atom type for the atom in the dumbbell pair being added to the system.
    pos -- position of the system atom where the dumbbell pair is added.
    ptd_id -- id of the system atom where the dumbbell pair is added.  Alternative to using pos.
    db_vect -- vector associated with the dumbbell interstitial.
    scale -- indicates if pos and db_vect are absolute (False) or box-relative (True). Default is False.
    
    Adds atom property old_id if it doesn't already exist that tracks the original atom ids.
    """ 
    
    pos_list = system.atoms.view['pos']
    
    #if pos is supplied, use isclose and where to identify the id of the atom at pos
    if pos is not None:
        if atol is None: 
            atol = uc.set_in_units(1e-3, 'angstrom')
        if scale:
            pos = system.unscale(pos)
        assert ptd_id is None,                                                      'pos and ptd_id cannot both be supplied'
        ptd_id = np.where(np.isclose(pos_list, pos, atol=atol).all(axis=1))
        assert len(ptd_id) == 1 and len(ptd_id[0]) == 1,                            'Unique atom at pos not identified'
        ptd_id = long(ptd_id[0][0])
    
    #test that ptd_id is a valid entry
    try:
        pos = pos_list[ptd_id]
    except:
        raise TypeError('Invalid ptd_id')
    
    assert isinstance(atype, (int, long)) and atype > 0,                            'atype must be a positive integer'   
    
    #unscale db_vect if scale is True
    if scale:
        db_vect = system.unscale(db_vect)
    
    #create new system and copy values over
    d_system = am.System(box=system.box, pbc=system.pbc, atoms=am.Atoms(natoms=system.natoms+1))
    for prop in system.atoms_prop():
        view = system.atoms.view[prop]
        value = np.asarray(np.vstack(( view[:ptd_id], view[ptd_id+1:], view[ptd_id], np.zeros_like(view[0]) )), dtype=system.atoms.dtype[prop])
        d_system.atoms_prop(key=prop, value=value)
        
    d_system.atoms_prop(a_id=d_system.natoms-1, key='atype', value=atype)
    d_system.atoms_prop(a_id=d_system.natoms-2, key='pos',   value=pos-db_vect)
    d_system.atoms_prop(a_id=d_system.natoms-1, key='pos',   value=pos+db_vect)
    
    #add property old_id with each atom's original id
    if d_system.atoms_prop(a_id=0, key='old_id') is None:
        d_system.atoms_prop(key='old_id', value=np.hstack(( np.arange(0, ptd_id), np.arange(ptd_id+1, system.natoms), ptd_id, system.natoms)), dtype='int32')
    else:
        old_id = max(system.atoms_prop(key='old_id')) + 1
        d_system.atoms_prop(a_id=d_system.natoms-1, key='old_id', value=old_id)
    
    return d_system
示例#16
0
    def __init__(self, model=None, name=None, params=None, **kwargs):
        """Initializes a Calculation object for a given style."""

        # Initialize subsets used by the calculation
        self.__units = Units(self)
        self.__system = AtommanSystemLoad(self)
        self.__defect = Dislocation(self)
        self.__elastic = AtommanElasticConstants(self)
        self.__gamma = AtommanGammaSurface(self)
        subsets = (self.system, self.elastic, self.gamma, self.defect,
                   self.units)

        # Initialize unique calculation attributes
        self.xnum = None
        self.xmax = None
        self.xstep = None
        self.xscale = False
        self.minimize_style = 'Powell'
        self.minimize_options = {}
        self.minimize_cycles = 10
        self.cutofflongrange = uc.set_in_units(1000, 'angstrom')
        self.tau = np.zeros((3, 3))
        self.alpha = 0.0
        self.beta = np.zeros((3, 3))
        self.cdiffelastic = False
        self.cdiffsurface = True
        self.cdiffstress = False
        self.halfwidth = uc.set_in_units(1, 'angstrom')
        self.normalizedisreg = True
        self.fullstress = True

        self.__sdvpn_solution = None
        self.__energies = None
        self.__disregistries = None

        # Define calc shortcut
        self.calc = sdvpn

        # Call parent constructor
        super().__init__(model=model,
                         name=name,
                         params=params,
                         subsets=subsets,
                         **kwargs)
示例#17
0
def ecoh_vs_r(lammps_exe, ucell, potential, symbols, rmin=2.0, rmax=5.0, rsteps=200):
    """Measure cohesive energy of a crystal as a function of nearest neighbor distance, r0"""
    
    #Initial size setup
    r_a = r_a_ratio(ucell)
    amin = rmin / r_a
    amax = rmax / r_a
    alat0 = (amax + amin) / 2.
    delta = (amax-amin)/alat0
    
    #Create unit cell with a = alat0
    ucell.box_set(a = alat0, b = alat0 * ucell.box.b / ucell.box.a, c = alat0 * ucell.box.c / ucell.box.a, scale=True)

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

    #Write the LAMMPS input file
    with open('alat.in','w') as script:
        script.write(alat_script(system_info, pair_info, delta=delta, steps=rsteps))
    
    #Run LAMMPS
    data = lmp.run(lammps_exe, 'alat.in')
 
    #extract thermo data from log output 
    avalues = np.array(data.finds('Lx')) / 3.
    rvalues = avalues * r_a
    evalues = np.array(data.finds('peatom'))  
        
    #Use potential units and atom_style terms to convert values to appropriate length and energy units
    lmp_units = lmp.style.unit(potential.units)
    avalues = uc.set_in_units(avalues, lmp_units['length'])
    rvalues = uc.set_in_units(rvalues, lmp_units['length'])
    evalues = uc.set_in_units(evalues, lmp_units['energy'])
        
    return rvalues, avalues, evalues    
示例#18
0
def energy_check(lammps_command: str,
                 system: am.System,
                 potential: lmp.Potential,
                 mpi_command: Optional[str] = None) -> dict:
    """
    Performs a quick run 0 calculation to evaluate the potential energy of a
    configuration.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The atomic configuration to evaluate.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        - **'E_pot'** (*float*) - The per-atom potential energy of the system.
    """
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='init.dat',
                              potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info

    # Fill in lammps input script
    template = read_calc_file('iprPy.calculation.energy_check', 'run0.template')
    script = filltemplate(template, lammps_variables, '<', '>')
    
    # Run LAMMPS
    output = lmp.run(lammps_command, script=script,
                     mpi_command=mpi_command, logfile=None)
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    results = {}
    results['E_pot'] = uc.set_in_units(thermo.v_peatom.values[-1],
                                       lammps_units['energy'])
    
    return results
    
示例#19
0
def vacancy(system, pos=None, ptd_id=None, scale=False, atol=None):
    """
    Returns a new System where a vacancy point defect has been inserted.
    
    Keyword Arguments:
    system -- base System that the defect is added to.
    pos -- position of the atom to be removed.
    ptd_id -- id of the atom to be removed.  Alternative to using pos.
    scale -- if pos is given, indicates if pos is absolute (False) or box-relative (True). Default is False.
    
    Adds atom property old_id if it doesn't already exist that tracks the original atom ids.
    """

    pos_list = system.atoms.view['pos']

    #if pos is supplied, use isclose and where to identify the id of the atom at pos
    if pos is not None:
        if atol is None:
            atol = uc.set_in_units(1e-3, 'angstrom')
        if scale:
            pos = system.unscale(pos)
        assert ptd_id is None, 'pos and ptd_id cannot both be supplied'
        ptd_id = np.where(np.isclose(pos_list, pos, atol=atol).all(axis=1))
        assert len(ptd_id) == 1 and len(
            ptd_id[0]) == 1, 'Unique atom at pos not identified'
        ptd_id = long(ptd_id[0][0])

    #test that ptd_id is a valid entry
    try:
        pos = pos_list[ptd_id]
    except:
        raise TypeError('Invalid ptd_id')

    #create new system and copy values over
    d_system = am.System(box=system.box,
                         pbc=system.pbc,
                         atoms=am.Atoms(natoms=system.natoms - 1))
    for prop in system.atoms_prop():
        view = system.atoms.view[prop]
        value = np.asarray(np.vstack((view[:ptd_id], view[ptd_id + 1:])),
                           dtype=system.atoms.dtype[prop])
        d_system.atoms_prop(key=prop, value=value)

    #add property old_id with each atom's original id
    if d_system.atoms_prop(key='old_id') is None:
        d_system.atoms_prop(key='old_id',
                            value=np.hstack((np.arange(0, ptd_id),
                                             np.arange(ptd_id + 1,
                                                       system.natoms))),
                            dtype='int32')

    return d_system
示例#20
0
def interstitial(system, atype=None, pos=None, scale=False, atol=None):
    """
    Returns a new System where a positional interstitial point defect has been inserted.
    
    Keyword Arguments:
    system -- base System that the defect is added to.
    atype -- atom type for the interstitial atom.
    pos -- position for adding the interstitial atom.
    scale -- if pos is given, indicates if pos is absolute (False) or box-relative (True). Default is False.
    
    Adds atom property old_id if it doesn't already exist that tracks the original atom ids.
    """

    pos_list = system.atoms.view['pos']

    if atol is None:
        atol = uc.set_in_units(1e-3, 'angstrom')
    if scale:
        pos = system.unscale(pos)

    #Use isclose and where to check that no atoms are already at pos
    ptd_id = np.where(np.isclose(pos_list, pos, atol=atol).all(axis=1))
    assert len(ptd_id) == 1 and len(ptd_id[0]) == 0, 'atom already at pos'

    assert isinstance(
        atype, (int, long)) and atype > 0, 'atype must be a positive integer'

    #create new system and copy values over
    d_system = am.System(box=system.box,
                         pbc=system.pbc,
                         atoms=am.Atoms(natoms=system.natoms + 1))
    for prop in system.atoms_prop():
        view = system.atoms.view[prop]
        value = np.asarray(np.vstack((view, np.zeros_like(view[0]))),
                           dtype=system.atoms.dtype[prop])
        d_system.atoms_prop(key=prop, value=value)
    d_system.atoms_prop(a_id=d_system.natoms - 1, key='atype', value=atype)
    d_system.atoms_prop(a_id=d_system.natoms - 1, key='pos', value=pos)

    #add property old_id with each atom's original id
    if d_system.atoms_prop(key='old_id') is None:
        d_system.atoms_prop(key='old_id',
                            value=np.arange(d_system.natoms),
                            dtype='int32')
    else:
        old_id = max(system.atoms_prop(key='old_id')) + 1
        d_system.atoms_prop(a_id=d_system.natoms - 1,
                            key='old_id',
                            value=old_id)

    return d_system
示例#21
0
def stackingfaultworker(lammps_command,
                        system,
                        potential,
                        shiftvector1,
                        shiftvector2,
                        shiftfraction1,
                        shiftfraction2,
                        mpi_command=None,
                        cutboxvector=None,
                        faultpos=0.5,
                        etol=0.0,
                        ftol=0.0,
                        maxiter=10000,
                        maxeval=100000,
                        dmax=uc.set_in_units(0.01, 'angstrom'),
                        lammps_date=None):
    """
    A wrapper function around stackingfaultpoint. Converts
    shiftfractions and shiftvectors to a faultshift, runs stackingfaultpoint,
    and adds keys 'shift1' and 'shift2' to the returned dictionary
    corresponding to the shiftfractions.
    """

    # Compute the faultshift
    faultshift = shiftfraction1 * shiftvector1 + shiftfraction2 * shiftvector2

    # Name the simulation directory based on shiftfractions
    sim_directory = 'a%.10f-b%.10f' % (shiftfraction1, shiftfraction2)

    # Evaluate the system at the shift
    sf = stackingfaultpoint(lammps_command,
                            system,
                            potential,
                            mpi_command=mpi_command,
                            cutboxvector=cutboxvector,
                            faultpos=faultpos,
                            etol=etol,
                            ftol=ftol,
                            maxiter=maxiter,
                            maxeval=maxeval,
                            dmax=dmax,
                            faultshift=faultshift,
                            sim_directory=sim_directory,
                            lammps_date=lammps_date)

    # Add shiftfractions to sf results
    sf['shift1'] = shiftfraction1
    sf['shift2'] = shiftfraction2

    return sf
示例#22
0
def value(input_dict: dict,
          key: str,
          default_unit: Optional[str] = None,
          default_term: Optional[str] = None) -> float:
    """
    Interprets a calculation parameter by converting it from a string to a
    float in working units.
        
    The parameter being converted is a str with one of two formats:
    - '<number>'
    - '<number> <unit>'
    
    Parameters
    ----------
    input_dict : dict
        Dictionary containing input parameter key-value pairs.
    key : str
        The key of input_dict to evaluate.
    default_unit : str, optional
        Default unit to use if not specified in the parameter value.  If not
        given, then no unit conversion will be done on unitless parameter
        values.
    default_term : str, optional
        Default str parameter value to use if key not in input_dict.  Can be
        specified as '<value> <unit>' to ensure that the default value is 
        always the same regardless of the working units or default_unit.  If
        not given, then key must be in input_dict.
    
    Returns
    -------
    float
        The interpreted value of the input parameter's str value in the
        working units.
    """
    term = input_dict.get(key, default_term)

    try:
        i = term.strip().index(' ')
        value = float(term[:i])
        unit = term[i + 1:]
    except:
        value = float(term)
        unit = default_unit

    return uc.set_in_units(value, unit)
示例#23
0
def vacancy(system, pos=None, ptd_id=None, scale=False, atol=None):
    """
    Returns a new System where a vacancy point defect has been inserted.
    
    Keyword Arguments:
    system -- base System that the defect is added to.
    pos -- position of the atom to be removed.
    ptd_id -- id of the atom to be removed.  Alternative to using pos.
    scale -- if pos is given, indicates if pos is absolute (False) or box-relative (True). Default is False.
    
    Adds atom property old_id if it doesn't already exist that tracks the original atom ids.
    """ 
    
    pos_list = system.atoms.view['pos']
    
    #if pos is supplied, use isclose and where to identify the id of the atom at pos
    if pos is not None:
        if atol is None: 
            atol = uc.set_in_units(1e-3, 'angstrom')
        if scale:
            pos = system.unscale(pos)
        assert ptd_id is None,                                                      'pos and ptd_id cannot both be supplied'
        ptd_id = np.where(np.isclose(pos_list, pos, atol=atol).all(axis=1))
        assert len(ptd_id) == 1 and len(ptd_id[0]) == 1,                            'Unique atom at pos not identified'
        ptd_id = long(ptd_id[0][0])
    
    #test that ptd_id is a valid entry
    try:
        pos = pos_list[ptd_id]
    except:
        raise TypeError('Invalid ptd_id')
    
    #create new system and copy values over
    d_system = am.System(box=system.box, pbc=system.pbc, atoms=am.Atoms(natoms=system.natoms-1))
    for prop in system.atoms_prop():
        view = system.atoms.view[prop]
        value = np.asarray(np.vstack(( view[:ptd_id], view[ptd_id+1:] )), dtype=system.atoms.dtype[prop])
        d_system.atoms_prop(key=prop, value=value)
    
    #add property old_id with each atom's original id
    if d_system.atoms_prop(key='old_id') is None:
        d_system.atoms_prop(key='old_id', value=np.hstack(( np.arange(0, ptd_id), np.arange(ptd_id+1, system.natoms) )), dtype='int32')
    
    return d_system
示例#24
0
def interstitial(system, atype=None, pos=None, scale=False, atol=None):
    """
    Returns a new System where a positional interstitial point defect has been inserted.
    
    Keyword Arguments:
    system -- base System that the defect is added to.
    atype -- atom type for the interstitial atom.
    pos -- position for adding the interstitial atom.
    scale -- if pos is given, indicates if pos is absolute (False) or box-relative (True). Default is False.
    
    Adds atom property old_id if it doesn't already exist that tracks the original atom ids.
    """  
  
    pos_list = system.atoms.view['pos']
    
    if atol is None: 
        atol = uc.set_in_units(1e-3, 'angstrom')
    if scale:
        pos = system.unscale(pos)

    #Use isclose and where to check that no atoms are already at pos
    ptd_id = np.where(np.isclose(pos_list, pos, atol=atol).all(axis=1))
    assert len(ptd_id) == 1 and len(ptd_id[0]) == 0,                                'atom already at pos'
    
    assert isinstance(atype, (int, long)) and atype > 0,                            'atype must be a positive integer'   
    
    #create new system and copy values over
    d_system = am.System(box=system.box, pbc=system.pbc, atoms=am.Atoms(natoms=system.natoms+1))
    for prop in system.atoms_prop():
        view = system.atoms.view[prop]
        value = np.asarray(np.vstack(( view, np.zeros_like(view[0]) )), dtype=system.atoms.dtype[prop])
        d_system.atoms_prop(key=prop, value=value)
    d_system.atoms_prop(a_id=d_system.natoms-1, key='atype', value=atype)
    d_system.atoms_prop(a_id=d_system.natoms-1, key='pos',   value=pos)
    
    #add property old_id with each atom's original id
    if d_system.atoms_prop(key='old_id') is None:
        d_system.atoms_prop(key='old_id', value=np.arange(d_system.natoms), dtype='int32')
    else:
        old_id = max(system.atoms_prop(key='old_id')) + 1
        d_system.atoms_prop(a_id=d_system.natoms-1, key='old_id', value=old_id)
    
    return d_system
示例#25
0
文件: value.py 项目: usnistgov/iprPy
def value(input_dict, key, default_unit=None, default_term=None):
    """
    Interprets a calculation parameter by converting it from a string to a
    float in working units.
        
    The parameter being converted is a str with one of two formats:
    - '<number>'
    - '<number> <unit>'
    
    Parameters
    ----------
    input_dict : dict
        Dictionary containing input parameter key-value pairs.
    key : str
        The key of input_dict to evaluate.
    default_unit : str, optional
        Default unit to use if not specified in the parameter value.  If not
        given, then no unit conversion will be done on unitless parameter
        values.
    default_term : str, optional
        Default str parameter value to use if key not in input_dict.  Can be
        specified as '<value> <unit>' to ensure that the default value is 
        always the same regardless of the working units or default_unit.  If
        not given, then key must be in input_dict.
    
    Returns
    -------
    float
        The interpreted value of the input parameter's str value in the
        working units.
    """
    term = input_dict.get(key, default_term)
    
    try:
        i = term.strip().index(' ')
        value = float(term[:i])
        unit = term[i+1:]
    except:
        value = float(term)
        unit = default_unit
    
    return uc.set_in_units(value, unit)
示例#26
0
    def __init__(self, model=None, name=None, params=None, **kwargs):
        """Initializes a Calculation object for a given style."""

        # Initialize subsets used by the calculation
        self.__potential = LammpsPotential(self)
        self.__commands = LammpsCommands(self)
        self.__units = Units(self)
        self.__system = AtommanSystemLoad(self)
        self.__minimize = LammpsMinimize(self)
        self.__defect = Dislocation(self)
        self.__elastic = AtommanElasticConstants(self)
        subsets = (self.commands, self.potential, self.system, self.elastic,
                   self.minimize, self.defect, self.units)

        # Initialize unique calculation attributes
        self.annealtemperature = 0.0
        self.annealsteps = None
        self.randomseed = None
        self.duplicatecutoff = uc.set_in_units(0.5, 'angstrom')
        self.boundarywidth = 0.0
        self.boundaryscale = False
        self.onlylinear = False
        self.__dumpfile_base = None
        self.__dumpfile_defect = None
        self.__symbols_base = None
        self.__symbols_defect = None
        self.__potential_energy_defect = None
        self.__dislocation = None
        self.__preln = None
        self.__K_tensor = None

        # Define calc shortcut
        self.calc = dislocation_array

        # Call parent constructor
        super().__init__(model=model,
                         name=name,
                         params=params,
                         subsets=subsets,
                         **kwargs)
def stackingfaultworker(lammps_command, system, potential,
                        shiftvector1, shiftvector2, shiftfraction1,
                        shiftfraction2, mpi_command=None, cutboxvector=None,
                        faultpos=0.5, etol=0.0, ftol=0.0, maxiter=10000,
                        maxeval=100000, 
                        dmax=uc.set_in_units(0.01, 'angstrom'),
                        lammps_date=None):
    """
    A wrapper function around stackingfaultpoint. Converts
    shiftfractions and shiftvectors to a faultshift, runs stackingfaultpoint,
    and adds keys 'shift1' and 'shift2' to the returned dictionary
    corresponding to the shiftfractions.
    """
    
    # Compute the faultshift
    faultshift = shiftfraction1 * shiftvector1 + shiftfraction2 * shiftvector2
    
    # Name the simulation directory based on shiftfractions
    sim_directory = 'a%.10f-b%.10f' % (shiftfraction1, shiftfraction2)
    
    # Evaluate the system at the shift
    sf = stackingfaultpoint(lammps_command, system, potential,
                            mpi_command=mpi_command,
                            cutboxvector=cutboxvector,
                            faultpos=faultpos,
                            etol=etol,
                            ftol=ftol,
                            maxiter=maxiter,
                            maxeval=maxeval,
                            dmax=dmax,
                            faultshift=faultshift,
                            sim_directory=sim_directory,
                            lammps_date=lammps_date)
    
    # Add shiftfractions to sf results
    sf['shift1'] = shiftfraction1
    sf['shift2'] = shiftfraction2
    
    return sf
def stackingfaultpoint(lammps_command, system, potential,
                       mpi_command=None, sim_directory=None,
                       cutboxvector='c', faultpos=0.5,
                       faultshift=[0.0, 0.0, 0.0], etol=0.0, ftol=0.0,
                       maxiter=10000, maxeval=100000,
                       dmax=uc.set_in_units(0.01, 'angstrom'),
                       lammps_date=None):
    """
    Perform a stacking fault relaxation simulation for a single faultshift.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simuation in.  If not
        given, will use the current working directory.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    cutboxvector : str, optional
        Indicates which of the three system box vectors, 'a', 'b', or 'c', to
        cut with a non-periodic boundary (default is 'c').
    faultpos : float, optional
        The fractional position along the cutboxvector where the stacking
        fault plane will be placed (default is 0.5).
    faultshift : list of float, optional
        The vector shift to apply to all atoms above the fault plane defined
        by faultpos (default is [0,0,0], i.e. no shift applied).
    lammps_date : datetime.date or None, optional
        The date version of the LAMMPS executable.  If None, will be identified from the lammps_command (default is None).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The filename of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed system.
        - **'system'** (*atomman.System*) - The relaxed system.
        - **'A_fault'** (*float*) - The area of the fault surface.
        - **'E_total'** (*float*) - The total potential energy of the relaxed
          system.
        - **'disp'** (*float*) - The center of mass difference between atoms
          above and below the fault plane in the cutboxvector direction.
    
    Raises
    ------
    ValueError
        For invalid cutboxvectors.
    """
    
    # Set options based on cutboxvector
    if cutboxvector == 'a':
        # Assert system is compatible with planeaxis value
        if system.box.xy != 0.0 or system.box.xz != 0.0:
            raise ValueError("box tilts xy and xz must be 0 for cutboxvector='a'")
        
        # Specify cutindex
        cutindex = 0
        
        # Identify atoms above fault
        faultpos = system.box.xlo + system.box.lx * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.bvect,
                                            system.box.cvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce NULL 0 0'
    
    elif cutboxvector == 'b':
        # Assert system is compatible with planeaxis value
        if system.box.yz != 0.0:
            raise ValueError("box tilt yz must be 0 for cutboxvector='b'")
        
        # Specify cutindex
        cutindex = 1
        
        # Identify atoms above fault
        faultpos = system.box.ylo + system.box.ly * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.cvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 NULL 0'
        
    elif cutboxvector == 'c':
        # Specify cutindex
        cutindex = 2
        
        # Identify atoms above fault
        faultpos = system.box.zlo + system.box.lz * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.bvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 0 NULL'
        
    else: 
        raise ValueError('Invalid cutboxvector')
    
    # Assert faultshift is in cut plane
    if faultshift[cutindex] != 0.0:
        raise ValueError('faultshift must be in cut plane')
    
    # Generate stacking fault system by shifting atoms above the fault
    sfsystem = deepcopy(system)
    sfsystem.pbc = [True, True, True]
    sfsystem.pbc[cutindex] = False
    sfsystem.atoms.pos[abovefault] += faultshift
    sfsystem.wrap()
    
    if sim_directory is not None:
        # Create sim_directory if it doesn't exist
        if not os.path.isdir(sim_directory):
            os.mkdir(sim_directory)
        
        # Add '/' to end of sim_directory string if needed
        if sim_directory[-1] != '/': 
            sim_directory = sim_directory + '/'
    else:
        # Set sim_directory if is None
        sim_directory = ''
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
       
    #Get lammps version date
    if lammps_date is None:
        lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = sfsystem.dump('atom_data',
                                f=os.path.join(sim_directory, 'system.dat'),
                                units=potential.units,
                                atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(sfsystem.symbols)
    lammps_variables['fix_cut_setforce'] = fix_cut_setforce
    lammps_variables['sim_directory'] = sim_directory
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'sfmin.template'
    lammps_script = os.path.join(sim_directory, 'sfmin.in')
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command,
                     logfile=os.path.join(sim_directory, 'log.lammps'))
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    logfile = os.path.join(sim_directory, 'log.lammps')
    dumpfile = os.path.join(sim_directory, '%i.dump' % thermo.Step.values[-1])
    E_total = uc.set_in_units(thermo.PotEng.values[-1],
                              lammps_units['energy'])
    
    #Load relaxed system
    sfsystem = am.load('atom_dump', dumpfile, symbols=sfsystem.symbols)
    
    # Find center of mass difference in top/bottom planes
    disp = (sfsystem.atoms.pos[abovefault, cutindex].mean()
            - sfsystem.atoms.pos[~abovefault, cutindex].mean())
    
    # Return results
    results_dict = {}
    results_dict['logfile'] = logfile
    results_dict['dumpfile'] = dumpfile
    results_dict['system'] = sfsystem
    results_dict['A_fault'] = faultarea
    results_dict['E_total'] = E_total
    results_dict['disp'] = disp
    
    return results_dict
def pointdiffusion(lammps_command, system, potential, point_kwargs,
                   mpi_command=None, temperature=300,
                   runsteps=200000, thermosteps=None, dumpsteps=0,
                   equilsteps=20000, randomseed=None):
                   
    """
    Evaluates the diffusion rate of a point defect at a given temperature. This
    method will run two simulations: an NVT run at the specified temperature to 
    equilibrate the system, then an NVE run to measure the defect's diffusion 
    rate. The diffusion rate is evaluated using the mean squared displacement of
    all atoms in the system, and using the assumption that diffusion is only due
    to the added defect(s).
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    point_kwargs : dict or list of dict
        One or more dictionaries containing the keyword arguments for
        the atomman.defect.point() function to generate specific point
        defect configuration(s).
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    temperature : float, optional
        The temperature to run at (default is 300.0).
    runsteps : int, optional
        The number of integration steps to perform (default is 200000).
    thermosteps : int, optional
        Thermo values will be reported every this many steps (default is
        100).
    dumpsteps : int or None, optional
        Dump files will be saved every this many steps (default is 0,
        which does not output dump files).
    equilsteps : int, optional
        The number of timesteps at the beginning of the simulation to
        exclude when computing average values (default is 20000).
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'natoms'** (*int*) - The number of atoms in the system.
        - **'temp'** (*float*) - The mean measured temperature.
        - **'pxx'** (*float*) - The mean measured normal xx pressure.
        - **'pyy'** (*float*) - The mean measured normal yy pressure.
        - **'pzz'** (*float*) - The mean measured normal zz pressure.
        - **'Epot'** (*numpy.array*) - The mean measured total potential 
          energy.
        - **'temp_std'** (*float*) - The standard deviation in the measured
          temperature values.
        - **'pxx_std'** (*float*) - The standard deviation in the measured
          normal xx pressure values.
        - **'pyy_std'** (*float*) - The standard deviation in the measured
          normal yy pressure values.
        - **'pzz_std'** (*float*) - The standard deviation in the measured
          normal zz pressure values.
        - **'Epot_std'** (*float*) - The standard deviation in the measured
          total potential energy values.
        - **'dx'** (*float*) - The computed diffusion constant along the 
          x-direction.
        - **'dy'** (*float*) - The computed diffusion constant along the 
          y-direction.
        - **'dz'** (*float*) - The computed diffusion constant along the 
          y-direction.
        - **'d'** (*float*) - The total computed diffusion constant.
    """
    
    # Add defect(s) to the initially perfect system
    if not isinstance(point_kwargs, (list, tuple)):
        point_kwargs = [point_kwargs]
    for pkwargs in point_kwargs:
        system = am.defect.point(system, **pkwargs)
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
    
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Check that temperature is greater than zero
    if temperature <= 0.0:
        raise ValueError('Temperature must be greater than zero')
    
    # Handle default values
    if thermosteps is None: 
        thermosteps = runsteps // 1000
        if thermosteps == 0:
            thermosteps = 1
    if dumpsteps is None:
        dumpsteps = runsteps
    if randomseed is None:
        randomseed = random.randint(1, 900000000)
    
    # Define lammps variables
    lammps_variables = {}
    system_info = system.dump('atom_data', f='initial.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    lammps_variables['temperature'] = temperature
    lammps_variables['runsteps'] = runsteps
    lammps_variables['equilsteps'] = equilsteps
    lammps_variables['thermosteps'] = thermosteps
    lammps_variables['dumpsteps'] = dumpsteps
    lammps_variables['randomseed'] = randomseed
    lammps_variables['timestep'] = lmp.style.timestep(potential.units)
    
    # Set dump_info
    if dumpsteps == 0:
        lammps_variables['dump_info'] = ''
    else:
        lammps_variables['dump_info'] = '\n'.join([
            '',
            '# Define dump files',
            'dump dumpit all custom ${dumpsteps} *.dump id type x y z c_peatom',
            'dump_modify dumpit format <dump_modify_format>',
            '',
        ])
        
        # Set dump_modify_format based on lammps_date
        if lammps_date < datetime.date(2016, 8, 3):
            lammps_variables['dump_modify_format'] = '"%d %d %.13e %.13e %.13e %.13e"'
        else:
            lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'diffusion.template'
    lammps_script = 'diffusion.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>'))
    
    # Run lammps
    output = lmp.run(lammps_command, 'diffusion.in', mpi_command)
    
    # Extract LAMMPS thermo data.
    thermo = output.simulations[1]['thermo']
    temps = thermo.Temp.values
    pxxs = uc.set_in_units(thermo.Pxx.values, lammps_units['pressure'])
    pyys = uc.set_in_units(thermo.Pyy.values, lammps_units['pressure'])
    pzzs = uc.set_in_units(thermo.Pzz.values, lammps_units['pressure'])
    potengs = uc.set_in_units(thermo.PotEng.values, lammps_units['energy'])
    steps = thermo.Step.values
    
    # Read user-defined thermo data
    if output.lammps_date < datetime.date(2016, 8, 1):
        msd_x = uc.set_in_units(thermo['msd[1]'].values,
                                lammps_units['length']+'^2')
        msd_y = uc.set_in_units(thermo['msd[2]'].values,
                                lammps_units['length']+'^2')
        msd_z = uc.set_in_units(thermo['msd[3]'].values,
                                lammps_units['length']+'^2')
        msd = uc.set_in_units(thermo['msd[4]'].values,
                              lammps_units['length']+'^2')
    else:
        msd_x = uc.set_in_units(thermo['c_msd[1]'].values,
                                lammps_units['length']+'^2')
        msd_y = uc.set_in_units(thermo['c_msd[2]'].values,
                                lammps_units['length']+'^2')
        msd_z = uc.set_in_units(thermo['c_msd[3]'].values,
                                lammps_units['length']+'^2')
        msd = uc.set_in_units(thermo['c_msd[4]'].values,
                              lammps_units['length']+'^2')
        
    # Initialize results dict
    results = {}
    results['natoms'] = system.natoms
    
    # Get mean and std for temperature, pressure, and potential energy
    results['temp'] = np.mean(temps)
    results['temp_std'] = np.std(temps)
    results['pxx'] = np.mean(pxxs)
    results['pxx_std'] = np.std(pxxs)
    results['pyy'] = np.mean(pyys)
    results['pyy_std'] = np.std(pyys)
    results['pzz'] = np.mean(pzzs)
    results['pzz_std'] = np.std(pzzs)
    results['Epot'] = np.mean(potengs)
    results['Epot_std'] = np.std(potengs)
    
    # Convert steps to times
    times = steps * uc.set_in_units(lammps_variables['timestep'], lammps_units['time'])
    
    # Estimate diffusion rates
    # MSD_ptd = natoms * MSD_atoms (if one defect in system)
    # MSD = 2 * ndim * D * t  -->  D = MSD/t / (2 * ndim)
    mx = np.polyfit(times, system.natoms * msd_x, 1)[0]
    my = np.polyfit(times, system.natoms * msd_y, 1)[0]
    mz = np.polyfit(times, system.natoms * msd_z, 1)[0]
    m = np.polyfit(times, system.natoms * msd, 1)[0]
    
    results['dx'] = mx / 2
    results['dy'] = my / 2
    results['dz'] = mz / 2
    results['d'] = m / 6
    
    return results
def surface_energy(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'),
                   cutboxvector='c'):
    """
    Evaluates surface formation energies by slicing along one periodic
    boundary of a bulk 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).
    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').
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed bulk system.
        - **'dumpfile_surf'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed system containing the free surfaces.
        - **'E_total_base'** (*float*) - The total potential energy of the
          relaxed bulk system.
        - **'E_total_surf'** (*float*) - The total potential energy of the
          relaxed system containing the free surfaces.
        - **'A_surf'** (*float*) - The area of the free surface.
        - **'E_coh'** (*float*) - The cohesive energy of the relaxed bulk
          system.
        - **'E_surf_f'** (*float*) - The computed surface formation energy.
    
    Raises
    ------
    ValueError
        For invalid cutboxvectors
    """
    
    # Evaluate perfect system
    system.pbc = [True, True, True]
    perfect = relax_system(lammps_command, system, potential,
                           mpi_command=mpi_command, etol=etol, ftol=ftol,
                           maxiter=maxiter, maxeval=maxeval, dmax=dmax)
    
    # Extract results from perfect system
    dumpfile_base = 'perfect.dump'
    shutil.move(perfect['finaldumpfile'], dumpfile_base)
    shutil.move('log.lammps', 'perfect-log.lammps')
    E_total_base = perfect['potentialenergy']
    
    # Set up defect system
    # A_surf is area of parallelogram defined by the two box vectors not along
    # the cutboxvector
    if   cutboxvector == 'a':
        system.pbc[0] = False
        A_surf = np.linalg.norm(np.cross(system.box.bvect, system.box.cvect))
    
    elif cutboxvector == 'b':
        system.pbc[1] = False
        A_surf = np.linalg.norm(np.cross(system.box.avect, system.box.cvect))
    
    elif cutboxvector == 'c':
        system.pbc[2] = False
        A_surf = np.linalg.norm(np.cross(system.box.avect, system.box.bvect))
    
    else:
        raise ValueError('Invalid cutboxvector')
    
    # Evaluate system with free surface
    surface = relax_system(lammps_command, system, potential,
                           mpi_command=mpi_command, etol=etol, ftol=ftol,
                           maxiter=maxiter, maxeval=maxeval, dmax=dmax)
    
    # Extract results from system with free surface
    dumpfile_surf = 'surface.dump'
    shutil.move(surface['finaldumpfile'], dumpfile_surf)
    shutil.move('log.lammps', 'surface-log.lammps')
    E_total_surf = surface['potentialenergy']
    
    # Compute the free surface formation energy
    E_surf_f = (E_total_surf - E_total_base) / (2 * A_surf)
    
    # Save values to results dictionary
    results_dict = {}
    
    results_dict['dumpfile_base'] = dumpfile_base
    results_dict['dumpfile_surf'] = dumpfile_surf
    results_dict['E_total_base'] = E_total_base
    results_dict['E_total_surf'] = E_total_surf
    results_dict['A_surf'] = A_surf
    results_dict['E_coh'] = E_total_base / system.natoms
    results_dict['E_surf_f'] = E_surf_f
    
    return results_dict
示例#31
0
def dislocation_array(system,
                      dislsol=None,
                      m=None,
                      n=None,
                      burgers=None,
                      bwidth=None,
                      cutoff=None):
    """
    Method that converts a bulk crystal system into a periodic array of
    dislocations.  A single dislocation is inserted using a dislocation
    solution.  The system's box and pbc are altered such that the system is
    periodic and compatible across the two box vectors contained in the slip
    plane.  The third box vector is non-periodic, resulting in free surfaces
    parallel to the dislocation's slip plane.
    
    Parameters
    ----------
    system : atomman.System
        A perfect, bulk atomic system.
    dislsol : atomman.defect.Stroh or atomman.defect.IsotropicVolterra, optional
        A dislocation solution to use to displace atoms by.  If not given,
        all atoms will be given linear displacements associated with the
        long-range limits.
    m : array-like object, optional
        The dislocation solution m unit vector.  This vector is in the slip
        plane and perpendicular to the dislocation line direction.  Only needed
        if dislsol is not given.
    n : array-like object, optional
        The dislocation solution n unit vector.  This vector is normal to the 
        slip plane.  Only needed if dislsol is not given.
    burgers : array-like object, optional
        The Cartesian Burger's vector for the dislocation relative to the
        given system's Cartesian coordinates.  Only needed if dislsol is not
        given.
    bwidth : float, optional
        The width of the boundary region at the free surfaces.  Atoms within
        the boundaries will be displaced by linear displacements instead of
        by the dislocation solution.  Only given if dislsol is not None.
        Default value if dislsol is given is 10 Angstroms.
    cutoff : float, optional
        Cutoff distance to use for identifying duplicate atoms to remove.
        For dislocations with an edge component, applying the displacements
        creates an extra half-plane of atoms that will have (nearly) identical
        positions with other atoms after altering the boundary conditions.
        Default cutoff value is 0.5 Angstrom.
    
    Returns
    -------
    atomman.System
        The resulting periodic array of dislocations system.  An additional
        atoms property 'old_id' will be added to map the atoms in the defect
        system back to the associated atoms in the original system.
    """

    # ------------------------ Parameter handling --------------------------- #

    # Input parameter setup for linear gradients only
    if dislsol is None:
        if m is None or n is None:
            raise ValueError('m and n are needed if no dislsol is given')
        m = np.asarray(m)
        n = np.asarray(n)
        burgers = np.asarray(burgers)
        try:
            assert np.isclose(np.linalg.norm(m), 1.0)
            assert np.isclose(np.linalg.norm(n), 1.0)
            assert np.isclose(m.dot(n), 0.0)
        except:
            raise ValueError('m and n must be perpendicular unit vectors')
        if bwidth is not None:
            raise ValueError('bwidth not allowed if dislsol is not given')

    # Input parameter setup for dislsol
    else:
        m = dislsol.m
        n = dislsol.n
        burgers = dislsol.burgers
        if bwidth is None:
            bwidth = uc.set_in_units(10, 'angstrom')
    if cutoff is None:
        cutoff = uc.set_in_units(0.5, 'angstrom')

    # Extract system values
    pos = system.atoms.pos
    vects = system.box.vects
    spos = system.atoms_prop(key='pos', scale=True)

    # -------------------- Orientation identification ----------------------- #

    # Find box vector alligned with u = m x n
    u = np.cross(m, n)
    line = np.isclose(np.abs(np.dot(u, vects)), np.linalg.norm(vects, axis=1))
    if np.sum(line) != 1:
        raise ValueError('box vector aligned with u = m x n not found')
    lineindex = np.where(line)[0][0]

    # Find out-of-plane box vector
    out = ~np.isclose(np.dot(n, vects), 0.0)
    if np.sum(out) > 1:
        raise ValueError('multiple box vectors have out-of-plane components')
    pnormindex = np.where(out)[0][0]

    # Set third box vector as edge direction
    motionindex = 3 - (lineindex + pnormindex)

    # Check for atoms exactly on the slip plane
    if np.isclose(spos[:, pnormindex], 0.5, rtol=0).sum() > 0:
        raise ValueError(
            "atom positions found on slip plane: apply a coordinate shift")

    # ---------------------- Boundary modification -------------------------- #

    # Modify box vector in the motion direction by +- burgers/2
    newvects = deepcopy(vects)
    if burgers.dot(m) > 0:
        newvects[motionindex] -= burgers / 2
    else:
        newvects[motionindex] += burgers / 2
    newbox = Box(vects=newvects, origin=system.box.origin)

    # Make boundary condition perpendicular to slip plane non-periodic
    newpbc = [True, True, True]
    newpbc[pnormindex] = False

    # Get length of system along motionindex
    # WHICH IS BEST!?!?!?
    length = np.abs(vects[motionindex].dot(m))
    #length = np.linalg.norm(newvects[motionindex])

    # -------------------- duplicate atom identification -------------------- #

    # Create test system to identify "duplicate" atoms
    testsystem = System(atoms=deepcopy(system.atoms),
                        box=newbox,
                        pbc=newpbc,
                        symbols=system.symbols)

    # Apply linear gradient shift to all atoms
    testsystem.atoms.pos += linear_displacement(pos, burgers, length, m, n)
    testsystem.atoms.old_id = range(testsystem.natoms)

    # Identify atoms at the motionindex boundary to include in the duplicate check
    spos = testsystem.atoms_prop(key='pos', scale=True)
    sburgers = np.abs(2 * burgers[motionindex] / (length))
    boundaryatoms = testsystem.atoms[(spos[:, motionindex] < sburgers)
                                     | (spos[:, motionindex] > 1.0 - sburgers)]

    # Compare distances between boundary atoms to identify duplicates
    dup_atom_ids = []
    for ni, i in enumerate(boundaryatoms.old_id[:-1]):
        js = boundaryatoms.old_id[ni + 1:]
        try:
            distances = np.linalg.norm(testsystem.dvect(i, js), axis=1)
            mindistance = distances.min()
        except:
            mindistance = np.linalg.norm(testsystem.dvect(i, js))
        if mindistance < cutoff:
            dup_atom_ids.append(i)
    ii = np.ones(system.natoms, dtype=bool)
    ii[dup_atom_ids] = False

    # Count found duplicate atoms
    found = system.natoms - ii.sum()

    # Count expected number of duplicates based on volume change
    expected = system.natoms - (system.natoms * newbox.volume /
                                system.box.volume)
    if np.isclose(expected, round(expected)):
        expected = int(round(expected))
    else:
        raise ValueError(
            'expected number of atoms to delete not an integer: check burgers vector'
        )

    # Compare found versus expected number of atoms
    if found != expected:
        raise ValueError(
            'Deleted atom mismatch: expected %i, found %i. Adjust system dimensions and/or cutoff'
            % (expected, found))

    # ---------------------- Build dislocation system ----------------------- #

    # Generate new system with duplicate atoms removed
    newsystem = System(atoms=system.atoms[ii],
                       box=newbox,
                       pbc=newpbc,
                       symbols=system.symbols)

    # Define old_id so atoms in newsystem can be mapped back to system
    newsystem.atoms.old_id = np.where(ii)[0]

    if dislsol is None:
        # Use only linear displacements
        disp = linear_displacement(newsystem.atoms.pos, burgers, length, m, n)

    else:
        # Identify boundary atoms
        miny = system.box.origin.dot(n)
        maxy = miny + vects[pnormindex].dot(n)
        if maxy < miny:
            miny, maxy = maxy, miny
        y = newsystem.atoms.pos.dot(n)
        ii = np.where((y <= miny + bwidth) | (y >= maxy - bwidth))

        # Use dislsol in middle and linear displacements at boundary
        disp = dislsol.displacement(newsystem.atoms.pos)
        disp[:, pnormindex] -= disp[:, pnormindex].mean()
        disp[ii] = linear_displacement(newsystem.atoms.pos[ii], burgers,
                                       length, m, n)

    # Displace atoms and wrap
    newsystem.atoms.pos += disp
    newsystem.wrap()

    return newsystem
示例#32
0
def phonon(lammps_command,
           ucell,
           potential,
           mpi_command=None,
           a_mult=3,
           b_mult=3,
           c_mult=3,
           distance=0.01,
           symprec=1e-5):

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

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

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

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

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

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

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

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

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

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

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

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

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

    # Set computed forces
    phonon.set_forces(forcearrays)

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

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

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

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

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

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

    return {}
示例#33
0
def calc_cij(lammps_exe, ucell, potential, symbols, p_xx=0.0, p_yy=0.0, p_zz=0.0):
    """Runs cij_script and returns current Cij, stress, Ecoh, and new ucell guess."""
    
    #setup system and pair info
    system_info = lmp.sys_gen(units =       potential.units,
                              atom_style =  potential.atom_style,
                              ucell =       ucell,
                              size =        np.array([[0,3], [0,3], [0,3]]))

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

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

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

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

    
    #extract the current stress state
    stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]],
                            [pxy[0], pyy[0], pyz[0]],
                            [pxz[0], pyz[0], pzz[0]]])
    
    s_xx = stress[0,0] + p_xx
    s_yy = stress[1,1] + p_yy
    s_zz = stress[2,2] + p_zz
    
    new_a = ucell.box.a / (S[0,0]*s_xx + S[0,1]*s_yy + S[0,2]*s_zz + 1)
    new_b = ucell.box.b / (S[1,0]*s_xx + S[1,1]*s_yy + S[1,2]*s_zz + 1)
    new_c = ucell.box.c / (S[2,0]*s_xx + S[2,1]*s_yy + S[2,2]*s_zz + 1)
    
    if new_a <= 0 or new_b <= 0 or new_c <=0:
        raise RuntimeError('Divergence of box dimensions to <= 0')
    
    newbox = am.Box(a=new_a, b=new_b, c=new_c)
    ucell_new = deepcopy(ucell)
    ucell_new.box_set(vects=newbox.vects, scale=True)
    
    return {'C':C, 'stress':stress, 'ecoh':pe[0], 'ucell_new':ucell_new}
示例#34
0
def e_vs_r(lammps_command, system, potential,
           mpi_command=None, ucell=None, 
           rmin=uc.set_in_units(2.0, 'angstrom'), 
           rmax=uc.set_in_units(6.0, 'angstrom'), rsteps=200):
    """
    Performs a cohesive energy scan over a range of interatomic spaces, r.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    ucell : atomman.System, optional
        The fundamental unit cell correspodning to system.  This is used to
        convert system dimensions to cell dimensions. If not given, ucell will
        be taken as system.
    rmin : float, optional
        The minimum r spacing to use (default value is 2.0 angstroms).
    rmax : float, optional
        The maximum r spacing to use (default value is 6.0 angstroms).
    rsteps : int, optional
        The number of r spacing steps to evaluate (default value is 200).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'r_values'** (*numpy.array of float*) - All interatomic spacings,
          r, explored.
        - **'a_values'** (*numpy.array of float*) - All unit cell a lattice
          constants corresponding to the values explored.
        - **'Ecoh_values'** (*numpy.array of float*) - The computed cohesive
          energies for each r value.
        - **'min_cell'** (*list of atomman.System*) - Systems corresponding to
          the minima identified in the Ecoh_values.
    """
    
    # Make system a deepcopy of itself (protect original from changes)
    system = deepcopy(system)
    
    # Set ucell = system if ucell not given
    if ucell is None:
        ucell = system
    
    # Calculate the r/a ratio for the unit cell
    r_a = r_a_ratio(ucell)
    
    # Get ratios of lx, ly, and lz of system relative to a of ucell
    lx_a = system.box.a / ucell.box.a
    ly_a = system.box.b / ucell.box.a
    lz_a = system.box.c / ucell.box.a
    alpha = system.box.alpha
    beta =  system.box.beta
    gamma = system.box.gamma
 
    # Build lists of values
    r_values = np.linspace(rmin, rmax, rsteps)
    a_values = r_values / r_a
    Ecoh_values = np.empty(rsteps)
    
    # Loop over values
    for i in range(rsteps):
        
        # Rescale system's box
        a = a_values[i]
        system.box_set(a = a * lx_a, 
                       b = a * ly_a, 
                       c = a * lz_a, 
                       alpha=alpha, beta=beta, gamma=gamma, scale=True)
        
        # Get lammps units
        lammps_units = lmp.style.unit(potential.units)
        
        # Define lammps variables
        lammps_variables = {}
        system_info = system.dump('atom_data', f='atom.dat',
                                  units=potential.units,
                                  atom_style=potential.atom_style)
        lammps_variables['atomman_system_info'] = system_info
        lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
        
        # Write lammps input script
        template_file = 'run0.template'
        lammps_script = 'run0.in'
        with open(template_file) as f:
            template = f.read()
        with open(lammps_script, 'w') as f:
            f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                             '<', '>'))
        
        # Run lammps and extract data
        output = lmp.run(lammps_command, lammps_script, mpi_command)
        
        thermo = output.simulations[0]['thermo']
        
        if output.lammps_date < datetime.date(2016, 8, 1):
            Ecoh_values[i] = uc.set_in_units(thermo.peatom.values[-1],
                                             lammps_units['energy'])
        else:
            Ecoh_values[i] = uc.set_in_units(thermo.v_peatom.values[-1],
                                             lammps_units['energy'])
        
        # Rename log.lammps
        shutil.move('log.lammps', 'run0-'+str(i)+'-log.lammps')
    
    # Find unit cell systems at the energy minimums
    min_cells = []
    for i in range(1, rsteps - 1):
        if (Ecoh_values[i] < Ecoh_values[i-1]
            and Ecoh_values[i] < Ecoh_values[i+1]):
            a = a_values[i]
            cell = deepcopy(ucell)
            cell.box_set(a = a,
                         b = a * ucell.box.b / ucell.box.a,
                         c = a * ucell.box.c / ucell.box.a, 
                         alpha=alpha, beta=beta, gamma=gamma, scale=True)
            min_cells.append(cell)
    
    # Collect results
    results_dict = {}
    results_dict['r_values'] = r_values
    results_dict['a_values'] = a_values
    results_dict['Ecoh_values'] = Ecoh_values
    results_dict['min_cell'] = min_cells
    
    return results_dict
示例#35
0
def load(data, pbc=(True, True, True), atom_style='atomic', units='metal'):
    """
    Read a LAMMPS-style atom data file and return a System.
    
    Argument:
    data = file name, file-like object or string to read data from.
    
    Keyword Arguments:
    pbc -- list or tuple of three boolean values indicating which System directions are periodic. Default is (True, True, True).
    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 read in, the units of all property values are automatically converted to atomman's set working units.
    """
    
    units_dict = style.unit(units)

    readtime = False
    count = 0
    xy = 0.0
    xz = 0.0
    yz = 0.0
    system = None
    with uber_open_rmode(data) as fp:
        #loop over all lines in fp
        for line in fp:
            terms = line.split()
            if len(terms)>0:
                
                #read atomic information if time to do so
                if readtime == True:
                    a_id = int(terms[0]) - 1
                    prop_vals[a_id] = terms[1:]
                    count += 1
                    
                    #save values to system once all atoms read in
                    if count == natoms:
                        readtime = False
                        count = 0
                        start = 0
                        
                        #iterate over all atom_style properties
                        for name, v in props.iteritems():
                            if name != 'a_id':
                                size, dim, dtype = v
                                value = np.asarray(prop_vals[:, start:start+size], dtype=dtype)
                                start += size
                                
                                #set units according to LAMMPS units style
                                unit = units_dict.get(dim, None)
                                system.atoms_prop(key=name, value=uc.set_in_units(value, unit))
                
                #read number of atoms 
                elif len(terms) == 2 and terms[1] == 'atoms':
                    natoms = int(terms[0])
                
                #read number of atom types
                elif len(terms) == 3 and terms[1] == 'atom' and terms[2] == 'types': 
                    natypes = int(terms[0])
                
                #read boundary info
                elif len(terms) == 4 and terms[2] == 'xlo' and terms[3] == 'xhi':
                    xlo = uc.set_in_units(float(terms[0]), units_dict['length'])
                    xhi = uc.set_in_units(float(terms[1]), units_dict['length'])
                elif len(terms) == 4 and terms[2] == 'ylo' and terms[3] == 'yhi':
                    ylo = uc.set_in_units(float(terms[0]), units_dict['length'])
                    yhi = uc.set_in_units(float(terms[1]), units_dict['length'])
                elif len(terms) == 4 and terms[2] == 'zlo' and terms[3] == 'zhi':
                    zlo = uc.set_in_units(float(terms[0]), units_dict['length'])
                    zhi = uc.set_in_units(float(terms[1]), units_dict['length'])
                elif len(terms) == 6 and terms[3] == 'xy' and terms[4] == 'xz' and terms[5] == 'yz':
                    xy = uc.set_in_units(float(terms[0]), units_dict['length'])
                    xz = uc.set_in_units(float(terms[1]), units_dict['length'])  
                    yz = uc.set_in_units(float(terms[2]), units_dict['length'])
                
                #Flag when reached data and setup for reading
                elif len(terms) == 1 and terms[0] in ('Atoms', 'Velocities'):
                    #create system if not already
                    if system is None:
                        box = am.Box(xlo=xlo, xhi=xhi, 
                                    ylo=ylo, yhi=yhi, 
                                    zlo=zlo, zhi=zhi, 
                                    xy=xy, xz=xz, yz=yz)
                                  
                        system = am.System(box=box, atoms=am.Atoms(natoms=natoms), pbc = pbc)    
                    
                    if terms[0] == 'Atoms':
                        props = style.atom(atom_style)
                    else:
                        props = style.velocity(atom_style)
                    
                    nvals = 0
                    for name, v in props.iteritems():
                        nvals += v[0]
                    prop_vals = np.empty((natoms, nvals-1), dtype=float)
                    readtime = True
                     
    assert system.natypes == natypes, 'Number of atom types does not match!'
    return system
示例#36
0
def interstitial(system, pos, scale=False, atol=None, **kwargs):
    """
    Generates a new system by adding an interstitial point defect.
    1. Adds a new atom to the end of the Atoms list.
    2. Adds per-atom property old_id if it doesn't exist corresponding to the
    atom ids in the original system.
    3. Sets any of the new atom's per-atom properties to values given as
    kwargs.  Any undefined properties are given zero values except atype,
    which is set to 1.
    
    Parameters
    ----------
    system : atomman.System
        The base System to add the defect to.
    pos : array-like object
        Position of the atom being added.
    scale : bool, optional
        Indicates if pos is Cartesian (False) or box-relative (True).  Default
        value is False.
    atol : float, optional
        Absolute tolerance for position-based searching. Default value is 0.01
        angstroms.
    **kwargs : any, optional
        Keyword arguments corresponding to per-atom property values for the
        new atom.  By default, atype==1 and all other properties are set to
        be all zeros for the property's shape.
    
    Returns
    -------
    atomman.System
        A new system with the interstitial added.
    """
    # Set default atol
    if atol is None:
        atol = uc.set_in_units(0.01, 'angstrom')
    
    if scale:
        pos = system.unscale(pos)
    
    # Check that no atoms are already at pos
    dist = np.linalg.norm(system.dvect(pos, system.atoms.pos), axis=1)
    ptd_id = np.where(np.isclose(dist, 0.0, atol=atol))
    if not (len(ptd_id) == 1 and len(ptd_id[0]) == 0):
        raise ValueError('atom already at pos')
    
    # Generate atomic index list for defect
    index = list(range(system.natoms))
    index.append(0)
    
    # Build new system with atom 0 copied
    d_system = System(box=deepcopy(system.box), pbc=deepcopy(system.pbc),
                      atoms=deepcopy(system.atoms[index]),
                      symbols=system.symbols)
    
    # Add property old_id with each atom's original id
    if 'old_id' not in d_system.atoms_prop():
        d_system.atoms.old_id = index
    
    # Set values for the new atom
    for prop in d_system.atoms_prop():
        if prop == 'atype':
            d_system.atoms.atype[-1] = kwargs.pop('atype', 1)
        elif prop == 'pos':
            d_system.atoms.pos[-1] = pos
        elif prop == 'old_id':
            d_system.atoms.old_id[-1] = kwargs.pop('old_id',
                                                   d_system.atoms.old_id.max()+1)
        else:
            d_system.atoms.view[prop][-1] = kwargs.pop(prop,
                                                       np.zeros_like(d_system.atoms.view[prop][-1]))
    
    return d_system
示例#37
0
def substitutional(system, pos=None, ptd_id=None, atype=1,scale=False,
                   atol=None, **kwargs):
    """
    Generates a new system by adding a substitutional point defect.
    1. Moves the indicated atom to the end of the list and changes its atype
    to the value given.
    2. Adds per-atom property old_id if it doesn't exist corresponding to the
    atom ids in the original system.
    3. Sets any of the moved atom's per-atom properties to values given as
    kwargs.  Any undefined properties are left unchanged.
    
    Parameters
    ----------
    system : atomman.System
        The base System to add the defect to.
    pos : array-like object, optional
        Position of the atom being modified.  Either pos or ptd_id must be
        given.
    ptd_id : int, optional
        Id of the atom to be modified.  Either pos or ptd_id must be given.
    atype : int, optional
        Integer atomic type to change the identified atom to.  Must be
        different than the atom's current id.  Default value is 1.
    scale : bool, optional
        Indicates if pos is Cartesian (False) or box-relative (True).  Default
        value is False.
    atol : float, optional
        Absolute tolerance for position-based searching. Default value is 0.01
        angstroms.
    **kwargs : any, optional
        Keyword arguments corresponding to per-atom property values for the
        modified atom.  By default, all properties (except atype) are left
        unchanged.
    
    Returns
    -------
    atomman.System
        A new system with the substitutional added.
    """
    # Set default atol
    if atol is None:
        atol = uc.set_in_units(0.01, 'angstrom')
    
    # Identify the id of the atom at pos
    if pos is not None:
        if ptd_id is not None:
            raise ValueError('pos and ptd_id cannot both be supplied')
        
        if scale:
            pos = system.unscale(pos)
        
        dist = np.linalg.norm(system.dvect(pos, system.atoms.pos), axis=1)
        ptd_id = np.where(np.isclose(dist, 0.0, atol=atol))
        if len(ptd_id) == 1 and len(ptd_id[0]) == 1:
            ptd_id = ptd_id[0][0]
        else:
            raise ValueError('Unique atom at pos not identified')
    
    elif ptd_id is not None:
        if ptd_id < 0:
            ptd_id += system.natoms
        if ptd_id < 0 or ptd_id >= system.natoms:
            raise ValueError('invalid ptd_id')
    
    else:
        raise ValueError('Either pos or ptd_id required')
    
    if system.atoms.atype[ptd_id] == atype:
        raise ValueError('identified atom is already of the specified atype')
    
    # Generate atomic index list for defect
    index = list(range(system.natoms))
    index.pop(ptd_id)
    index.append(ptd_id)
    
    # Build new system
    d_system = System(box=deepcopy(system.box), pbc=deepcopy(system.pbc),
                      atoms=deepcopy(system.atoms[index]),
                      symbols=system.symbols)
    
    # Add property old_id with each atom's original id
    if 'old_id' not in d_system.atoms_prop():
        d_system.atoms.old_id = index
    
    # Set values for the new atom
    for prop in d_system.atoms_prop():
        if prop == 'atype':

            d_system.atoms.atype[-1] = atype
        elif prop == 'pos':
            pass
        else:
            d_system.atoms.view[prop][-1] = kwargs.pop(prop,
                                                       d_system.atoms.view[prop][-1])
    
    return d_system
示例#38
0
def dumbbell(system, pos=None, ptd_id=None, db_vect=None, scale=False,
             atol=None, **kwargs):
    """
    Generates a new system by adding a dumbbell interstitial point defect.
    
    1. Copies the indicated atom and moves both the original and copy to the
    end of the Atoms list.
    2. Displaces the dumbbell atoms position's by +-db_vect.
    3. Adds per-atom property old_id if it doesn't exist corresponding to the
    atom ids in the original system.
    4. Sets any of the new atom's per-atom properties to values given as
    kwargs.  Any undefined properties are left unchanged.
    
    Parameters
    ----------
    system : atomman.System
        The base System to add the defect to.
    pos : array-like object, optional
        Position of the atom being modified.  Either pos or ptd_id must be
        given.
    ptd_id : int, optional
        Id of the atom to be modified.  Either pos or ptd_id must be given.
    db_vect : array-like object
        Vector shift to apply to the atoms in the dumbbell.
    scale : bool, optional
        Indicates if pos and db_vect are Cartesian (False) or box-relative
        (True).  Default value is False.
    atol : float, optional
        Absolute tolerance for position-based searching. Default value is 0.01
        angstroms.
    \*\*kwargs : any, optional
        Keyword arguments corresponding to per-atom property values for the
        new atom in the dumbbell.  By default, all properties are left
        unchanged (i.e. same as atom that was copied).
    
    Returns
    -------
    atomman.System
        A new system with the dumbbell added.
    """
    # Set default atol
    if atol is None:
        atol = uc.set_in_units(0.01, 'angstrom')
    
    # Identify the id of the atom at pos
    if pos is not None:
        if ptd_id is not None:
            raise ValueError('pos and ptd_id cannot both be supplied')
        
        if scale:
            pos = system.unscale(pos)
        
        dist = np.linalg.norm(system.dvect(pos, system.atoms.pos), axis=1)
        ptd_id = np.where(np.isclose(dist, 0.0, atol=atol))
        if len(ptd_id) == 1 and len(ptd_id[0]) == 1:
            ptd_id = ptd_id[0][0]
        else:
            raise ValueError('Unique atom at pos not identified')
    
    elif ptd_id is not None:
        if ptd_id < 0:
            ptd_id += system.natoms
        if ptd_id < 0 or ptd_id >= system.natoms:
            raise ValueError('invalid ptd_id')
    
    else:
        raise ValueError('Either pos or ptd_id required')
        
    # Unscale db_vect
    if scale:
        db_vect = system.unscale(db_vect)
    
    # Generate atomic index list for defect
    index = list(range(system.natoms))
    index.pop(ptd_id)
    index.append(ptd_id)
    index.append(ptd_id)
    
    # Build new system
    d_system = System(box=deepcopy(system.box), pbc=deepcopy(system.pbc),
                      atoms=deepcopy(system.atoms[index]),
                      symbols=system.symbols)
    
    # Add property old_id with each atom's original id
    if 'old_id' not in d_system.atoms_prop():
        d_system.atoms.old_id = index
    
    # Set values for the new atom
    for prop in d_system.atoms_prop():
        if prop == 'pos':
            d_system.atoms.pos[-2] -= db_vect
            d_system.atoms.pos[-1] += db_vect
        elif prop == 'old_id':
            d_system.atoms.old_id[-1] = kwargs.pop('old_id',
                                                   d_system.atoms.old_id.max()+1)
        else:
            d_system.atoms.view[prop][-1] = kwargs.pop(prop,
                                                       d_system.atoms.view[prop][-1])
    
    return d_system
示例#39
0
文件: load.py 项目: usnistgov/atomman
def load(data, pbc=(True, True, True), symbols=None, atom_style='atomic', units='metal'):
    """
    Read a LAMMPS-style atom data file.
    
    Parameters
    ----------
    data : str or file-like object
        The atom data content to read.  Can be str content, path name, or open
        file-like object.
    pbc : list of bool
        Three boolean values indicating which System directions are periodic.
        Default value is (True, True, True).
    symbols : tuple, optional
        Allows the list of element symbols to be assigned during loading.
    atom_style :str
        The LAMMPS atom_style option associated with the data file.  Default
        value is 'atomic'.
    units : str 
        The LAMMPS units option associated with the data file. Default value
        is 'metal'.
    
    Returns
    -------
    atomman.System
        The corresponding system.  Note all property values will be
        automatically converted to atomman.unitconvert's set working units.
    """
    
    # Get units information
    units_dict = style.unit(units)
    
    # Initialize parameter values
    atomsstart = None
    velocitiesstart = None
    xy = 0.0
    xz = 0.0
    yz = 0.0
    
    # Read str and files in the same way
    with uber_open_rmode(data) as fp:
        
        # Loop over all lines in fp
        for i, line in enumerate(fp):
            try:
                line = line.decode('UTF-8')
            except:
                pass
            
            # Remove comments after '#'
            try:
                comment_index = line.index('#')
            except:
                pass
            else:
                line = line[:comment_index]
            
            terms = line.split()

            # Skip blank lines
            if len(terms)>0:
                
                # Read number of atoms 
                if len(terms) == 2 and terms[1] == 'atoms':
                    natoms = int(terms[0])
                
                # Read number of atom types
                elif len(terms) == 3 and terms[1] == 'atom' and terms[2] == 'types': 
                    #natypes = int(terms[0])
                    pass
                
                # Read boundary info
                elif len(terms) == 4 and terms[2] == 'xlo' and terms[3] == 'xhi':
                    xlo = uc.set_in_units(float(terms[0]), units_dict['length'])
                    xhi = uc.set_in_units(float(terms[1]), units_dict['length'])
                elif len(terms) == 4 and terms[2] == 'ylo' and terms[3] == 'yhi':
                    ylo = uc.set_in_units(float(terms[0]), units_dict['length'])
                    yhi = uc.set_in_units(float(terms[1]), units_dict['length'])
                elif len(terms) == 4 and terms[2] == 'zlo' and terms[3] == 'zhi':
                    zlo = uc.set_in_units(float(terms[0]), units_dict['length'])
                    zhi = uc.set_in_units(float(terms[1]), units_dict['length'])
                elif len(terms) == 6 and terms[3] == 'xy' and terms[4] == 'xz' and terms[5] == 'yz':
                    xy = uc.set_in_units(float(terms[0]), units_dict['length'])
                    xz = uc.set_in_units(float(terms[1]), units_dict['length'])
                    yz = uc.set_in_units(float(terms[2]), units_dict['length'])
                
                # Identify starting line number for Atoms data
                elif len(terms) == 1 and terms[0] == 'Atoms':
                    atomsstart = i + 1
                
                # Identify starting line number for Velocity data
                elif len(terms) == 1 and terms[0] == 'Velocities':
                    velocitiesstart = i + 1
    
    # Create system
    box = Box(xlo=xlo, xhi=xhi,
              ylo=ylo, yhi=yhi,
              zlo=zlo, zhi=zhi,
              xy=xy, xz=xz, yz=yz)
    atoms = Atoms(natoms=natoms)
    system = System(box=box, atoms=atoms, pbc=pbc)
    
    # Read in Atoms info
    if atomsstart is not None:
        prop_info = atoms_prop_info(atom_style, units)
        system = load_table(data, box=system.box, system=system, symbols=symbols,
                            prop_info=prop_info, skiprows=atomsstart, nrows=natoms,
                            comment='#')
    else:
        raise ValueError('No Atoms section found!')
    
    # Read in Velocities info
    if velocitiesstart is not None:
        prop_info = velocities_prop_info(atom_style, units)
        system = load_table(data, box=system.box, system=system,
                            prop_info=prop_info,
                            skiprows=velocitiesstart, nrows=natoms)
    
    return system
示例#40
0
def vacancy(system, pos=None, ptd_id=None, scale=False, atol=None):
    """
    Generates a new system by adding a vacancy point defect.
    1. Removes the indicated atom from the system
    2. Adds per-atom property old_id if it doesn't exist corresponding to the
    atom ids in the original system.
    
    Parameters
    ----------
    system : atomman.System
        The base System to add the defect to.
    pos : array-like object, optional
        Position of the atom to be removed.  Either pos or ptd_id must be
        given.
    ptd_id : int, optional
        Id of the atom to be removed.  Either pos or ptd_id must be given.
    scale : bool, optional
        Indicates if pos is Cartesian (False) or box-relative (True). Default
        value is False.
    atol : float, optional
        Absolute tolerance for position-based searching. Default value is 0.01
        angstroms.
    
    Returns
    -------
    atomman.System
        A new system with the vacancy added.
    """
    # Set default atol
    if atol is None:
        atol = uc.set_in_units(0.01, 'angstrom')
    
    # Identify the id of the atom at pos
    if pos is not None:
        if ptd_id is not None:
            raise ValueError('pos and ptd_id cannot both be supplied')
        
        if scale:
            pos = system.unscale(pos)
        
        dist = np.linalg.norm(system.dvect(pos, system.atoms.pos), axis=1)
        ptd_id = np.where(np.isclose(dist, 0.0, atol=atol))
        if len(ptd_id) == 1 and len(ptd_id[0]) == 1:
            ptd_id = ptd_id[0][0]
        else:
            raise ValueError('Unique atom at pos not identified')
    
    elif ptd_id is not None:
        if ptd_id < 0:
            ptd_id += system.natoms
        if ptd_id < 0 or ptd_id >= system.natoms:
            raise ValueError('invalid ptd_id')
    
    else:
        raise ValueError('Either pos or ptd_id required')
    
    # Generate atomic index list for defect
    index = list(range(system.natoms))
    try:
        index.pop(ptd_id)
    except:
        raise TypeError('ptd_id must be an integer type')
    
    # Build new system
    d_system = System(box=deepcopy(system.box), pbc=deepcopy(system.pbc),
                      atoms=deepcopy(system.atoms[index]),
                      symbols=system.symbols)
    
    # Add property old_id with each atom's original id
    if 'old_id' not in d_system.atoms_prop():
        d_system.atoms.old_id = index
    
    return d_system
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
示例#42
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
示例#43
0
def e_vs_r_scan(lammps_command: str,
                system: am.System,
                potential: am.lammps.Potential,
                mpi_command: Optional[str] = None,
                ucell: Optional[am.System] = None,
                rmin: float = uc.set_in_units(2.0, 'angstrom'),
                rmax: float = uc.set_in_units(6.0, 'angstrom'),
                rsteps: int = 200) -> dict:
    """
    Performs a cohesive energy scan over a range of interatomic spaces, r.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    ucell : atomman.System, optional
        The fundamental unit cell correspodning to system.  This is used to
        convert system dimensions to cell dimensions. If not given, ucell will
        be taken as system.
    rmin : float, optional
        The minimum r spacing to use (default value is 2.0 angstroms).
    rmax : float, optional
        The maximum r spacing to use (default value is 6.0 angstroms).
    rsteps : int, optional
        The number of r spacing steps to evaluate (default value is 200).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'r_values'** (*numpy.array of float*) - All interatomic spacings,
          r, explored.
        - **'a_values'** (*numpy.array of float*) - All unit cell a lattice
          constants corresponding to the values explored.
        - **'Ecoh_values'** (*numpy.array of float*) - The computed cohesive
          energies for each r value.
        - **'min_cell'** (*list of atomman.System*) - Systems corresponding to
          the minima identified in the Ecoh_values.
    """

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return results_dict
def dislocationmonopole(lammps_command, system, potential, burgers,
                        C, mpi_command=None, axes=None, m=[0,1,0], n=[0,0,1],
                        lineboxvector='a', randomseed=None,
                        etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000,
                        dmax=uc.set_in_units(0.01, 'angstrom'),
                        annealtemp=0.0, bshape='circle',
                        bwidth=uc.set_in_units(10, 'angstrom')):
    """
    Creates and relaxes a dislocation monopole system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The bulk system to add the defect to.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    burgers : list or numpy.array of float
        The burgers vector for the dislocation being added.
    C : atomman.ElasticConstants
        The system's elastic constants.
    mpi_command : str or None, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    axes : numpy.array of float or None, optional
        The 3x3 axes used to rotate the system by during creation.  If given,
        will be used to transform burgers and C from the standard
        crystallographic orientations to the system's Cartesian units.
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    annealtemp : float, optional
        The temperature to perform a dynamic relaxation at. (Default is 0.0,
        which will skip the dynamic relaxation.)
    bshape : str, optional
        The shape to make the boundary region.  Options are 'circle' and
        'rect' (default is 'circle').
    bwidth : float, optional
        The minimum thickness of the boundary region (default is 10
        Angstroms).
    
    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.
    """
    # Initialize results dict
    results_dict = {}
    
    # Save initial perfect system
    system.dump('atom_dump', f='base.dump')
    results_dict['dumpfile_base'] = 'base.dump'
    results_dict['symbols_base'] = system.symbols
    
    # Solve Stroh method for dislocation
    stroh = am.defect.Stroh(C, burgers, axes=axes, m=m, n=n)
    results_dict['Stroh_preln'] = stroh.preln
    results_dict['Stroh_K_tensor'] = stroh.K_tensor
    
    # Generate dislocation system by displacing atoms
    disp = stroh.displacement(system.atoms.pos)
    system.atoms.pos += disp
    
    # Apply fixed boundary conditions
    system = disl_boundary_fix(system, bwidth, bshape=bshape, lineboxvector=lineboxvector, m=m, n=n)
    
    # Relax system
    relaxed = disl_relax(lammps_command, system, potential,
                         mpi_command = mpi_command, 
                         annealtemp = annealtemp,
                         etol = etol, 
                         ftol = ftol, 
                         maxiter = maxiter, 
                         maxeval = maxeval)
    
    # Save relaxed dislocation system with original box vects
    system_disl = am.load('atom_dump', relaxed['dumpfile'], symbols=system.symbols)
    
    system_disl.box_set(vects=system.box.vects, origin=system.box.origin)
    system_disl.dump('atom_dump', f='disl.dump')
    results_dict['dumpfile_disl'] = 'disl.dump'
    results_dict['symbols_disl'] = system_disl.symbols
    
    results_dict['E_total_disl'] = relaxed['E_total']
    
    # Cleanup files
    os.remove('0.dump')
    os.remove(relaxed['dumpfile'])
    for dumpjsonfile in glob.iglob('*.dump.json'):
        os.remove(dumpjsonfile)
    
    return results_dict
def stackingfault(lammps_command, system, potential,
                  mpi_command=None, cutboxvector=None, 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')):
    """
    Computes the generalized stacking fault value 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.
    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).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'E_gsf'** (*float*) - The stacking fault formation energy.
        - **'E_total_0'** (*float*) - The total potential energy of the
          system before applying the faultshift.
        - **'E_total_sf'** (*float*) - The total potential energy of the
          system after applying the faultshift.
        - **'delta_disp'** (*float*) - The change in the center of mass
          difference between before and after applying the faultshift.
        - **'disp_0'** (*float*) - The center of mass difference between atoms
          above and below the fault plane in the cutboxvector direction for
          the system before applying the faultshift.
        - **'disp_sf'** (*float*) - The center of mass difference between 
          atoms above and below the fault plane in the cutboxvector direction
          for the system after applying the faultshift.
        - **'A_fault'** (*float*) - The area of the fault surface.
        - **'dumpfile_0'** (*str*) - The name of the LAMMMPS dump file
          associated with the relaxed system before applying the faultshift.
        - **'dumpfile_sf'** (*str*) - The name of the LAMMMPS dump file
          associated with the relaxed system after applying the faultshift.
    """
    
    # Evaluate the system without shifting along the fault plane
    zeroshift = stackingfaultpoint(lammps_command, system, potential,
                                   mpi_command=mpi_command,
                                   cutboxvector=cutboxvector,
                                   faultpos=faultpos,
                                   etol=etol, ftol=ftol, maxiter=maxiter,
                                   maxeval=maxeval, dmax=dmax,
                                   faultshift=[0.0, 0.0, 0.0])
    
    # Extract terms
    E_total_0 = zeroshift['E_total']
    disp_0 = zeroshift['disp']
    A_fault = zeroshift['A_fault']
    shutil.move('log.lammps', 'zeroshift-log.lammps')
    shutil.move(zeroshift['dumpfile'], 'zeroshift.dump')
    
    # Evaluate the system after shifting along the fault plane
    shifted = stackingfaultpoint(lammps_command, system, potential,
                                 mpi_command=mpi_command,
                                 cutboxvector=cutboxvector,
                                 faultpos=faultpos, etol=etol, ftol=ftol,
                                 maxiter=maxiter, maxeval=maxeval, dmax=dmax,
                                 faultshift=faultshift)
    
    # Extract terms
    E_total_sf = shifted['E_total']
    disp_sf = shifted['disp']
    shutil.move('log.lammps', 'shifted-log.lammps')
    shutil.move(shifted['dumpfile'], 'shifted.dump')

    # Compute the stacking fault energy
    E_gsf = (E_total_sf - E_total_0) / A_fault
    
    # Compute the change in displacement normal to fault plane
    delta_disp = disp_sf - disp_0
    
    # Return processed results
    results = {}
    results['E_gsf'] = E_gsf
    results['E_total_0'] = E_total_0
    results['E_total_sf'] = E_total_sf
    results['delta_disp'] = delta_disp
    results['disp_0'] = disp_0
    results['disp_sf'] = disp_sf
    results['A_fault'] = A_fault
    results['dumpfile_0'] = 'zeroshift.dump'
    results['dumpfile_sf'] = 'shifted.dump'
    
    return results
def 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
示例#47
0
文件: load.py 项目: samk07/atomman
def load(data,
         pbc=(True, True, True),
         symbols=None,
         atom_style='atomic',
         units='metal'):
    """
    Read a LAMMPS-style atom data file.
    
    Parameters
    ----------
    data : str or file-like object
        The atom data content to read.  Can be str content, path name, or open
        file-like object.
    pbc : list of bool
        Three boolean values indicating which System directions are periodic.
        Default value is (True, True, True).
    symbols : tuple, optional
        Allows the list of element symbols to be assigned during loading.
    atom_style :str
        The LAMMPS atom_style option associated with the data file.  Default
        value is 'atomic'.
    units : str 
        The LAMMPS units option associated with the data file. Default value
        is 'metal'.
    
    Returns
    -------
    atomman.System
        The corresponding system.  Note all property values will be
        automatically converted to atomman.unitconvert's set working units.
    """

    # Get units information
    units_dict = style.unit(units)

    # Initialize parameter values
    atomsstart = None
    velocitiesstart = None
    xy = 0.0
    xz = 0.0
    yz = 0.0

    # Read str and files in the same way
    with uber_open_rmode(data) as fp:

        # Loop over all lines in fp
        for i, line in enumerate(fp):
            try:
                line = line.decode('UTF-8')
            except:
                pass

            # Remove comments after '#'
            try:
                comment_index = line.index('#')
            except:
                pass
            else:
                line = line[:comment_index]

            terms = line.split()

            # Skip blank lines
            if len(terms) > 0:

                # Read number of atoms
                if len(terms) == 2 and terms[1] == 'atoms':
                    natoms = int(terms[0])

                # Read number of atom types
                elif len(terms
                         ) == 3 and terms[1] == 'atom' and terms[2] == 'types':
                    #natypes = int(terms[0])
                    pass

                # Read boundary info
                elif len(terms
                         ) == 4 and terms[2] == 'xlo' and terms[3] == 'xhi':
                    xlo = uc.set_in_units(float(terms[0]),
                                          units_dict['length'])
                    xhi = uc.set_in_units(float(terms[1]),
                                          units_dict['length'])
                elif len(terms
                         ) == 4 and terms[2] == 'ylo' and terms[3] == 'yhi':
                    ylo = uc.set_in_units(float(terms[0]),
                                          units_dict['length'])
                    yhi = uc.set_in_units(float(terms[1]),
                                          units_dict['length'])
                elif len(terms
                         ) == 4 and terms[2] == 'zlo' and terms[3] == 'zhi':
                    zlo = uc.set_in_units(float(terms[0]),
                                          units_dict['length'])
                    zhi = uc.set_in_units(float(terms[1]),
                                          units_dict['length'])
                elif len(terms) == 6 and terms[3] == 'xy' and terms[
                        4] == 'xz' and terms[5] == 'yz':
                    xy = uc.set_in_units(float(terms[0]), units_dict['length'])
                    xz = uc.set_in_units(float(terms[1]), units_dict['length'])
                    yz = uc.set_in_units(float(terms[2]), units_dict['length'])

                # Identify starting line number for Atoms data
                elif len(terms) == 1 and terms[0] == 'Atoms':
                    atomsstart = i + 1

                # Identify starting line number for Velocity data
                elif len(terms) == 1 and terms[0] == 'Velocities':
                    velocitiesstart = i + 1

    # Create system
    box = Box(xlo=xlo,
              xhi=xhi,
              ylo=ylo,
              yhi=yhi,
              zlo=zlo,
              zhi=zhi,
              xy=xy,
              xz=xz,
              yz=yz)
    atoms = Atoms(natoms=natoms)
    system = System(box=box, atoms=atoms, pbc=pbc)

    # Read in Atoms info
    if atomsstart is not None:
        prop_info = atoms_prop_info(atom_style, units)
        system = load_table(data,
                            box=system.box,
                            system=system,
                            symbols=symbols,
                            prop_info=prop_info,
                            skiprows=atomsstart,
                            nrows=natoms,
                            comment='#')
    else:
        raise ValueError('No Atoms section found!')

    # Read in Velocities info
    if velocitiesstart is not None:
        prop_info = velocities_prop_info(atom_style, units)
        system = load_table(data,
                            box=system.box,
                            system=system,
                            prop_info=prop_info,
                            skiprows=velocitiesstart,
                            nrows=natoms)

    return system
示例#48
0
def load(data, prop_info=None):
    """
    Read a LAMMPS-style dump file and return a System.
    
    Argument:
    data = file name, file-like object or string to read data from.
    
    Keyword Argument:
    prop_info -- DataModelDict for relating the per-atom properties to/from the dump file and the System. Will create a default json instance <data>.json if prop_info is not given and <data>.json doesn't already exist.
    """
    
    #read in prop_info if supplied
    if prop_info is not None:
        if isinstance(prop_info, (str, unicode)) and os.path.isfile(prop_info):
            with open(prop_info) as f:
                prop_info = f.read()
        prop_info = DataModelDict(prop_info)
    
    #check for default prop_info file
    else:
        try:
            with open(data+'.json') as fj:
                prop_info = DataModelDict(fj)
        except:
            prop_info = None
            box_unit = None
   
    #read box_unit if specified in prop_info
    if prop_info is not None:
        prop_info = prop_info.find('LAMMPS-dump-atoms_prop-relate')
        box_unit = prop_info['box_prop'].get('unit', None)

    with uber_open_rmode(data) as f:
        pbc = None
        box = None
        natoms = None
        system = None
        
        readnatoms = False
        readatoms = False
        readtimestep = False
        acount = 0
        bcount = 3
        
        #loop over all lines in file
        for line in f:
            terms = line.split()
            if len(terms) > 0:
    
                #read atomic values if time to do so
                if readatoms:
                    #sort values by a_id and save to prop_vals
                    a_id = long(terms[id_index]) - 1
                    prop_vals[a_id] = terms
                    acount += 1
                    
                    #save values to sys once all atoms read in
                    if acount == natoms:
                        readatoms = False
                        
                        #cycle over the defined atoms_prop in prop_info
                        for prop, p_keys in prop_info['atoms_prop'].iteritems():
                            #set default keys
                            dtype = p_keys.get('dtype', None)
                            shape = p_keys.get('shape', None)
                            shape = (natoms,) + np.empty(shape).shape
                            
                            value = np.empty(shape)
                            
                            #cycle over the defined LAMMPS-attributes in prop_info
                            for attr, a_keys in prop_info['LAMMPS-attribute'].iteritems():
                                
                                #cycle over list of relations for each LAMMPS-attribute
                                for relation in a_keys.iteraslist('relation'):
                                    
                                    #if atoms prop and relation prop match
                                    if relation['prop'] == prop:
                                        #get unit and scale info
                                        unit = relation.get('unit', None)

                                        if unit == 'scaled':
                                            unit = None
                                            scale = True
                                        else:
                                            scale = False
                                        
                                        #find index of attribute in name_list
                                        a_index = name_list.index(attr) 
                                        #check if relation has index listed
                                        try:
                                            index = relation['index']
                                            if isinstance(index, list):
                                                index = (Ellipsis,) + tuple(index)
                                            else:
                                                index = (Ellipsis,) + (index,)
                                                
                                            value[index] = prop_vals[:, a_index]
                                        #scalar if no index
                                        except:
                                            value[:] = prop_vals[:, a_index]
                            #test if values are ints if dtype not specified
                            if dtype is None and np.allclose(np.asarray(value, dtype=int), value):
                                value = np.asarray(value, dtype=int)
                            else:
                                value = np.asarray(value, dtype=dtype)
                            
                            #save prop values to system
                            system.atoms_prop(key=prop, value=uc.set_in_units(value, unit), scale=scale)
                
                #read number of atoms if time to do so
                elif readnatoms:                
                    natoms = int(terms[0])
                    readnatoms = False
                
                elif readtimestep:
                    timestep = int(terms[0])
                    readtimestep = False
                
                #read x boundary condition values if time to do so
                elif bcount == 0:
                    xlo = uc.set_in_units(float(terms[0]), box_unit)
                    xhi = uc.set_in_units(float(terms[1]), box_unit)
                    if len(terms) == 3:
                        xy = uc.set_in_units(float(terms[2]), box_unit)
                    bcount += 1
                    
                #read y boundary condition values if time to do so
                elif bcount == 1:
                    ylo = uc.set_in_units(float(terms[0]), box_unit)
                    yhi = uc.set_in_units(float(terms[1]), box_unit)
                    if len(terms) == 3:
                        xz = uc.set_in_units(float(terms[2]), box_unit)
                    bcount += 1
                    
                #read z boundary condition values if time to do so
                elif bcount == 2:
                    zlo = uc.set_in_units(float(terms[0]), box_unit)
                    zhi = uc.set_in_units(float(terms[1]), box_unit)
                    if len(terms) == 3:
                        yz = uc.set_in_units(float(terms[2]), box_unit)
                        xlo = xlo - min((0.0, xy, xz, xy + xz))
                        xhi = xhi - max((0.0, xy, xz, xy + xz))
                        ylo = ylo - min((0.0, yz))
                        yhi = yhi - max((0.0, yz))
                        box = am.Box(xlo=xlo, xhi=xhi, ylo=ylo, yhi=yhi, zlo=zlo, zhi=zhi, xy=xy, xz=xz, yz=yz)
                    else:
                        box = am.Box(xlo=xlo, xhi=xhi, ylo=ylo, yhi=yhi, zlo=zlo, zhi=zhi) 
                    bcount += 1
                
                #if not time to read value, check the ITEM: header information
                else:
                    
                    #only consider ITEM: lines
                    if terms[0] == 'ITEM:':
                    
                        #ITEM: TIMESTEP indicates it is time to read the timestep
                        if terms[1] == 'TIMESTEP':
                            readtimestep = True                        
                        
                        #ITEM: NUMBER indicates it is time to read natoms
                        elif terms[1] == 'NUMBER':
                            readnatoms = True
                            
                        #ITEM: BOX gives pbc and indicates it is time to read box parameters
                        elif terms[1] == 'BOX':
                            pbc = [True, True, True]
                            for i in xrange(3):
                                if terms[i + len(terms) - 3] != 'pp':
                                    pbc[i] = False
                            bcount = 0
                            
                        #ITEM: ATOMS gives list of per-Atom property names and indicates it is time to read atomic values      
                        elif terms[1] == 'ATOMS':
                            assert box is not None,  'Box information not found'
                            assert natoms is not None,  'Number of atoms not found'
                            
                            #read list of property names
                            name_list = terms[2:]
                            id_index = name_list.index('id')
                            
                            #create empty array for reading property values
                            prop_vals = np.empty((natoms, len(name_list)))
                            
                            #create and save default prop_info Data Model if needed
                            if prop_info is None:                                                         
                                prop_info = __prop_info_default_load(name_list)
                                if isinstance(data, (str, unicode)) and len(data) < 80:
                                    with open(data+'.json', 'w') as fj:
                                        prop_info.json(fp=fj, indent=4)
                                prop_info = prop_info.find('LAMMPS-dump-atoms_prop-relate')
                            
                            #create system and flag that it is time to read data
                            system = am.System(atoms=am.Atoms(natoms=natoms), box=box, pbc=pbc)
                            system.prop['timestep'] = timestep
                            readatoms = True 
    if system is None:
        raise ValueError('Failed to properly load dump file '+str(data)[:50])
    
    return system      
示例#49
0
 def __set(self, x, disregistry, gamma, axes, K_tensor, **kwargs):
     """
     Sets default values for class parameters.
     
     Parameters
     ----------
     x : numpy.ndarray
         An array of shape (N) giving the x coordinates corresponding to
         the disregistry solution.
     disregistry : numpy.ndarray
         A (N,3) array giving the initial disregistry vector guess at each
         x coordinate.
     gamma : atomman.defect.GammaSurface
         The gamma surface (stacking fault map) to use for computing the
         misfit energy.
     axes : numpy.ndarray
         A (3,3) array defining the crystal directions of the dislocation
         system. Used to orient the disregistry vector to the gamma surface
         in computing the misfit energy.
     K_tensor : numpy.ndarray
         A (3,3) array giving the anisotropic elastic energy coefficients
         corresponding to the dislocation system's orientation.  Can be
         computed with atomman.defect.Stroh.
     tau : numpy.ndarray, optional
         A (3,3) array giving the stress tensor to apply to the system
         using the stress energy term.  Only the xy, yy, and yz components
         are used.  Default value is all zeros.
     alpha : list of float, optional
         The alpha coefficient(s) used by the nonlocal energy term.  Default
         value is [0.0].
     beta : numpy.ndarray, optional
         The (3,3) array of beta coefficient(s) used by the surface energy
         term.  Default value is all zeros.
     cutofflongrange : float, optional
         The cutoff distance to use for computing the long-range energy.
         Default value is 1000 angstroms.
     burgers : numpy.ndarray, optional
         The (3,) array of the dislocation's Burgers vector relative to the
         dislocation system.  Used only by the long-range energy.  Default
         value is all zeros (long-range energy will be excluded).
     fullstress : bool, optional
         Flag indicating which stress energy algorithm to use.  Default
         value is True.
     cdiffelastic : bool, optional
         Flag indicating if the dislocation density for the elastic energy
         component is computed with central difference (True) or simply
         neighboring values (False).  Default value is False.
     cdiffsurface : bool, optional
         Flag indicating if the dislocation density for the surface energy
         component is computed with central difference (True) or simply
         neighboring values (False).  Default value is True.
     cdiffstress : bool, optional
         Flag indicating if the dislocation density for the stress energy
         component is computed with central difference (True) or simply
         neighboring values (False).  Only matters if fullstress is True.
         Default value is False.
     min_method : str, optional
         The scipy.optimize.minimize method to use.  Default value is
         'Powell'.
     min_options : dict, optional
         Any options to pass on to scipy.optimize.minimize. Default value
         is {}.
     """
     
     # Set mandatory parameters
     self.__x = x
     self.__disregistry = disregistry
     self.__gamma = gamma
     self.__axes = axes
     self.__K_tensor = K_tensor
     
     # Set optional keyword parameters
     self.__tau = kwargs.get('tau', np.zeros((3,3)))
     self.__alpha = kwargs.get('alpha', [0.0])
     if not isinstance(self.__alpha, list):
         self.__alpha = [self.__alpha]
     self.__beta = kwargs.get('beta', np.zeros((3,3)))
     self.__cutofflongrange = kwargs.get('cutofflongrange',
                                         uc.set_in_units(1000, 'angstrom'))
     self.__burgers = kwargs.get('burgers', np.zeros(3))
     self.__fullstress = kwargs.get('fullstress', True)
     self.__cdiffelastic = kwargs.get('cdiffelastic', False)
     self.__cdiffsurface = kwargs.get('cdiffsurface', True)
     self.__cdiffstress = kwargs.get('cdiffstress', False)
     self.__min_method = kwargs.get('min_method', 'Powell')
     self.__min_options = kwargs.get('min_options', {})
示例#50
0
    def make_screw_plate_old(self,
                             size=[40, 60, 3],
                             rad=[100, 115],
                             move=[0., 0., 0.],
                             filename="lmp_init.txt",
                             opt=None):
        alat = uc.set_in_units(self.pot['lattice'], 'angstrom')
        C11 = uc.set_in_units(self.pot['c11'], 'GPa')
        C12 = uc.set_in_units(self.pot['c12'], 'GPa')
        C44 = uc.set_in_units(self.pot['c44'], 'GPa')

        axes = np.array([[1, -2, 1], [1, 0, -1], [1, 1, 1]])

        unitx = alat * np.sqrt(6)
        unity = alat * np.sqrt(2)
        sizex = size[0]
        sizey = size[1]

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

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

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

        # shftx = 0.5 * alat * np.sqrt(6.) / 3.
        shftx = 0.0
        shfty = -1. / 3. * alat * np.sqrt(2) / 2.
        # shfty = 2. / 3. * alat * np.sqrt(2) / 2.

        center = [0.5 * unitx * sizex, 0.5 * unity * sizey]
        new_pos = system.atoms_prop(key='pos') + np.array([shftx, shfty, 0.0])
        system.atoms_prop(key='pos', value=new_pos)
        system.supersize((0, sizex), (0, sizey), (0, sizez))

        # to make a plate #
        radius2 = rad[0] * rad[0]
        radiusout2 = rad[1] * rad[1]

        elements = []
        for atom in system.atoms:
            elements.append('Nb')

        ase_atoms = am.convert.ase_Atoms.dump(system, elements)
        pos = ase_atoms.get_positions()
        delindex = []
        for i in range(len(pos)):
            atom = ase_atoms[i]
            dx = pos[i, 0] - center[0]
            dy = pos[i, 1] - center[1]
            r = dx * dx + dy * dy

            if r > radiusout2:
                delindex.append(atom.index)
            if r < radius2:
                atom.symbol = 'W'
        del ase_atoms[delindex]

        (system, elements) = am.convert.ase_Atoms.load(ase_atoms)

        # use neb, it's to generate init configuration
        if opt in ['neb']:
            system_init = copy.deepcopy(system)

            shift = np.array([-0.5, -0.5, 0.0])
            new_pos = system_init.atoms_prop(key='pos', scale=True) + shift
            system_init.atoms_prop(key='pos', value=new_pos, scale=True)

            disp = stroh.displacement(system_init.atoms_prop(key='pos'))
            system_init.atoms_prop(key='pos',
                                   value=system_init.atoms_prop(key='pos') +
                                   disp)

            shift = np.array([0.5, 0.50, 0.0])
            new_pos = system_init.atoms_prop(key='pos', scale=True) + shift
            system_init.atoms_prop(key='pos', value=new_pos, scale=True)

            # for lammps read structure
            lmp.atom_data.dump(system_init, "init.txt")

        # for dd map plot
        ase.io.write("lmp_perf.cfg", images=ase_atoms, format='cfg')
        lmp.atom_data.dump(system, "lmp_perf.txt")

        shift = np.array([-0.5, -0.5, 0.0])
        new_pos = system.atoms_prop(key='pos', scale=True) + shift
        system.atoms_prop(key='pos', value=new_pos, scale=True)

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

        disp = stroh.displacement(system.atoms_prop(key='pos'))

        # pull
        pull = False
        if pull is True:
            core_rows = [disp[:, 2].argsort()[-3:][::-1]]
            print(disp[core_rows])
            exclus = np.arange(len(disp), dtype=int)
            unitburger = np.mean(disp[core_rows][:, 2])
            print(unitburger)
            exclus = np.delete(exclus, core_rows)
            disp[core_rows] -= 1. / 3. * unitburger
            # disp[exclus] -= 1. / 3. * unitburger

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

        new_pos = system.atoms_prop(key='pos') - move
        system.atoms_prop(key='pos', value=new_pos)

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

        new_pos = system.atoms_prop(key='pos', scale=False)

        # for lammps read structure
        lmp.atom_data.dump(system, filename)

        dis_atoms = am.convert.ase_Atoms.dump(system, elements)
        return (ase_atoms, dis_atoms)
def relax_system(lammps_command, system, potential,
                 mpi_command=None, etol=0.0, ftol=0.0, maxiter=10000,
                 maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Sets up and runs the min.in LAMMPS script for performing an energy/force
    minimization to relax a system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The name of the LAMMPS log file.
        - **'initialdatafile'** (*str*) - The name of the LAMMPS data file
          used to import an inital configuration.
        - **'initialdumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the inital configuration.
        - **'finaldumpfile'** (*str*) - The name of the LAMMPS dump file
          corresponding to the relaxed configuration.
        - **'potentialenergy'** (*float*) - The total potential energy of
          the relaxed system.
    """
    
    # Ensure all atoms are within the system's box
    system.wrap()
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
      
    #Get lammps version date
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    
    # Generate system and pair info
    system_info = system.dump('atom_data', f='system.dat',
                              units=potential.units,
                              atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols)
    
    # Pass in run parameters
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'min.template'
    lammps_script = 'min.in'
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    results = {}
    results['logfile'] = 'log.lammps'
    results['initialdatafile'] = 'system.dat'
    results['initialdumpfile'] = 'atom.0'
    results['finaldumpfile'] = 'atom.%i' % thermo.Step.values[-1]
    results['potentialenergy'] = uc.set_in_units(thermo.PotEng.values[-1],
                                                 lammps_units['energy'])
    
    return results
def 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
示例#53
0
def diatom_scan(lammps_command: str,
                potential: am.lammps.Potential,
                symbols: list,
                mpi_command: Optional[str] = None,
                rmin: float = uc.set_in_units(0.02, 'angstrom'),
                rmax: float = uc.set_in_units(6.0, 'angstrom'),
                rsteps: int = 300) -> dict:
    """
    Performs a diatom energy scan over a range of interatomic spaces, r.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    symbols : list
        The potential symbols associated with the two atoms in the diatom.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    rmin : float, optional
        The minimum r spacing to use (default value is 0.02 angstroms).
    rmax : float, optional
        The maximum r spacing to use (default value is 6.0 angstroms).
    rsteps : int, optional
        The number of r spacing steps to evaluate (default value is 300).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'r_values'** (*numpy.array of float*) - All interatomic spacings,
          r, explored.
        - **'energy_values'** (*numpy.array of float*) - The computed potential
          energies for each r value.
    """

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

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

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

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

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

    # Define lammps variables
    lammps_variables = {}

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

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

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

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

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

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

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

    return results_dict
示例#54
0
文件: load.py 项目: usnistgov/atomman
def load(data, symbols=None, lammps_units='metal', prop_name=None,
         table_name=None, shape=None, unit=None, dtype=None,
         prop_info=None, return_prop_info=False):
    """
    Reads in a LAMMPS atomic dump file into a System.
    
    Parameters
    ----------
    data : str or file-like object
        The content, file path or file-like object containing the content to
        read.
    symbols : tuple, optional
        Allows the list of element symbols to be assigned during loading.
    lammps_units : str
        The LAMMPS units option associated with the parameters.  Default value
        is 'metal'.
    prop_name : list, optional
         The Atoms properties to generate.
    table_name : list, optional
        The table column name(s) that correspond to each prop_name.  If
        prop_name, table_name and prop_info are not given, prop_name and
        table_name will be read in from data.
    shape : list, optional
        The shape of each per-atom property.  If not given, will be taken from
        standard LAMMPS parameter names, or left at () for direct 
        property-table conversion.
    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, all unit values will be set to None
        (i.e. no conversions).
    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.
    return_prop_info : bool, optional
        Flag indicating if the full prop_info is to be returned.  Default value
        is False.
        
    Returns
    -------
    system : atomman.System
        The generated system.
    prop_info : list of dict
        The full prop_info detailing the property-table conversion. Returned
        if return_prop_info is True.
    """
    
    lammps_unit = style.unit(lammps_units)
    
    # Initialize parameter values
    pbc = None
    box = None
    natoms = None
    atomsstart = None
    xy = 0.0
    xz = 0.0
    yz = 0.0
    
    readnatoms = False
    readtimestep = False
    bcount = 3
    
    # Read str and files in the same way
    with uber_open_rmode(data) as fp:
        
        # Loop over all lines in fp
        for i, line in enumerate(fp):
            terms = line.decode('UTF-8').split()
            
            # Skip blank lines
            if len(terms) > 0:
                
                # Read number of atoms if time to do so
                if readnatoms:
                    natoms = int(terms[0])
                    readnatoms = False
                
                # Read timestep if time to do so
                elif readtimestep:
                    #timestep = int(terms[0])
                    readtimestep = False
                
                # Read x boundary condition values if time to do so
                elif bcount == 0:
                    xlo = uc.set_in_units(float(terms[0]), lammps_unit['length'])
                    xhi = uc.set_in_units(float(terms[1]), lammps_unit['length'])
                    if len(terms) == 3:
                        xy = uc.set_in_units(float(terms[2]),
                                             lammps_unit['length'])
                    bcount += 1
                
                # Read y boundary condition values if time to do so
                elif bcount == 1:
                    ylo = uc.set_in_units(float(terms[0]), lammps_unit['length'])
                    yhi = uc.set_in_units(float(terms[1]), lammps_unit['length'])
                    if len(terms) == 3:
                        xz = uc.set_in_units(float(terms[2]),
                                             lammps_unit['length'])
                    bcount += 1
                
                # Read z boundary condition values if time to do so
                elif bcount == 2:
                    zlo = uc.set_in_units(float(terms[0]), lammps_unit['length'])
                    zhi = uc.set_in_units(float(terms[1]), lammps_unit['length'])
                    if len(terms) == 3:
                        yz = uc.set_in_units(float(terms[2]),
                                             lammps_unit['length'])
                        
                        # Convert from max, min to hi, lo
                        xlo = xlo - min((0.0, xy, xz, xy + xz))
                        xhi = xhi - max((0.0, xy, xz, xy + xz))
                        ylo = ylo - min((0.0, yz))
                        yhi = yhi - max((0.0, yz))
                    bcount += 1
                
                # Otherwise, only check lines starting with ITEM
                elif terms[0] == 'ITEM:':
                    
                    # ITEM: TIMESTEP indicates it is time to read the timestep
                    if terms[1] == 'TIMESTEP':
                        readtimestep = True
                    
                    # ITEM: NUMBER indicates it is time to read natoms
                    elif terms[1] == 'NUMBER':
                        readnatoms = True
                    
                    # ITEM: BOX gives pbc and indicates it is time to read box parameters
                    elif terms[1] == 'BOX':
                        pbc = [True, True, True]
                        for i in range(3):
                            if terms[i + len(terms) - 3] != 'pp':
                                pbc[i] = False
                        bcount = 0
                        
                    # ITEM: ATOMS gives list of property names and indicates it is time to read atomic values
                    elif terms[1] == 'ATOMS':
                        
                        # Read list of property names
                        name_list = terms[2:]
                        
                        # Create default prop_name and table_name if needed
                        if prop_info is None and prop_name is None:
                            assert table_name is None, 'table_name cannot be given without prop_name'
                            prop_name, table_name = matchprops(name_list)
                            
                        atomsstart = i + 1
    
    # Create system
    box = Box(xlo=xlo, xhi=xhi,
              ylo=ylo, yhi=yhi,
              zlo=zlo, zhi=zhi,
              xy=xy, xz=xz, yz=yz)
    atoms = Atoms(natoms=natoms)
    system = System(box=box, atoms=atoms, pbc=pbc)
    
    # Generate prop_info
    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)
    
    # Remove duplicate pos fields
    firstpos = True
    short_prop_info = []
    for pinfo in prop_info:
        if pinfo['prop_name'] in ['pos', 'spos', 'upos', 'supos']:
            if firstpos:
                pinfo['prop_name'] = 'pos'
            else:
                continue
        short_prop_info.append(pinfo)
    
    # Read atoms into system
    system = load_table(data, box=system.box, symbols=symbols, system=system,
                        prop_info=short_prop_info, skiprows=atomsstart,
                        nrows=natoms)
    
    if return_prop_info:
        return system, short_prop_info
    else:
        return system
示例#55
0
文件: load.py 项目: usnistgov/atomman
def load(table, box, symbols=None, system=None, prop_name=None, table_name=None,
         shape=None, unit=None, dtype=None, prop_info=None, skiprows=None,
         nrows=None, comment=None):
    """
    Reads in tabular data into atomic properties.
    
    Parameters
    ----------
    table : str or file-like object
        The table content, file path or file-like object containing the
        content to read.
    box : atomman.Box
        The atomic box to use when generating a System around the data.
    symbols : tuple, optional
        Allows the list of element symbols to be assigned during loading.
    system : atomman.System, optional
        The atomic system to load the values to.  If not given, a new system
        will be constructed.
    prop_name : list, optional
         The Atoms properties to generate.  Must be given if prop_info is not.
    table_name : list, optional
        The table column name(s) that correspond to each prop_name.  If not
        given, the table_name values will be based on the prop_name 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, all unit values will be set to None
        (i.e. no conversions).
    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.
    skiprows : int, optional
        Number of rows to skip before reading the data.
    nrows : int, optional
        Number of rows of data to read.
    comment : str, optional
        Specifies a character which indicates all text on a given line after
        is to be considered to be a comment and ignored by parser. This is
        often '#'.
        
    Returns
    -------
    atomman.System
        The generated system.
    """
    # 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)
    
    # Build list of all table_names
    table_name = []
    for prop in prop_info:
        table_name += prop['table_name']
    
    # Read in table to dataframe
    with uber_open_rmode(table) as f:
        df = pd.read_csv(f, delim_whitespace=True, names=table_name, skiprows=skiprows,
                         nrows=nrows, comment=comment)
    if 'id' in df:
        df = df.sort_values('id')
    
    # Generate System
    natoms = len(df)
    if system is None:
        system = System(atoms=Atoms(natoms=natoms), box=box)
    
    # Loop over all properties
    for prop in prop_info:
        pname = prop['prop_name']
        if pname == 'a_id':
            continue
        
        # Get values
        value = df[prop['table_name']].values.reshape((natoms,) + prop['shape'])
        
        if prop['unit'] is not None:
            if prop['unit'] == "scaled":
                value = system.unscale(value)
            else:
                value = uc.set_in_units(value, prop['unit'])
        value = np.asarray(value, dtype=prop['dtype'])
        system.atoms.view[pname] = value
    
    # Set symbols if given
    if symbols is not None:
        system.symbols = symbols
    
    return system
示例#56
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)
示例#57
0
    def __init__(self,
                 volterra=None,
                 gamma=None,
                 model=None,
                 tau=np.zeros((3, 3)),
                 alpha=0.0,
                 beta=np.zeros((3, 3)),
                 cutofflongrange=None,
                 fullstress=True,
                 cdiffelastic=False,
                 cdiffsurface=True,
                 cdiffstress=False,
                 min_method='Powell',
                 min_kwargs=None,
                 min_options=None):
        """
        Initializes an SDVPN object.
        
        Parameters
        ----------
        volterra : atomman.defect.VolterraDislocation, optional
            The elastic solution for a Volterra dislocation to use as the basis
            of the model. Either volterra or model are required, and both cannot
            be given at the same time.
        gamma : atomman.defect.GammaSurface, optional
            The gamma surface to use for the solution.  Required unless model
            is given and the model content contains gamma surface data.
        model : str or DataModelDict, optional
            Saved data from previous SDVPN runs to load.  Either volterra or
            model are required, and both cannot be given at the same time.
        tau : numpy.ndarray, optional
            A (3,3) array giving the stress tensor to apply to the system
            using the stress energy term.  Only the xy, yy, and yz components
            are used.  Default value is all zeros.
        alpha : list of float, optional
            The alpha coefficient(s) used by the nonlocal energy term.  Default
            value is [0.0].
        beta : numpy.ndarray, optional
            The (3,3) array of beta coefficient(s) used by the surface energy
            term.  Default value is all zeros.
        cutofflongrange : float, optional
            The cutoff distance to use for computing the long-range energy.
            Default value is 1000 angstroms.
        fullstress : bool, optional
            Flag indicating which stress energy algorithm to use.  Default
            value is True.
        cdiffelastic : bool, optional
            Flag indicating if the dislocation density for the elastic energy
            component is computed with central difference (True) or simply
            neighboring values (False).  Default value is False.
        cdiffsurface : bool, optional
            Flag indicating if the dislocation density for the surface energy
            component is computed with central difference (True) or simply
            neighboring values (False).  Default value is True.
        cdiffstress : bool, optional
            Flag indicating if the dislocation density for the stress energy
            component is computed with central difference (True) or simply
            neighboring values (False).  Only matters if fullstress is True.
            Default value is False.
        min_method : str, optional
            The scipy.optimize.minimize method to use.  Default value is
            'Powell'.
        min_options : dict, optional
            Any options to pass on to scipy.optimize.minimize. Default value
            is {}.
        """

        # Load solution from existing model
        if model is not None:
            if volterra is not None:
                raise ValueError('model cannot be given with volterra')

            self.load(model, gamma=gamma)

        # Extract parameters and check solution compatibility
        elif volterra is not None:

            # Check that gamma is given
            if gamma is None:
                raise ValueError('gamma is required if volterra is given')

            # Check that burgers is in the slip plane
            if not np.isclose(np.dot(volterra.n, volterra.burgers), 0.0):
                raise ValueError(
                    'dislocation burgers vector must be in the slip plane')

            # Get m, n, ξ, K_tensor, burgers, and transform from volterra
            m, n, ξ = volterra.m, volterra.n, volterra.ξ
            K_tensor = volterra.K_tensor
            burgers = volterra.burgers
            transform = volterra.transform

            # Transform K_tensor, burgers and transform to [m,n,ξ] orientation
            mnξ = np.array(
                [m, n, ξ])  # This is transformation matrix to [m,n,ξ] setting
            K_tensor = mnξ.dot(K_tensor.dot(mnξ.T))
            burgers = mnξ.dot(burgers)
            transform = np.matmul(mnξ, transform)

            # Check if dislocation system is compatible with gamma surface
            planenormal = transform.dot(gamma.planenormal)
            if not np.isclose(planenormal[0], 0.0) or not np.isclose(
                    planenormal[2], 0.0):
                raise ValueError(
                    'different slip planes for gamma and volterra found')

            # Set basic solution definition
            self.__K_tensor = K_tensor
            self.__burgers = burgers
            self.__transform = transform
            self.__gamma = gamma

            # Set options
            self.tau = tau
            self.alpha = alpha
            self.beta = beta
            if cutofflongrange is None:
                self.cutofflongrange = uc.set_in_units(1000, 'angstrom')
            else:
                self.cutofflongrange = cutofflongrange
            self.fullstress = fullstress
            self.cdiffelastic = cdiffelastic
            self.cdiffsurface = cdiffsurface
            self.cdiffstress = cdiffstress
            self.min_method = min_method
            self.min_options = min_options
            self.min_kwargs = min_kwargs

        else:
            raise ValueError('either dislsol or model must be given')
示例#58
0
def dislocation_array(system, dislsol=None, m=None, n=None, burgers=None, bwidth=None, cutoff=None):
    """
    Method that converts a bulk crystal system into a periodic array of
    dislocations.  A single dislocation is inserted using a dislocation
    solution.  The system's box and pbc are altered such that the system is
    periodic and compatible across the two box vectors contained in the slip
    plane.  The third box vector is non-periodic, resulting in free surfaces
    parallel to the dislocation's slip plane.
    
    Parameters
    ----------
    system : atomman.System
        A perfect, bulk atomic system.
    dislsol : atomman.defect.Stroh or atomman.defect.IsotropicVolterra, optional
        A dislocation solution to use to displace atoms by.  If not given,
        all atoms will be given linear displacements associated with the
        long-range limits.
    m : array-like object, optional
        The dislocation solution m unit vector.  This vector is in the slip
        plane and perpendicular to the dislocation line direction.  Only needed
        if dislsol is not given.
    n : array-like object, optional
        The dislocation solution n unit vector.  This vector is normal to the 
        slip plane.  Only needed if dislsol is not given.
    burgers : array-like object, optional
        The Cartesian Burger's vector for the dislocation relative to the
        given system's Cartesian coordinates.  Only needed if dislsol is not
        given.
    bwidth : float, optional
        The width of the boundary region at the free surfaces.  Atoms within
        the boundaries will be displaced by linear displacements instead of
        by the dislocation solution.  Only given if dislsol is not None.
        Default value if dislsol is given is 10 Angstroms.
    cutoff : float, optional
        Cutoff distance to use for identifying duplicate atoms to remove.
        For dislocations with an edge component, applying the displacements
        creates an extra half-plane of atoms that will have (nearly) identical
        positions with other atoms after altering the boundary conditions.
        Default cutoff value is 0.5 Angstrom.
    """
    # Input parameter setup
    if dislsol is None:
        if m is None or n is None:
            raise ValueError('m and n are needed if no dislsol is given')
        try:
            assert np.isclose(np.linalg.norm(m), 1.0)
            assert np.isclose(np.linalg.norm(n), 1.0)
            assert np.isclose(m.dot(n), 0.0)
        except:
            raise ValueError('m and n must be perpendicular unit vectors')
        if bwidth is not None:
            raise ValueError('bwidth not allowed if dislsol is not given')
    else:
        m = dislsol.m
        n = dislsol.n
        burgers = dislsol.burgers
        if bwidth is None:
            bwidth = uc.set_in_units(10, 'angstrom')
    if cutoff is None:
        cutoff = uc.set_in_units(0.5, 'angstrom')
    
    # Extract system values
    pos = system.atoms.pos
    vects = system.box.vects
    spos = system.atoms_prop(key='pos', scale=True)
    
    # Identify system orientation indices
    motionindex = None
    lineindex = None
    pnormindex = None
    for i in range(3):
        if np.isclose(np.abs(vects[i].dot(np.cross(m, n))), np.linalg.norm(vects[i])):
            lineindex = i
        elif not np.isclose(vects[i].dot(n), 0.0):
            if pnormindex is not None:
                raise ValueError("Multiple box vectors have components normal to dislocation solution's slip plane")
            pnormindex = i
    if lineindex is None:
        raise ValueError("No box vectors found parallel to dislocation solution's line vector")
    if pnormindex is None:
        raise ValueError("No box vectors have components normal to dislocation solution's slip plane")
    motionindex = 3 - (lineindex + pnormindex)
    
    # Compute new box vects and pbc
    newvects = deepcopy(system.box.vects)
    if burgers[motionindex] > 0:
        newvects[motionindex] -= burgers / 2
    else:
        newvects[motionindex] += burgers / 2
    newbox = Box(vects=newvects, origin=system.box.origin)
    newpbc = [True, True, True]
    newpbc[pnormindex] = False
    
    # Generate a test system to identify duplicate atoms
    length = np.abs(vects[motionindex].dot(m))
    testsystem = System(atoms=deepcopy(system.atoms), box=newbox, pbc=newpbc, symbols=system.symbols)
    testsystem.atoms.pos += linear_displacement(pos, burgers, length, m, n)
    testsystem.atoms.old_id = range(testsystem.natoms)
    
    # Identify boundary atoms to check
    spos = testsystem.atoms_prop(key='pos', scale=True)
    sburgers = 2 * burgers[motionindex] / (length)
    boundaryatoms = testsystem.atoms[(spos[:, motionindex] < sburgers) | (spos[:, motionindex] > 1.0 - sburgers)]
    
    # Compare distances between boundary atoms to identify duplicates
    dup_atom_ids = []
    mins = []
    for ni, i in enumerate(boundaryatoms.old_id[:-1]):
        js = boundaryatoms.old_id[ni+1:]
        try:
            distances = np.linalg.norm(testsystem.dvect(i, js), axis=1)
            mindistance = distances.min()
        except:
            mindistance = np.linalg.norm(testsystem.dvect(i, js))
        if mindistance < cutoff:
            dup_atom_ids.append(i)
    ii = np.ones(system.natoms, dtype=bool)
    ii[dup_atom_ids] = False
    
    # Generate new system with duplicate atoms removed
    newsystem = System(atoms=system.atoms[ii], box=newbox, pbc=newpbc, symbols=system.symbols)
    
    # Check if number of atoms deleted matches expected value
    expected = len(pos[(pos[:, motionindex] >= 0.0) & (pos[:, motionindex] <= np.abs(burgers[motionindex]))]) // 2
    actual = system.natoms - newsystem.natoms
    if expected != actual:
        #warnings.warn('%i deleted atoms expected but %i atoms deleted' %(expected, actual), Warning)
        raise ValueError('Deleted atom mismatch: expected %i, actual %i. Adjust system dimensions and/or cutoff' %(expected, actual))
    if dislsol is None:
        # Use only linear displacements
        disp = linear_displacement(newsystem.atoms.pos, burgers, length, m, n)
    
    else:
        # Identify boundary atoms
        miny = system.box.origin.dot(n)
        maxy = miny + vects[pnormindex].dot(n)
        if maxy < miny:
            miny, maxy = maxy, miny
        y = newsystem.atoms.pos.dot(n)
        ii = np.where((y <= miny + bwidth) | (y >= maxy - bwidth))
        
        # Use dislsol in middle and linear displacements at boundary
        disp = dislsol.displacement(newsystem.atoms.pos)
        disp[:, pnormindex] -= disp[:, pnormindex].mean()
        disp[ii] = linear_displacement(newsystem.atoms.pos[ii], burgers, length, m, n)
    
    # Displace atoms and wrap
    newsystem.atoms.pos += disp
    newsystem.wrap()
    
    return newsystem
def dislocationmonopole(lammps_command,
                        system,
                        potential,
                        burgers,
                        C,
                        mpi_command=None,
                        axes=None,
                        m=[0, 1, 0],
                        n=[0, 0, 1],
                        lineboxvector='a',
                        randomseed=None,
                        etol=0.0,
                        ftol=0.0,
                        maxiter=10000,
                        maxeval=100000,
                        dmax=uc.set_in_units(0.01, 'angstrom'),
                        annealtemp=0.0,
                        bshape='circle',
                        bwidth=uc.set_in_units(10, 'angstrom')):
    """
    Creates and relaxes a dislocation monopole system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The bulk system to add the defect to.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    burgers : list or numpy.array of float
        The burgers vector for the dislocation being added.
    C : atomman.ElasticConstants
        The system's elastic constants.
    mpi_command : str or None, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    axes : numpy.array of float or None, optional
        The 3x3 axes used to rotate the system by during creation.  If given,
        will be used to transform burgers and C from the standard
        crystallographic orientations to the system's Cartesian units.
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  (Default is None which will select a
        random int between 1 and 900000000.)
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    annealtemp : float, optional
        The temperature to perform a dynamic relaxation at. (Default is 0.0,
        which will skip the dynamic relaxation.)
    bshape : str, optional
        The shape to make the boundary region.  Options are 'circle' and
        'rect' (default is 'circle').
    bwidth : float, optional
        The minimum thickness of the boundary region (default is 10
        Angstroms).
    
    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.
    """
    # Initialize results dict
    results_dict = {}

    # Save initial perfect system
    system.dump('atom_dump', f='base.dump')
    results_dict['dumpfile_base'] = 'base.dump'
    results_dict['symbols_base'] = system.symbols

    # Solve Stroh method for dislocation
    stroh = am.defect.Stroh(C, burgers, axes=axes, m=m, n=n)
    results_dict['Stroh_preln'] = stroh.preln
    results_dict['Stroh_K_tensor'] = stroh.K_tensor

    # Generate dislocation system by displacing atoms
    disp = stroh.displacement(system.atoms.pos)
    system.atoms.pos += disp

    # Apply fixed boundary conditions
    system = disl_boundary_fix(system,
                               bwidth,
                               bshape=bshape,
                               lineboxvector=lineboxvector,
                               m=m,
                               n=n)

    # Relax system
    relaxed = disl_relax(lammps_command,
                         system,
                         potential,
                         mpi_command=mpi_command,
                         annealtemp=annealtemp,
                         etol=etol,
                         ftol=ftol,
                         maxiter=maxiter,
                         maxeval=maxeval)

    # Save relaxed dislocation system with original box vects
    system_disl = am.load('atom_dump',
                          relaxed['dumpfile'],
                          symbols=system.symbols)

    system_disl.box_set(vects=system.box.vects, origin=system.box.origin)
    system_disl.dump('atom_dump', f='disl.dump')
    results_dict['dumpfile_disl'] = 'disl.dump'
    results_dict['symbols_disl'] = system_disl.symbols

    results_dict['E_total_disl'] = relaxed['E_total']

    # Cleanup files
    os.remove('0.dump')
    os.remove(relaxed['dumpfile'])
    for dumpjsonfile in glob.iglob('*.dump.json'):
        os.remove(dumpjsonfile)

    return results_dict
def stackingfaultmap(lammps_command, system, potential,
                     shiftvector1, shiftvector2, mpi_command=None,
                     numshifts1=11, numshifts2=11,
                     cutboxvector=None, faultpos=0.5,
                     etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000,
                     dmax=uc.set_in_units(0.01, 'angstrom')):
    """
    Computes a generalized stacking fault map for shifts along a regular 2D
    grid.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    shiftvector1 : list of floats or numpy.array
        One of the generalized stacking fault shifting vectors.
    shiftvector2 : list of floats or numpy.array
        One of the generalized stacking fault shifting vectors.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    cutboxvector : str, optional
        Indicates which of the three system box vectors, 'a', 'b', or 'c', to
        cut with a non-periodic boundary (default is 'c').
    numshifts1 : int, optional
        The number of equally spaced shiftfractions to evaluate along
        shiftvector1.
    numshifts2 : int, optional
        The number of equally spaced shiftfractions to evaluate along
        shiftvector2.
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'shift1'** (*numpy.array of float*) - The fractional shifts along
          shiftvector1 where the stacking fault was evaluated.
        - **'shift2'** (*numpy.array of float*) - The fractional shifts along
          shiftvector2 where the stacking fault was evaluated.
        - **'E_gsf'** (*numpy.array of float*) - The stacking fault formation
          energies measured for all the (shift1, shift2) coordinates.
        - **'delta_disp'** (*numpy.array of float*) - The change in the center
          of mass difference between before and after applying the faultshift
          for all the (shift1, shift2) coordinates.
        - **'A_fault'** (*float*) - The area of the fault surface.
    """
   
    # Start sf_df as empty list
    sf_df = []

    # Construct mesh of regular points
    shifts1, shifts2 = np.meshgrid(np.linspace(0, 1, numshifts1),
                                   np.linspace(0, 1, numshifts2))
    
    # Identify lammps_date version
    lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Loop over all shift combinations
    for shiftfraction1, shiftfraction2 in zip(shifts1.flat, shifts2.flat):
        
        # Evaluate the system at the shift
        sf_df.append(stackingfaultworker(lammps_command, system, potential,
                                         shiftvector1, shiftvector2,
                                         shiftfraction1, shiftfraction2,
                                         mpi_command=mpi_command,
                                         cutboxvector=cutboxvector,
                                         faultpos=faultpos,
                                         etol=etol,
                                         ftol=ftol,
                                         maxiter=maxiter,
                                         maxeval=maxeval,
                                         dmax=dmax,
                                         lammps_date=lammps_date))
    
    # Convert sf_df to pandas DataFrame
    sf_df = pd.DataFrame(sf_df)
    
    # Identify the zeroshift column
    zeroshift = sf_df[(np.isclose(sf_df.shift1, 0.0, atol=1e-10, rtol=0.0)
                     & np.isclose(sf_df.shift2, 0.0, atol=1e-10, rtol=0.0))]
    assert len(zeroshift) == 1, 'zeroshift simulation not uniquely identified'
    
    # Get zeroshift values
    E_total_0 = zeroshift.E_total.values[0]
    A_fault = zeroshift.A_fault.values[0]
    disp_0 = zeroshift.disp.values[0]
    
    # Compute the stacking fault energy
    E_gsf = (sf_df.E_total.values - E_total_0) / A_fault
    
    # Compute the change in displacement normal to fault plane
    delta_disp = sf_df.disp.values - disp_0
    
    results_dict = {}
    results_dict['shift1'] = sf_df.shift1.values
    results_dict['shift2'] = sf_df.shift2.values
    results_dict['E_gsf'] = E_gsf
    results_dict['delta_disp'] = delta_disp
    results_dict['A_fault'] = A_fault
    
    return results_dict