def cost_function(pos, dislo, bulk, cylinder_r, elastic_param, print_info=True): """Cost function for fitting analytical displacement field and detecting dislcoation core position. Uses `compare_configurations` function for the minisation of the core position. Parameters ---------- pos : list of float Positions of the core to build the analytical solution [x, y]. dislo : ase.Atoms Dislocation configuration. bulk : ase.Atoms Corresponding bulk configuration for calculation of displacements. cylinder_r : float or None Radius of region of comparison around the dislocation coreself. If None makes global comparison based on the radius of `dislo` configuration, else compares the regions with `cylinder_r` around the dislocation core position. elastic_param : list of float List containing alat, C11, C12, C44 print_info : bool Flag to switch print statement about the type of the comparison Returns ------- float Error for optimisation (result from `compare_configurations` function) """ # Create a Stroh ojbect with junk data stroh = am.defect.Stroh(am.ElasticConstants(C11=141, C12=110, C44=98), np.array([0, 0, 1])) axes = np.array([[1, 1, -2], [-1, 1, 0], [1, 1, 1]]) alat, C11, C12, C44 = elastic_param c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat * np.array([1., 1., 1.])/2. # Solving a new problem with Stroh.solve stroh.solve(c, burgers, axes=axes) center = (pos[0], pos[1], 0.0) u = stroh.displacement(bulk.positions - center) dislo_guess = bulk.copy() dislo_guess.positions += u err = compare_configurations(dislo, bulk, dislo_guess, bulk, alat, cylinder_r=cylinder_r, print_info=print_info) return err
def intro_edge(self): self.find_angles_1100(il=[[1], [1]], jl=[1]) # 58.361 ux = self.pot['ahcp'] uy = self.pot['chcp'] uz = self.pot['ahcp'] * sqrt(3.) atoms = ase.io.read("dump/dump.00017", format='lammps-dump') atoms.rotate(self.ag[0][0], 'z') # rotate atoms.translate(np.array([80 * ux, 0.0, 0])) # # introduce dislocation axes = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() c.hexagonal(C11=self.pot['C11'], C12=self.pot['C12'], C33=self.pot['C33'], C13=self.pot['C13'], C44=self.pot['C44']) stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cx = 60 * ux + 0.01 cy = 60 * uy + 0.01 shift = np.ones(pos.shape) * np.array([cx, cy, 0.0]) disp = stroh.displacement(pos - shift) atoms.set_positions(pos + np.real(disp)) atoms.translate(np.array([-80 * ux, 0.0, 0])) atoms.rotate(-self.ag[0][0], 'z') # rotate back # try with non periodict boundary conditions cell = atoms.get_cell() cell[0, 0] += 30 cell[1, 1] += 30 atoms.translate(np.array([15, 15, 0.0])) atoms.set_cell(cell) # ase.io.write("pos02", images=atoms, format='cfg') self.write_lmp_config_data(atoms, "pos02")
def build_screw_basal_hcp_atoms_dipole(self, atoms): axes = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() c.hexagonal(C11=64.3218889159844, C33=70.9452244231304, C12=25.4175328222502, C13=20.306421440903, C44=18.0690056385527) # curtin stroh1 = stroh_solve.Stroh(c, burgers, axes=axes) # stroh2 = stroh_solve.Stroh(c, -burgers, axes=axes) cell = atoms.get_cell() pos = atoms.get_positions() cx = 0.25 * cell[0, 0] + 0.01 cy1 = 0.25 * cell[1, 1] + 0.01 cy2 = 0.75 * cell[1, 1] + 0.01 pos = atoms.get_positions() print(cy1, cy2) shift1 = np.ones(pos.shape) * np.array([cx, cy1, 0.0]) shift2 = np.ones(pos.shape) * np.array([cx, cy2, 0.0]) disp1 = stroh1.displacement(pos - shift1) disp2 = stroh1.displacement(pos - shift2) # atoms.set_positions(pos + np.real(disp1)) atoms.set_positions(pos + np.real(disp1) - np.real(disp2)) return atoms
def hcp_edge_dislocation(self): sz = (40, 40, 10) atoms = othoHCP(latticeconstant=(self.pot['ahcp'], self.pot['chcp'], self.pot['ahcp'] * sqrt(3.)), size=sz, symbol=self.pot['element']) # let the axes consistent with the elastic constants axes = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() # the lattice constant has conventional direction as # x - [1 -2 1 0] y[1, 0, -1, 0] z [0, 0, 0, 1] c.hexagonal(C11=61.8, C33=67.5, C12=25.9, C13=21.9, C44=18.2) # kim stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cx = 20 * self.pot["ahcp"] + 0.01 cy = 20 * self.pot["chcp"] + 0.01 s1 = np.ones(pos.shape) * np.array([cx, cy, 0.0]) d1 = stroh.displacement(pos - s1) # cy = 60 * self.pot["chcp"] - 0.01 # s2 = np.ones(pos.shape) * np.array([cx, cy, 0.0]) # d2 = stroh.displacement(pos - s2) atoms.set_positions(pos + np.real(d1)) # cut a layer normal the burger direction # atoms = self.cut_x_normal_atoms(atoms, lata, 1, sqrt(3) / 4.0) # atoms = self.cut_x_normal_atoms(atoms) atoms = self.cut_y_normal_atoms(atoms) atoms = self.cut_x_normal_atoms(atoms) self.write_lmp_config_data(atoms)
def build_screw_basal_hcp_atoms(self, atoms): axes = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() # the lattice constant has conventional direction as # x - [1 -2 1 0] y[1, 0, -1, 0] z [0, 0, 0, 1] # c.hexagonal(C11=61.8, C33=67.5, C12=25.9, C13=21.9, C44=18.2) # kim c.hexagonal(C11=64.3218889159844, C33=70.9452244231304, C12=25.4175328222502, C13=20.306421440903, C44=18.0690056385527) # curtin stroh = stroh_solve.Stroh(c, burgers, axes=axes) cell = atoms.get_cell() cx = 0.25 * cell[0, 0] + 0.01 cy = 0.50 * cell[1, 1] + 0.01 pos = atoms.get_positions() shift = np.ones(pos.shape) * np.array([cx, cy, 0.0]) disp = stroh.displacement(pos - shift) atoms.set_positions(pos + np.real(disp)) # pos = atoms.get_positions() # shiftN = np.ones(pos.shape) * np.array([cx + 0.5 * cell[0, 0], # cy, 0.0]) # dispN = stroh.displacement(pos - shiftN) # atoms.set_positions(pos + np.real(dispN)) return atoms
def load_model(self, model, name=None): """ Loads record contents from a given model. Parameters ---------- model : str or DataModelDict The model contents of the record to load. name : str, optional The name to assign to the record. Often inferred from other attributes if not given. """ # Load universal and subset content super().load_model(model, name=name) calc = self.model[self.modelroot] # Load calculation-specific content run_params = calc['calculation']['run-parameter'] self.strainrange = run_params['strain-range'] # Load results if self.status == 'finished': self.__raw_Cij_negative = uc.value_unit(calc['raw-elastic-constants'][0]['Cij']) self.__raw_Cij_positive = uc.value_unit(calc['raw-elastic-constants'][1]['Cij']) Cij = uc.value_unit(calc['elastic-constants']['Cij']) self.__C = am.ElasticConstants(Cij=Cij)
def load_model(self, model): """Loads subset attributes from an existing model.""" try: c_model = DM([('elastic-constants', model[self.modelroot])]) except: self.__C = None else: self.__C = am.ElasticConstants(model=c_model)
def fcc_edge(self): axes = np.array([[1, 0, -1], [1, 1, 1], [1, -2, 1]]) alat = uc.set_in_units(4.0248, 'angstrom') C11 = uc.set_in_units(113.76, 'GPa') C12 = uc.set_in_units(61.71, 'GPa') C44 = uc.set_in_units(31.25, 'GPa') c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat / 2 * np.array([1., 0., -1.]) # initializing a new Stroh object using the data stroh = am.defect.Stroh(c, burgers, axes=axes) pos_test = uc.set_in_units(np.array([12.4, 13.5, -10.6]), 'angstrom') disp = stroh.displacement(pos_test) print("displacement =", uc.get_in_units(disp, 'angstrom'), 'angstrom') # monopole system box = am.Box(a=alat, b=alat, c=alat) atoms = am.Atoms(natoms=4, prop={ 'atype': 1, 'pos': [[0.0, 0.0, 0.0], [0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]] }) ucell = am.System(atoms=atoms, box=box, scale=True) system = am.rotate_cubic(ucell, axes) shift = np.array( [0.12500000000000, 0.50000000000000, 0.00000000000000]) new_pos = system.atoms_prop(key='pos', scale=True) + shift system.atoms_prop(key='pos', value=new_pos, scale=True) system.supersize((-7, 7), (-6, 6), (0, 1)) disp = stroh.displacement(system.atoms_prop(key='pos')) system.atoms_prop(key='pos', value=system.atoms_prop(key='pos') + disp) system.pbc = (False, False, True) system.wrap() pos = system.atoms_prop(key='pos') x = uc.get_in_units(pos[:, 0], 'angstrom') y = uc.get_in_units(pos[:, 1], 'angstrom') plt.figure(figsize=(8, 8)) plt.scatter(x, y, s=30) plt.xlim(min(x), max(x)) plt.ylim(min(y), max(y)) plt.xlabel('x-position (Angstrom)', fontsize='large') plt.ylabel('y-position (Angstrom)', fontsize='large') plt.show()
def estimate_elastic_constants(raw_dict): """Computes elastic constants using the calculation data.""" results_dict = {} # Copy the zero strain data to results_dict results_dict.update(raw_dict['0']) # Initialize cij and cij_std arrays cij = np.empty((6,6)) cij_std = np.zeros((6,6)) # Relate the simulation states to strain components all_eps = [['xx+', 'xx-'], ['yy+', 'yy-'], ['zz+', 'zz-'], ['yz+', 'yz-'], ['xz+', 'xz-'], ['xy+', 'xy-']] # Iterate over strain components for i, eps in enumerate(all_eps): # Calculate elastic constants delta_strain = raw_dict[eps[0]]['strain'][i] - raw_dict[eps[1]]['strain'][i] print delta_strain delta_stress = - np.array([ raw_dict[eps[0]]['pxx'][0] - raw_dict[eps[1]]['pxx'][0], raw_dict[eps[0]]['pyy'][0] - raw_dict[eps[1]]['pyy'][0], raw_dict[eps[0]]['pzz'][0] - raw_dict[eps[1]]['pzz'][0], raw_dict[eps[0]]['pyz'][0] - raw_dict[eps[1]]['pyz'][0], raw_dict[eps[0]]['pxz'][0] - raw_dict[eps[1]]['pxz'][0], raw_dict[eps[0]]['pxy'][0] - raw_dict[eps[1]]['pxy'][0] ]) print np.array_str(delta_stress, precision=4, supress_small=True) cij[i] = delta_stress / delta_strain # Calculate error cij_std[i] = np.array([ raw_dict[eps[0]]['pxx'][1]**2 + raw_dict[eps[1]]['pxx'][1]**2, raw_dict[eps[0]]['pyy'][1]**2 + raw_dict[eps[1]]['pyy'][1]**2, raw_dict[eps[0]]['pzz'][1]**2 + raw_dict[eps[1]]['pzz'][1]**2, raw_dict[eps[0]]['pyz'][1]**2 + raw_dict[eps[1]]['pyz'][1]**2, raw_dict[eps[0]]['pxz'][1]**2 + raw_dict[eps[1]]['pxz'][1]**2, raw_dict[eps[0]]['pxy'][1]**2 + raw_dict[eps[1]]['pxy'][1]**2 ])**0.5 / delta_strain print print np.array_str(cij, precision=4, suppress_small=True) print np.array_str(cij_std, precision=4, suppress_small=True) # Average symmetric terms for i in xrange(6): for j in xrange(i): cij[i,j] = cij[j,i] = (cij[i,j] + cij[j,i]) / 2 cij_std[i,j] = cij_std[j,i] = (cij_std[i,j] + cij_std[j,i]) / 2 print print np.array_str(cij, precision=4, suppress_small=True) print np.array_str(cij_std, precision=4, suppress_small=True) results_dict['C'] = am.ElasticConstants(Cij=cij) results_dict['cij_std'] = cij_std return results_dict
def make_screw_plate(self, size=[70, 90, 3], rad=[150, 160], move=[0., 0., 0.], filename="lmp_init.txt", opt=None): e1 = [1, -2, 1] e2 = [1, 0, -1] e3 = [1, 1, 1] axes = np.array([e1, e2, e3]) alat = self.pot['lattice'] c = am.ElasticConstants(C11=self.pot['c11'], C12=self.pot['c12'], C44=self.pot['c44']) ux = np.sqrt(6) / 3. * self.pot['lattice'] uy = np.sqrt(2) / 2. * self.pot['lattice'] burgers = 0.5 * alat * np.array([1., 1., 1.]) stroh = am.defect.Stroh(c, burgers, axes=axes) atoms = self.set_bcc_convention([e1, e2, e3], (size[0], size[1], size[2])) pos = atoms.get_positions() center = np.array([3 * 0.5 * size[0] * ux, size[1] * uy]) delindex = [] radius2 = rad[0] * rad[0] radiusout2 = rad[1] * rad[1] for i in range(len(pos)): atom = atoms[i] dx = pos[i, 0] - center[0] dy = pos[i, 1] - center[1] r = dx * dx + dy * dy if r > radiusout2: delindex.append(atom.index) if r < radius2: # atom.symbol = 'W' continue del atoms[delindex] pos = atoms.get_positions() discenter = np.array( [center[0] + 0.5 * ux, center[1] + 1. / 3. * uy, 0.0]) shf = np.ones(pos.shape) * discenter print(pos - shf) d1 = stroh.displacement(pos - shf) # before displace generate perfect atoms self.write_lmp_config_data(atoms, 'lmp_perf.txt') atoms.set_positions(pos + np.real(d1)) self.write_lmp_config_data(atoms, 'lmp_init.txt') np.savetxt("dis_center.txt", discenter)
def build_hcp_ase_1100_with_edge(self): self.find_angles_1100(il=[[1], [1]], jl=[1]) # 58.361 ux = self.pot['ahcp'] uy = self.pot['chcp'] uz = self.pot['ahcp'] * sqrt(3.) print(self.ag) atoms = othoHCP(latticeconstant=(ux, uy, uz), size=(80, 80, 2), symbol=self.pot['element']) # introduce dislocation axes = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() c.hexagonal(C11=self.pot['C11'], C12=self.pot['C12'], C33=self.pot['C33'], C13=self.pot['C13'], C44=self.pot['C44']) stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cell = atoms.get_cell() cell[0, 0], cell[1, 1] = 8 * self.ag[0][1], 300 cx = 30 * ux + 0.01 cy = 30 * uy + 0.01 shift = np.ones(pos.shape) * np.array([cx, cy, 0.0]) disp = stroh.displacement(pos - shift) atoms.set_positions(pos + np.real(disp)) atoms.rotate(self.ag[0][0], 'z') atoms.translate(np.array([cell[0, 0], -100, 0])) lob = np.array([0.0, 10, 0.0]) hib = np.array([cell[0, 0], 0.5 * cell[1, 1] + 0.3, cell[2, 2]]) atoms = self.make_cubic('out', atoms, lob, hib) atoms2 = othoHCP(latticeconstant=(ux, uy, uz), size=(80, 80, 2), symbol=self.pot['element']) lob = np.array([0.0, 0.5 * cell[1, 1], 0.0]) hib = np.array([cell[0, 0], cell[1, 1] - 10, cell[2, 2]]) atoms2.rotate(-self.ag[0][0], 'z') atoms2.translate(np.array([-90, cell[1, 1], 0])) # for 72.877 atoms2 = self.make_cubic('out', atoms2, lob, hib) atoms.extend(atoms2) # try with non periodict boundary conditions atoms.set_cell(cell) self.write_lmp_config_data(atoms, "lmp_init.txt")
def cal_screw_const(self, tag='intro'): axes = np.array([[1, 1, -2], [-1, 1, 0], [1, 1, 1]]) alat = uc.set_in_units(self.pot['lattice'], 'angstrom') C11 = uc.set_in_units(self.pot['c11'], 'GPa') C12 = uc.set_in_units(self.pot['c12'], 'GPa') C44 = uc.set_in_units(self.pot['c44'], 'GPa') c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat / 2 * np.array([1., 1., 1.]) stroh = am.defect.Stroh(c, burgers, axes=axes) print("K tensor", stroh.K_tensor) print("K (biKijbj)", stroh.K_coeff, "eV/A") print("pre-ln alpha = biKijbj/4pi", stroh.preln, "ev/A")
def build_screw_basal_hcp(self): sz = (100, 100, 5) ux = self.pot["ahcp"] * sqrt(3) uy = self.pot["chcp"] uz = self.pot['ahcp'] atoms = othoHCPB(latticeconstant=(ux, uy, uz), size=sz, symbol=self.pot['element']) # let the axes consistent with the elastic constants axes = np.array([[0, 1, 0], [0, 0, 1], [1, 0, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() # the lattice constant has conventional direction as # x - [1 -2 1 0] y[1, 0, -1, 0] z [0, 0, 0, 1] # c.hexagonal(C11=61.8, C33=67.5, C12=25.9, C13=21.9, C44=18.2) # c.hexagonal(C11=62.807, C33=69.615, C12=25.974, # C13=21.184, C44=17.138) # Kim c.hexagonal(C11=64.3218889159844, C33=70.9452244231304, C12=25.4175328222502, C13=20.306421440903, C44=18.0690056385527) # curtin stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cx = 0.5 * sz[0] * ux + 0.01 cy = 0.5 * sz[1] * uy + 0.01 print(cx, cy) self.write_lmp_config_data(atoms, "before.txt") shift = np.ones(pos.shape) * np.array([cx, cy, 0.0]) disp = stroh.displacement(pos - shift) atoms.set_positions(pos + np.real(disp)) self.write_lmp_config_data(atoms) ase.io.write("SCREW.cfg", atoms, format="cfg") return atoms
def print_dis_constants(self): struct = "hex" if struct in ["cubic"]: # Cubic c = tool_elastic_constants.elastic_constants(C11=self.pot['c11'], C12=self.pot['c12'], C44=self.pot['c44']) axes = np.array([[1, -1, 1], [2, 1, -1], [0, 1, 1]]) burgers = self.pot['lattice'] / 2 * np.array([1., 1., 1.]) stroh = stroh_solve.Stroh(c, burgers, axes=axes) print(stroh.A[0]) # hexagonal if struct in ["hex"]: print(self.pot["lattice"]) axes = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) burgers = self.pot['lattice'] / 2 * np.array([1., 1, 0]) c = am.ElasticConstants() c.hexagonal(C11=326.08, C33=357.50, C12=129.56, C13=119.48, C44=92.54) stroh = stroh_solve.Stroh(c, burgers, axes=axes) print(stroh.A) print(stroh.L)
def build_edge_basal_hcp_atoms(self, atoms, center, sign=1): # let the axes consistent with the elastic constants axes = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) * sign c = am.ElasticConstants() print("build edge basial hcp") # x - [1 -2 1 0] y[1, 0, -1, 0] z [0, 0, 0, 1] # c.hexagonal(C11=61.8, C33=67.5, C12=25.9, C13=21.9, C44=18.2) # kim c.hexagonal(C11=64.3218889159844, C33=70.9452244231304, C12=25.4175328222502, C13=20.306421440903, C44=18.0690056385527) # curtin stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cx = center[0] + 0.01 cy = center[1] + 0.01 print(cx, cy) shift = np.ones(pos.shape) * np.array([cx, cy, 0.0]) disp = stroh.displacement(pos - shift) atoms.set_positions(pos + np.real(disp)) return atoms
def build_edge_basal_hcp(self): sz = (200, 100, 3) atoms = othoHCP(latticeconstant=(self.pot['ahcp'], self.pot['chcp'], self.pot['ahcp'] * sqrt(3.)), size=sz, symbol=self.pot['element']) # let the axes consistent with the elastic constants axes = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() # the lattice constant has conventional direction as # x - [1 -2 1 0] y[1, 0, -1, 0] z [0, 0, 0, 1] # c.hexagonal(C11=61.8, C33=67.5, C12=25.9, C13=21.9, C44=18.2) # kim # c.hexagonal(C11=62.369, C33=67.788, C12=26.252, # C13=22.113, C44=18.274) # Coco # c.hexagonal(C11=62.807, C33=69.615, C12=25.974, # C13=21.184, C44=17.138) # Kim c.hexagonal(C11=64.3556, C33=70.9849, C12=25.460289, C13=20.333408, C44=18.06331) # Curtin stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cx = 0.5 * sz[0] * self.pot["ahcp"] + 0.01 cy = 0.5 * sz[1] * self.pot["chcp"] + 0.01 print(cx, cy) shift = np.ones(pos.shape) * np.array([cx, cy, 0.0]) disp = stroh.displacement(pos - shift) atoms.set_positions(pos + np.real(disp)) self.write_lmp_config_data(atoms) return atoms
def todict(self, record_model, params, full=True, flat=False): """ Converts the structured content to a simpler dictionary. Parameters ---------- record_model : DataModelDict.DataModelDict The record content (after root element) to interpret. params : dict The dictionary to add the interpreted content to 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). """ prefix = self.prefix modelprefix = prefix.replace('_', '-') c_model = DM() c_model['elastic-constants'] = record_model[ f'{modelprefix}elastic-constants'] if flat is True: for C in c_model['elastic-constants'].aslist('C'): params['C' + str(C['ij'][0]) + str(C['ij'][2])] = uc.value_unit(C['stiffness']) else: try: params['C'] = am.ElasticConstants(model=calc) except: params['C'] = 'Invalid'
def formular_make_edge_large(self): self.find_angles_1100(il=[[1], [1]], jl=[1]) # 58.361 ux = self.pot['ahcp'] uy = self.pot['chcp'] uz = self.pot['ahcp'] * np.sqrt(3.) atoms = ase.io.read(glob.glob("dump_init_v2/dump.*")[-1], format='lammps-dump') atoms.rotate(180 - self.ag[0][0], 'z') # rotate atoms.translate(np.array([80 * ux, 0, 0])) axes = np.array([[1, 0, 0], [0, 0, 1], [0, -1, 0]]) burgers = self.pot['lattice'] * np.array([1, 0, 0]) c = am.ElasticConstants() c.hexagonal(C11=self.pot['C11'], C12=self.pot['C12'], C33=self.pot['C33'], C13=self.pot['C13'], C44=self.pot['C44']) stroh = stroh_solve.Stroh(c, burgers, axes=axes) pos = atoms.get_positions() cell = atoms.get_cell() sh1 = np.ones(pos.shape) * \ np.array([-30 * ux + 0.1, 10 * uy, cell[2, 2]]) sh2 = np.ones(pos.shape) * \ np.array([-30 * ux + 0.1, 30 * uy, cell[2, 2]]) d1 = stroh.displacement(pos - sh1) d2 = stroh.displacement(pos - sh2) atoms.set_positions(pos + np.real(d1) - np.real(d2)) atoms.translate(np.array([-80 * ux, 0.0, 0])) atoms.rotate(self.ag[0][0] - 180, 'z') # rotate back self.write_lmp_config_data(atoms, "lmp_init02.txt")
def make_edge_cyl_001_100(a0, C11, C12, C44, cylinder_r, cutoff=5.5, tol=1e-6, symbol="W"): """Function to produce consistent edge dislocation configuration. Parameters ---------- alat : float Lattice constant of the material. C11 : float C11 elastic constant of the material. C12 : float C12 elastic constant of the material. C44 : float C44 elastic constant of the material. cylinder_r : float Radius of cylinder of unconstrained atoms around the dislocation in angstrom. cutoff : float Potential cutoff for determenition of size of fixed atoms region (2*cutoff) tol : float Tolerance for generation of self consistent solution. symbol : string Symbol of the element to pass to ase.lattuce.cubic.SimpleCubicFactory default is "W" for tungsten Returns ------- bulk : ase.Atoms object Bulk configuration. disloc : ase.Atoms object Dislocation configuration. disp : np.array Correstonding displacement. """ # Create a Stroh ojbect with junk data stroh = am.defect.Stroh(am.ElasticConstants(C11=141, C12=110, C44=98), np.array([0, 0, 1])) axes = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = a0 * np.array([1., 0., 0.]) # Solving a new problem with Stroh.solve # Does not work with the new version of atomman stroh.solve(c, burgers, axes=axes) unit_cell = BodyCenteredCubic(directions=axes.tolist(), size=(1, 1, 1), symbol=symbol, pbc=(False, False, True), latticeconstant=a0) bulk = unit_cell.copy() # shift to make the zeros of the cell betweem the atomic planes # and under the midplane on Y axes X_midplane_shift = -0.25*a0 Y_midplane_shift = -0.25*a0 bulk_shift = [X_midplane_shift, Y_midplane_shift, 0.0] bulk.positions += bulk_shift tot_r = cylinder_r + 2*cutoff + 0.01 Lx = int(round(tot_r/a0)) Ly = int(round(tot_r/a0)) # factor 2 to make sure odd number of images is translated # it is important for the correct centering of the dislocation core bulk = bulk * (2*Lx, 2*Ly, 1) center_shift = [Lx * a0, Ly * a0, 0.0] bulk.positions -= center_shift # bulk.write("before.xyz") disp1 = stroh.displacement(bulk.positions) disloc = bulk.copy() res = np.inf i = 0 while res > tol: disloc.positions = bulk.positions + disp1 disp2 = stroh.displacement(disloc.positions) res = np.abs(disp1 - disp2).max() disp1 = disp2 print 'disloc SCF', i, '|d1-d2|_inf =', res i += 1 if i > 10: raise RuntimeError('Self-consistency did \ not converge in 10 cycles') disp = disp2 x, y, z = disloc.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask = radius_x_y_zero < tot_r disloc = disloc[mask] bulk = bulk[mask] # disloc.write("after_disp.xyz") x, y, z = disloc.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask_zero = radius_x_y_zero > cylinder_r fix_atoms = FixAtoms(mask=mask_zero) disloc.set_constraint(fix_atoms) return bulk, disloc, disp
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] # Extract minimization info subset('lammps_minimize').todict(calc, params, full=full, flat=flat) params['strainrange'] = calc['calculation']['run-parameter'][ 'strain-range'] # Extract potential info subset('lammps_potential').todict(calc, params, full=full, flat=flat) # Extract system info subset('atomman_systemload').todict(calc, params, full=full, flat=flat) subset('atomman_systemmanipulate').todict(calc, params, full=full, flat=flat) if full is True and params['status'] == 'finished': cij = uc.value_unit(calc['elastic-constants']['Cij']) if flat is True: params['C11'] = cij[0, 0] params['C12'] = cij[0, 1] params['C13'] = cij[0, 2] params['C14'] = cij[0, 3] params['C15'] = cij[0, 4] params['C16'] = cij[0, 5] params['C22'] = cij[1, 1] params['C23'] = cij[1, 2] params['C24'] = cij[1, 3] params['C25'] = cij[1, 4] params['C26'] = cij[1, 5] params['C33'] = cij[2, 2] params['C34'] = cij[2, 3] params['C35'] = cij[2, 4] params['C36'] = cij[2, 5] params['C44'] = cij[3, 3] params['C45'] = cij[3, 4] params['C46'] = cij[3, 5] params['C55'] = cij[4, 4] params['C56'] = cij[4, 5] params['C66'] = cij[5, 5] else: params['raw_cij_negative'] = uc.value_unit( calc['raw-elastic-constants'][0]['Cij']) params['raw_cij_positive'] = uc.value_unit( calc['raw-elastic-constants'][1]['Cij']) params['C'] = am.ElasticConstants(Cij=cij) return params
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['atomman_version'] = calc['calculation']['atomman-version'] rp = calc['calculation']['run-parameter'] params['halfwidth'] = uc.value_unit(rp['halfwidth']) params['xmax'] = rp['xmax'] params['xnum'] = rp['xnum'] params['xstep'] = rp['xstep'] 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']) params['dislocation_key'] = calc['dislocation-monopole']['key'] params['dislocation_id'] = calc['dislocation-monopole']['id'] params['gammasurface_calc_key'] = calc['gamma-surface']['calc_key'] pnp = calc['semidiscrete-variational-Peierls-Nabarro']['parameter'] try: K_tensor = uc.value_unit(pnp['K_tensor']) except: K_tensor = np.nan tau = uc.value_unit(pnp['tau']) alpha = uc.value_unit(pnp['alpha']) beta = uc.value_unit(pnp['beta']) params['cdiffelastic'] = pnp['cdiffelastic'] params['cdiffsurface'] = pnp['cdiffsurface'] params['cdiffstress'] = pnp['cdiffstress'] params['cutofflongrange'] = uc.value_unit(pnp['cutofflongrange']) params['fullstress'] = pnp['fullstress'] params['min_method'] = pnp['min_method'] params['min_options'] = pnp['min_options'] if flat is True: params['symbols'] = ' '.join(symbols) for i in range(3): for j in range(i, 3): try: params['K' + str(i + 1) + str(j + 1)] = K_tensor[i, j] except: params['K' + str(i + 1) + str(j + 1)] = np.nan params['tau' + str(i + 1) + str(j + 1)] = tau[i, j] params['beta' + str(i + 1) + str(j + 1)] = beta[i, j] if not isinstance(alpha, list): alpha = [alpha] for i in range(len(alpha)): params['alpha' + str(i + 1)] = alpha[i] else: params['symbols'] = symbols params['K_tensor'] = K_tensor params['tau'] = tau params['alpha'] = alpha params['beta'] = beta params['status'] = calc.get('status', 'finished') if full is True: if params['status'] == 'error': params['error'] = calc['error'] elif params['status'] == 'not calculated': pass else: if flat is True: pass else: try: params['C'] = am.ElasticConstants(model=calc) except: params['C'] = np.nan if True: params['SDVPN_model'] = DM() params['SDVPN_model'][ 'semidiscrete-variational-Peierls-Nabarro'] = calc.find( 'semidiscrete-variational-Peierls-Nabarro') else: params['SDVPN_model'] = np.nan return params
def elastic_constants_static(lammps_command, system, potential, mpi_command=None, strainrange=1e-6, etol=0.0, ftol=0.0, maxiter=10000, maxeval=100000, dmax=uc.set_in_units(0.01, 'angstrom')): """ Repeatedly runs the ELASTIC example distributed with LAMMPS until box dimensions converge within a tolerance. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. strainrange : float, optional The small strain value to apply when calculating the elastic constants (default is 1e-6). etol : float, optional The energy tolerance for the structure minimization. This value is unitless. (Default is 0.0). ftol : float, optional The force tolerance for the structure minimization. This value is in units of force. (Default is 0.0). maxiter : int, optional The maximum number of minimization iterations to use (default is 10000). maxeval : int, optional The maximum number of minimization evaluations to use (default is 100000). dmax : float, optional The maximum distance in length units that any atom is allowed to relax in any direction during a single minimization iteration (default is 0.01 Angstroms). Returns ------- dict Dictionary of results consisting of keys: - **'a_lat'** (*float*) - The relaxed a lattice constant. - **'b_lat'** (*float*) - The relaxed b lattice constant. - **'c_lat'** (*float*) - The relaxed c lattice constant. - **'alpha_lat'** (*float*) - The alpha lattice angle. - **'beta_lat'** (*float*) - The beta lattice angle. - **'gamma_lat'** (*float*) - The gamma lattice angle. - **'E_coh'** (*float*) - The cohesive energy of the relaxed system. - **'stress'** (*numpy.array*) - The measured stress state of the relaxed system. - **'C_elastic'** (*atomman.ElasticConstants*) - The relaxed system's elastic constants. - **'system_relaxed'** (*atomman.System*) - The relaxed system. """ # Convert hexagonal cells to orthorhombic to avoid LAMMPS tilt issues if am.tools.ishexagonal(system.box): system = system.rotate([[2, -1, -1, 0], [0, 1, -1, 0], [0, 0, 0, 1]]) # Get lammps units lammps_units = lmp.style.unit(potential.units) # Get lammps version date lammps_date = lmp.checkversion(lammps_command)['date'] # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) lammps_variables['strainrange'] = strainrange lammps_variables['etol'] = etol lammps_variables['ftol'] = uc.get_in_units(ftol, lammps_units['force']) lammps_variables['maxiter'] = maxiter lammps_variables['maxeval'] = maxeval lammps_variables['dmax'] = uc.get_in_units(dmax, lammps_units['length']) # Fill in template files template_file = 'cij.template' lammps_script = 'cij.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) template_file2 = 'potential.template' lammps_script2 = 'potential.in' with open(template_file2) as f: template = f.read() with open(lammps_script2, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run LAMMPS output = lmp.run(lammps_command, lammps_script, mpi_command) # Pull out initial state thermo = output.simulations[0]['thermo'] pxx0 = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy0 = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz0 = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz0 = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz0 = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy0 = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Negative strains cij_n = np.empty((6, 6)) for i in range(6): j = 1 + i * 2 # Pull out strained state thermo = output.simulations[j]['thermo'] pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Calculate cij_n using stress changes cij_n[i] = np.array([ pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0, pxy - pxy0 ]) / strainrange # Positive strains cij_p = np.empty((6, 6)) for i in range(6): j = 2 + i * 2 # Pull out strained state thermo = output.simulations[j]['thermo'] pxx = uc.set_in_units(thermo.Pxx.values[-1], lammps_units['pressure']) pyy = uc.set_in_units(thermo.Pyy.values[-1], lammps_units['pressure']) pzz = uc.set_in_units(thermo.Pzz.values[-1], lammps_units['pressure']) pyz = uc.set_in_units(thermo.Pyz.values[-1], lammps_units['pressure']) pxz = uc.set_in_units(thermo.Pxz.values[-1], lammps_units['pressure']) pxy = uc.set_in_units(thermo.Pxy.values[-1], lammps_units['pressure']) # Calculate cij_p using stress changes cij_p[i] = -np.array([ pxx - pxx0, pyy - pyy0, pzz - pzz0, pyz - pyz0, pxz - pxz0, pxy - pxy0 ]) / strainrange # Average symmetric values cij = (cij_n + cij_p) / 2 for i in range(6): for j in range(i): cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2 # Define results_dict results_dict = {} results_dict['raw_cij_negative'] = cij_n results_dict['raw_cij_positive'] = cij_p results_dict['C'] = am.ElasticConstants(Cij=cij) return results_dict
def 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] # Extract minimization info subset('lammps_minimize').todict(calc, params, full=full, flat=flat) params['annealtemperature'] = calc['calculation']['run-parameter']['annealtemperature'] params['annealsteps'] = calc['calculation']['run-parameter']['annealsteps'] # Extract potential info subset('lammps_potential').todict(calc, params, full=full, flat=flat) # Extract system info subset('atomman_systemload').todict(calc, params, full=full, flat=flat) subset('dislocation').todict(calc, params, full=full, flat=flat) if full is True and params['status'] == 'finished': params['preln'] = uc.value_unit(calc['elastic-solution']['pre-ln-factor']) K_tensor = uc.value_unit(calc['elastic-solution']['K-tensor']) if flat is True: for C in calc['elastic-constants'].aslist('C'): params['C'+str(C['ij'][0])+str(C['ij'][2])] = uc.value_unit(C['stiffness']) params['K11'] = K_tensor[0,0] params['K12'] = K_tensor[0,1] params['K13'] = K_tensor[0,2] params['K22'] = K_tensor[1,1] params['K23'] = K_tensor[1,2] params['K33'] = K_tensor[2,2] else: try: params['C'] = am.ElasticConstants(model=calc) except: params['C'] = 'Invalid' params['K_tensor'] = K_tensor return params
def calc_cij(lammps_command, system, potential, mpi_command=None, p_xx=0.0, p_yy=0.0, p_zz=0.0, strainrange=1e-6, cycle=0): """ Runs cij.in LAMMPS script to evaluate Cij, and E_coh of the current system, and define a new system with updated box dimensions to test. Parameters ---------- lammps_command :str Command for running LAMMPS. system : atomman.System The system to perform the calculation on. potential : atomman.lammps.Potential The LAMMPS implemented potential to use. mpi_command : str, optional The MPI command for running LAMMPS in parallel. If not given, LAMMPS will run serially. strainrange : float, optional The small strain value to apply when calculating the elastic constants (default is 1e-6). p_xx : float, optional The value to relax the x tensile pressure component to (default is 0.0). p_yy : float, optional The value to relax the y tensile pressure component to (default is 0.0). p_zz : float, optional The value to relax the z tensile pressure component to (default is 0.0). cycle : int, optional Indicates the iteration cycle of quick_a_Cij(). This is used to uniquely save the LAMMPS input and output files. Returns ------- dict Dictionary of results consisting of keys: - **'E_coh'** (*float*) - The cohesive energy of the supplied system. - **'stress'** (*numpy.array*) - The measured stress state of the supplied system. - **'C_elastic'** (*atomman.ElasticConstants*) - The supplied system's elastic constants. - **'system_new'** (*atomman.System*) - System with updated box dimensions. Raises ------ RuntimeError If any of the new box dimensions are less than zero. """ try: # Get script's location if __file__ exists script_dir = Path(__file__).parent except: # Use cwd otherwise script_dir = Path.cwd() # Get lammps units lammps_units = lmp.style.unit(potential.units) # Define lammps variables lammps_variables = {} system_info = system.dump('atom_data', f='init' + str(cycle) + '.dat', units=potential.units, atom_style=potential.atom_style) lammps_variables['atomman_system_info'] = system_info lammps_variables['atomman_pair_info'] = potential.pair_info(system.symbols) lammps_variables['delta'] = strainrange lammps_variables['steps'] = 2 # Write lammps input script template_file = Path(script_dir, 'cij.template') lammps_script = 'cij.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) # Run lammps output = lmp.run(lammps_command, lammps_script, mpi_command=mpi_command, return_style='model') shutil.move('log.lammps', 'cij-' + str(cycle) + '-log.lammps') # Extract LAMMPS thermo data. Each term ranges i=0-12 where i=0 is undeformed # The remaining values are for -/+ strain pairs in the six unique directions lx = uc.set_in_units(np.array(output.finds('Lx')), lammps_units['length']) ly = uc.set_in_units(np.array(output.finds('Ly')), lammps_units['length']) lz = uc.set_in_units(np.array(output.finds('Lz')), lammps_units['length']) xy = uc.set_in_units(np.array(output.finds('Xy')), lammps_units['length']) xz = uc.set_in_units(np.array(output.finds('Xz')), lammps_units['length']) yz = uc.set_in_units(np.array(output.finds('Yz')), lammps_units['length']) pxx = uc.set_in_units(np.array(output.finds('Pxx')), lammps_units['pressure']) pyy = uc.set_in_units(np.array(output.finds('Pyy')), lammps_units['pressure']) pzz = uc.set_in_units(np.array(output.finds('Pzz')), lammps_units['pressure']) pxy = uc.set_in_units(np.array(output.finds('Pxy')), lammps_units['pressure']) pxz = uc.set_in_units(np.array(output.finds('Pxz')), lammps_units['pressure']) pyz = uc.set_in_units(np.array(output.finds('Pyz')), lammps_units['pressure']) pe = uc.set_in_units( np.array(output.finds('PotEng')) / system.natoms, lammps_units['energy']) # Set the six non-zero strain values strains = np.array([(lx[2] - lx[1]) / lx[0], (ly[4] - ly[3]) / ly[0], (lz[6] - lz[5]) / lz[0], (yz[8] - yz[7]) / lz[0], (xz[10] - xz[9]) / lz[0], (xy[12] - xy[11]) / ly[0]]) # Calculate cij using stress changes associated with each non-zero strain cij = np.empty((6, 6)) for i in range(6): delta_stress = np.array([ pxx[2 * i + 1] - pxx[2 * i + 2], pyy[2 * i + 1] - pyy[2 * i + 2], pzz[2 * i + 1] - pzz[2 * i + 2], pyz[2 * i + 1] - pyz[2 * i + 2], pxz[2 * i + 1] - pxz[2 * i + 2], pxy[2 * i + 1] - pxy[2 * i + 2] ]) cij[i] = delta_stress / strains[i] for i in range(6): for j in range(i): cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2 C = am.ElasticConstants(Cij=cij) S = C.Sij # Extract the current stress state stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]], [pxy[0], pyy[0], pyz[0]], [pxz[0], pyz[0], pzz[0]]]) s_xx = stress[0, 0] + p_xx s_yy = stress[1, 1] + p_yy s_zz = stress[2, 2] + p_zz new_a = system.box.a / (S[0, 0] * s_xx + S[0, 1] * s_yy + S[0, 2] * s_zz + 1) new_b = system.box.b / (S[1, 0] * s_xx + S[1, 1] * s_yy + S[1, 2] * s_zz + 1) new_c = system.box.c / (S[2, 0] * s_xx + S[2, 1] * s_yy + S[2, 2] * s_zz + 1) if new_a <= 0 or new_b <= 0 or new_c <= 0: raise RuntimeError('Divergence of box dimensions to <= 0') system_new = deepcopy(system) system_new.box_set(a=new_a, b=new_b, c=new_c, scale=True) results_dict = {} results_dict['E_coh'] = pe[0] results_dict['system_new'] = system_new results_dict['measured_pxx'] = pxx[0] results_dict['measured_pyy'] = pyy[0] results_dict['measured_pzz'] = pzz[0] results_dict['measured_pxy'] = pxy[0] results_dict['measured_pxz'] = pxz[0] results_dict['measured_pyz'] = pyz[0] return results_dict
def screw_cyl_octahedral(alat, C11, C12, C44, scan_r=15, symbol="W", imp_symbol='H', hard_core=False, center=(0., 0., 0.)): """Generates a set of octahedral positions with `scan_r` radius. Applies the screw dislocation displacement for creating an initial guess for the H positions at dislocation core. Parameters ---------- alat : float Lattice constant of the material. C11 : float C11 elastic constant of the material. C12 : float C12 elastic constant of the material. C44 : float C44 elastic constant of the material. symbol : string Symbol of the element to pass to ase.lattuce.cubic.SimpleCubicFactory default is "W" for tungsten imp_symbol : string Symbol of the elemnt to pass creat Atoms object default is "H" for hydrogen symbol : string Symbol of the elemnt to pass creat Atoms object hard_core : float Type of the dislocatino core if True then -u (sign of displacement is flipped) is applied. Default is False i.e. soft core is created. center : tuple of floats Coordinates of dislocation core (center) (x, y, z). Default is (0., 0., 0.) Returns ------- ase.Atoms object Atoms object with predicted tetrahedral positions around dislocation core. """ # TODO: Make one function for impurities and pass factory to it: # TODO: i.e. octahedral or terahedral axes = np.array([[1, 1, -2], [-1, 1, 0], [1, 1, 1]]) unit_cell = BodyCenteredCubic(directions=axes.tolist(), size=(1, 1, 1), symbol=symbol, pbc=(False, False, True), latticeconstant=alat) BCCOctas = BodyCenteredCubicOctahedralFactory() impurities = BCCOctas(directions=axes.tolist(), size=(1, 1, 1), symbol=imp_symbol, pbc=(False, False, True), latticeconstant=alat) impurities = impurities[impurities.positions.T[2] < alat*1.2] impurities.set_cell(unit_cell.get_cell()) impurities.wrap() disloCenterY = alat * np.sqrt(2.)/6.0 disloCenterX = alat * np.sqrt(6.)/6.0 impurities.positions[:, 0] -= disloCenterX impurities.positions[:, 1] -= disloCenterY L = int(round(2.0*scan_r/(alat*np.sqrt(2.)))) + 1 bulk_octa = impurities * (L, L, 1) # make 0, 0, at the center bulk_octa.positions[:, 0] -= L * alat * np.sqrt(6.)/2. - center[0] bulk_octa.positions[:, 1] -= L * alat * np.sqrt(2.)/2. - center[1] x, y, z = bulk_octa.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask_zero = radius_x_y_zero < scan_r radius_x_y_center = np.sqrt((x - center[0])**2 + (y - center[1])**2) mask_center = radius_x_y_center < scan_r final_mask = mask_center | mask_zero # leave only atoms inside the cylinder bulk_octa = bulk_octa[final_mask] # Create a Stroh ojbect with junk data stroh = am.defect.Stroh(am.ElasticConstants(C11=141, C12=110, C44=98), np.array([0, 0, 1])) c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat * np.array([1., 1., 1.])/2. # Solving a new problem with Stroh.solve stroh.solve(c, burgers, axes=axes) dislo_octa = bulk_octa.copy() impurities_u = stroh.displacement(bulk_octa.positions - center) impurities_u = -impurities_u if hard_core else impurities_u dislo_octa.positions += impurities_u return dislo_octa
def make_edge_cyl(alat, C11, C12, C44, cylinder_r=10, cutoff=5.5, symbol='W'): ''' makes edge dislocation using atomman library cylinder_r - radius of cylinder of unconstrained atoms around the dislocation in angstrom cutoff - potential cutoff for marinica potentials for FS cutoff = 4.4 symbol : string Symbol of the element to pass to ase.lattuce.cubic.SimpleCubicFactory default is "W" for tungsten ''' # Create a Stroh ojbect with junk data stroh = am.defect.Stroh(am.ElasticConstants(C11=141, C12=110, C44=98), np.array([0, 0, 1])) axes = np.array([[1, 1, 1], [1, -1, 0], [1, 1, -2]]) c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat * np.array([1., 1., 1.])/2. # Solving a new problem with Stroh.solve # Does not work with the new version of atomman stroh.solve(c, burgers, axes=axes) unit_cell = BodyCenteredCubic(directions=axes.tolist(), size=(1, 1, 1), symbol='W', pbc=(False, False, True), latticeconstant=alat) bulk = unit_cell.copy() # shift to make the zeros of the cell betweem the atomic planes # and under the midplane on Y axes X_midplane_shift = (1.0/3.0)*alat*np.sqrt(3.0)/2.0 Y_midplane_shift = 0.25*alat*np.sqrt(2.0) bulk_shift = [X_midplane_shift, Y_midplane_shift, 0.0] bulk.positions += bulk_shift tot_r = cylinder_r + cutoff + 0.01 Lx = int(round(tot_r/(alat*np.sqrt(3.0)/2.0))) Ly = int(round(tot_r/(alat*np.sqrt(2.)))) # factor 2 to make shure odd number of images is translated # it is important for the correct centering of the dislocation core bulk = bulk * (2*Lx, 2*Ly, 1) center_shift = [Lx * alat * np.sqrt(3.0)/2., Ly * alat * np.sqrt(2.), 0.0] bulk.positions -= center_shift ED = bulk.copy() disp = stroh.displacement(ED.positions) ED.positions += disp x, y, z = ED.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask = radius_x_y_zero < tot_r ED = ED[mask] bulk = bulk[mask] bulk.write("before.xyz") ED.write("after_disp.xyz") x, y, z = ED.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask_zero = radius_x_y_zero > cylinder_r fix_atoms = FixAtoms(mask=mask_zero) ED.set_constraint(fix_atoms) x, y, z = bulk.positions.T # move lower left segment bulk.positions[(y < 0.0) & (x < X_midplane_shift)] -= \ [alat * np.sqrt(3.0) / 2.0, 0.0, 0.0] # make the doslocation extraplane center bulk.positions += [(1.0/3.0)*alat*np.sqrt(3.0)/2.0, 0.0, 0.0] return ED, bulk
def read_input(f, uuid=None): """Reads the calc_*.in input commands for this calculation.""" #Read input file in as dictionary input_dict = input.file_to_dict(f) #Load a dislocation model if given if 'dislocation_model' in input_dict: assert 'x-axis' not in input_dict, 'x-axis and dislocation_model cannot both be supplied' assert 'y-axis' not in input_dict, 'y-axis and dislocation_model cannot both be supplied' assert 'z-axis' not in input_dict, 'z-axis and dislocation_model cannot both be supplied' with open(input_dict['dislocation_model']) as f: input_dict['dislocation_model'] = DM(f) params = input_dict['dislocation_model'].find( 'atomman-defect-Stroh-parameters') x_axis = params['crystallographic-axes']['x-axis'] y_axis = params['crystallographic-axes']['y-axis'] z_axis = params['crystallographic-axes']['z-axis'] else: #Remove any axes so system is not rotated input_dict['dislocation_model'] = None x_axis = input_dict.pop('x-axis', [1, 0, 0]) y_axis = input_dict.pop('y-axis', [0, 1, 0]) z_axis = input_dict.pop('z-axis', [0, 0, 1]) #Interpret input terms common across calculations input.process_common_terms(input_dict, uuid) #Add axes back to input_dict input_dict['x-axis'] = x_axis input_dict['y-axis'] = y_axis input_dict['z-axis'] = z_axis #Interpret input terms unique to this calculation. input_dict['chi_angle'] = float(input_dict.get('chi_angle', 0.0)) input_dict['rss_steps'] = int(input_dict.get('rss_steps', 0)) input_dict['sigma'] = input.value_unit( input_dict, 'sigma', default_unit=input_dict['pressure_unit'], default_term='0.0 GPa') input_dict['tau_1'] = input.value_unit( input_dict, 'tau_1', default_unit=input_dict['pressure_unit'], default_term='0.0 GPa') input_dict['tau_2'] = input.value_unit( input_dict, 'tau_2', default_unit=input_dict['pressure_unit'], default_term='0.0 GPa') input_dict['press'] = input.value_unit( input_dict, 'press', default_unit=input_dict['pressure_unit'], default_term='0.0 GPa') input_dict['energy_tolerance'] = float( input_dict.get('energy_tolerance', 0.0)) input_dict['force_tolerance'] = input.value_unit( input_dict, 'force_tolerance', default_unit=input_dict['force_unit'], default_term='1e-6 eV/angstrom') input_dict['maximum_iterations'] = int( input_dict.get('maximum_iterations', 100000)) input_dict['maximum_evaluations'] = int( input_dict.get('maximum_evaluations', 100000)) #Extract explicit elastic constants from input_dict Cdict = {} for key in input_dict.iterkeys(): if key[0] == 'C': Cdict[key] = input.value_unit( input_dict, key, default_unit=input_dict['pressure_unit']) if len(Cdict) > 0: assert 'elastic_constants_model' not in input_dict, 'Cij values and elastic_constants_model cannot both be specified.' input_dict['elastic_constants_model'] = None input_dict['C'] = am.ElasticConstants(**Cdict) #If no Cij elastic constants defined check for elastic_constants_model else: #load file may be the elastic_constants_model file input_dict['elastic_constants_model'] = input_dict.get( 'elastic_constants_model', input_dict['load'].split()[1]) with open(input_dict['elastic_constants_model']) as f: C_model = DM(f) try: input_dict['elastic_constants_model'] = DM([ ('elastic-constants', C_model.find('elastic-constants')) ]) input_dict['C'] = am.ElasticConstants( model=input_dict['elastic_constants_model']) except: input_dict['elastic_constants_model'] = None input_dict['C'] = None return input_dict
def calc_cij(lammps_command, system, potential, symbols, p_xx=0.0, p_yy=0.0, p_zz=0.0, delta=1e-5, cycle=0): """Runs cij_script and returns current Cij, stress, Ecoh, and new system guess.""" #Get lammps units lammps_units = lmp.style.unit(potential.units) #Define lammps variables lammps_variables = {} lammps_variables['atomman_system_info'] = lmp.sys_gen( units=potential.units, atom_style=potential.atom_style, ucell=system, size=np.array([[0, 3], [0, 3], [0, 3]])) lammps_variables['atomman_pair_info'] = potential.pair_info(symbols) lammps_variables['delta'] = delta lammps_variables['steps'] = 2 #Write lammps input script template_file = 'cij.template' lammps_script = 'cij.in' with open(template_file) as f: template = f.read() with open(lammps_script, 'w') as f: f.write(iprPy.tools.filltemplate(template, lammps_variables, '<', '>')) #Run lammps output = lmp.run(lammps_command, lammps_script) shutil.move('log.lammps', 'cij-' + str(cycle) + '-log.lammps') #Extract LAMMPS thermo data. Each term ranges i=0-12 where i=0 is undeformed #The remaining values are for -/+ strain pairs in the six unique directions lx = uc.set_in_units(np.array(output.finds('Lx')), lammps_units['length']) ly = uc.set_in_units(np.array(output.finds('Ly')), lammps_units['length']) lz = uc.set_in_units(np.array(output.finds('Lz')), lammps_units['length']) xy = uc.set_in_units(np.array(output.finds('Xy')), lammps_units['length']) xz = uc.set_in_units(np.array(output.finds('Xz')), lammps_units['length']) yz = uc.set_in_units(np.array(output.finds('Yz')), lammps_units['length']) pxx = uc.set_in_units(np.array(output.finds('Pxx')), lammps_units['pressure']) pyy = uc.set_in_units(np.array(output.finds('Pyy')), lammps_units['pressure']) pzz = uc.set_in_units(np.array(output.finds('Pzz')), lammps_units['pressure']) pxy = uc.set_in_units(np.array(output.finds('Pxy')), lammps_units['pressure']) pxz = uc.set_in_units(np.array(output.finds('Pxz')), lammps_units['pressure']) pyz = uc.set_in_units(np.array(output.finds('Pyz')), lammps_units['pressure']) try: pe = uc.set_in_units(np.array(output.finds('v_peatom')), lammps_units['energy']) assert len(pe) > 0 except: pe = uc.set_in_units(np.array(output.finds('peatom')), lammps_units['energy']) #Set the six non-zero strain values strains = np.array([(lx[2] - lx[1]) / lx[0], (ly[4] - ly[3]) / ly[0], (lz[6] - lz[5]) / lz[0], (yz[8] - yz[7]) / lz[0], (xz[10] - xz[9]) / lz[0], (xy[12] - xy[11]) / ly[0]]) #calculate cij using stress changes associated with each non-zero strain cij = np.empty((6, 6)) for i in xrange(6): delta_stress = np.array([ pxx[2 * i + 1] - pxx[2 * i + 2], pyy[2 * i + 1] - pyy[2 * i + 2], pzz[2 * i + 1] - pzz[2 * i + 2], pyz[2 * i + 1] - pyz[2 * i + 2], pxz[2 * i + 1] - pxz[2 * i + 2], pxy[2 * i + 1] - pxy[2 * i + 2] ]) cij[i] = delta_stress / strains[i] for i in xrange(6): for j in xrange(i): cij[i, j] = cij[j, i] = (cij[i, j] + cij[j, i]) / 2 C = am.ElasticConstants(Cij=cij) if np.allclose(C.Cij, 0.0): raise RuntimeError('Divergence of elastic constants to <= 0') try: S = C.Sij except: raise RuntimeError('singular C:\n' + str(C.Cij)) #extract the current stress state stress = -1 * np.array([[pxx[0], pxy[0], pxz[0]], [pxy[0], pyy[0], pyz[0]], [pxz[0], pyz[0], pzz[0]]]) s_xx = stress[0, 0] + p_xx s_yy = stress[1, 1] + p_yy s_zz = stress[2, 2] + p_zz new_a = system.box.a / (S[0, 0] * s_xx + S[0, 1] * s_yy + S[0, 2] * s_zz + 1) new_b = system.box.b / (S[1, 0] * s_xx + S[1, 1] * s_yy + S[1, 2] * s_zz + 1) new_c = system.box.c / (S[2, 0] * s_xx + S[2, 1] * s_yy + S[2, 2] * s_zz + 1) if new_a <= 0 or new_b <= 0 or new_c <= 0: raise RuntimeError('Divergence of box dimensions to <= 0') newbox = am.Box(a=new_a, b=new_b, c=new_c) system_new = deepcopy(system) system_new.box_set(vects=newbox.vects, scale=True) return {'C': C, 'stress': stress, 'ecoh': pe[0], 'system_new': system_new}
def make_screw_plate_old(self, size=[40, 60, 3], rad=[100, 115], move=[0., 0., 0.], filename="lmp_init.txt", opt=None): alat = uc.set_in_units(self.pot['lattice'], 'angstrom') C11 = uc.set_in_units(self.pot['c11'], 'GPa') C12 = uc.set_in_units(self.pot['c12'], 'GPa') C44 = uc.set_in_units(self.pot['c44'], 'GPa') axes = np.array([[1, -2, 1], [1, 0, -1], [1, 1, 1]]) unitx = alat * np.sqrt(6) unity = alat * np.sqrt(2) sizex = size[0] sizey = size[1] sizez = size[2] c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = 0.5 * alat * -np.array([1., 1., 1.]) # initializing a new Stroh object using the data stroh = am.defect.Stroh(c, burgers, axes=axes) # monopole system box = am.Box(a=alat, b=alat, c=alat) atoms = am.Atoms(natoms=2, prop={ 'atype': 2, 'pos': [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5]] }) ucell = am.System(atoms=atoms, box=box, scale=True) system = am.rotate_cubic(ucell, axes) # shftx = 0.5 * alat * np.sqrt(6.) / 3. shftx = 0.0 shfty = -1. / 3. * alat * np.sqrt(2) / 2. # shfty = 2. / 3. * alat * np.sqrt(2) / 2. center = [0.5 * unitx * sizex, 0.5 * unity * sizey] new_pos = system.atoms_prop(key='pos') + np.array([shftx, shfty, 0.0]) system.atoms_prop(key='pos', value=new_pos) system.supersize((0, sizex), (0, sizey), (0, sizez)) # to make a plate # radius2 = rad[0] * rad[0] radiusout2 = rad[1] * rad[1] elements = [] for atom in system.atoms: elements.append('Nb') ase_atoms = am.convert.ase_Atoms.dump(system, elements) pos = ase_atoms.get_positions() delindex = [] for i in range(len(pos)): atom = ase_atoms[i] dx = pos[i, 0] - center[0] dy = pos[i, 1] - center[1] r = dx * dx + dy * dy if r > radiusout2: delindex.append(atom.index) if r < radius2: atom.symbol = 'W' del ase_atoms[delindex] (system, elements) = am.convert.ase_Atoms.load(ase_atoms) # use neb, it's to generate init configuration if opt in ['neb']: system_init = copy.deepcopy(system) shift = np.array([-0.5, -0.5, 0.0]) new_pos = system_init.atoms_prop(key='pos', scale=True) + shift system_init.atoms_prop(key='pos', value=new_pos, scale=True) disp = stroh.displacement(system_init.atoms_prop(key='pos')) system_init.atoms_prop(key='pos', value=system_init.atoms_prop(key='pos') + disp) shift = np.array([0.5, 0.50, 0.0]) new_pos = system_init.atoms_prop(key='pos', scale=True) + shift system_init.atoms_prop(key='pos', value=new_pos, scale=True) # for lammps read structure lmp.atom_data.dump(system_init, "init.txt") # for dd map plot ase.io.write("lmp_perf.cfg", images=ase_atoms, format='cfg') lmp.atom_data.dump(system, "lmp_perf.txt") shift = np.array([-0.5, -0.5, 0.0]) new_pos = system.atoms_prop(key='pos', scale=True) + shift system.atoms_prop(key='pos', value=new_pos, scale=True) new_pos = system.atoms_prop(key='pos') + move system.atoms_prop(key='pos', value=new_pos) disp = stroh.displacement(system.atoms_prop(key='pos')) # pull pull = False if pull is True: core_rows = [disp[:, 2].argsort()[-3:][::-1]] print(disp[core_rows]) exclus = np.arange(len(disp), dtype=int) unitburger = np.mean(disp[core_rows][:, 2]) print(unitburger) exclus = np.delete(exclus, core_rows) disp[core_rows] -= 1. / 3. * unitburger # disp[exclus] -= 1. / 3. * unitburger system.atoms_prop(key='pos', value=system.atoms_prop(key='pos') + disp) new_pos = system.atoms_prop(key='pos') - move system.atoms_prop(key='pos', value=new_pos) shift = np.array([0.500000, 0.500000, 0.000000]) new_pos = system.atoms_prop(key='pos', scale=True) + shift system.atoms_prop(key='pos', value=new_pos, scale=True) new_pos = system.atoms_prop(key='pos', scale=False) # for lammps read structure lmp.atom_data.dump(system, filename) dis_atoms = am.convert.ase_Atoms.dump(system, elements) return (ase_atoms, dis_atoms)
def make_screw_cyl(alat, C11, C12, C44, cylinder_r=10, cutoff=5.5, hard_core=False, center=(0., 0., 0.), l_extend=(0., 0., 0.), symbol='W'): """Makes screw dislocation using atomman library Parameters ---------- alat : float Lattice constant of the material. C11 : float C11 elastic constant of the material. C12 : float C12 elastic constant of the material. C44 : float C44 elastic constant of the material. cylinder_r : float radius of cylinder of unconstrained atoms around the dislocation in angstrom cutoff : float Potential cutoff for marinica potentials for FS cutoff = 4.4 hard_core : bool Description of parameter `hard_core`. center : type The position of the dislocation core and the center of the cylinder with FixAtoms condition l_extend : float extention of the box. used for creation of initial dislocation position with box equivalent to the final position symbol : string Symbol of the element to pass to ase.lattuce.cubic.SimpleCubicFactory default is "W" for tungsten Returns ------- disloc : ase.Atoms object screw dislocation cylinder. bulk : ase.Atoms object bulk disk used to generate dislocation u : np.array displacement per atom. """ # Create a Stroh ojbect with junk data stroh = am.defect.Stroh(am.ElasticConstants(C11=141, C12=110, C44=98), np.array([0, 0, 1])) axes = np.array([[1, 1, -2], [-1, 1, 0], [1, 1, 1]]) c = am.ElasticConstants(C11=C11, C12=C12, C44=C44) burgers = alat * np.array([1., 1., 1.])/2. # Solving a new problem with Stroh.solve stroh.solve(c, burgers, axes=axes) # test the solution that it does not crash # pos_test = uc.set_in_units(np.array([12.4, 13.5, -10.6]), 'angstrom') # disp = stroh.displacement(pos_test) # print("displacement =", uc.get_in_units(disp, 'angstrom'), 'angstrom') unit_cell = BodyCenteredCubic(directions=axes.tolist(), size=(1, 1, 1), symbol=symbol, pbc=(False, False, True), latticeconstant=alat) # make the dislocation core center of the box disloCenterX = alat * np.sqrt(6.)/6.0 disloCenterY = alat * np.sqrt(2.)/6.0 unit_cell.positions[:, 0] -= disloCenterX unit_cell.positions[:, 1] -= disloCenterY # shift to move the fixed atoms boundary condition for the # configuration with shifted dislocation core shift_x = 2.0 * center[0] shift_y = 2.0 * center[1] l_shift_x = 2.0 * l_extend[0] l_shift_y = 2.0 * l_extend[1] # size of the cubic cell as a 112 direction Lx = int(round((cylinder_r + 3.*cutoff + shift_x + l_shift_x) / (alat * np.sqrt(6.)))) # size of the cubic cell as a 110 direction Ly = int(round((cylinder_r + 3.*cutoff + shift_y + l_shift_y) / (alat * np.sqrt(2.)))) # factor 2 to make shure odd number of images is translated # it is important for the correct centering of the dislocation core bulk = unit_cell * (2*Lx, 2*Ly, 1) # make 0, 0, at the center bulk.positions[:, 0] -= Lx * alat * np.sqrt(6.) bulk.positions[:, 1] -= Ly * alat * np.sqrt(2.) # wrap # bulk.set_scaled_positions(bulk.get_scaled_positions()) # apply shear here: # bulk.cell *= D # bulk.positions *= D x, y, z = bulk.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask_zero = radius_x_y_zero < cylinder_r + 2.*cutoff radius_x_y_center = np.sqrt((x - center[0])**2 + (y - center[1])**2) mask_center = radius_x_y_center < cylinder_r + 2.*cutoff radius_x_y_l_shift = np.sqrt((x - l_extend[0])**2 + (y - l_extend[1])**2) mask_l_shift = radius_x_y_l_shift < cylinder_r + 2.*cutoff final_mask = mask_center | mask_zero | mask_l_shift # leave only atoms inside the cylinder bulk = bulk[final_mask] disloc = bulk.copy() # calculate and apply the displacements for atomic positions u = stroh.displacement(bulk.positions - center) u = -u if hard_core else u disloc.positions += u x, y, z = disloc.positions.T radius_x_y_zero = np.sqrt(x**2 + y**2) mask_zero = radius_x_y_zero > cylinder_r radius_x_y_center = np.sqrt((x - center[0])**2 + (y - center[1])**2) mask_center = radius_x_y_center > cylinder_r radius_x_y_l_shift = np.sqrt((x - l_extend[0])**2 + (y - l_extend[1])**2) mask_l_shift = radius_x_y_l_shift > cylinder_r fix_mask = mask_center & mask_zero & mask_l_shift # leave only atoms inside the cylinder fix_atoms = FixAtoms(mask=fix_mask) disloc.set_constraint(fix_atoms) # make an "region" array to map bulk and fixed atoms # all atoms are "MM" by default region = np.full_like(disloc, "MM") region[fix_mask] = np.full_like(disloc[fix_mask], "fixed") disloc.new_array("region", region) return disloc, bulk, u