Пример #1
0
    def load(self, style, *args, **kwargs):
        """
        Wrapper around atomman.load() for loading files that also saves the
        file loading options as class attributes.  Any parameters not given
        will use the values already set to the object.
        """
        
        # Load ucell
        self.__ucell = am.load(style, *args, **kwargs)
        self.ucell.wrap()

        # Check if first variable positional argument is a file
        try:
            load_file = Path(args[0])
        except:
            self.load_file = None
        else:
            if load_file.is_file():
                self.load_file = load_file
            else:
                self.load_file = None

        # Set load style
        if self.load_file is None:
            self.load_style = style
        else:
            self.load_style = 'system_model'
Пример #2
0
    def load_model(self, model):
        """Loads subset attributes from an existing model."""
        sub = model[self.modelroot]

        d = {}
        d['family'] = sub['family']

        if 'artifact' in sub:
            if  'initial-atomic-system' in sub:
                ValueError('found both load file and embedded content for the initial system')
            d['load_style'] = sub['artifact']['format']
            d['load_file'] = sub['artifact']['file']
            load_options = sub['artifact'].get('load_options', None)
        elif 'initial-atomic-system' in sub:
            d['ucell'] = am.load('system_model', sub, key='initial-atomic-system')
        else:
            ValueError('neither load file nor embedded content found for the initial system')

        d['symbols'] = sub['symbol']
        d['composition'] = sub.get('composition', None)

        if load_options is not None:
            d['load_options'] = {}
            load_options_keys = ['key', 'index', 'data_set', 'pbc', 'atom_style',
                                'units', 'prop_info']
            d['load_options'] = termtodict(load_options, load_options_keys)
            if 'index' in d['load_options']:
                d['load_options']['index'] = int(d['load_options']['index'])

        self.set_values(**d)
Пример #3
0
def stacking_fault_generation(structure, sizemults, a1, a2, hkl, a1vect_uvw, a2vect_uvw):
    '''
    Generate stacking fault struture using Atomman.  
    Please note the below about Atomman:  
    One layer means one 'ucell' in the defect.StackingFault, along the 'hkl' direction.  
    So 'sizemults[2]=6' means 6 ucell above the stacking fault slide plane, and 6 layers below.  
    i.e. totally 12 ucell/layers in the surface system

    Args:
        structure: pymatgen structure
        sizemults: how to extend the struture when building surface
        a1: fraction of the slide movement along a1vect_uvu
        a2: fraction of the slide movement along a2vect_uvu
        hkl: the stacking fault slide plane
        a1vect_uvw: the stacking fault moving direction
        a2vect_uvw: the stacking fault moving direction

    Return:
        pymatgen structure containing stacking fault
    '''
    
    # generate stakcing fault object using atomman, and then change to pymatgen for random occupuation
    stru_atomman = atomman.load('pymatgen_Structure', structure)
    stack_fault = atomman.defect.StackingFault(hkl=hkl, ucell=stru_atomman, 
                                                a1vect_uvw=a1vect_uvw, a2vect_uvw=a2vect_uvw)
   
    # it is a bit wired that surface_sys is not used in the rest of the code,
    # but that is how atomman is designed
    surface_sys = stack_fault.surface(shift=stack_fault.shifts[0], even=True, 
                                        sizemults=sizemults, vacuumwidth=15)
    fault_sys = stack_fault.fault(a1=a1, a2=a2)
    fault_sys_pymatgen = Structure.from_str(fault_sys.dump('poscar'), fmt='poscar')
    return fault_sys_pymatgen
Пример #4
0
 def spg_ucell(self):
     """atomman.System: The unit cell identified following the space-group analysis"""
     if self.__spg_ucell is None:
         raise ValueError('No results yet!')
     if not isinstance(self.__spg_ucell, am.System):
         self.__spg_ucell = am.load('system_model', self.__spg_ucell)
     return self.__spg_ucell
Пример #5
0
 def load_system_defect(self):
     if self.__system_defect is None:
         fname = self.dumpfile_defect
         tar = self.database.get_tar(record=self)
         f = tar.extractfile(fname)
         self.__system_defect = am.load('atom_dump',
                                        f,
                                        symbols=self.symbols_defect)
Пример #6
0
    def todict(self, full=True, flat=False):
        """
        Converts the structured content to a simpler dictionary.
        
        Parameters
        ----------
        full : bool, optional
            Flag used by the calculation records.  A True value will include
            terms for both the calculation's input and results, while a value
            of False will only include input terms (Default is True).
        flat : bool, optional
            Flag affecting the format of the dictionary terms.  If True, the
            dictionary terms are limited to having only str, int, and float
            values, which is useful for comparisons.  If False, the term
            values can be of any data type, which is convenient for analysis.
            (Default is False).
            
        Returns
        -------
        dict
            A dictionary representation of the record's content.
        """
        # Fetch universal record params
        params = super().todict(full=full, flat=flat)
        
        crystal = self.content[self.contentroot]
        params['method'] = crystal['method']
        params['standing'] = crystal.get('standing', 'good')
        
        # Extract potential info
        subset('lammps_potential').todict(crystal, params, full=full, flat=flat)
        
        params['family'] = crystal['system-info']['family']
        params['parent_key'] = crystal['system-info']['parent_key']
        
        ucell = am.load('system_model', self.content, key='atomic-system')
        params['composition'] = ucell.composition

        #print('potential-energy' in crystal)
        if 'potential-energy' in crystal:
            params['E_pot'] = uc.value_unit(crystal['potential-energy'])
            params['E_coh'] = uc.value_unit(crystal['cohesive-energy'])
        else:
            params['E_coh'] = params['E_pot'] = uc.value_unit(crystal['cohesive-energy'])
        
        if flat is True:
            params['symbols'] = list(ucell.symbols)
            params['a'] = ucell.box.a
            params['b'] = ucell.box.b
            params['c'] = ucell.box.c
            params['alpha'] = ucell.box.alpha
            params['beta'] = ucell.box.beta
            params['gamma'] = ucell.box.gamma
            params['natoms'] = ucell.natoms
        else:
            params['ucell'] = ucell
        
        return params
Пример #7
0
 def load(self, style, input, **kwargs):
     """Load a System."""
     
     system, symbols = am.load(style, input, **kwargs)
     self.__box = system.box
     self.__atoms = system.atoms
     self.__pbc = system.pbc
     self.__prop = system.prop
     return symbols
Пример #8
0
 def min_cells(self):
     """list : atomman.Systems for the dimensions scanned with local energy minima."""
     if self.__min_cells is None:
         raise ValueError('No results yet!')
     for i in range(len(self.__min_cells)):
         if not isinstance(self.__min_cells[i], am.System):
             self.__min_cells[i] = am.load('system_model',
                                           self.__min_cells[i])
     return self.__min_cells
Пример #9
0
    def todict(self, full=True, flat=False):
        """
        Converts the structured content to a simpler dictionary.
        
        Parameters
        ----------
        full : bool, optional
            Flag used by the calculation records.  A True value will include
            terms for both the calculation's input and results, while a value
            of False will only include input terms (Default is True).
        flat : bool, optional
            Flag affecting the format of the dictionary terms.  If True, the
            dictionary terms are limited to having only str, int, and float
            values, which is useful for comparisons.  If False, the term
            values can be of any data type, which is convenient for analysis.
            (Default is False).
            
        Returns
        -------
        dict
            A dictionary representation of the record's content.
        """
        # Fetch universal record params
        params = super().todict(full=full, flat=flat)

        proto = self.content[self.contentroot]
        params['name'] = proto['name']
        params['prototype'] = proto['prototype']
        params['Pearson_symbol'] = proto['Pearson-symbol']
        params['Strukturbericht'] = proto['Strukturbericht']
        
        params['sg_number'] = proto['space-group']['number']
        params['sg_HG'] = proto['space-group']['Hermann-Maguin']
        params['sg_Schoen'] = proto['space-group']['Schoenflies']
        
        ucell = am.load('system_model', self.content, key='atomic-system')

        params['crystal_family'] = am.tools.identifyfamily(ucell.box)
        params['natypes'] = ucell.natypes
        
        if flat is True:
            params['a'] = ucell.box.a
            params['b'] = ucell.box.b
            params['c'] = ucell.box.c
            params['alpha'] = ucell.box.alpha
            params['beta'] = ucell.box.beta
            params['gamma'] = ucell.box.gamma
            params['natoms'] = ucell.natoms
        else:
            params['ucell'] = ucell
        
        return params
