def displace_site_positions( structure, displacement_vector_distribution_function_dictionary_by_type=None): """ Displaces the sites of structure using the displacement vector dist func provided for each type. If a dist func is not provided for a given site type, a zero function is used. """ Structure.validate(structure) if ( displacement_vector_distribution_function_dictionary_by_type == None ) or len(displacement_vector_distribution_function_dictionary_by_type ) == 0: raise Exception( "A displacement vector function for at least one atom type must be specified." ) for species_type in displacement_vector_distribution_function_dictionary_by_type: if not species_type in structure.sites.keys(): raise Exception("Strucuture does not have a site of type " + str(species_type)) #If a distribution function is not provided for a given type, set that type's function to the zero vector function for species_type in structure.sites.keys(): if not species_type in displacement_vector_distribution_function_dictionary_by_type: displacement_vector_distribution_function_dictionary_by_type[ species_type] = lambda: [0.0, 0.0, 0.0] for site in structure.sites: site.randomly_displace( displacement_vector_distribution_function_dictionary_by_type[ site['type']], structure.lattice)
def __init__(self, reference_structure, hessian, distorted_structure=None): """ If distorted_structure is inputted, then this will be used to seed the strain vector and eigen_component amplitudes. """ Structure.validate(reference_structure) eigen_pairs = hessian.get_sorted_hessian_eigen_pairs_list() # for eigen_pair in eigen_pairs: # print eigen_pair self.voigt_strains_list = [0.0] * 6 self.eigen_components_list = [] for eigen_pair in eigen_pairs: eigen_component = EigenComponent(eigen_pair, amplitude=0.0) self.eigen_components_list.append(eigen_component) if len(self.eigen_components_list ) != 3 * reference_structure.site_count: raise Exception( "Number of eigen components", len(self.eigen_components_list), "and number of degrees of freedom in reference structure", 3 * reference_structure.site_count, "are not equal.") self.reference_structure = reference_structure if distorted_structure: self.set_strains_and_amplitudes_from_distorted_structure( distorted_structure)
def any_sites_are_too_close( structure, minimum_atomic_distances_nested_dictionary_by_type, nearest_neighbors_max=3): """ Returns True if any two sites in this structure are within minimum_atomic_distance (angstroms) of each other. Min distances is different for each type pair, as specified by the input dictionary. nearest_neighbors_max controls how many nearest neighbor cell images to search. minimum_atomic_distances_nested_dictionary_by_type is in angstroms and looks like: { 'Ba': {'Ba': 1.5, 'Ti': 1.2, 'O': 1.4}, 'Ti': {'Ba': 1.2, 'Ti': 1.3, 'O': 1.3}, 'O': {'Ba': 1.4, 'Ti': 1.3, 'O': 0.8} } """ Structure.validate(structure) sites_list = structure.sites.get_sorted_list() for site_1_index in range(len(sites_list)): for site_2_index in range(site_1_index + 1, len(sites_list)): site_1 = sites_list[site_1_index] site_2 = sites_list[site_2_index] minimum_atomic_distance = minimum_atomic_distances_nested_dictionary_by_type[ site_1['type']][site_2['type']] if StructureAnalyzer.site_pair_is_too_close( structure, site_1, site_2, minimum_atomic_distance, nearest_neighbors_max): return True return False
def convert_phonopy_atoms_to_structure(phonopy_atoms_structure): """ Converts phonopy's representation of a structure to an instance of Structure. """ temporary_write_path = Path.get_temporary_path() Path.validate_does_not_exist(temporary_write_path) Path.validate_writeable(temporary_write_path) write_vasp(temporary_write_path, phonopy_atoms_structure) species_list = convert_phonopy_symbols_to_unique_species_list( phonopy_atoms_structure.symbols) structure_poscar_file = File(temporary_write_path) structure_poscar_file.insert( 5, " ".join(species_list)) #phonopy uses bad poscar format structure_poscar_file.write_to_path() final_structure = Structure(temporary_write_path) Path.remove(temporary_write_path) Structure.validate(final_structure) return final_structure
def __init__(self, path, structures_list, vasp_run_inputs_dictionary, wavecar_path=None): """ path should be the main path of the calculation set structures_list should be the set of structures for which forces are calculated. vasp_run_inputs_dictionary should look like: vasp_run_inputs_dictionary = { 'kpoint_scheme': 'Monkhorst', 'kpoint_subdivisions_list': [4, 4, 4], 'encut': 800, 'npar': 1 (optional) } wavecar_path should be the wavecar of a similar structure to the input structures of the structures_list. """ for structure in structures_list: Structure.validate(structure) self.path = path self.structures_list = structures_list self.vasp_run_inputs = copy.deepcopy(vasp_run_inputs_dictionary) self.wavecar_path = wavecar_path Path.make(path) self.initialize_vasp_runs()
def __init__(self, reference_structure, coordinate_mode='Cartesian'): """ """ Structure.validate(reference_structure) if not coordinate_mode in ['Cartesian', 'Direct']: raise Exception("Invalid coordinate mode given:", coordinate_mode) self.reference_structure = reference_structure self.displacement_vector = [0.0] * 3 * reference_structure.site_count self.coordinate_mode = coordinate_mode
def __init__(self, q_points_list, primitive_cell_structure): """ q_points_list should be the set of q_points instances. No particular ordering is necessary, but the order of this list will be preserved in the ordered dicitonary which stores this list. primitive_cell_structure should be the unit_cell used to generate the phonon normal mode data. """ Structure.validate(primitive_cell_structure) self.primitive_cell_structure = primitive_cell_structure self.initialize_q_points(q_points_list) self.validate_data()
def __init__(self, path, reference_structure=None, distorted_structure=None, vasp_run_inputs_dictionary=None): """ reference_structure should be a Structure instance with the same lattice as distorted structure. Usually this reference is chosen to be centrosymmetry (like ideal perovskite) so that absolute polarizations of the distorted structure can be caluclated. distorted_structure should be a Structure instance with the same lattice as the reference structure, but some of the atoms shifted to cause a change in polarization. vasp_run_inputs_dictionary should look like: vasp_run_inputs_dictionary = { 'kpoint_scheme': 'Monkhorst', 'kpoint_subdivisions_list': [4, 4, 4], 'encut': 800, } """ self.path = Path.expand(path) if (reference_structure == None) or (distorted_structure == None) or (vasp_run_inputs_dictionary == None): self.load() else: Structure.validate(reference_structure) Structure.validate(distorted_structure) if not reference_structure.lattice.equals( distorted_structure.lattice): raise Exception( "Warning: It's very difficult to interpret polarization results when the lattices of the reference and distorted structures are not equal. This is likely an error.", reference_structure.lattice, distorted_structure.lattice) self.reference_structure = reference_structure self.distorted_structure = distorted_structure self.vasp_run_inputs = copy.deepcopy(vasp_run_inputs_dictionary) self.vasp_run_list = [] self.save() Path.make(path) self.initialize_vasp_runs()
def get_interpolated_structure_list(self, interpolation_increment=0.2): structure_list = [] for i in range(0, 1 + int(1 / interpolation_increment)): def interpolation_function(da, db, dc): return interpolation_increment * (i + 0.01) interpolated_sites = self.get_interpolated_site_collection( self.site_collection_initial, interpolation_function) structure_list.append( Structure(lattice=self.lattice, sites=interpolated_sites)) return structure_list # def get_average_displacement_vector_type_dictionary(self): # """ # Returns dict that looks like {'Ba':average_direct_coord_vec_Ba_atoms_from_eachother_in_mapping, 'Ti':...} # """ # average_displacement_vector_dictionary = OrderedDict() # for type_string in self.site_collection_initial.keys(): # average_x = sum([site_mapping.displacement_vector[0] for site_mapping in self.mapping_dictionary[type_string]])/len(self.mapping_dictionary[type_string]) # average_y = sum([site_mapping.displacement_vector[1] for site_mapping in self.mapping_dictionary[type_string]])/len(self.mapping_dictionary[type_string]) # average_z = sum([site_mapping.displacement_vector[2] for site_mapping in self.mapping_dictionary[type_string]])/len(self.mapping_dictionary[type_string]) # average_displacement_vector_dictionary[type_string] = [average_x, average_y, average_z] # return average_displacement_vector_dictionary
def __init__(self, path, initial_structures_list, reference_structure, vasp_relaxation_inputs_dictionary, reference_lattice_constant, misfit_strains_list, supercell_dimensions_list, calculate_polarizations=False): """ path should be the main path of the calculation set initial_structures_list should be the set of structures that are relaxed at each misfit strain reference structure can have any lattice but its atom positions must be in direct coords as the positions to compare polarizations to (choose a centrosymmetric structure if possible) vasp_relaxation_inputs_dictionary should look something like: { 'external_relaxation_count': 4, 'kpoint_schemes_list': ['Gamma'], 'kpoint_subdivisions_lists': [[1, 1, 1], [1, 1, 2], [2, 2, 4]], 'submission_script_modification_keys_list': ['100', 'standard', 'standard_gamma'], #optional - will default to whatever queue adapter gives 'submission_node_count_list': [1, 2], 'ediff': [0.001, 0.00001, 0.0000001], 'encut': [200, 400, 600, 800], 'isif' : [5, 2, 3] #any other incar parameters with value as a list } reference_lattice_constant should be the lattice constant a0 which, when multiplied by the list of misfit strains, generates the new in-plane lattice constant at those strains. For each lattice constant and each structure, a relaxation is performed. Then, the lowest energy structures at each misfit strain can be determined, and a convex hull results. """ for structure in initial_structures_list: Structure.validate(structure) basic_validators.validate_real_number(reference_lattice_constant) for misfit_strain in misfit_strains_list: basic_validators.validate_real_number(misfit_strain) self.path = path self.initial_structures_list = initial_structures_list self.reference_structure = reference_structure self.vasp_relaxation_inputs_dictionary = vasp_relaxation_inputs_dictionary self.reference_lattice_constant = reference_lattice_constant self.misfit_strains_list = misfit_strains_list self.supercell_dimensions_list = supercell_dimensions_list self.calculate_polarizations = calculate_polarizations Path.make(path) self.initialize_vasp_relaxations()
def get_supercell(structure, supercell_dimensions_list): """ Returns a new structure that is a supercell structure (structure is not modified) Argument supercell_dimensions_list could look like [1,3,4]. """ Structure.validate(structure) if not len(supercell_dimensions_list) == 3: raise Exception( "Argument supercell_dimensions_list must be of length 3") for dimension in supercell_dimensions_list: if not int(dimension) == dimension: raise Exception( "Only integer values accepted for supercell dimension factors." ) structure.convert_sites_to_direct_coordinates() new_lattice = structure.lattice.get_super_lattice( supercell_dimensions_list) new_sites = SiteCollection() for original_site in structure.sites: for a in range(supercell_dimensions_list[0]): for b in range(supercell_dimensions_list[1]): for c in range(supercell_dimensions_list[2]): a_frac = float(a) / float(supercell_dimensions_list[0]) b_frac = float(b) / float(supercell_dimensions_list[1]) c_frac = float(c) / float(supercell_dimensions_list[2]) new_site = copy.deepcopy(original_site) old_position = original_site['position'] new_site['position'] = [ old_position[0] / supercell_dimensions_list[0] + a_frac, old_position[1] / supercell_dimensions_list[1] + b_frac, old_position[2] / supercell_dimensions_list[2] + c_frac ] new_sites.append(new_site) return Structure(lattice=new_lattice, sites=new_sites)
def convert_structure_to_phonopy_atoms(structure): """ Takes structure, a Structure instance, and returns a PhonopyAtoms class (phonopy's representation of structures) """ temporary_write_path = Path.get_temporary_path() Structure.validate(structure) Path.validate_does_not_exist(temporary_write_path) Path.validate_writeable(temporary_write_path) structure.to_poscar_file_path(temporary_write_path) phonopy_structure = read_vasp(temporary_write_path) Path.remove(temporary_write_path) return phonopy_structure
def __init__(self, eigenvector, frequency, q_point_fractional_coordinates, band_index, primitive_cell_structure, atomic_masses_list): """ eigenvector should be a list that looks like [atom_1_x_component_of_generalized_displacement_complex_number, atom_1_y..., ..., atom_2_x, ...] -> the sqrt(mass) term should be left in for each component. This vector should be a list of length Nat, where Nat is the number of atoms in the primitive cell used to generate the phonon band structure. The q_point input argument should be a tuple of three real numbers in reduced coordinates with no factor of 2Pi, e.g. (0.5, 0.5, 0.0). frequency should be a complex number - if imaginary only then mode is unstable. band_index should start at zero and go to Nat*3-1 atomic_masses_list should look like [atomic_1_mass, atomic_2_mass, ..., atomic_Natoms_in_cell_mass] """ complex_vector_magnitude = np.linalg.norm(eigenvector) basic_validators.validate_approximately_equal(complex_vector_magnitude, 1.0, tolerance=0.000001) basic_validators.validate_complex_number(frequency) if (frequency.real > 0.0) and (frequency.imag > 0.0): raise Exception("Frequency cannot have mixed real and imaginary components. Frequency is", frequency) if not len(q_point_fractional_coordinates) == 3: raise Exception("Qpoint argument must have three compononents. Argument is", q_point_fractional_coordinates) if not isinstance(q_point_fractional_coordinates, tuple): raise Exception("q_point_fractional_coordinates must be a tuple", q_point_fractional_coordinates) if primitive_cell_structure.site_count != len(atomic_masses_list): raise Exception("Length of atomic masses list and number of sites are not equal.") Structure.validate(primitive_cell_structure) self.eigenvector = eigenvector self.frequency = frequency self.q_point_fractional_coordinates = q_point_fractional_coordinates self.band_index = band_index #0 through Nat self.primitive_cell_structure = primitive_cell_structure self.atomic_masses_list = atomic_masses_list self.eigen_displacements_list = self.get_eigen_displacements_vector()
def test_init(self): site = Site() site['position'] = [0.1, 0.2, 0.4, "dir"] site['type'] = 'Ba' site_2 = Site() site_2['position'] = [0.6, 0.22, 0.44, "cart"] site_2['type'] = 'Ti' structure = Structure(lattice=Lattice(a=[3.4, 0.1, 0.2], b=[2.3, 5.0, 0.0], c=[0.0, 0.0, 1.0]), sites=SiteCollection([site, site_2])) self.assertEqual(structure.lattice.to_array(), [[3.4, 0.1, 0.2], [2.3, 5.0, 0.0], [0.0, 0.0, 1.0]]) self.assertEqual(structure.sites.get_sorted_list()[0]['type'], 'Ba') self.assertEqual(structure.sites.get_coordinates_list(), [[0.1, 0.2, 0.4], [0.6, 0.22, 0.44]]) file_path = Path.clean(self.data_path, "poscar_small") structure = Structure(file_path) self.assertEqual(structure.lattice.to_array(), [[5.0, 0.2, 0.1], [0.4, 6.0, 0.7], [-0.2, 0.0, 7.7]]) self.assertEqual( structure.sites.get_sorted_list()[1]['coordinate_mode'], 'Direct') self.assertEqual( structure.get_coordinates_list(), [[0.11, 0.22, 0.33], [0.33, 0.22, 0.11], [0.22, 0.33, 0.11]]) write_file_path = Path.clean(self.data_path, "poscar_small_rewritten") structure.to_poscar_file_path(write_file_path) structure = Structure(write_file_path) self.assertEqual(structure.lattice.to_array(), [[5.0, 0.2, 0.1], [0.4, 6.0, 0.7], [-0.2, 0.0, 7.7]]) self.assertEqual( structure.sites.get_sorted_list()[1]['coordinate_mode'], 'Direct') self.assertEqual( structure.get_coordinates_list(), [[0.11, 0.22, 0.33], [0.33, 0.22, 0.11], [0.22, 0.33, 0.11]])
def get_structure_list(path): """loads in poscars at path and returns list of structures""" path = Path.clean(path) files = Path.get_list_of_files_at_path(path) structs = {} for file in files: structs[file] = Structure(Path.join(path, file)) return structs
def get_sample(calculation_path): structure = Structure(file_path=Path.join(calculation_path, 'POSCAR')) structure.shift_sites_so_first_atom_is_at_origin() outcar = Outcar(file_path=Path.join(calculation_path, 'OUTCAR')) lattice_information = structure.get_magnitudes_and_angles() ideal_perovskite_structure = Perovskite( supercell_dimensions=[1, 1, 1], lattice=copy.deepcopy(structure.lattice), species_list=structure.get_species_list()) displacement_vector = DisplacementVector.get_instance_from_displaced_structure_relative_to_reference_structure( reference_structure=ideal_perovskite_structure, displaced_structure=structure, coordinate_mode='Cartesian') energy = outcar.energy forces = outcar.final_forces_list stresses = outcar.final_stresses_list volume = structure.lattice.get_volume() #in A^3 for z in range(len(stresses)): stresses[z] /= volume row_of_data = lattice_information + displacement_vector.to_list() + [ energy ] + forces + stresses output_string = ' '.join([str(x) for x in row_of_data]) return row_of_data, output_string
def __init__(self, q_point_fractional_coordinates, normal_modes_list, primitive_cell_structure): """ The q_point input argument should be a tuple of three real numbers in reduced coordinates with no factor of 2Pi, e.g. (0.5, 0.5, 0.0). normal_modes_list should be a list of normal modes with identical q_point values. This list should be arranged by order of band_index. """ if not len(q_point_fractional_coordinates) == 3: raise Exception("Qpoint argument must have three compononents. Argument is", q_point_fractional_coordinates) Structure.validate(primitive_cell_structure) if primitive_cell_structure.site_count*3 != len(normal_modes_list): raise Exception("Number of normal mode instances should be equal to the number of atoms in the primitive cell times three. Normal_modes_list is", normal_modes_list, "primitive structure is", primitive_cell_structure) self.q_point_fractional_coordinates = q_point_fractional_coordinates self.normal_modes_list = normal_modes_list self.primitive_cell_structure = primitive_cell_structure self.validate_normal_modes_list()
def __init__(self, primitive_cell_structure, phonon_band_structure, supercell_dimensions_list, distorted_structure=None): """ primitive_cell_structure should be the primitive cell Structure class instance that was used to generate the phonon band structure. phonon_band_structure should be a PhononBandStructure instance with, at minimim, normal modes for the necessary wave vectors, loosely (38.10, pg 295 Born and Huang) -->For example, if a 2x1x1 supercell is expected, the following q points must be provided: (+1/2, 0, 0), (0, 0, 0) supercell_dimensions if distorted_structure is given, the dispalcements of this structure relative to the supercell reference structure are used to determine the normal coordinates list. """ Structure.validate(primitive_cell_structure) self.primitive_cell_structure = primitive_cell_structure self.phonon_band_structure = phonon_band_structure self.supercell_dimensions_list = supercell_dimensions_list #the total number of degrees of freedom to describe the ionic displacments of the system self.dof_count = 3 * self.primitive_cell_structure.site_count * self.supercell_dimensions_list[ 0] * self.supercell_dimensions_list[ 1] * self.supercell_dimensions_list[2] self.reference_supercell_structure = StructureManipulator.get_supercell( primitive_cell_structure, supercell_dimensions_list) self.validate_necessary_wave_vectors_exist() #FIX::self.number_of_normal_coordinates = 2*self.primitive_cell_structure.site_count*3*supercell_dimensions_list[0]*supercell_dimensions_list[1]*supercell_dimensions_list[2] self.initialize_normal_coordinates_list() if distorted_structure != None: self.set_normal_coordinates_list_from_distorted_structure( distorted_structure)
def get_next_structure(self): """If first relax, return self.input_initial_structure, else, get the contcar from the current run""" if self.run_count == 0: return self.input_initial_structure current_contcar_path = self.get_current_vasp_run().get_extended_path('CONTCAR') if not Path.exists(current_contcar_path): raise Exception("Method get_next_structure called, but Contcar for current run doesn't exist") elif not self.get_current_vasp_run().complete: raise Exception("Method get_next_structure called, but current run is not yet complete") return Structure(current_contcar_path)
def __init__(self, primitive_cell_structure, phonon_band_structure, supercell_dimensions_list, normal_coordinate_instances_list=None): """ primitive_cell_structure should be the primitive cell Structure class instance that was used to generate the phonon band structure. phonon_band_structure should be a PhononBandStructure instance with, at minimim, normal modes for the necessary wave vectors, loosely (38.10, pg 295 Born and Huang) -->For example, if a 2x1x1 supercell is expected, the following q points must be provided: (+1/2, 0, 0), (0, 0, 0) supercell_dimensions if normal_coordinate_instances_list, this list is used to set the normal coordinates, else, the normal coordinates are initialized to zero. """ Structure.validate(primitive_cell_structure) self.primitive_cell_structure = primitive_cell_structure self.phonon_band_structure = phonon_band_structure self.supercell_dimensions_list = supercell_dimensions_list self.reference_supercell_structure = StructureManipulator.get_supercell(primitive_cell_structure, supercell_dimensions_list) self.validate_necessary_wave_vectors_exist() #FIX::self.number_of_normal_coordinates = 2*self.primitive_cell_structure.site_count*3*supercell_dimensions_list[0]*supercell_dimensions_list[1]*supercell_dimensions_list[2] if normal_coordinate_instances_list != None: # if len(normal_coordinate_instances_list) != self.number_of_normal_coordinates: # raise Exception("The number of given normal coordinates is not equal to the number needed to describe the structural distortions. Normal coordinates list given is", normal_coordinate_instances_list) # else: self.normal_coordinates_list = copy.deepcopy(normal_coordinate_instances_list) else: self.initialize_normal_coordinates_list()
def get_parent_structures_list(self): structure_list = [] i = 0 while True: parent_path = self.get_extended_path('.parent_poscar_' + str(i) + '.vasp') if Path.exists(parent_path): structure_list.append(Structure(parent_path)) i += 1 else: break return structure_list
def structure_is_duplicate(self, structure, misfit_path): """ Returns true if this has been the initial structure for any previous relaxation """ for i in range(10000): relax_path = Path.join(misfit_path, 'structure_' + str(i)) if not Path.exists(relax_path): return False else: comparison_structure = Structure(file_path=Path.join(relax_path, 'original_initial_structure')) if structure.is_equivalent_to_structure(comparison_structure): print "FOUND DUPLICATE in epitaxial_relaxer.py" return True
def test_conversion(self): file_path = Path.clean(self.data_path, "big_posc") structure = Structure(file_path) structure_2 = Structure(file_path) structure_2.convert_sites_to_cartesian_coordinates() structure_2.convert_sites_to_direct_coordinates() print structure for i in range(len(structure.sites)): for j in range(3): self.assertTrue( structure.sites[i]['position'][j] - structure_2.sites[i]['position'][j] < 0.00000000001)
def displace_site_positions_with_minimum_distance_constraints( structure, displacement_vector_distribution_function_dictionary_by_type=None, minimum_atomic_distances_nested_dictionary_by_type=None): """ Displaces the atoms of structure using the specified probability distribution functions for each atom type (modifies in place) while ensuring the site positions obey provided minimum atommic distance constraints. Algorithm: 1. Randomly displace sites according to the given distribution funcitons for each type. 2. For those atoms that are too close under p.b.c. (as defined in the minimum distance matrix), randomly choose on of the 'collided' pair and re-displace according to its distribution function. 3. Repeat until no two atoms are too close. displacement_vector_distribution_function_dictionary_by_type should look like: { 'Ba': dist_func_1, #dist funcs are methods that return cartesian vectors in angstroms ([x, y, z]) using distributions of your choosing 'Ti': dist_func_2, ... } minimum_atomic_distances_nested_dictionary_by_type is in angstroms and looks like: { 'Ba': {'Ba': 1.5, 'Ti': 1.2, 'O': 1.4}, 'Ti': {'Ba': 1.2, 'Ti': 1.3, 'O': 1.3}, 'O': {'Ba': 1.4, 'Ti': 1.3, 'O': 0.8} } Calling any of the dist_funcs must return a displacement vector that uses cartesian coordinates and angstroms as its units. If no function is given for a type, the zero vector function is used. This method should approximately preserve the overall distribution rho(x1, y1, z1, x2, y2, z2, ...) resulting from multiplication of the indiviidual independent atomic distributions but with the regions of atoms too close (distance < min_atomic_dist) set to rho = 0. This just renormalizes the other parts of the distribution space so integral of rho sums to unity. """ Structure.validate(structure) original_structure = copy.deepcopy(structure) original_sites_list = copy.deepcopy(structure.sites.get_sorted_list()) new_sites_list = structure.sites.get_sorted_list() sites_to_check_indices_list = range( len(new_sites_list) ) #start with checking every index (looks like [0, 1, 2, ..., num_atoms-1]) StructureManipulator.displace_site_positions( structure, displacement_vector_distribution_function_dictionary_by_type) for try_count in range(200): indices_of_site_pairs_that_are_too_close_list = StructureAnalyzer.get_indices_of_site_pairs_that_are_too_close_to_sites_list( structure, sites_to_check_indices_list, minimum_atomic_distances_nested_dictionary_by_type) sites_to_check_indices_list = [] indices_to_displace_list = [] if indices_of_site_pairs_that_are_too_close_list != []: print indices_of_site_pairs_that_are_too_close_list for (site_1_index, site_2_index ) in indices_of_site_pairs_that_are_too_close_list: probabilities_list = [0.5, 0.5] random_selector = RandomSelector(probabilities_list) event_index = random_selector.get_event_index() if event_index == 0: index_to_displace = site_1_index else: index_to_displace = site_2_index print "moving randomly selected index " + str( index_to_displace) + " of pair " + str( (site_1_index, site_2_index)) if index_to_displace in indices_to_displace_list: print "already in index list of sites that have been moved" continue new_sites_list[index_to_displace][ 'coordinate_mode'] = original_sites_list[ index_to_displace]['coordinate_mode'] new_sites_list[index_to_displace][ 'position'] = copy.deepcopy( original_sites_list[index_to_displace]['position']) new_sites_list[index_to_displace].randomly_displace( displacement_vector_distribution_function_dictionary_by_type[ new_sites_list[index_to_displace]['type']], structure.lattice) sites_to_check_indices_list.append(index_to_displace) indices_to_displace_list.append(index_to_displace) else: return raise Exception( "Could not displace this structure while satisfying the given constraints" )
def get_final_structure(self): if Path.exists(self.get_extended_path('./CONTCAR')) and self.complete: return Structure(self.get_extended_path('./CONTCAR')) else: return None
def current_structure(self): if Path.exists(self.get_extended_path('./CONTCAR')): return Structure(self.get_extended_path('./CONTCAR')) else: return self.initial_structure
def poscar(self): if Path.exists(self.get_extended_path('./POSCAR')): return Structure(self.get_extended_path('./POSCAR')) else: return None
# print # print "magnitudes" # for eig in eigs_list: # print "magnitude: ", np.linalg.norm(eig) # for other_eig in eigs_list: # print "dot:", np.dot(eig, other_eig) if __name__ == '__main__': reference_structure_path = '../data/reference_structure.vasp' reference_structure = Structure(reference_structure_path) # displacement_vector = cplus # displacement_vector *= 0.7 # distorted_structure = DisplacementVector.displace_structure(reference_structure=reference_structure, displacement_vector=displacement_vector, displacement_coordinate_mode='Cartesian') # distorted_structure.to_poscar_file_path('../data/cplus.vasp') relaxed_structure = Structure('../data/relaxed_structure.vasp') relaxed_structure.lattice = copy.deepcopy(reference_structure.lattice) total_displacement_vector_instance = DisplacementVector.get_instance_from_displaced_structure_relative_to_reference_structure( reference_structure=reference_structure,
def __init__(self, vasp_calculation_input_dictionary): vasp_calculation_input_dictionary = copy.deepcopy( vasp_calculation_input_dictionary) vasp_calculation_input_dictionary = { k.lower(): v for k, v in vasp_calculation_input_dictionary.items() } #enforce all keys lowercase path = Path.clean(vasp_calculation_input_dictionary.pop('path')) information_structure = None #used for number of nodes and potcar if isinstance(vasp_calculation_input_dictionary['structure'], Structure): initial_structure = vasp_calculation_input_dictionary.pop( 'structure') information_structure = initial_structure contcar_path = None else: initial_structure = None contcar_path = vasp_calculation_input_dictionary.pop('structure') information_structure = Structure(contcar_path) wavecar_path = vasp_calculation_input_dictionary.pop( 'wavecar_path' ) if 'wavecar_path' in vasp_calculation_input_dictionary else None chargecar_path = vasp_calculation_input_dictionary.pop( 'chargecar_path' ) if 'chargecar_path' in vasp_calculation_input_dictionary else None incar_template = vasp_calculation_input_dictionary.pop( 'incar_template' ) if 'incar_template' in vasp_calculation_input_dictionary else None kpoints_scheme = vasp_calculation_input_dictionary.pop( 'kpoints_scheme' ) if 'kpoints_scheme' in vasp_calculation_input_dictionary else None kpoints_list = [ int(x) for x in vasp_calculation_input_dictionary.pop( 'kpoints_list').split(' ') ] if ('kpoints_list' in vasp_calculation_input_dictionary and vasp_calculation_input_dictionary['kpoints_list'] != None ) else None potcar_type = vasp_calculation_input_dictionary.pop( 'potcar_type' ) if 'potcar_type' in vasp_calculation_input_dictionary else 'lda_paw' vasp_code_type = vasp_calculation_input_dictionary.pop( 'vasp_code_type' ) if 'vasp_code_type' in vasp_calculation_input_dictionary else 'standard' node_count = vasp_calculation_input_dictionary.pop( 'node_count' ) if 'node_count' in vasp_calculation_input_dictionary else None use_mp_hubbard_u = vasp_calculation_input_dictionary.pop( 'use_mp_hubbard_u' ) if 'use_mp_hubbard_u' in vasp_calculation_input_dictionary else None for file_path in [wavecar_path, chargecar_path]: if file_path != None and not Path.exists(file_path): print "Warning: Path " + str( file_path) + " specified does not exist. Not using." #raise Exception("Path " + str(file_path) + " specified does not exist.") if kpoints_scheme != None and kpoints_list != None: kpoints = Kpoints(scheme_string=kpoints_scheme, subdivisions_list=kpoints_list) if 'kspacing' in vasp_calculation_input_dictionary: raise Exception( "kpoints are being specified by more than one method.") elif 'kspacing' not in vasp_calculation_input_dictionary: raise Exception( "If kpoints aren't explicitly defined through a scheme and a list, the kspacing tag must be present in the incar." ) else: kpoints = None potcar = Potcar(elements_list=information_structure.get_species_list(), calculation_type=potcar_type) submission_script_file = QueueAdapter.get_submission_file() if os.environ['QUEUE_ADAPTER_HOST'] != 'Savio': if node_count == None: submission_script_file = QueueAdapter.modify_number_of_cores_from_num_atoms( submission_script_file, information_structure.site_count) else: submission_script_file = QueueAdapter.set_number_of_nodes( submission_script_file, node_count) submission_script_file = QueueAdapter.modify_submission_script( submission_script_file, modification_key=vasp_code_type) incar_modifiers = vasp_calculation_input_dictionary #whatever is left should only be incar modifiers - we popped all other keys for key, value in incar_modifiers.items( ): #make sure there are no None values - these should not be put in INCAR if value in [None, 'None']: del incar_modifiers[key] if 'encut' in incar_modifiers and ( 0.1 < incar_modifiers['encut']) and ( incar_modifiers['encut'] < 10.0): #Should use this as a multiplier times enmax enmax = potcar.get_enmax() incar_modifiers['encut'] = int(incar_modifiers['encut'] * enmax) if incar_template == 'static': incar = IncarMaker.get_static_incar( custom_parameters_dictionary=incar_modifiers) elif incar_template == 'external_relaxation': incar = IncarMaker.get_external_relaxation_incar( custom_parameters_dictionary=incar_modifiers) elif incar_template != None: raise Exception("Incar template " + str(incar_template) + " not valid") else: incar = Incar() incar.modify_from_dictionary(incar_modifiers) if 'lreal' not in incar: if information_structure.site_count > 20: incar['lreal'] = 'Auto' else: incar['lreal'] = False if 'npar' not in incar: if os.environ['QUEUE_ADAPTER_HOST'] == 'Savio': incar['npar'] = 4 else: incar['npar'] = QueueAdapter.get_optimal_npar( submission_script_file) elif incar['npar'] in ['Remove', 'remove']: del incar['npar'] ###################TEMPORARILY HARDCODED FOR PEROVSKITES########################################## if use_mp_hubbard_u: u_species = initial_structure.get_species_list()[1] mp_hubbard_u_values = { 'Co': 3.32, 'Cr': 3.7, 'Fe': 5.3, 'Mn': 3.9, 'Mo': 4.38, 'Ni': 6.2, 'V': 3.25, 'W': 6.2 } if u_species in mp_hubbard_u_values.keys(): u_value = mp_hubbard_u_values[u_species] incar['LDAU'] = True incar['LDAUJ'] = '0 0 0' incar['LDAUL'] = '0 2 0' incar['LDAUPRINT'] = 1 incar['LDAUTYPE'] = 2 incar['LDAUU'] = '0 ' + str(u_value) + ' 0' incar['LMAXMIX'] = 4 incar['LORBIT'] = 11 super(VaspCalculationGenerator, self).__init__(path=path, initial_structure=initial_structure, incar=incar, kpoints=kpoints, potcar=potcar, submission_script_file=submission_script_file, contcar_path=contcar_path, wavecar_path=wavecar_path, chargecar_path=chargecar_path)
def update(self): """ """ epitaxial_path = Path.join(self.path, 'epitaxial_runs') inputs_dictionaries = copy.deepcopy(self.inputs_dictionaries) for structure_tag, input_dictionary in inputs_dictionaries.items(): print "\nUpdating Epitaxial Workflow for " + structure_tag + "\n" misfit_strains_list = input_dictionary.pop('misfit_strains_list') reference_lattice_constant = input_dictionary.pop('reference_lattice_constant') number_of_trials = input_dictionary.pop('number_of_trials') supercell_dimensions_list = input_dictionary.pop('supercell_dimensions_list') max_displacement_magnitude = input_dictionary.pop('max_displacement_magnitude') max_strain_magnitude = input_dictionary.pop('max_strain_magnitude') self.data_dictionaries[structure_tag] = {} for misfit_strain in misfit_strains_list: self.data_dictionaries[structure_tag][misfit_strain] = [] print "Misfit strain: " + str(misfit_strain) misfit_path = Path.join(epitaxial_path, str(misfit_strain).replace('-', 'n')) Path.make(misfit_path) relaxations_set_path = Path.join(misfit_path, structure_tag) Path.make(relaxations_set_path) lattice_constant = reference_lattice_constant*(1.0+misfit_strain) for i in range(number_of_trials): self.data_dictionaries[structure_tag][misfit_strain].append({}) relaxation_path = Path.join(relaxations_set_path, 'trial_' + str(i)) initial_structure_path = Path.join(self.path, 'initial_structures', structure_tag) initial_structure = Structure(initial_structure_path) saved_initial_structure = copy.deepcopy(initial_structure) if abs(initial_structure.lattice[0][1]) > 0.0 or abs(initial_structure.lattice[0][2]) > 0.0 or abs(initial_structure.lattice[1][0]) > 0.0 or abs(initial_structure.lattice[1][2]) > 0.0: raise Exception("Current lattice is incompatible with (100) epitaxy: ", str(initial_structure.lattice)) initial_structure.lattice[0][0] = lattice_constant*supercell_dimensions_list[0] initial_structure.lattice[1][1] = lattice_constant*supercell_dimensions_list[1] initial_structure.randomly_displace_sites(max_displacement_magnitude=max_displacement_magnitude) random_out_of_plane_strain_tensor = [[1.0, 0.0, 0.5*random.uniform(-1.0*max_strain_magnitude, max_strain_magnitude)], [0.0, 1.0, 0.5*random.uniform(-1.0*max_strain_magnitude, max_strain_magnitude)], [0.0, 0.0, 1.0 + random.uniform(-1.0*max_strain_magnitude, max_strain_magnitude)]] initial_structure.lattice.strain(strain_tensor=random_out_of_plane_strain_tensor) relaxation = VaspRelaxationCalculation(path=relaxation_path, initial_structure=initial_structure, input_dictionary=self.relaxation_inputs_dictionaries[structure_tag]) relaxation.update() # if self.calculate_polarizations and relaxation.complete: # self.update_polarization_run(relaxation, structure_tag) saved_initial_structure.to_poscar_file_path(Path.join(relaxation_path, 'original_initial_structure')) if relaxation.complete: spg_symprecs = [0.1, 0.01, 0.001] final_structure = relaxation.get_final_structure() self.data_dictionaries[structure_tag][misfit_strain][-1]['energy_per_atom'] = relaxation.get_final_energy(per_atom=True) self.data_dictionaries[structure_tag][misfit_strain][-1]['energy'] = relaxation.get_final_energy(per_atom=False) self.data_dictionaries[structure_tag][misfit_strain][-1]['final_structure'] = final_structure self.data_dictionaries[structure_tag][misfit_strain][-1]['path'] = relaxation.path + '/static' self.data_dictionaries[structure_tag][misfit_strain][-1]['lattice_parameters'] = final_structure.get_magnitudes_and_angles() for symprec in spg_symprecs: self.data_dictionaries[structure_tag][misfit_strain][-1]['spg_' + str(symprec)] = final_structure.get_spacegroup_string(symprec) print