Пример #10
0
def assign_composition(df, database, lib_directory=None):
    """
    Assigns compositions to calculations.
    """
    # Build counts for available prototypes
    prototypes = database.get_records(style='crystal_prototype')
    counts = {}
    for prototype in prototypes:
        counts[prototype.name] = np.unique(
            prototype.content.finds('component'), return_counts=True)[1]

    # Set default lib_directory (for ref structures)
    if lib_directory is None:
        lib_directory = Settings().library_directory

    # Identify compositions
    compositions = []
    for i in range(len(df)):
        series = df.iloc[i]

        # Use ucell system if available (crystal_space_group)
        if 'ucell' in series:
            composition = series.ucell.composition
            if composition is not None:
                compositions.append(composition)
            else:
                compositions.append(np.nan)

        # Use symbols and family info if available (E_vs_r_scan, relax_*)
        elif 'symbols' in series and 'family' in series:

            # If family is a prototype
            if series.family in counts:
                compositions.append(
                    am.tools.compositionstr(series.symbols,
                                            counts[series.family]))

            # If family is a ref
            else:
                fname = Path(lib_directory, 'reference_crystal',
                             series.family + '.json')
                try:
                    ucell = am.load('system_model', fname)
                except:
                    compositions.append(np.nan)
                else:
                    count = np.unique(ucell.atoms.atype, return_counts=True)[1]
                    compositions.append(
                        am.tools.compositionstr(ucell.symbols, count))
        else:
            compositions.append(np.nan)
    df['composition'] = compositions
Пример #11
0
    def todict(self, full=True, flat=False):
        """
        Converts the structured content to a simpler dictionary.
        
        Parameters
        ----------
        full : bool, optional
            Flag used by the calculation records.  A True value will include
            terms for both the calculation's input and results, while a value
            of False will only include input terms (Default is True).
        flat : bool, optional
            Flag affecting the format of the dictionary terms.  If True, the
            dictionary terms are limited to having only str, int, and float
            values, which is useful for comparisons.  If False, the term
            values can be of any data type, which is convenient for analysis.
            (Default is False).
            
        Returns
        -------
        dict
            A dictionary representation of the record's content.
        """
        # Fetch universal record params
        params = super().todict(full=full, flat=flat)

        crystal = self.content[self.contentroot]
        params['sourcename'] = crystal['source']['name']
        params['sourcelink'] = crystal['source']['link']

        ucell = am.load('system_model', self.content, key='atomic-system')
        params['composition'] = ucell.composition
        params['natypes'] = ucell.natypes

        if flat is True:
            params['symbols'] = list(ucell.symbols)
            params['a'] = ucell.box.a
            params['b'] = ucell.box.b
            params['c'] = ucell.box.c
            params['alpha'] = ucell.box.alpha
            params['beta'] = ucell.box.beta
            params['gamma'] = ucell.box.gamma
            params['natoms'] = ucell.natoms
        else:
            params['ucell'] = ucell

        return params
Пример #12
0
    def load_dump(self, system1, atom_style=None, units=None, potential=None):
        """
        Utility function that dumps, loads, and dumps, then asserts two dumps
        are equivalent
        """
        # dump system1 to content1
        content1 = system1.dump('atom_data', atom_style=atom_style,
                                units=units, potential=potential,
                                return_info=False)

        # load content1 to system2
        system2 = am.load('atom_data', content1, pbc=system1.pbc,
                          symbols=system1.symbols, atom_style=atom_style,
                          units=units, potential=potential)

        # dump system2 to content2
        content2 = system2.dump('atom_data', atom_style=atom_style,
                                units=units, potential=potential,
                                return_info=False)
        
        # Check that the two dumps are equivalent
        assert content1 == content2
Пример #13
0
    def test_badfile_no_box(self):
        """Raise FileFormatError if any *lo *hl lines are missing"""
        
        # missing xlo xhi
        content = '\n'.join(self.data_lines[:3] + self.data_lines[4:])
        with pytest.raises(FileFormatError):
            am.load('atom_data', content)

        # missing ylo yhi
        content = '\n'.join(self.data_lines[:4] + self.data_lines[5:])
        with pytest.raises(FileFormatError):
            am.load('atom_data', content)

        # missing zlo zhi
        content = '\n'.join(self.data_lines[:5] + self.data_lines[6:])
        with pytest.raises(FileFormatError):
            am.load('atom_data', content)
Пример #14
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
Пример #15
0
def pointdefect(
    lammps_command: str,
    system: am.System,
    potential: lmp.Potential,
    point_kwargs: Union[list, dict],
    mpi_command: Optional[str] = None,
    etol: float = 0.0,
    ftol: float = 0.0,
    maxiter: int = 10000,
    maxeval: int = 100000,
    dmax: float = uc.set_in_units(0.01, 'angstrom')
) -> dict:
    """
    Adds one or more point defects to a system and evaluates the defect 
    formation energy.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    point_kwargs : dict or list of dict
        One or more dictionaries containing the keyword arguments for
        the atomman.defect.point() function to generate specific point
        defect configuration(s).
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simulation in.  If not
        given, will use the current working directory.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'E_pot'** (*float*) - The per-atom potential energy of the bulk system.
        - **'E_ptd_f'** (*float*) - The point defect formation energy.
        - **'E_total_base'** (*float*) - The total potential energy of the
          relaxed bulk system.
        - **'E_total_ptd'** (*float*) - The total potential energy of the
          relaxed defect system.
        - **'pij_tensor'** (*numpy.ndarray of float*) - The elastic dipole
          tensor associated with the defect.
        - **'system_base'** (*atomman.System*) - The relaxed bulk system.
        - **'system_ptd'** (*atomman.System*) - The relaxed defect system.
        - **'dumpfile_base'** (*str*) - The filename of the LAMMPS dump file
          for the relaxed bulk system.
        - **'dumpfile_ptd'** (*str*) - The filename of the LAMMPS dump file
          for the relaxed defect system.
    """
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)

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

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

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

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

    # Run lammps to relax perfect.dat
    output = lmp.run(lammps_command,
                     script_name=lammps_script,
                     mpi_command=mpi_command)

    # Extract LAMMPS thermo data.
    thermo = output.simulations[0]['thermo']
    E_total_base = uc.set_in_units(thermo.PotEng.values[-1],
                                   lammps_units['energy'])
    E_pot = E_total_base / system.natoms

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

    # Rename log file
    shutil.move('log.lammps', 'min-perfect-log.lammps')

    # Load relaxed system from dump file and copy old box vectors because
    # dump files crop the values.
    last_dump_file = 'atom.' + str(thermo.Step.values[-1])
    system_base = am.load('atom_dump', last_dump_file, symbols=system.symbols)
    system_base.box_set(vects=system.box.vects)
    system_base.dump('atom_dump', f='perfect.dump')

    # Add defect(s)
    system_ptd = deepcopy(system_base)
    if not isinstance(point_kwargs, (list, tuple)):
        point_kwargs = [point_kwargs]
    for pkwargs in point_kwargs:
        system_ptd = am.defect.point(system_ptd, **pkwargs)

    # Update lammps variables
    system_info = system_ptd.dump('atom_data',
                                  f='defect.dat',
                                  potential=potential)
    lammps_variables['atomman_system_pair_info'] = system_info

    # Write lammps input script
    with open(lammps_script, 'w') as f:
        f.write(filltemplate(template, lammps_variables, '<', '>'))

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

    # Extract lammps thermo data
    thermo = output.simulations[0]['thermo']
    E_total_ptd = uc.set_in_units(thermo.PotEng.values[-1],
                                  lammps_units['energy'])
    pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure'])
    pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure'])
    pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure'])
    pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure'])
    pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure'])
    pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure'])
    pressure_ptd = np.array([[pxx, pxy, pxz], [pxy, pyy, pyz], [pxz, pyz,
                                                                pzz]])

    # Rename log file
    shutil.move('log.lammps', 'min-defect-log.lammps')

    # Load relaxed system from dump file and copy old vects as
    # the dump files crop the values
    last_dump_file = 'atom.' + str(thermo.Step.values[-1])
    system_ptd = am.load('atom_dump',
                         last_dump_file,
                         symbols=system_ptd.symbols)
    system_ptd.box_set(vects=system.box.vects)
    system_ptd.dump('atom_dump', f='defect.dump')

    # Compute defect formation energy
    E_ptd_f = E_total_ptd - E_pot * system_ptd.natoms

    # Compute strain tensor
    pij = -(pressure_base - pressure_ptd) * system_base.box.volume

    # Cleanup files
    for fname in Path.cwd().glob('atom.*'):
        fname.unlink()
    for dumpjsonfile in Path.cwd().glob('*.dump.json'):
        dumpjsonfile.unlink()

    # Return results
    results_dict = {}
    results_dict['E_pot'] = E_pot
    results_dict['E_ptd_f'] = E_ptd_f
    results_dict['E_total_base'] = E_total_base
    results_dict['E_total_ptd'] = E_total_ptd
    results_dict['pij_tensor'] = pij
    results_dict['system_base'] = system_base
    results_dict['system_ptd'] = system_ptd
    results_dict['dumpfile_base'] = 'perfect.dump'
    results_dict['dumpfile_ptd'] = 'defect.dump'

    return results_dict
Пример #16
0
def get_oqmd_structures(elements, lib_directory=None):
    """
    Accesses the Materials Project and downloads all structures for a list of
    elements as poscar files.
    
    Parameters
    ----------
    elements : list
        A list of element symbols.
    lib_directory : str
        Path to the lib_directory to save the poscar files to.  Default uses
        the iprPy library/dft_structures directory.
    """
    # Function-specific imports
    import requests
    
    # Get default lib_directory
    if lib_directory is None:
        lib_directory = os.path.join(os.path.dirname(rootdir), 'library', 'ref')
    lib_directory = os.path.abspath(lib_directory)
    
    # Set comp_directory
    elements.sort()
    have = []
    for subelements in subsets(elements):
        elements_string = '-'.join(subelements)
        comp_directory = os.path.join(lib_directory, elements_string)
        if not os.path.isdir(comp_directory):
            os.makedirs(comp_directory)
        
        # Build list of downloaded entries
        for fname in glob.iglob(os.path.join(comp_directory, 'oqmd-*.poscar')):
            have.append(os.path.splitext(os.path.basename(fname))[0])
    #print('Have', len(have), 'records')
    
    # Build list of missing OQMD entries
    elements_string = '-'.join(elements)
    
    composition_r = requests.get('http://oqmd.org/materials/composition/' + elements_string)
    composition_html = composition_r.text
    
    missing = []
    count = 0
    while True:
        count += 1
        try:
            start = composition_html.index('href="/materials/entry/') + len('href="/materials/entry/')
        except:
            break
        else:
            end = start + composition_html[start:].index('">')
            entry_number = composition_html[start:end]
            composition_html = composition_html[end+2:]
            entry_id = 'oqmd-'+entry_number
            if entry_id not in have and entry_id not in missing:
                missing.append(entry_id)
        if count > 100:
            raise ValueError('Loop likely infinite')
    #print('Missing', len(missing), 'records')
    
    # Download missing entries
    for entry_id in missing:
        entry_number = entry_id.replace('oqmd-', '')
        entry_r = requests.get('http://oqmd.org/materials/entry/' + entry_number)
        entry_html = entry_r.text
        
        start = entry_html.index('href="/materials/structure/') + len('href="/materials/structure/')
        end = start + entry_html[start:].index('">')
        structure_number = entry_html[start:end]
        
        try:
            structure_url = 'http://oqmd.org/materials/export/conventional/poscar/' + structure_number
            structure_r = requests.get(structure_url)
            structure_r.raise_for_status()
        except:
            try:
                structure_url = 'http://oqmd.org/materials/export/primitive/poscar/' + structure_number
                structure_r = requests.get(structure_url)
                structure_r.raise_for_status()
            except:
                continue
        
        # Save poscar
        poscar = structure_r.text
        system = am.load('poscar', poscar)
        system = system.normalize()
        elements_string = '-'.join(system.symbols)
        structure_file = os.path.join(lib_directory, elements_string, entry_id + '.poscar')
        
        with open(structure_file, 'w') as f:
            f.write(poscar)
        print('Added', entry_id)
Пример #17
0
def atomman_systemload(input_dict, build=True, **kwargs):
    """
    Interprets calculation parameters associated with building a ucell system.
    
    The input_dict keys used by this function (which can be renamed using the
    function's keyword arguments):
    
    - **'load_file'** file to load system information from.
    - **'load_style'** file format of load_file. 
    - **'load_content'** alternate file or content to load instead of
      specified load_file.  This is used by prepare functions.
    - **'load_options'** any additional options associated with loading the
      load file as an atomman.System.
    - **'symbols'** the list of atomic symbols associated with ucell's atom
      types.  Optional if this information is in load/load_content.
    - **'box_parameters'** the string of box parameters to scale the system
      by.  Optional if the load file already is properly scaled.
    - **'family'** if input_dict or the load file contains a family term, then
      it is passed on. Otherwise, a new family is created based on the
      load file's name.
    - **'ucell'** this is where the resulting system is saved.
    
    Parameters
    ----------
    input_dict : dict
        Dictionary containing input parameter key-value pairs.
    build : bool
        If False, parameters will be interpreted, but objects won't be built
        from them (Default is True).
    load_file : str
        Replacement parameter key name for 'load_file'.
    load_style : str
        Replacement parameter key name for 'load_style'.
    load_options : str
        Replacement parameter key name for 'load_options'.
    symbols : str
        Replacement parameter key name for 'symbols'.
    box_parameters : str
        Replacement parameter key name for 'box_parameters'.
    family : str
        Replacement parameter key name for 'family'.
    ucell : str
        Replacement parameter key name for 'ucell'.
    """

    # Set default keynames
    keynames = [
        'load_file', 'load_style', 'load_options', 'load_content',
        'box_parameters', 'family', 'ucell', 'symbols'
    ]
    for keyname in keynames:
        kwargs[keyname] = kwargs.get(keyname, keyname)

    # Extract input values and assign default values
    load_file = input_dict[kwargs['load_file']]
    load_style = input_dict.get(kwargs['load_style'], 'system_model')
    load_options = input_dict.get(kwargs['load_options'], None)
    load_content = input_dict.get(kwargs['load_content'], None)
    family = input_dict.get(kwargs['family'], None)
    symbols = input_dict.get(kwargs['symbols'], None)
    box_parameters = input_dict.get(kwargs['box_parameters'], None)

    # Use load_content instead of load_file if given
    if load_content is not None:
        load_file = load_content

    # Separate load_options terms
    load_options_kwargs = {}
    if load_options is not None:
        load_options_keys = [
            'key', 'index', 'data_set', 'pbc', 'atom_style', 'units',
            'prop_info'
        ]
        load_options_kwargs = termtodict(load_options, load_options_keys)
        if 'index' in load_options_kwargs:
            load_options_kwargs['index'] = int(load_options_kwargs['index'])

    # Build ucell
    if build is True:

        # Load ucell
        ucell = am.load(load_style, load_file, **load_options_kwargs)

        # Replace symbols if given
        if symbols is not None:
            ucell.symbols = symbols.split()
        symbols = list(ucell.symbols)

        # Scale ucell by box_parameters
        if box_parameters is not None:
            box_params = box_parameters.split()

            # len of 4 or 7 indicates that last term is a length unit
            if len(box_params) == 4 or len(box_params) == 7:
                unit = box_params[-1]
                box_params = box_params[:-1]

            # Use calculation's length_unit if unit not given in box_parameters
            else:
                unit = input_dict['length_unit']

            # Convert to the specified units
            box_params = np.array(box_params, dtype=float)
            box_params[:3] = uc.set_in_units(box_params[:3], unit)

            # Three box_parameters means a, b, c
            if len(box_params) == 3:
                ucell.box_set(a=box_params[0],
                              b=box_params[1],
                              c=box_params[2],
                              scale=True)

            # Six box_parameters means a, b, c, alpha, beta, gamma
            elif len(box_params) == 6:
                ucell.box_set(a=box_params[0],
                              b=box_params[1],
                              c=box_params[2],
                              alpha=box_params[3],
                              beta=box_params[4],
                              gamma=box_params[5],
                              scale=True)

            # Other options are invalid
            else:
                ValueError('Invalid box_parameters command')

    # Don't build
    else:
        # Try to get symbols by loading file
        if symbols is None:
            try:
                ucell = am.load(load_style, load_file, **load_options_kwargs)
            except:
                pass
            else:
                symbols = list(ucell.symbols)
        ucell = None

    # Extract system_family (and possibly symbols) from system_model
    if load_style == 'system_model':
        model = DM(load_file)

        # Check if family in model
        if family is None:
            try:
                family = model.find('system-info')['family']
            except:
                family = None

        # Extract symbols if needed
        if symbols is None or symbols[0] is None:
            try:
                symbols = model.find('system-info')['symbol']
            except:
                pass
            else:
                if ucell is not None:
                    ucell.symbols = symbols
                    symbols = list(ucell.symbols)

    # Pull single symbols out of the list
    if len(symbols) == 1:
        symbols = symbols[0]

    # If no family given/found, use load_file's basename
    if family is None:
        family = os.path.splitext(
            os.path.basename(input_dict[kwargs['load_file']]))[0]

    # Save processed terms
    input_dict[kwargs['load_style']] = load_style
    input_dict[kwargs['load_options']] = load_options
    input_dict[kwargs['box_parameters']] = box_parameters
    input_dict[kwargs['family']] = family
    input_dict[kwargs['ucell']] = ucell
    input_dict[kwargs['symbols']] = symbols
 def todict(self, full=True, flat=False):
     """
     Converts the structured content to a simpler dictionary.
     
     Parameters
     ----------
     full : bool, optional
         Flag used by the calculation records.  A True value will include
         terms for both the calculation's input and results, while a value
         of False will only include input terms (Default is True).
     flat : bool, optional
         Flag affecting the format of the dictionary terms.  If True, the
         dictionary terms are limited to having only str, int, and float
         values, which is useful for comparisons.  If False, the term
         values can be of any data type, which is convenient for analysis.
         (Default is False).
         
     Returns
     -------
     dict
         A dictionary representation of the record's content.
     """
     
     calc = self.content[self.contentroot]
     params = {}
     params['key'] = calc['key']
     params['script'] = calc['calculation']['script']
     params['iprPy_version'] = calc['calculation']['iprPy-version']
     
     params['symmetryprecision'] = calc['calculation']['run-parameter']['symmetryprecision']
     params['primitivecell'] = calc['calculation']['run-parameter']['primitivecell']
     params['idealcell'] = calc['calculation']['run-parameter']['idealcell']
     
     params['load_file'] = calc['system-info']['artifact']['file']
     params['load_style'] = calc['system-info']['artifact']['format']
     params['load_options'] = calc['system-info']['artifact']['load_options']
     params['family'] = calc['system-info']['family']
     
     symbols = aslist(calc['system-info']['symbol'])
     if flat is True:
         try:
             params['symbols'] = ' '.join(symbols)
         except:
             params['symbols'] = np.nan
     else:
         params['symbols'] = symbols
     
     params['status'] = calc.get('status', 'finished')
     params['error'] = calc.get('error', np.nan)
     
     if full is True and params['status'] == 'finished':
         ucell = am.load('system_model', self.content, key='unit-cell-atomic-system')
         params['pearson_symbol'] = calc['Pearson-symbol']
         params['spacegroup_number'] = calc['space-group']['number']
         params['spacegroup_international'] = calc['space-group']['Hermann-Maguin']
         params['spacegroup_Schoenflies'] = calc['space-group']['Schoenflies']
         params['wykoff_letters'] = ' '.join(calc['space-group'].finds('letter'))
         
         if flat is True:
             params['a'] = ucell.box.a
             params['b'] = ucell.box.b
             params['c'] = ucell.box.c
             params['alpha'] = ucell.box.alpha
             params['beta'] = ucell.box.beta
             params['gamma'] = ucell.box.gamma
             params['natoms'] = ucell.natoms
         else:
             params['ucell'] = ucell
     
     return params
Пример #19
0
def dislocation_monopole(lammps_command: str,
                         ucell: am.System,
                         potential: lmp.Potential,
                         C: am.ElasticConstants,
                         burgers: Union[list, np.ndarray],
                         ξ_uvw: Union[list, np.ndarray],
                         slip_hkl: Union[list, np.ndarray],
                         mpi_command: Optional[str] = None,
                         m: Union[list, np.ndarray] = [0, 1, 0],
                         n: Union[list, np.ndarray] = [0, 0, 1],
                         sizemults=None,
                         amin: float = None,
                         bmin: float = None,
                         cmin: float = None,
                         shift: Union[list, np.ndarray, None] = None,
                         shiftscale: bool = False,
                         shiftindex: int = None,
                         tol: float = 1e-8,
                         etol: float = 0.0,
                         ftol: float = 0.0,
                         maxiter: int = 10000,
                         maxeval: int = 100000,
                         dmax: float = uc.set_in_units(0.01, 'angstrom'),
                         annealtemp: float = 0.0,
                         annealsteps: Optional[int] = None,
                         randomseed: Optional[int] = None,
                         boundaryshape: str = 'cylinder',
                         boundarywidth: float = 0.0,
                         boundaryscale: bool = False) -> dict:
    """
    Creates and relaxes a dislocation monopole system.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    ucell : atomman.System
        The unit cell to use as the seed for generating the dislocation
        monopole system.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    C : atomman.ElasticConstants
        The elastic constants associated with the bulk crystal structure
        for ucell.
    burgers : array-like object
        The dislocation's Burgers vector given as a Miller or
        Miller-Bravais vector relative to ucell.
    ξ_uvw : array-like object
        The dislocation's line direction given as a Miller or
        Miller-Bravais vector relative to ucell.
    slip_hkl : array-like object
        The dislocation's slip plane given as a Miller or Miller-Bravais
        plane relative to ucell.
    mpi_command : str or None, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    m : array-like object, optional
        The m unit vector for the dislocation solution.  m, n, and ξ
        (dislocation line) should be right-hand orthogonal.  Default value
        is [0,1,0] (y-axis).
    n : array-like object, optional
        The n unit vector for the dislocation solution.  m, n, and ξ
        (dislocation line) should be right-hand orthogonal.  Default value
        is [0,0,1] (z-axis). n is normal to the dislocation slip plane.
    sizemults : tuple, optional
        The size multipliers to use when generating the system.  Values are
        limited to being positive integers.  The multipliers for the two
        non-periodic directions must be even.  If not given, the default
        multipliers will be 2 for the non-periodic directions and 1 for the
        periodic direction.
    amin : float, optional
        A minimum thickness to use for the a box vector direction of the
        final system.  Default value is 0.0.  For the non-periodic
        directions, the resulting vector multiplier will be even.  If both
        amin and sizemults is given, then the larger multiplier for the two
        will be used.
    bmin : float, optional
        A minimum thickness to use for the b box vector direction of the
        final system.  Default value is 0.0.  For the non-periodic
        directions, the resulting vector multiplier will be even.  If both
        bmin and sizemults is given, then the larger multiplier for the two
        will be used.
    cmin : float, optional
        A minimum thickness to use for the c box vector direction of the
        final system.  Default value is 0.0.  For the non-periodic
        directions, the resulting vector multiplier will be even.  If both
        cmin and sizemults is given, then the larger multiplier for the two
        will be used.
    shift : float, optional
        A rigid body shift to apply to the rotated cell prior to inserting
        the dislocation.  Should be selected such that the ideal slip plane
        does not correspond to any atomic planes.  Is taken as absolute if
        shiftscale is False, or relative to the rotated cell's box vectors
        if shiftscale is True.  Cannot be given with shiftindex.  If
        neither shift nor shiftindex is given then shiftindex = 0 is used.
    shiftindex : float, optional
        The index of the identified optimum shifts based on the rotated
        cell to use.  Different values allow for the selection of different
        atomic planes neighboring the slip plane.  Note that shiftindex
        values only apply shifts normal to the slip plane; best shifts for
        non-planar dislocations (like bcc screw) may also need a shift in
        the slip plane.  Cannot be given with shiftindex.  If neither shift
        nor shiftindex is given then shiftindex = 0 is used.
    shiftscale : bool, optional
        If False (default), a given shift value will be taken as absolute
        Cartesian.  If True, a given shift will be taken relative to the
        rotated cell's box vectors.
    tol : float
        A cutoff tolerance used with obtaining the dislocation solution.
        Only needs to be changed if there are issues with obtaining a
        solution.
    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.
    annealsteps : int, optional
        The number of time steps to run the dynamic relaxation for.  Default
        is None, which will run for 10000 steps if annealtemp is not 0.0.  
    randomseed : int or None, optional
        Random number seed used by LAMMPS in creating velocities and with
        the Langevin thermostat.  Default is None which will select a
        random int between 1 and 900000000.
    boundaryshape : str, optional
        Indicates the shape of the boundary region to use.  Options are
        'cylinder' (default) and 'box'.  For 'cylinder', the non-boundary
        region is defined by a cylinder with axis along the dislocation
        line and a radius that ensures the boundary is at least
        boundarywidth thick.  For 'box', the boundary region will be
        exactly boundarywidth thick all around.      
    boundarywidth : float, optional
        The width of the boundary region to apply.  Default value is 0.0,
        i.e. no boundary region.  All atoms in the boundary region will
        have their atype values changed.
    boundaryscale : bool, optional
        If False (Default), the boundarywidth will be taken as absolute.
        If True, the boundarywidth will be taken relative to the magnitude
        of the unit cell's a box vector.
    
    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.
        - **'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.
        - **'dislocation'** (*atomman.defect.Dislocation*) - The Dislocation
          object used to generate the monopole system.
        - **'E_total_disl'** (*float*) - The total potential energy of the
          dislocation monopole system.
    """
    # Construct dislocation configuration generator
    dislocation = am.defect.Dislocation(ucell,
                                        C,
                                        burgers,
                                        ξ_uvw,
                                        slip_hkl,
                                        m=m,
                                        n=n,
                                        shift=shift,
                                        shiftindex=shiftindex,
                                        shiftscale=shiftscale,
                                        tol=tol)

    # Generate the base and dislocation systems
    base_system, disl_system = dislocation.monopole(
        sizemults=sizemults,
        amin=amin,
        bmin=bmin,
        cmin=cmin,
        shift=shift,
        shiftindex=shiftindex,
        shiftscale=shiftscale,
        boundaryshape=boundaryshape,
        boundarywidth=boundarywidth,
        boundaryscale=boundaryscale,
        return_base_system=True)

    # Initialize results dict
    results_dict = {}

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

    # Save dislocation generator
    results_dict['dislocation'] = dislocation

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

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

    system_disl.box_set(vects=disl_system.box.vects,
                        origin=disl_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
    Path('0.dump').unlink()
    Path(relaxed['dumpfile']).unlink()
    for dumpjsonfile in Path('.').glob('*.dump.json'):
        dumpjsonfile.unlink()

    return results_dict
Пример #20
0
def plot_vitek(dislo, bulk, show=True, save_file=None,
               alat=3.16, plot_axes=None, xyscale=10):
    """Plots vitek map from ase configurations.

    Parameters
    ----------
    dislo : ase.Atoms
        Dislocation configuration.
    bulk : ase.Atoms
        Corresponding bulk configuration for calculation of displacements.
    show : bool
        Show the figure after plotting. Default is True.
    save_file : str, optional
        If given then the plot will be saved to a file with this name.
    alat : float
        Lattice parameter for calculation of neghbour list cutoff.
    plot_axes : matplotlib.Axes.axes object
        Existing axes to plot on, allows to pass existing matplotlib axes
        have full control of the graph outside the function.
        Makes possible to plot multiple differential displacement
        maps using subplots.
        Default is None, then new graph is created by plt.subplots()
        Description of parameter `plot_axes`.
    xyscale : float
        xyscale of the graph

    Returns
    -------
    None

    """

    lengthB = 0.5*np.sqrt(3.)*alat
    burgers = np.array([0.0, 0.0, lengthB])

    base_system = am.load("ase_Atoms", bulk)
    disl_system = am.load("ase_Atoms", dislo)

    neighborListCutoff = 0.95 * alat

    # plot window is +-10 angstroms in x,y directions, and one Burgerx vector
    # thickness along z direction
    plot_range = np.array([[-xyscale, xyscale],
                          [-xyscale, xyscale],
                          [-0.1, alat * 3.**(0.5) / 2.]])

    # This scales arrows such that b/2 corresponds to the
    # distance between atoms on the plot
    plot_scale = 1.885618083

    am.defect.differential_displacement(base_system, disl_system,
                                        burgers,
                                        cutoff=neighborListCutoff,
                                        xlim=plot_range[0],
                                        ylim=plot_range[1],
                                        zlim=plot_range[2],
                                        plot_scale=plot_scale,
                                        show=show, save_file=save_file,
                                        plot_axes=plot_axes)

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

    # Run lammps to relax perfect.dat
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract LAMMPS thermo data.
    thermo = output.simulations[0]['thermo']
    E_total_base = uc.set_in_units(thermo.PotEng.values[-1],
                                   lammps_units['energy'])
    E_coh = E_total_base / system.natoms
    
    # Rename log file
    shutil.move('log.lammps', 'min-perfect-log.lammps')
    
    # Load relaxed system from dump file and copy old box vectors because 
    # dump files crop the values.
    last_dump_file = 'atom.' + str(thermo.Step.values[-1])
    system_base = am.load('atom_dump', last_dump_file, symbols=system.symbols)
    system_base.box_set(vects=system.box.vects)
    system_base.dump('atom_dump', f='perfect.dump')
    
    # Add defect(s)
    system_ptd = deepcopy(system_base)
    if not isinstance(point_kwargs, (list, tuple)):
        point_kwargs = [point_kwargs]
    for pkwargs in point_kwargs:
        system_ptd = am.defect.point(system_ptd, **pkwargs)
    
    # Update lammps variables
    system_info = system_ptd.dump('atom_data', f='defect.dat',
                                  units = potential.units, 
                                  atom_style = potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    
    # Write lammps input script
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run lammps
    output = lmp.run(lammps_command, lammps_script, mpi_command)
    
    # Extract lammps thermo data
    thermo = output.simulations[0]['thermo']
    E_total_ptd = uc.set_in_units(thermo.PotEng.values[-1],
                                  lammps_units['energy'])
    
    # Rename log file
    shutil.move('log.lammps', 'min-defect-log.lammps')
    
    # Load relaxed system from dump file and copy old vects as 
    # the dump files crop the values
    last_dump_file = 'atom.'+str(thermo.Step.values[-1])
    system_ptd = am.load('atom_dump', last_dump_file, symbols=system_ptd.symbols)
    system_ptd.box_set(vects=system.box.vects)
    system_ptd.dump('atom_dump', f='defect.dump')
    
    # Compute defect formation energy
    E_ptd_f = E_total_ptd - E_coh * system_ptd.natoms
    
    # Cleanup files
    for fname in glob.iglob('atom.*'):
        os.remove(fname)
    for dumpjsonfile in glob.iglob('*.dump.json'):
        os.remove(dumpjsonfile)
    
    # Return results
    results_dict = {}
    results_dict['E_coh'] = E_coh
    results_dict['E_ptd_f'] = E_ptd_f
    results_dict['E_total_base'] = E_total_base
    results_dict['E_total_ptd'] = E_total_ptd
    results_dict['system_base'] = system_base
    results_dict['system_ptd'] = system_ptd
    results_dict['dumpfile_base'] = 'perfect.dump'
    results_dict['dumpfile_ptd'] = 'defect.dump'
    
    return results_dict
Пример #22
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 {}
Пример #23
0
    def interpret(self, input_dict, build=True):
        """
        Interprets calculation parameters.
        
        Parameters
        ----------
        input_dict : dict
            Dictionary containing input parameter key-value pairs.
        """

        # Set default keynames
        keymap = self.keymap

        # Extract input values and assign default values
        load_file = input_dict[keymap['load_file']]
        load_style = input_dict.get(keymap['load_style'], 'system_model')
        load_options = input_dict.get(keymap['load_options'], None)
        load_content = input_dict.get(keymap['load_content'], None)
        family = input_dict.get(keymap['family'], None)
        symbols = input_dict.get(keymap['symbols'], None)
        box_parameters = input_dict.get(keymap['box_parameters'], None)
        elastic_file = input_dict.get(keymap['elasticconstants_content'], None)

        # Use load_content instead of load_file if given
        if load_content is not None:
            load_file = load_content

        # Separate load_options terms
        load_options_kwargs = {}
        if load_options is not None:
            load_options_keys = [
                'key', 'index', 'data_set', 'pbc', 'atom_style', 'units',
                'prop_info'
            ]
            load_options_kwargs = termtodict(load_options, load_options_keys)
            if 'index' in load_options_kwargs:
                load_options_kwargs['index'] = int(
                    load_options_kwargs['index'])

        # Build ucell
        if build is True:

            # Load ucell
            ucell = am.load(load_style, load_file, **load_options_kwargs)

            # Replace symbols if given
            if symbols is not None:
                ucell.symbols = symbols.split()
            symbols = list(ucell.symbols)

            # Scale ucell by box_parameters
            if box_parameters is not None:
                box_params = box_parameters.split()

                # len of 4 or 7 indicates that last term is a length unit
                if len(box_params) == 4 or len(box_params) == 7:
                    unit = box_params[-1]
                    box_params = box_params[:-1]

                # Use calculation's length_unit if unit not given in box_parameters
                else:
                    unit = input_dict['length_unit']

                # Convert to the specified units
                box_params = np.array(box_params, dtype=float)
                box_params[:3] = uc.set_in_units(box_params[:3], unit)

                # Three box_parameters means a, b, c
                if len(box_params) == 3:
                    ucell.box_set(a=box_params[0],
                                  b=box_params[1],
                                  c=box_params[2],
                                  scale=True)

                # Six box_parameters means a, b, c, alpha, beta, gamma
                elif len(box_params) == 6:
                    ucell.box_set(a=box_params[0],
                                  b=box_params[1],
                                  c=box_params[2],
                                  alpha=box_params[3],
                                  beta=box_params[4],
                                  gamma=box_params[5],
                                  scale=True)

                # Other options are invalid
                else:
                    ValueError('Invalid box_parameters command')

            # Add model-specific charges if needed
            if (keymap['potential'] in input_dict
                    and 'charge' not in ucell.atoms_prop()):
                potential = input_dict[keymap['potential']]
                ucell.atoms.prop_atype('charge',
                                       potential.charges(ucell.symbols))

        # Don't build
        else:
            # Try to get symbols by loading file
            if symbols is None:
                try:
                    ucell = am.load(load_style, load_file,
                                    **load_options_kwargs)
                except:
                    pass
                else:
                    symbols = list(ucell.symbols)
            ucell = None

        # Extract system_family (and possibly symbols) from parent model
        if elastic_file is not None:
            model = DM(elastic_file)
        elif load_style == 'system_model':
            model = DM(load_file)
        else:
            model = None

        if model is not None:
            # Check if family in model
            if family is None:
                try:
                    family = model.find('system-info')['family']
                except:
                    family = None

            # Extract symbols if needed
            if symbols is None or symbols[0] is None:
                try:
                    symbols = model.find('system-info')['symbol']
                except:
                    pass
                else:
                    if ucell is not None:
                        ucell.symbols = symbols
                        symbols = list(ucell.symbols)

        # Pull single symbols out of the list
        if len(symbols) == 1:
            symbols = symbols[0]

        # If no family given/found, use load_file's stem
        if family is None:
            family = Path(input_dict[keymap['load_file']]).stem

        # Save processed terms
        input_dict[keymap['load_style']] = load_style
        input_dict[keymap['load_options']] = load_options
        input_dict[keymap['box_parameters']] = box_parameters
        input_dict[keymap['family']] = family
        input_dict[keymap['ucell']] = ucell
        input_dict[keymap['symbols']] = symbols
Пример #24
0
def stackingfaultpoint(lammps_command, system, potential,
                       mpi_command=None, sim_directory=None,
                       cutboxvector='c', faultpos=0.5,
                       faultshift=[0.0, 0.0, 0.0], etol=0.0, ftol=0.0,
                       maxiter=10000, maxeval=100000,
                       dmax=uc.set_in_units(0.01, 'angstrom'),
                       lammps_date=None):
    """
    Perform a stacking fault relaxation simulation for a single faultshift.
    
    Parameters
    ----------
    lammps_command :str
        Command for running LAMMPS.
    system : atomman.System
        The system to perform the calculation on.
    potential : atomman.lammps.Potential
        The LAMMPS implemented potential to use.
    mpi_command : str, optional
        The MPI command for running LAMMPS in parallel.  If not given, LAMMPS
        will run serially.
    sim_directory : str, optional
        The path to the directory to perform the simuation in.  If not
        given, will use the current working directory.
    etol : float, optional
        The energy tolerance for the structure minimization. This value is
        unitless. (Default is 0.0).
    ftol : float, optional
        The force tolerance for the structure minimization. This value is in
        units of force. (Default is 0.0).
    maxiter : int, optional
        The maximum number of minimization iterations to use (default is 
        10000).
    maxeval : int, optional
        The maximum number of minimization evaluations to use (default is 
        100000).
    dmax : float, optional
        The maximum distance in length units that any atom is allowed to relax
        in any direction during a single minimization iteration (default is
        0.01 Angstroms).
    cutboxvector : str, optional
        Indicates which of the three system box vectors, 'a', 'b', or 'c', to
        cut with a non-periodic boundary (default is 'c').
    faultpos : float, optional
        The fractional position along the cutboxvector where the stacking
        fault plane will be placed (default is 0.5).
    faultshift : list of float, optional
        The vector shift to apply to all atoms above the fault plane defined
        by faultpos (default is [0,0,0], i.e. no shift applied).
    lammps_date : datetime.date or None, optional
        The date version of the LAMMPS executable.  If None, will be identified from the lammps_command (default is None).
    
    Returns
    -------
    dict
        Dictionary of results consisting of keys:
        
        - **'logfile'** (*str*) - The filename of the LAMMPS log file.
        - **'dumpfile'** (*str*) - The filename of the LAMMPS dump file
          of the relaxed system.
        - **'system'** (*atomman.System*) - The relaxed system.
        - **'A_fault'** (*float*) - The area of the fault surface.
        - **'E_total'** (*float*) - The total potential energy of the relaxed
          system.
        - **'disp'** (*float*) - The center of mass difference between atoms
          above and below the fault plane in the cutboxvector direction.
    
    Raises
    ------
    ValueError
        For invalid cutboxvectors.
    """
    
    # Set options based on cutboxvector
    if cutboxvector == 'a':
        # Assert system is compatible with planeaxis value
        if system.box.xy != 0.0 or system.box.xz != 0.0:
            raise ValueError("box tilts xy and xz must be 0 for cutboxvector='a'")
        
        # Specify cutindex
        cutindex = 0
        
        # Identify atoms above fault
        faultpos = system.box.xlo + system.box.lx * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.bvect,
                                            system.box.cvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce NULL 0 0'
    
    elif cutboxvector == 'b':
        # Assert system is compatible with planeaxis value
        if system.box.yz != 0.0:
            raise ValueError("box tilt yz must be 0 for cutboxvector='b'")
        
        # Specify cutindex
        cutindex = 1
        
        # Identify atoms above fault
        faultpos = system.box.ylo + system.box.ly * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.cvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 NULL 0'
        
    elif cutboxvector == 'c':
        # Specify cutindex
        cutindex = 2
        
        # Identify atoms above fault
        faultpos = system.box.zlo + system.box.lz * faultpos
        abovefault = system.atoms.pos[:, cutindex] > (faultpos)
        
        # Compute fault area
        faultarea = np.linalg.norm(np.cross(system.box.avect,
                                            system.box.bvect))
        
        # Give correct LAMMPS fix setforce command
        fix_cut_setforce = 'fix cut all setforce 0 0 NULL'
        
    else: 
        raise ValueError('Invalid cutboxvector')
    
    # Assert faultshift is in cut plane
    if faultshift[cutindex] != 0.0:
        raise ValueError('faultshift must be in cut plane')
    
    # Generate stacking fault system by shifting atoms above the fault
    sfsystem = deepcopy(system)
    sfsystem.pbc = [True, True, True]
    sfsystem.pbc[cutindex] = False
    sfsystem.atoms.pos[abovefault] += faultshift
    sfsystem.wrap()
    
    if sim_directory is not None:
        # Create sim_directory if it doesn't exist
        if not os.path.isdir(sim_directory):
            os.mkdir(sim_directory)
        
        # Add '/' to end of sim_directory string if needed
        if sim_directory[-1] != '/': 
            sim_directory = sim_directory + '/'
    else:
        # Set sim_directory if is None
        sim_directory = ''
    
    # Get lammps units
    lammps_units = lmp.style.unit(potential.units)
       
    #Get lammps version date
    if lammps_date is None:
        lammps_date = lmp.checkversion(lammps_command)['date']
    
    # Define lammps variables
    lammps_variables = {}
    system_info = sfsystem.dump('atom_data',
                                f=os.path.join(sim_directory, 'system.dat'),
                                units=potential.units,
                                atom_style=potential.atom_style)
    lammps_variables['atomman_system_info'] = system_info
    lammps_variables['atomman_pair_info'] = potential.pair_info(sfsystem.symbols)
    lammps_variables['fix_cut_setforce'] = fix_cut_setforce
    lammps_variables['sim_directory'] = sim_directory
    lammps_variables['etol'] = etol
    lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force'])
    lammps_variables['maxiter'] = maxiter
    lammps_variables['maxeval'] = maxeval
    lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length'])
    
    # Set dump_modify format based on dump_modify_version
    if lammps_date < datetime.date(2016, 8, 3):
        lammps_variables['dump_modify_format'] = '"%i %i %.13e %.13e %.13e %.13e"'
    else:
        lammps_variables['dump_modify_format'] = 'float %.13e'
    
    # Write lammps input script
    template_file = 'sfmin.template'
    lammps_script = os.path.join(sim_directory, 'sfmin.in')
    with open(template_file) as f:
        template = f.read()
    with open(lammps_script, 'w') as f:
        f.write(iprPy.tools.filltemplate(template, lammps_variables,
                                         '<', '>'))
    
    # Run LAMMPS
    output = lmp.run(lammps_command, lammps_script, mpi_command,
                     logfile=os.path.join(sim_directory, 'log.lammps'))
    
    # Extract output values
    thermo = output.simulations[-1]['thermo']
    logfile = os.path.join(sim_directory, 'log.lammps')
    dumpfile = os.path.join(sim_directory, '%i.dump' % thermo.Step.values[-1])
    E_total = uc.set_in_units(thermo.PotEng.values[-1],
                              lammps_units['energy'])
    
    #Load relaxed system
    sfsystem = am.load('atom_dump', dumpfile, symbols=sfsystem.symbols)
    
    # Find center of mass difference in top/bottom planes
    disp = (sfsystem.atoms.pos[abovefault, cutindex].mean()
            - sfsystem.atoms.pos[~abovefault, cutindex].mean())
    
    # Return results
    results_dict = {}
    results_dict['logfile'] = logfile
    results_dict['dumpfile'] = dumpfile
    results_dict['system'] = sfsystem
    results_dict['A_fault'] = faultarea
    results_dict['E_total'] = E_total
    results_dict['disp'] = disp
    
    return results_dict
Пример #25
0
def crystal_space_group(system,
                        symprec=1e-5,
                        to_primitive=False,
                        no_idealize=False):
    """
    Uses spglib to evaluate space group information for a given system.
    
    Parameters
    ----------
    system : atomman.System
        The system to analyze.
    symprec : float
        Absolute length tolerance to use in identifying symmetry of atomic
        sites and system boundaries.
    to_primitive : bool
        Indicates if the returned unit cell is conventional (False) or
        primitive (True). Default value is False.
    no_idealize : bool
        Indicates if the atom positions in the returned unit cell are averaged
        (True) or idealized based on the structure (False).  Default value is
        False.
    
    Returns
    -------
    dict
        Results dictionary containing space group information and an associated
        unit cell system.
    """
    # Identify the standardized unit cell representation
    ucell = spglib.standardize_cell(system.dump('spglib_cell'),
                                    to_primitive=to_primitive,
                                    no_idealize=no_idealize,
                                    symprec=symprec)

    # Convert back to atomman systems and normalize
    ucell = am.load('spglib_cell', ucell, symbols=system.symbols)
    ucell.atoms.pos -= ucell.atoms.pos[0]
    ucell = ucell.normalize()

    # Get space group metadata
    sym_data = spglib.get_symmetry_dataset(ucell.dump('spglib_cell'))
    spg_type = spglib.get_spacegroup_type(sym_data['hall_number'])

    # Generate Pearson symbol
    if spg_type['number'] <= 2:
        crystalclass = 'a'
    elif spg_type['number'] <= 15:
        crystalclass = 'm'
    elif spg_type['number'] <= 74:
        crystalclass = 'o'
    elif spg_type['number'] <= 142:
        crystalclass = 't'
    elif spg_type['number'] <= 194:
        crystalclass = 'h'
    else:
        crystalclass = 'c'

    latticetype = spg_type['international'][0]
    if latticetype in ['A', 'B']:
        latticetype = 'C'

    natoms = str(ucell.natoms)
    pearson = crystalclass + latticetype + natoms

    # Return results
    results_dict = spg_type
    results_dict['ucell'] = ucell
    results_dict['hall_number'] = sym_data['hall_number']
    results_dict['wyckoffs'] = sym_data['wyckoffs']
    results_dict['pearson'] = pearson

    return results_dict
Пример #26
0
def get_mp_structures(elements, api_key=None, lib_directory=None):
    """
    Accesses the Materials Project and downloads all structures for a list of
    elements as poscar files.
    
    Parameters
    ----------
    elements : list
        A list of element symbols.
    api_key : str, optional
        The user's Materials Project API key. If not given, will use "MAPI_KEY"
        environment variable
    lib_directory : str
        Path to the lib_directory to save the poscar files to.  Default uses
        the iprPy library/dft_structures directory.
    """
    # Function-specific imports
    import pymatgen as pmg
    from pymatgen.ext.matproj import MPRester
    
    # Handle lib_directory
    if lib_directory is None:
        lib_directory = os.path.join(os.path.dirname(rootdir), 'library', 'ref')
    lib_directory = os.path.abspath(lib_directory)
    
    elements.sort()
    
    # Open connection to Materials Project
    with MPRester(api_key) as m:
        
        # Loop over subsets of elements
        for subelements in subsets(elements):
            
            # Set comp_directory
            elements_string = '-'.join(subelements)
            comp_directory = os.path.join(lib_directory, elements_string)
            if not os.path.isdir(comp_directory):
                os.makedirs(comp_directory)
            
            # Build list of downloaded entries
            have = []
            for fname in glob.iglob(os.path.join(comp_directory, 'mp-*.poscar')):
                have.append(os.path.splitext(os.path.basename(fname))[0])
            #print('Have', len(have), elements_string, 'records')
            
            # Query MP for all entries corresponding to the elements
            entries = m.query({"elements": subelements}, ["material_id"])
            
            # Add entries to the list if not there
            missing = []
            for entry in entries:
                if entry['material_id'] not in have and entry['material_id'] not in missing:
                    missing.append(entry['material_id'])
            #print('Missing', len(missing), elements_string, 'records')
            
            # Download missing entries
            entries = m.query({"material_id": {"$in": missing}}, ['material_id', 'cif'])
            
            # Convert cif to poscar and save
            for entry in entries:
                struct = pmg.Structure.from_str(entry['cif'], fmt='cif')
                struct = pmg.symmetry.analyzer.SpacegroupAnalyzer(struct).get_conventional_standard_structure()
                system = am.load('pymatgen_Structure', struct)
                system = system.normalize()
                structure_file = os.path.join(comp_directory, entry['material_id']+'.poscar')
                system.dump('poscar', f=structure_file)
                print('Added', entry['material_id'])
Пример #27
0
def crystal_space_group(system,
                        symprec=1e-5,
                        to_primitive=False,
                        no_idealize=False):
    """
    Uses spglib to evaluate space group information for a given system.
    
    Parameters
    ----------
    system : atomman.System
        The system to analyze.
    symprec : float
        Absolute length tolerance to use in identifying symmetry of atomic
        sites and system boundaries.
    to_primitive : bool
        Indicates if the returned unit cell is conventional (False) or
        primitive (True). Default value is False.
    no_idealize : bool
        Indicates if the atom positions in the returned unit cell are averaged
        (True) or idealized based on the structure (False).  Default value is
        False.
    
    Returns
    -------
    dict
        Results dictionary containing space group information and an associated
        unit cell system.
    """
    # Identify the standardized unit cell representation
    sym_data = spglib.get_symmetry_dataset(system.dump('spglib_cell'),
                                           symprec=symprec)
    ucell = spglib.standardize_cell(system.dump('spglib_cell'),
                                    to_primitive=to_primitive,
                                    no_idealize=no_idealize,
                                    symprec=symprec)

    # Convert back to atomman systems and normalize
    ucell = am.load('spglib_cell', ucell, symbols=system.symbols)
    ucell.atoms.pos -= ucell.atoms.pos[0]
    ucell = ucell.normalize()

    # Average extra per-atom properties by mappings to primitive
    for index in np.unique(sym_data['mapping_to_primitive']):
        for key in system.atoms.prop():
            if key in ['atype', 'pos']:
                continue
            value = system.atoms.view[key][sym_data['mapping_to_primitive'] ==
                                           index].mean()
            if key not in ucell.atoms.prop():
                ucell.atoms.view[key] = np.zeros_like(value)
            ucell.atoms.view[key][sym_data['std_mapping_to_primitive'] ==
                                  index] = value

    # Get space group metadata
    sym_data = spglib.get_symmetry_dataset(ucell.dump('spglib_cell'))
    spg_type = spglib.get_spacegroup_type(sym_data['hall_number'])

    # Generate Pearson symbol
    if spg_type['number'] <= 2:
        crystalclass = 'a'
    elif spg_type['number'] <= 15:
        crystalclass = 'm'
    elif spg_type['number'] <= 74:
        crystalclass = 'o'
    elif spg_type['number'] <= 142:
        crystalclass = 't'
    elif spg_type['number'] <= 194:
        crystalclass = 'h'
    else:
        crystalclass = 'c'

    latticetype = spg_type['international'][0]
    if latticetype in ['A', 'B']:
        latticetype = 'C'

    natoms = str(ucell.natoms)
    pearson = crystalclass + latticetype + natoms

    # Generate Wyckoff fingerprint
    fingerprint_dict = {}
    usites, uindices = np.unique(sym_data['equivalent_atoms'],
                                 return_index=True)
    for usite, uindex in zip(usites, uindices):
        atype = ucell.atoms.atype[uindex]
        wykoff = sym_data['wyckoffs'][uindex]
        if atype not in fingerprint_dict:
            fingerprint_dict[atype] = [wykoff]
        else:
            fingerprint_dict[atype].append(wykoff)
    fingerprint = []
    for atype in sorted(fingerprint_dict.keys()):
        fingerprint.append(''.join(sorted(fingerprint_dict[atype])))
    fingerprint = ' '.join(fingerprint)

    # Return results
    results_dict = spg_type
    results_dict['ucell'] = ucell
    results_dict['hall_number'] = sym_data['hall_number']
    results_dict['wyckoffs'] = sym_data['wyckoffs']
    results_dict['equivalent_atoms'] = sym_data['equivalent_atoms']
    results_dict['pearson'] = pearson
    results_dict['wyckoff_fingerprint'] = fingerprint

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

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

        # Specify cutindex
        cutindex = 0

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

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

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

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

        # Specify cutindex
        cutindex = 1

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

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

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

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

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

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

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

    else:
        raise ValueError('Invalid cutboxvector')

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return results_dict
Пример #29
0
def get_mp_structures(elements, api_key=None, lib_directory=None):
    """
    Accesses the Materials Project and downloads all structures for a list of
    elements as poscar files.
    
    Parameters
    ----------
    elements : list
        A list of element symbols.
    api_key : str, optional
        The user's Materials Project API key. If not given, will use "MAPI_KEY"
        environment variable
    lib_directory : str
        Path to the lib_directory to save the poscar files to.  Default uses
        the iprPy library/reference_crystal directory.
    """
    # Function-specific imports
    import pymatgen as pmg
    from pymatgen.ext.matproj import MPRester

    # Set source name and link
    sourcename = "Materials Project"
    sourcelink = "https://materialsproject.org/"

    # Handle lib_directory
    if lib_directory is None:
        lib_directory = Path(libdir, 'reference_crystal')
    if not lib_directory.is_dir():
        lib_directory.mkdir()

    elements.sort()

    # Build list of downloaded entries
    have = []
    for fname in lib_directory.glob('*.json'):
        have.append(fname.stem)

    # Open connection to Materials Project
    with MPRester(api_key) as m:

        # Loop over subsets of elements
        for subelements in subsets(elements):

            # Query MP for all entries corresponding to the elements
            entries = m.query({"elements": subelements}, ["material_id"])

            # Add entries to the list if not there
            missing = []
            for entry in entries:
                if entry['material_id'] not in have and entry[
                        'material_id'] not in missing:
                    missing.append(entry['material_id'])

            # Download missing entries
            try:
                entries = m.query({"material_id": {
                    "$in": missing
                }}, ['material_id', 'cif'])
            except:
                pass
            else:
                # Convert cif to model and save
                for entry in entries:
                    name = entry['material_id']
                    struct = pmg.Structure.from_str(entry['cif'], fmt='cif')
                    struct = pmg.symmetry.analyzer.SpacegroupAnalyzer(
                        struct).get_conventional_standard_structure()
                    ucell = am.load('pymatgen_Structure', struct).normalize()
                    model = build_reference_crystal_model(
                        name, ucell, sourcename, sourcelink)
                    with open(Path(lib_directory, name + '.json'), 'w') as f:
                        model.json(fp=f, indent=4)
                    print('Added', entry['material_id'])
Пример #30
0
def crystal_space_group(system, symprec=1e-5, to_primitive=False,
                        no_idealize=False):
    """
    Uses spglib to evaluate space group information for a given system.
    
    Parameters
    ----------
    system : atomman.System
        The system to analyze.
    symprec : float
        Absolute length tolerance to use in identifying symmetry of atomic
        sites and system boundaries.
    to_primitive : bool
        Indicates if the returned unit cell is conventional (False) or
        primitive (True). Default value is False.
    no_idealize : bool
        Indicates if the atom positions in the returned unit cell are averaged
        (True) or idealized based on the structure (False).  Default value is
        False.
    
    Returns
    -------
    dict
        Results dictionary containing space group information and an associated
        unit cell system.
    """
    # Identify the standardized unit cell representation
    ucell = spglib.standardize_cell(system.dump('spglib_cell'),
                                    to_primitive=to_primitive,
                                    no_idealize=no_idealize, symprec=symprec)
    
    # Convert back to atomman systems and normalize
    ucell = am.load('spglib_cell', ucell, symbols=system.symbols)
    ucell.atoms.pos -= ucell.atoms.pos[0]
    ucell = ucell.normalize()
    
    # Get space group metadata
    sym_data = spglib.get_symmetry_dataset(ucell.dump('spglib_cell'))
    spg_type = spglib.get_spacegroup_type(sym_data['hall_number'])
    
    # Generate Pearson symbol
    if spg_type['number'] <= 2:
        crystalclass = 'a'
    elif spg_type['number'] <= 15:
        crystalclass = 'm'
    elif spg_type['number'] <= 74:
        crystalclass = 'o'
    elif spg_type['number'] <= 142:
        crystalclass = 't'
    elif spg_type['number'] <= 194:
        crystalclass = 'h'
    else:
        crystalclass = 'c'
    
    latticetype = spg_type['international'][0]
    if latticetype in ['A', 'B']:
        latticetype = 'C'
    
    natoms = str(ucell.natoms)
    pearson = crystalclass + latticetype + natoms
    
    # Return results
    results_dict = spg_type
    results_dict['ucell'] = ucell
    results_dict['hall_number'] = sym_data['hall_number']
    results_dict['wyckoffs'] = sym_data['wyckoffs']
    results_dict['pearson'] = pearson
    
    return results_dict
Пример #31
0
def phononcalc(lammps_command,
               ucell,
               potential,
               mpi_command=None,
               a_mult=3,
               b_mult=3,
               c_mult=3,
               distance=0.01,
               symprec=1e-5,
               savefile='phonopy_params.yaml',
               plot=True,
               lammps_date=None):
    """
    Uses phonopy to compute the phonons for a unit cell structure using a
    LAMMPS interatomic potential.

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

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

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

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

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

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

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

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

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

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

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

    results = {}

    # Set computed forces
    phonon.set_forces(forcearrays)

    # Save to yaml file
    phonon.save(savefile)

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

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

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

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

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

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

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

    # Define lammps variables
    lammps_variables = {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return results
Пример #33
0
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
Пример #34
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
Пример #35
0
    def todict(self, full=True, flat=False):
        """
        Converts the structured content to a simpler dictionary.
        
        Parameters
        ----------
        full : bool, optional
            Flag used by the calculation records.  A True value will include
            terms for both the calculation's input and results, while a value
            of False will only include input terms (Default is True).
        flat : bool, optional
            Flag affecting the format of the dictionary terms.  If True, the
            dictionary terms are limited to having only str, int, and float
            values, which is useful for comparisons.  If False, the term
            values can be of any data type, which is convenient for analysis.
            (Default is False).
            
        Returns
        -------
        dict
            A dictionary representation of the record's content.
        """

        calc = self.content[self.contentroot]
        params = {}
        params['key'] = calc['key']
        params['script'] = calc['calculation']['script']
        params['iprPy_version'] = calc['calculation']['iprPy-version']

        params['symmetryprecision'] = calc['calculation']['run-parameter'][
            'symmetryprecision']
        params['primitivecell'] = calc['calculation']['run-parameter'][
            'primitivecell']
        params['idealcell'] = calc['calculation']['run-parameter']['idealcell']

        params['load_file'] = calc['system-info']['artifact']['file']
        params['load_style'] = calc['system-info']['artifact']['format']
        params['load_options'] = calc['system-info']['artifact'][
            'load_options']
        params['family'] = calc['system-info']['family']

        symbols = aslist(calc['system-info']['symbol'])
        if flat is True:
            try:
                params['symbols'] = ' '.join(symbols)
            except:
                params['symbols'] = np.nan
        else:
            params['symbols'] = symbols

        params['status'] = calc.get('status', 'finished')
        params['error'] = calc.get('error', np.nan)

        if full is True and params['status'] == 'finished':
            ucell = am.load('system_model',
                            self.content,
                            key='unit-cell-atomic-system')
            params['pearson_symbol'] = calc['Pearson-symbol']
            params['spacegroup_number'] = calc['space-group']['number']
            params['spacegroup_international'] = calc['space-group'][
                'Hermann-Maguin']
            params['spacegroup_Schoenflies'] = calc['space-group'][
                'Schoenflies']
            params['wykoff_letters'] = ' '.join(
                calc['space-group'].finds('letter'))

            if flat is True:
                params['a'] = ucell.box.a
                params['b'] = ucell.box.b
                params['c'] = ucell.box.c
                params['alpha'] = ucell.box.alpha
                params['beta'] = ucell.box.beta
                params['gamma'] = ucell.box.gamma
                params['natoms'] = ucell.natoms
            else:
                params['ucell'] = ucell

        return params
Пример #36
0
    def todict(self, full=True, flat=False):
        """
        Converts the structured content to a simpler dictionary.
        
        Parameters
        ----------
        full : bool, optional
            Flag used by the calculation records.  A True value will include
            terms for both the calculation's input and results, while a value
            of False will only include input terms (Default is True).
        flat : bool, optional
            Flag affecting the format of the dictionary terms.  If True, the
            dictionary terms are limited to having only str, int, and float
            values, which is useful for comparisons.  If False, the term
            values can be of any data type, which is convenient for analysis.
            (Default is False).
            
        Returns
        -------
        dict
            A dictionary representation of the record's content.
        """

        # Extract universal content
        params = super().todict(full=full, flat=flat)

        calc = self.content[self.contentroot]

        params['symmetryprecision'] = calc['calculation']['run-parameter'][
            'symmetryprecision']
        params['primitivecell'] = calc['calculation']['run-parameter'][
            'primitivecell']
        params['idealcell'] = calc['calculation']['run-parameter']['idealcell']

        # Extract system info
        subset('atomman_systemload').todict(calc, params, full=full, flat=flat)

        params['status'] = calc.get('status', 'finished')
        params['error'] = calc.get('error', np.nan)

        if full is True and params['status'] == 'finished':
            ucell = am.load('system_model',
                            self.content,
                            key='unit-cell-atomic-system')
            params['pearson_symbol'] = calc['Pearson-symbol']
            params['spacegroup_number'] = calc['space-group']['number']
            params['spacegroup_international'] = calc['space-group'][
                'Hermann-Maguin']
            params['spacegroup_Schoenflies'] = calc['space-group'][
                'Schoenflies']
            params['wykoff_fingerprint'] = calc['space-group'][
                'Wyckoff-fingerprint']
            params['composition'] = ucell.composition

            if flat is True:
                params['a'] = ucell.box.a
                params['b'] = ucell.box.b
                params['c'] = ucell.box.c
                params['alpha'] = ucell.box.alpha
                params['beta'] = ucell.box.beta
                params['gamma'] = ucell.box.gamma
                params['natoms'] = ucell.natoms
            else:
                params['ucell'] = ucell

        return params
Пример #37
0
    def load_ucell(self, **kwargs):
        """
        Wrapper around atomman.load() for loading files that also saves the
        file loading options as class attributes.  Any parameters not given
        will use the values already set to the object.

        Parameters
        ----------
        load_style : str, optional
            The style for atomman.load() to use.
        load_file : str, optional
            The path to the file to load.
        symbols : list or None, optional
            The list of interaction model symbols to associate with the atom
            types in the load file.  A value of None will default to the
            symbols listed in the load file if the style contains that
            information.
        load_options : dict, optional
            Any other atomman.load() keyword options to use when loading.
        box_parameters : list or None, optional
            A list of 3 orthorhombic box parameters or 6 trigonal box length
            and angle parameters to scale the loaded system by.  Setting a
            value of None will perform no scaling.
        family : str or None, optional
            The system's family identifier.  If None, then the family will be
            set according to the family value in the load file if it has one,
            or as the load file's name otherwise.
        """
        self.set_values(**kwargs)

        # Check for file and contents
        if self.load_content is not None:
            load_file = self.load_content
        elif self.load_file is not None:
            load_file = self.load_file
        else:
            raise ValueError('load_file not set')

        # Change load symbols kwarg to None if symbols attribute is empty
        if self.symbols is None or len(self.symbols) == 0:
            symbols = None
        else:
            symbols = self.symbols

        # Load ucell
        self.__ucell = am.load(self.load_style, load_file,
                               symbols=symbols, **self.load_options)
        self.ucell.wrap()
        
        # Update object's symbols and composition
        self.symbols = self.ucell.symbols
        self.composition
        self.scale_ucell()

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