def make_supercell( cell_structure: pmg.Structure, scaling_matrix: Optional[ScalingMatrix] = None, scaling_factors: Optional[Tuple[int, int, int]] = None, ) -> pmg.Structure: """Transforms a pymatgen ``Structure`` object into a supercell according to the scaling parameters. :param cell_structure: A pymatgen ``Structure`` object. :param scaling_matrix: A matrix of transforming the lattice vectors. Has to be all integers. e.g., [[2,1,0],[0,3,0],[0,0,1]] generates a new structure with lattice vectors a" = 2a + b, b" = 3b, c" = c where a, b, and c are the lattice vectors of the original structure. :param scaling_factors: A tuple of three numbers used to scale each lattice vector. Same as: ``scaling_matrix=[[scale_a, 0, 0], [0, scale_b, 0], [0, 0, scale_c]]`` :return: A pymatgen ``Structure`` object. """ if scaling_matrix is not None: cell_transformation: SupercellTransformation = SupercellTransformation( scaling_matrix=scaling_matrix) return cell_transformation.apply_transformation(cell_structure) elif scaling_factors is not None: cell_transformation = SupercellTransformation.from_scaling_factors( scale_a=scaling_factors[0], scale_b=scaling_factors[1], scale_c=scaling_factors[2], ) return cell_transformation.apply_transformation(cell_structure) else: return cell_structure
def test_append_transformation(self): t = SubstitutionTransformation({"Fe": "Mn"}) self.trans.append_transformation(t) self.assertEqual( "NaMnPO4", self.trans.final_structure.composition.reduced_formula) self.assertEqual(len(self.trans.structures), 3) coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = [ [3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603], ] struct = Structure(lattice, ["Si4+", "Si4+"], coords) ts = TransformedStructure(struct, []) ts.append_transformation( SupercellTransformation.from_scaling_factors(2, 1, 1)) alt = ts.append_transformation( PartialRemoveSpecieTransformation( "Si4+", 0.5, algo=PartialRemoveSpecieTransformation.ALGO_COMPLETE), 5, ) self.assertEqual(len(alt), 2)
def test_from_scaling_factors(self): scale_factors = [random.randint(1, 5) for i in range(3)] t = SupercellTransformation.from_scaling_factors(*scale_factors) s = t.apply_transformation(self.struct) self.assertEqual( s.num_sites, 4 * functools.reduce(lambda a, b: a * b, scale_factors))
def abi_sanitize(self, symprec=1e-3, primitive=True): """ Returns a new structure in which: * Oxidation states are removed. * Structure is refined. * Reduced to primitive settings. * Lattice vectors are exchanged if the triple product is negative Args: symprec: Symmetry precision used to refine the structure. if `symprec` is None, so structure refinement is peformed. primitive (bool): Whether to convert to a primitive cell. """ from pymatgen.transformations.standard_transformations import OxidationStateRemovalTransformation, \ PrimitiveCellTransformation, SupercellTransformation # Remove oxidation states. remove_ox = OxidationStateRemovalTransformation() structure = remove_ox.apply_transformation(self) # Refine structure if symprec is not None: sym_finder = SpacegroupAnalyzer(structure=structure, symprec=symprec) structure = sym_finder.get_refined_structure() # Convert to primitive structure. if primitive: get_prim = PrimitiveCellTransformation() structure = get_prim.apply_transformation(structure) # Exchange last two lattice vectors if triple product is negative. m = structure.lattice.matrix x_prod = np.dot(np.cross(m[0], m[1]), m[2]) if x_prod < 0: trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) structure = trans.apply_transformation(structure) m = structure.lattice.matrix x_prod = np.dot(np.cross(m[0], m[1]), m[2]) if x_prod < 0: raise RuntimeError("x_prod is still negative!") return structure
def abi_sanitize(self, symprec=1e-3, angle_tolerance=5, primitive=True, primitive_standard=False): """ Returns a new structure in which: * Structure is refined. * Reduced to primitive settings. * Lattice vectors are exchanged if the triple product is negative Args: symprec: Symmetry precision used to refine the structure. if `symprec` is None, so structure refinement is peformed. primitive (bool): Whether to convert to a primitive cell. """ from pymatgen.transformations.standard_transformations import PrimitiveCellTransformation, SupercellTransformation structure = self.__class__.from_sites(self) # Refine structure if symprec is not None and angle_tolerance is not None: sym_finder = SpacegroupAnalyzer(structure=structure, symprec=symprec, angle_tolerance=angle_tolerance) structure = sym_finder.get_refined_structure() # Convert to primitive structure. if primitive: if primitive_standard: sym_finder_prim = SpacegroupAnalyzer(structure=structure, symprec=symprec, angle_tolerance=angle_tolerance) structure = sym_finder_prim.get_primitive_standard_structure() else: get_prim = PrimitiveCellTransformation() structure = get_prim.apply_transformation(structure) # Exchange last two lattice vectors if triple product is negative. m = structure.lattice.matrix x_prod = np.dot(np.cross(m[0], m[1]), m[2]) if x_prod < 0: trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) structure = trans.apply_transformation(structure) m = structure.lattice.matrix x_prod = np.dot(np.cross(m[0], m[1]), m[2]) if x_prod < 0: raise RuntimeError("x_prod is still negative!") return structure
def refine_structure(structure, symprec=1e-3): remove_ox = OxidationStateRemovalTransformation() structure = remove_ox.apply_transformation(structure) sym_finder = SpacegroupAnalyzer(structure=structure, symprec=symprec) structure = sym_finder.get_refined_structure() get_prim = PrimitiveCellTransformation() structure = get_prim.apply_transformation(structure) m = structure.lattice.matrix x_prod = np.dot(np.cross(m[0], m[1]), m[2]) if x_prod < 0: print(x_prod) trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) structure = trans.apply_transformation(structure) m = structure.lattice.matrix x_prod = np.dot(np.cross(m[0], m[1]), m[2]) print(x_prod) if x_prod < 0: raise RuntimeError return structure
def create_input(self): """ create input for all the benchmark calculations :return 0 on succes """ self.reset_bar() print('testing executable %s' % self.subject) print( "creating input for %s system sizes and %s calculations per size:" % (len(self.sizes), int(self.total / len(self.sizes)))) sys.stdout.write(self.bar_len * "-" + "\n") sys.stdout.flush() for s in self.sizes: sys.stdout.write("|") sys.stdout.flush() struc = copy.deepcopy(self.structure) trans = SupercellTransformation.from_scaling_factors(scale_a=s, scale_b=s, scale_c=s) struc = trans.apply_transformation(struc) for n in self.np_list: if self.kpar: kpar = itr(n) else: kpar = 1 self.inpset.incar_settings.update({'KPAR': kpar}) for x in self.parameter_lists: for o in self.parameter_lists[x]: sys.stdout.write("*") sys.stdout.flush() v = functions(x)(o, int(n / kpar)) self.inpset.incar_settings.update({x: v}) path = '%s_super%s_par%s%s%s' % (self.name, s, n, x, o) path = os.path.join(os.getcwd(), path) self.inpset.incar_settings.update( {'system': '%s_super%s' % (self.name, s)}) self.inpset.write_input(structure=struc, output_dir=path) q = self.manager.qadapter q.set_mpi_procs(n) job_string = q.get_script_str( job_name=self.name + 's' + str(s) + 'np' + str(n), executable=self.subject, launch_dir=path, partition=None, qerr_path='qerr.out', qout_path='qout.out') self.script_list.append(os.path.join(path, 'job.sh')) f = open(self.script_list[-1], 'w') f.write(job_string) f.close() sys.stdout.write("|\n") sys.stdout.flush() return 0
def planar_structure_normalization(structure): ''' This function does the following: 1. check whether the structure is planar using coordniates standard deviation 2. move the planar layer to the center of c-direction Args: structure: pymatgen structure Return: a boolean whether the structure is planar tranformed pymatgen structure ''' tol = 1E-3 # tolerance to check whether the structure is planar is_planar = True coords = structure.frac_coords ts = TransformedStructure(structure, []) if np.std(coords[:,2]) < tol : center_translate = 0.5 - coords[:,2].mean() elif np.std(coords[:,0]) < tol : ts.append_transformation(SupercellTransformation([[0,0,1],[0,1,0],[1,0,0]])) ts.append_transformation(RotationTransformation([0,1,0], 90)) center_translate = 0.5 - coords[:,0].mean() elif np.std(coords[:,1]) < tol : ts.append_transformation(SupercellTransformation([[1,0,0],[0,0,1],[0,1,0]])) ts.append_transformation(RotationTransformation([1,0,0], 90)) center_translate = 0.5 - coords[:,1].mean() else : is_planar = False transformed_structure = None if is_planar: ts.append_transformation(TranslateSitesTransformation( list(range(len(structure))), [0,0,center_translate])) # Use pymatgen 2019.7.2, ts.structures[-1] may change in a newer version transformed_structure = ts.structures[-1] return is_planar, transformed_structure
def test_append_transformation(self): t = SubstitutionTransformation({"Fe":"Mn"}) self.trans.append_transformation(t) self.assertEqual("NaMnPO4", self.trans.final_structure.composition.reduced_formula) self.assertEqual(len(self.trans.structures), 3) coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = [[ 3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]] struct = Structure(lattice, ["Si4+", "Si4+"], coords) ts = TransformedStructure(struct, []) ts.append_transformation(SupercellTransformation.from_scaling_factors(2, 1, 1)) alt = ts.append_transformation(PartialRemoveSpecieTransformation('Si4+', 0.5, algo=PartialRemoveSpecieTransformation.ALGO_COMPLETE), 5) self.assertEqual(len(alt), 2)
def create_input(self): """ create input for all the benchmark calculations :return 0 on succes """ self.reset_bar() print('testing executable %s' % self.subject) print("creating input for %s system sizes and %s calculations per size:" % (len(self.sizes), int(self.total / len(self.sizes)))) sys.stdout.write(self.bar_len*"-"+"\n") sys.stdout.flush() for s in self.sizes: sys.stdout.write("|") sys.stdout.flush() struc = copy.deepcopy(self.structure) trans = SupercellTransformation.from_scaling_factors(scale_a=s, scale_b=s, scale_c=s) struc = trans.apply_transformation(struc) for n in self.np_list: if self.kpar: kpar = itr(n) else: kpar = 1 self.inpset.incar_settings.update({'KPAR': kpar}) for x in self.parameter_lists: for o in self.parameter_lists[x]: sys.stdout.write("*") sys.stdout.flush() v = functions(x)(o, int(n/kpar)) self.inpset.incar_settings.update({x: v}) path = '%s_super%s_par%s%s%s' % (self.name, s, n, x, o) path = os.path.join(os.getcwd(), path) self.inpset.incar_settings.update({'system': '%s_super%s' % (self.name, s)}) self.inpset.write_input(structure=struc, output_dir=path) q = self.manager.qadapter q.set_mpi_procs(n) job_string = q.get_script_str(job_name=self.name+'s'+str(s)+'np'+str(n), executable=self.subject, launch_dir=path, partition=None, qerr_path='qerr.out', qout_path='qout.out') self.script_list.append(os.path.join(path, 'job.sh')) f = open(self.script_list[-1], 'w') f.write(job_string) f.close() sys.stdout.write("|\n") sys.stdout.flush() return 0
def test_apply_transformation(self): t = SupercellTransformation([[2, 1, 0], [0, 2, 0], [1, 0, 2]]) s = t.apply_transformation(self.struct) self.assertEqual(s.composition.formula, "Li16 O16")
def get_slab_fw(slab, transmuter=False, db_file=None, vasp_input_set=None, parents=None, vasp_cmd="vasp", name="", add_slab_metadata=True, user_incar_settings=None): """ Function to generate a a slab firework. Returns a TransmuterFW if bulk_structure is specified, constructing the necessary transformations from the slab and slab generator parameters, or an OptimizeFW if only a slab is specified. Args: slab (Slab or Structure): structure or slab corresponding to the slab to be calculated transmuter (bool): whether or not to use a TransmuterFW based on slab params, if this option is selected, input slab must be a Slab object (as opposed to Structure) vasp_input_set (VaspInputSet): vasp_input_set corresponding to the slab calculation parents (Fireworks or list of ints): parent FWs db_file (string): path to database file vasp_cmd (string): vasp command name (string): name of firework add_slab_metadata (bool): whether to add slab metadata to task doc Returns: Firework corresponding to slab calculation """ vasp_input_set = vasp_input_set or MPSurfaceSet( slab, user_incar_settings=user_incar_settings) # If a bulk_structure is specified, generate the set of transformations, # else just create an optimize FW with the slab if transmuter: if not isinstance(slab, Slab): raise ValueError( "transmuter mode requires slab to be a Slab object") # Get transformation from oriented bulk and slab oriented_bulk = slab.oriented_unit_cell slab_trans_params = get_slab_trans_params(slab) trans_struct = SlabTransformation(**slab_trans_params) slab_from_bulk = trans_struct.apply_transformation(oriented_bulk) # Ensures supercell construction supercell_trans = SupercellTransformation.from_scaling_factors( round(slab.lattice.a / slab_from_bulk.lattice.a), round(slab.lattice.b / slab_from_bulk.lattice.b)) # Get site properties, set velocities to zero if not set to avoid # custodian issue site_props = slab.site_properties if 'velocities' not in site_props: site_props['velocities'] = [0. for s in slab] # Get adsorbates for InsertSitesTransformation if "adsorbate" in slab.site_properties.get("surface_properties", ""): ads_sites = [ site for site in slab if site.properties["surface_properties"] == "adsorbate" ] else: ads_sites = [] transformations = [ "SlabTransformation", "SupercellTransformation", "InsertSitesTransformation", "AddSitePropertyTransformation" ] trans_params = [ slab_trans_params, { "scaling_matrix": supercell_trans.scaling_matrix }, { "species": [site.species_string for site in ads_sites], "coords": [site.frac_coords for site in ads_sites] }, { "site_properties": site_props } ] fw = TransmuterFW(name=name, structure=oriented_bulk, transformations=transformations, transformation_params=trans_params, copy_vasp_outputs=True, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, vasp_input_set=vasp_input_set) else: fw = OptimizeFW(name=name, structure=slab, vasp_input_set=vasp_input_set, vasp_cmd=vasp_cmd, db_file=db_file, parents=parents, job_type="normal") # Add slab metadata if add_slab_metadata: parent_structure_metadata = get_meta_from_structure( slab.oriented_unit_cell) fw.tasks[-1]["additional_fields"].update({ "slab": slab, "parent_structure": slab.oriented_unit_cell, "parent_structure_metadata": parent_structure_metadata }) return fw
def correct(self): backup(VASP_BACKUP_FILES | {self.output_filename}) actions = [] vi = VaspInput.from_directory(".") if self.errors.intersection(["tet", "dentet"]): actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}) if "inv_rot_mat" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brmix" in self.errors: # If there is not a valid OUTCAR already, increment # error count to 1 to skip first fix if self.error_count['brmix'] == 0: try: assert (Outcar(zpath(os.path.join( os.getcwd(), "OUTCAR"))).is_stopped is False) except: self.error_count['brmix'] += 1 if self.error_count['brmix'] == 0: # Valid OUTCAR - simply rerun the job and increment # error count for next time actions.append({"dict": "INCAR", "action": {"_set": {"ISTART": 1}}}) self.error_count['brmix'] += 1 elif self.error_count['brmix'] == 1: # Use Kerker mixing w/default values for other parameters actions.append({"dict": "INCAR", "action": {"_set": {"IMIX": 1}}}) self.error_count['brmix'] += 1 elif self.error_count['brmix'] == 2 and vi["KPOINTS"].style \ == Kpoints.supported_modes.Gamma: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Monkhorst"}}}) actions.append({"dict": "INCAR", "action": {"_unset": {"IMIX": 1}}}) self.error_count['brmix'] += 1 elif self.error_count['brmix'] in [2, 3] and vi["KPOINTS"].style \ == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) actions.append({"dict": "INCAR", "action": {"_unset": {"IMIX": 1}}}) self.error_count['brmix'] += 1 if vi["KPOINTS"].num_kpts < 1: all_kpts_even = all([ bool(n % 2 == 0) for n in vi["KPOINTS"].kpts[0] ]) print("all_kpts_even = {}".format(all_kpts_even)) if all_kpts_even: new_kpts = ( tuple(n + 1 for n in vi["KPOINTS"].kpts[0]),) print("new_kpts = {}".format(new_kpts)) actions.append({"dict": "KPOINTS", "action": {"_set": { "kpoints": new_kpts }}}) else: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": { "_set": {"generation_style": "Gamma"}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": { "_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": { "_file_delete": {'mode': "actual"}}}) if "zpotrf" in self.errors: # Usually caused by short bond distances. If on the first step, # volume needs to be increased. Otherwise, it was due to a step # being too big and POTIM should be decreased. If a static run # try turning off symmetry. try: oszicar = Oszicar("OSZICAR") nsteps = len(oszicar.ionic_steps) except: nsteps = 0 if nsteps >= 1: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append( {"dict": "INCAR", "action": {"_set": {"ISYM": 0, "POTIM": potim}}}) elif vi["INCAR"].get("NSW", 0) == 0 \ or vi["INCAR"].get("ISIF", 0) in range(3): actions.append( {"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) else: s = vi["POSCAR"].structure s.apply_strain(0.2) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": s.as_dict()}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if self.errors.intersection(["subspacematrix", "rspher", "real_optlay", "nicht_konv"]): s = vi["POSCAR"].structure if len(s) < self.natoms_large_cell: actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) else: # for large supercell, try an in-between option LREAL = True # prior to LREAL = False if self.error_count['real_optlay'] == 0: # use real space projectors generated by pot actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": True}}}) self.error_count['real_optlay'] += 1 elif self.error_count['real_optlay'] == 1: actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) self.error_count['real_optlay'] += 1 if self.errors.intersection(["tetirr", "incorrect_shift"]): if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": { "_set": {"generation_style": "Gamma"}}}) if "rot_matrix" in self.errors: if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": { "_set": {"generation_style": "Gamma"}}}) else: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if "amin" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}}) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": new_s.as_dict()}}, "transformation": trans.as_dict()}) if "pricel" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}}) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if "zbrent" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}}) actions.append({"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}) if "pssyevx" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) if "eddrmm" in self.errors: # RMM algorithm is not stable for this calculation if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) else: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if "edddav" in self.errors: if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "All"}}}) if "grad_not_orth" in self.errors: if vi["INCAR"].get("ISMEAR", 1) < 0: actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": "0"}}}) if "zheev" in self.errors: if vi["INCAR"].get("ALGO", "Fast").lower() != "exact": actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Exact"}}}) if "elf_kpar" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"KPAR": 1}}}) if "rhosyg" in self.errors: if vi["INCAR"].get("SYMPREC", 1e-4) == 1e-4: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-4}}}) if "posmap" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-6}}}) VaspModder(vi=vi).apply_actions(actions) return {"errors": list(self.errors), "actions": actions}
def correct(self): backup(VASP_BACKUP_FILES | {self.output_filename}) actions = [] vi = VaspInput.from_directory(".") if self.errors.intersection(["tet", "dentet"]): actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}) if "inv_rot_mat" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brmix" in self.errors: # If there is not a valid OUTCAR already, increment # error count to 1 to skip first fix if self.error_count['brmix'] == 0: try: assert(Outcar(zpath(os.path.join( os.getcwd(), "OUTCAR"))).is_stopped is False) except: self.error_count['brmix'] += 1 if self.error_count['brmix'] == 0: # Valid OUTCAR - simply rerun the job and increment # error count for next time actions.append({"dict": "INCAR", "action": {"_set": {"ISTART": 1}}}) self.error_count['brmix'] += 1 elif self.error_count['brmix'] == 1: # Use Kerker mixing w/default values for other parameters actions.append({"dict": "INCAR", "action": {"_set": {"IMIX": 1}}}) self.error_count['brmix'] += 1 elif self.error_count['brmix'] == 2 and vi["KPOINTS"].style \ == Kpoints.supported_modes.Gamma: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Monkhorst"}}}) actions.append({"dict": "INCAR", "action": {"_unset": {"IMIX": 1}}}) self.error_count['brmix'] += 1 elif self.error_count['brmix'] in [2, 3] and vi["KPOINTS"].style \ == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) actions.append({"dict": "INCAR", "action": {"_unset": {"IMIX": 1}}}) self.error_count['brmix'] += 1 if vi["KPOINTS"].num_kpts < 1: all_kpts_even = all([ bool(n % 2 == 0) for n in vi["KPOINTS"].kpts[0] ]) print("all_kpts_even = {}".format(all_kpts_even)) if all_kpts_even: new_kpts = (tuple(n+1 for n in vi["KPOINTS"].kpts[0]),) print("new_kpts = {}".format(new_kpts)) actions.append({"dict": "KPOINTS", "action": {"_set": { "kpoints": new_kpts }}}) else: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if "zpotrf" in self.errors: # Usually caused by short bond distances. If on the first step, # volume needs to be increased. Otherwise, it was due to a step # being too big and POTIM should be decreased. try: oszicar = Oszicar("OSZICAR") nsteps = len(oszicar.ionic_steps) except: nsteps = 0 if nsteps >= 1: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append( {"dict": "INCAR", "action": {"_set": {"ISYM": 0, "POTIM": potim}}}) else: s = vi["POSCAR"].structure s.apply_strain(0.2) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": s.as_dict()}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if self.errors.intersection(["subspacematrix", "rspher", "real_optlay"]): actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) if self.errors.intersection(["tetirr", "incorrect_shift"]): if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) if "rot_matrix" in self.errors: if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) else: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if "amin" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}}) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": new_s.as_dict()}}, "transformation": trans.as_dict()}) if "pricel" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}}) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if "zbrent" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}}) actions.append({"file": "CONTCAR", "action": {"_file_copy": {"dest": "POSCAR"}}}) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}) if "pssyevx" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) if "eddrmm" in self.errors: # RMM algorithm is not stable for this calculation if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) else: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if "edddav" in self.errors: if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "All"}}}) if "grad_not_orth" in self.errors: if vi["INCAR"].get("ISMEAR", 1) < 0: actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": "0"}}}) VaspModder(vi=vi).apply_actions(actions) return {"errors": list(self.errors), "actions": actions}
def correct(self): backup(VASP_BACKUP_FILES | {self.output_filename}) actions = [] vi = VaspInput.from_directory(".") if self.errors.intersection(["tet", "dentet"]): actions.append({ "dict": "INCAR", "action": { "_set": { "ISMEAR": 0 } } }) if "inv_rot_mat" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "SYMPREC": 1e-8 } } }) if "brmix" in self.errors: if self.error_count['brmix'] == 0 and vi[ "KPOINTS"].style == Kpoints.supported_modes.Gamma: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Monkhorst" } } }) self.error_count['brmix'] += 1 elif self.error_count['brmix'] <= 1 and vi[ "KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Gamma" } } }) self.error_count['brmix'] += 1 if vi["KPOINTS"].num_kpts < 1: all_kpts_even = all( [bool(n % 2 == 0) for n in vi["KPOINTS"].kpts[0]]) print("all_kpts_even = {}".format(all_kpts_even)) if all_kpts_even: new_kpts = (tuple(n + 1 for n in vi["KPOINTS"].kpts[0]), ) print("new_kpts = {}".format(new_kpts)) actions.append({ "dict": "KPOINTS", "action": { "_set": { "kpoints": new_kpts } } }) else: actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0 } } }) if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Gamma" } } }) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "file": "WAVECAR", "action": { "_file_delete": { 'mode': "actual" } } }) if "zpotrf" in self.errors: # Usually caused by short bond distances. If on the first step, # volume needs to be increased. Otherwise, it was due to a step # being too big and POTIM should be decreased. try: oszicar = Oszicar("OSZICAR") nsteps = len(oszicar.ionic_steps) except: nsteps = 0 if nsteps >= 1: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0, "POTIM": potim } } }) else: s = vi["POSCAR"].structure s.apply_strain(0.2) actions.append({ "dict": "POSCAR", "action": { "_set": { "structure": s.as_dict() } } }) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "file": "WAVECAR", "action": { "_file_delete": { 'mode': "actual" } } }) if self.errors.intersection( ["subspacematrix", "rspher", "real_optlay"]): actions.append({ "dict": "INCAR", "action": { "_set": { "LREAL": False } } }) if self.errors.intersection(["tetirr", "incorrect_shift"]): if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Gamma" } } }) if "rot_matrix" in self.errors: if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Gamma" } } }) else: actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0 } } }) if "amin" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "AMIN": "0.01" } } }) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({ "dict": "POSCAR", "action": { "_set": { "structure": new_s.as_dict() } }, "transformation": trans.as_dict() }) if "pricel" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "SYMPREC": 1e-8, "ISYM": 0 } } }) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({ "dict": "INCAR", "action": { "_set": { "POTIM": potim } } }) if "zbrent" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "IBRION": 1 } } }) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({ "dict": "INCAR", "action": { "_set": { "NBANDS": int(1.1 * nbands) } } }) if "pssyevx" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "Normal" } } }) if "eddrmm" in self.errors: #RMM algorithm is not stable for this calculation if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]: actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "Normal" } } }) else: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({ "dict": "INCAR", "action": { "_set": { "POTIM": potim } } }) actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "file": "WAVECAR", "action": { "_file_delete": { 'mode': "actual" } } }) if "edddav" in self.errors: actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "All" } } }) VaspModder(vi=vi).apply_actions(actions) return {"errors": list(self.errors), "actions": actions}
def create_SNL(dirbase, molecules, atoms, spc_present, num_each_spc, struct, s): layers = len(molecules) with MPRester("sm5RbuEp83T9Wo7P") as m: first_mol = struct[0] mono_or_homo = 0 #if system is a monolayer or homogeneous use its proper .cif file, else use generic WTe2 for heterostructures if (layers == 1) or all(x == first_mol for x in struct): mono_or_homo = 1 if (first_mol == molec[0]): structure = m.get_structure_by_material_id("mp-2815") #MoS2 ref = m.get_materials_id_references("mp-2815") r1 = np.array([0, 2, 4]) elif (first_mol == molec[1]): structure = m.get_structure_by_material_id("mp-1634") #MoSe2 ref = m.get_materials_id_references("mp-1634") r1 = np.array([0, 2, 4]) elif (first_mol == molec[2]): structure = m.get_structure_by_material_id("mp-602") #MoTe2 ref = m.get_materials_id_references("mp-602") r1 = np.array([1, 2, 5]) elif (first_mol == molec[3]): structure = m.get_structure_by_material_id("mp-224") #WS2 ref = m.get_materials_id_references("mp-224") r1 = np.array([0, 3, 5]) elif (first_mol == molec[4]): structure = m.get_structure_by_material_id("mp-1821") #WSe2 ref = m.get_materials_id_references("mp-1821") r1 = np.array([0, 2, 4]) elif (first_mol == molec[5]): structure = m.get_structure_by_material_id("mp-1019322") #WTe2 ref = m.get_materials_id_references("mp-1019322") r1 = np.array([0, 3, 5]) else: structure = m.get_structure_by_material_id("mp-1019322") #WTe2 ref = m.get_materials_id_references("mp-1019322") r1 = np.array([0, 3, 5]) # initialize history history = [] #half the height of original unit cell...to be used for vacuum length calculation later halfz = (structure.lattice.c) / 2 #make supercell if necessary levels = layers if (levels % 2 == 1): levels = levels + 1 tsuper = SupercellTransformation([[1, 0, 0], [0, 1, 0], [0, 0, (levels) / 2]]) history.append(history_node(tsuper)) supercell = tsuper.apply_transformation(structure) #make species replacements for heterostructures with more than one layer levels = layers if (levels % 2 == 1): levels = levels + 1 #if heterostructure has more than one layer: if (mono_or_homo == 0): for i in range(0, len(molecules)): if (molecules[i] == 5): continue else: TMspc = elems[atoms[2 * i]] TMloc = (levels * 2) + (i % 2) * (levels / 2) + int( np.floor((i) / 2)) DCspc = elems[atoms[2 * i + 1]] DCloc1 = (levels - (levels / 2)) - i % 2 * (levels / 2) + int( np.floor((i) / 2)) DCloc2 = levels + i % 2 * (levels / 2) + int( np.floor((i) / 2)) t1 = ReplaceSiteSpeciesTransformation({TMloc: TMspc}) t2 = ReplaceSiteSpeciesTransformation({DCloc1: DCspc}) t3 = ReplaceSiteSpeciesTransformation({DCloc2: DCspc}) history.append(history_node(t1)) history.append(history_node(t2)) history.append(history_node(t3)) supercell = t1.apply_transformation(supercell) supercell = t2.apply_transformation(supercell) supercell = t3.apply_transformation(supercell) #remove top layer of atom if necessary mult_factor = (layers + 1) / 2 - 1 r = r1 + (r1 + 1) * mult_factor tremove = RemoveSitesTransformation(r) if (layers % 2 == 1): supercell = tremove.apply_transformation(supercell) history.append(history_node(tremove)) #sort structure supercell = supercell.get_sorted_structure() #extend z-axis cell vector to add vaccuum to supercell vacuum = 10.0 old_lattice = supercell.lattice if (layers % 2 == 1): new_c = old_lattice.c - halfz + vacuum else: new_c = old_lattice.c + vacuum new_lattice = Lattice.from_parameters(old_lattice.a, old_lattice.b, new_c, old_lattice.alpha, old_lattice.beta, old_lattice.gamma) final_structure = Structure( new_lattice, supercell.species, supercell.frac_coords * np.array([1., 1., (old_lattice.c / new_lattice.c)]), coords_are_cartesian=False) hnode = { 'name': 'add vaccuum', 'url': '', 'description': 'increase z-direction cell vector by 10 angstroms' } history.append(hnode) #creat final SNL authors = [{"name": "Lindsay Bassman", "email": "*****@*****.**"}] projects = ["TMDC-Heterostructures"] remarks = [ "MAGICS calculation of band structures of 2D TMDC stacked heterostructures" ] final_snl = StructureNL(final_structure, authors, projects=projects, remarks=remarks, references=ref, history=history) #optionally write POSCAR file poscar = Poscar(final_structure, s) poscar.write_file(dirbase + "POSCAR", direct=False)
def correct(self): backup(VASP_BACKUP_FILES | {self.output_filename}) actions = [] vi = VaspInput.from_directory(".") if self.errors.intersection(["tet", "dentet"]): actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}) if "inv_rot_mat" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brmix" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. actions.append({"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {"mode": "actual"}}}) if "zpotrf" in self.errors: # Usually caused by short bond distances. If on the first step, # volume needs to be increased. Otherwise, it was due to a step # being too big and POTIM should be decreased. try: oszicar = Oszicar("OSZICAR") nsteps = len(oszicar.ionic_steps) except: nsteps = 0 if nsteps >= 1: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0, "POTIM": potim}}}) else: s = vi["POSCAR"].structure s.apply_strain(0.2) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": s.as_dict()}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. actions.append({"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {"mode": "actual"}}}) if self.errors.intersection(["subspacematrix", "rspher", "real_optlay"]): actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) if self.errors.intersection(["tetirr", "incorrect_shift"]): if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) if "rot_matrix" in self.errors: if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) else: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if "amin" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}}) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append( { "dict": "POSCAR", "action": {"_set": {"structure": new_s.as_dict()}}, "transformation": trans.as_dict(), } ) if "pricel" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}}) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if "zbrent" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}}) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}) if "pssyevx" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) if "eddrmm" in self.errors: # RMM algorithm is not stable for this calculation if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) else: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) actions.append({"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {"mode": "actual"}}}) if "edddav" in self.errors: actions.append({"file": "CHGCAR", "action": {"_file_delete": {"mode": "actual"}}}) actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "All"}}}) VaspModder(vi=vi).apply_actions(actions) return {"errors": list(self.errors), "actions": actions}
def get_slab_fw(slab, transmuter=False, db_file=None, vasp_input_set=None, parents=None, vasp_cmd="vasp", name="", add_slab_metadata=True): """ Function to generate a a slab firework. Returns a TransmuterFW if bulk_structure is specified, constructing the necessary transformations from the slab and slab generator parameters, or an OptimizeFW if only a slab is specified. Args: slab (Slab or Structure): structure or slab corresponding to the slab to be calculated transmuter (bool): whether or not to use a TransmuterFW based on slab params, if this option is selected, input slab must be a Slab object (as opposed to Structure) vasp_input_set (VaspInputSet): vasp_input_set corresponding to the slab calculation parents (Fireworks or list of ints): parent FWs db_file (string): path to database file vasp_cmd (string): vasp command name (string): name of firework add_slab_metadata (bool): whether to add slab metadata to task doc Returns: Firework corresponding to slab calculation """ vasp_input_set = vasp_input_set or MPSurfaceSet(slab) # If a bulk_structure is specified, generate the set of transformations, # else just create an optimize FW with the slab if transmuter: if not isinstance(slab, Slab): raise ValueError("transmuter mode requires slab to be a Slab object") # Get transformation from oriented bulk and slab oriented_bulk = slab.oriented_unit_cell slab_trans_params = get_slab_trans_params(slab) trans_struct = SlabTransformation(**slab_trans_params) slab_from_bulk = trans_struct.apply_transformation(oriented_bulk) # Ensures supercell construction supercell_trans = SupercellTransformation.from_scaling_factors( round(slab.lattice.a / slab_from_bulk.lattice.a), round(slab.lattice.b / slab_from_bulk.lattice.b)) # Get site properties, set velocities to zero if not set to avoid # custodian issue site_props = slab.site_properties if 'velocities' not in site_props: site_props['velocities'] = [0. for s in slab] # Get adsorbates for InsertSitesTransformation if "adsorbate" in slab.site_properties.get("surface_properties", ""): ads_sites = [site for site in slab if site.properties["surface_properties"] == "adsorbate"] else: ads_sites = [] transformations = [ "SlabTransformation", "SupercellTransformation", "InsertSitesTransformation", "AddSitePropertyTransformation"] trans_params = [slab_trans_params, {"scaling_matrix": supercell_trans.scaling_matrix}, {"species": [site.species_string for site in ads_sites], "coords": [site.frac_coords for site in ads_sites]}, {"site_properties": site_props}] fw = TransmuterFW(name=name, structure=oriented_bulk, transformations=transformations, transformation_params=trans_params, copy_vasp_outputs=True, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, vasp_input_set=vasp_input_set) else: fw = OptimizeFW(name=name, structure=slab, vasp_input_set=vasp_input_set, vasp_cmd=vasp_cmd, db_file=db_file, parents=parents, job_type="normal") # Add slab metadata if add_slab_metadata: parent_structure_metadata = get_meta_from_structure( slab.oriented_unit_cell) fw.tasks[-1]["additional_fields"].update( {"slab": slab, "parent_structure": slab.oriented_unit_cell, "parent_structure_metadata": parent_structure_metadata}) return fw
def get_slab_fw(slab, bulk_structure=None, slab_gen_params={}, db_file=None, vasp_input_set=None, parents=None, vasp_cmd="vasp", name=""): """ Function to generate a a slab firework. Returns a TransmuterFW if bulk_structure is specified, constructing the necessary transformations from the slab and slab generator parameters, or an OptimizeFW if only a slab is specified. Args: slab (Slab or Structure): structure or slab corresponding to the slab to be calculated bulk_structure (Structure): bulk structure corresponding to slab, if provided, slab firework is constructed as a TransmuterFW using the necessary transformations to get the slab from the bulk slab_gen_params (dict): dictionary of slab generation parameters used to generate the slab, necessary to get the slab that corresponds to the bulk structure vasp_input_set (VaspInputSet): vasp_input_set corresponding to the slab calculation parents (Fireworks or list of ints): parent FWs db_file (string): path to database file vasp_cmd (string): vasp command Returns: Firework """ vasp_input_set = vasp_input_set or MVLSlabSet(slab) # If a bulk_structure is specified, generate the set of transformations, else # just create an optimize FW with the slab if bulk_structure: if not isinstance(slab, Slab): raise ValueError( "structure input to get_slab_fw requires slab to be a slab object!" ) slab_trans_params = { "miller_index": slab.miller_index, "shift": slab.shift } slab_trans_params.update(slab_gen_params) # Get supercell parameters trans_struct = SlabTransformation(**slab_trans_params) slab_from_bulk = trans_struct.apply_transformation(bulk_structure) supercell_trans = SupercellTransformation.from_scaling_factors( round(slab.lattice.a / slab_from_bulk.lattice.a), round(slab.lattice.b / slab_from_bulk.lattice.b)) # Get adsorbates for InsertSitesTransformation if "adsorbate" in slab.site_properties.get("surface_properties", [None]): ads_sites = [ site for site in slab if site.properties["surface_properties"] == "adsorbate" ] else: ads_sites = [] transformations = [ "SlabTransformation", "SupercellTransformation", "InsertSitesTransformation", "AddSitePropertyTransformation" ] trans_params = [ slab_trans_params, { "scaling_matrix": supercell_trans.scaling_matrix }, { "species": [site.species_string for site in ads_sites], "coords": [site.frac_coords for site in ads_sites] }, { "site_properties": slab.site_properties } ] return TransmuterFW(name=name, structure=bulk_structure, transformations=transformations, transformation_params=trans_params, copy_vasp_outputs=True, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, vasp_input_set=vasp_input_set) else: return OptimizeFW(name=name, structure=slab, vasp_input_set=vasp_input_set, vasp_cmd=vasp_cmd, db_file=db_file, parents=parents, job_type="normal")
def correct(self): backup(orig_handlers.VASP_BACKUP_FILES | {self.output_filename}) actions = [] vi = VaspInput.from_directory(".") if self.errors.intersection(["tet", "dentet"]): actions.append({ "dict": "INCAR", "action": { "_set": { "ISMEAR": 0 } } }) if "inv_rot_mat" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "SYMPREC": 1e-8 } } }) # ----- added --------------------------------------------------- if "plane_wave_coeff" in self.errors: actions.append({ "file": "WAVECAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) # --------------------------------------------------------------- if "zpotrf" in self.errors: # Usually caused by short bond distances. If on the first step, # volume needs to be increased. Otherwise, it was due to a step # being too big and POTIM should be decreased. If a static run # try turning off symmetry. try: oszicar = Oszicar("OSZICAR") nsteps = len(oszicar.ionic_steps) except: nsteps = 0 if nsteps >= 1: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0, "POTIM": potim } } }) elif vi["INCAR"].get("NSW", 0) == 0 \ or vi["INCAR"].get("ISIF", 0) in range(3): actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0 } } }) else: s = vi["POSCAR"].structure s.apply_strain(0.2) actions.append({ "dict": "POSCAR", "action": { "_set": { "structure": s.as_dict() } } }) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "file": "WAVECAR", "action": { "_file_delete": { 'mode': "actual" } } }) if self.errors.intersection(["subspacematrix"]): if self.error_count["subspacematrix"] == 0: actions.append({ "dict": "INCAR", "action": { "_set": { "LREAL": False } } }) else: actions.append({ "dict": "INCAR", "action": { "_set": { "PREC": "Accurate" } } }) self.error_count["subspacematrix"] += 1 if self.errors.intersection(["rspher", "real_optlay", "nicht_konv"]): s = vi["POSCAR"].structure if len(s) < self.natoms_large_cell: actions.append({ "dict": "INCAR", "action": { "_set": { "LREAL": False } } }) else: # for large supercell, try an in-between option LREAL = True # prior to LREAL = False if self.error_count['real_optlay'] == 0: # use real space projectors generated by pot actions.append({ "dict": "INCAR", "action": { "_set": { "LREAL": True } } }) elif self.error_count['real_optlay'] == 1: actions.append({ "dict": "INCAR", "action": { "_set": { "LREAL": False } } }) self.error_count['real_optlay'] += 1 if self.errors.intersection(["tetirr", "incorrect_shift"]): # --Modified------------------------------------------------------ if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst or \ vi["KPOINTS"].kpts_shift != [0.0, 0.0, 0.0]: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Gamma", "usershift": [0.0, 0.0, 0.0] } } }) # ----------------------------------------------------------- if "rot_matrix" in self.errors: # --Modified------------------------------------------------------ if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst or \ vi["KPOINTS"].kpts_shift != [0.0, 0.0, 0.0]: actions.append({ "dict": "KPOINTS", "action": { "_set": { "generation_style": "Gamma", "usershift": [0.0, 0.0, 0.0] } } }) # ----------------------------------------------------------- else: actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0 } } }) if "amin" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "AMIN": "0.01" } } }) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({ "dict": "POSCAR", "action": { "_set": { "structure": new_s.as_dict() } }, "transformation": trans.as_dict() }) if "pricel" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "SYMPREC": 1e-8, "ISYM": 0 } } }) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({ "dict": "INCAR", "action": { "_set": { "POTIM": potim } } }) if "zbrent" in self.errors: # Modified so as not to use IBRION=1 as it does not show the # eigenvalues in vasprun.xml >>>>>>>>>>>> actions.append({ "dict": "INCAR", "action": { "_set": { "ADDGRID": True } } }) actions.append({ "file": "CONTCAR", "action": { "_file_copy": { "dest": "POSCAR" } } }) # actions.append({"dict": "INCAR", # "action": {"_set": {"IBRION": 1}}}) # actions.append({"file": "CONTCAR", # "action": {"_file_copy": {"dest": "POSCAR"}}}) # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({ "dict": "INCAR", "action": { "_set": { "NBANDS": int(1.1 * nbands) } } }) if "pssyevx" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "Normal" } } }) if "eddrmm" in self.errors: # RMM algorithm is not stable for this calculation if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]: actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "Normal" } } }) else: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({ "dict": "INCAR", "action": { "_set": { "POTIM": potim } } }) if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "file": "WAVECAR", "action": { "_file_delete": { 'mode': "actual" } } }) if "edddav" in self.errors: if vi["INCAR"].get("ICHARG", 0) < 10: actions.append({ "file": "CHGCAR", "action": { "_file_delete": { 'mode': "actual" } } }) actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "All" } } }) if "grad_not_orth" in self.errors: if vi["INCAR"].get("ISMEAR", 1) < 0: actions.append({ "dict": "INCAR", "action": { "_set": { "ISMEAR": "0" } } }) if "zheev" in self.errors: if vi["INCAR"].get("ALGO", "Fast").lower() != "exact": actions.append({ "dict": "INCAR", "action": { "_set": { "ALGO": "Exact" } } }) if "elf_kpar" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"KPAR": 1}}}) if "rhosyg" in self.errors: if vi["INCAR"].get("SYMPREC", 1e-4) == 1e-4: actions.append({ "dict": "INCAR", "action": { "_set": { "ISYM": 0 } } }) actions.append({ "dict": "INCAR", "action": { "_set": { "SYMPREC": 1e-4 } } }) if "posmap" in self.errors: actions.append({ "dict": "INCAR", "action": { "_set": { "SYMPREC": 1e-6 } } }) if "point_group" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) ViseVaspModder(vi=vi).apply_actions(actions) return {"errors": list(self.errors), "actions": actions}
def correct(self): backup(self.output_filename) actions = [] vi = VaspInput.from_directory(".") if "tet" in self.errors or "dentet" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}) if "inv_rot_mat" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brmix" in self.errors or "zpotrf" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if "subspacematrix" in self.errors or "rspher" in self.errors or \ "real_optlay" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) if "tetirr" in self.errors or "incorrect_shift" in self.errors: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) if "amin" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}}) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": new_s.to_dict}}, "transformation": trans.to_dict}) if "rot_matrix" in self.errors or "pricel" in self.errors: s = vi["POSCAR"].structure trans = PerturbStructureTransformation(0.05) new_s = trans.apply_transformation(s) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": new_s.to_dict}}, "transformation": trans.to_dict}) actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) m = Modder() modified = [] for a in actions: modified.append(a["dict"]) vi[a["dict"]] = m.modify_object(a["action"], vi[a["dict"]]) for f in modified: vi[f].write_file(f) return {"errors": list(self.errors), "actions": actions}
def correct(self): backup([self.output_filename, "INCAR", "KPOINTS", "POSCAR", "OUTCAR", "OSZICAR", "vasprun.xml"]) actions = [] vi = VaspInput.from_directory(".") if self.errors.intersection(["tet", "dentet"]): actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}) if "inv_rot_mat" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brmix" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if "zpotrf" in self.errors: # Usually caused by short bond distances. If on the first step, # volume needs to be increased. Otherwise, it was due to a step # being too big and POTIM should be decreased. try: oszicar = Oszicar("OSZICAR") nsteps = len(oszicar.ionic_steps) except: nsteps = 0 if nsteps >= 1: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append( {"dict": "INCAR", "action": {"_set": {"ISYM": 0, "POTIM": potim}}}) else: s = vi["POSCAR"].structure s.apply_strain(0.2) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": s.as_dict()}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with this error. actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if self.errors.intersection(["subspacematrix", "rspher", "real_optlay"]): actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) if self.errors.intersection(["tetirr", "incorrect_shift"]): if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) if "rot_matrix" in self.errors: if vi["KPOINTS"].style == Kpoints.supported_modes.Monkhorst: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) else: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) if "amin" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}}) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": new_s.as_dict()}}, "transformation": trans.as_dict()}) if "pricel" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}}) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if "zbrent" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}}) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}) if "aliasing" in self.errors: with open("OUTCAR") as f: grid_adjusted = False changes_dict = {} r = re.compile(".+aliasing errors.*(NG.)\s*to\s*(\d+)") for line in f: m = r.match(line) if m: changes_dict[m.group(1)] = int(m.group(2)) grid_adjusted = True #Ensure that all NGX, NGY, NGZ have been checked if grid_adjusted and 'NGZ' in line: actions.extend( [{"dict": "INCAR", "action": {"_set": changes_dict}}, {"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}, {"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}]) break if "aliasing_incar" in self.errors: #vasp seems to give different warnings depending on whether the #aliasing error was caused by user supplied inputs d = {k: 1 for k in ['NGX', 'NGY', 'NGZ'] if k in vi['INCAR'].keys()} actions.extend([{"dict": "INCAR", "action": {"_unset": d}}, {"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}, {"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}]) if "pssyevx" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) if "eddrmm" in self.errors: #RMM algorithm is not stable for this calculation if vi["INCAR"].get("ALGO", "Normal") in ["Fast", "VeryFast"]: actions.append({"dict": "INCAR", "action": {"_set": {"ALGO": "Normal"}}}) else: potim = float(vi["INCAR"].get("POTIM", 0.5)) / 2.0 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) VaspModder(vi=vi).apply_actions(actions) return {"errors": list(self.errors), "actions": actions}
def correct(self): #pylint: disable=R0915,R0912,R0914 """ "brmix"にてINCARにADDGRIDを追加する """ backup([self.output_filename, "INCAR", "KPOINTS", "POSCAR", "OUTCAR", "vasprun.xml"]) actions = [] vi = VaspInput.from_directory(".") if "tet" in self.errors or "dentet" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISMEAR": 0}}}) if "inv_rot_mat" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8}}}) if "brmix" in self.errors and "NELECT" in vi["INCAR"]: #brmix error always shows up after DAV steps if NELECT is specified self.errors.remove("brmix") if "brmix" in self.errors or "zpotrf" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"ISYM": 0}}}) #140814 add actions.append({"dict": "INCAR", "action": {"_set": {"ADDGRID": True}}}) # Based on VASP forum's recommendation, you should delete the # CHGCAR and WAVECAR when dealing with these errors. actions.append({"file": "CHGCAR", "action": {"_file_delete": {'mode': "actual"}}}) actions.append({"file": "WAVECAR", "action": {"_file_delete": {'mode': "actual"}}}) if "subspacematrix" in self.errors or "rspher" in self.errors or \ "real_optlay" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"LREAL": False}}}) if "tetirr" in self.errors or "incorrect_shift" in self.errors or \ "rot_matrix" in self.errors: actions.append({"dict": "KPOINTS", "action": {"_set": {"generation_style": "Gamma"}}}) if "amin" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"AMIN": "0.01"}}}) if "triple_product" in self.errors: s = vi["POSCAR"].structure trans = SupercellTransformation(((1, 0, 0), (0, 0, 1), (0, 1, 0))) new_s = trans.apply_transformation(s) actions.append({"dict": "POSCAR", "action": {"_set": {"structure": new_s.to_dict}}, "transformation": trans.to_dict}) if "pricel" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"SYMPREC": 1e-8, "ISYM": 0}}}) if "brions" in self.errors: potim = float(vi["INCAR"].get("POTIM", 0.5)) + 0.1 actions.append({"dict": "INCAR", "action": {"_set": {"POTIM": potim}}}) if "zbrent" in self.errors: actions.append({"dict": "INCAR", "action": {"_set": {"IBRION": 1}}}) if "too_few_bands" in self.errors: if "NBANDS" in vi["INCAR"]: nbands = int(vi["INCAR"]["NBANDS"]) else: with open("OUTCAR") as f: for line in f: if "NBANDS" in line: try: d = line.split("=") nbands = int(d[-1].strip()) break except (IndexError, ValueError): pass actions.append({"dict": "INCAR", "action": {"_set": {"NBANDS": int(1.1 * nbands)}}}) if "aliasing" in self.errors: with open("OUTCAR") as f: grid_adjusted = False changes_dict = {} for line in f: if "aliasing errors" in line: try: grid_vector = line.split(" NG", 1)[1] value = [int(s) for s in grid_vector.split(" ") if s.isdigit()][0] changes_dict["NG" + grid_vector[0]] = value grid_adjusted = True except (IndexError, ValueError): pass #Ensure that all NGX, NGY, NGZ have been checked if grid_adjusted and 'NGZ' in line: actions.append({"dict": "INCAR", "action": {"_set": changes_dict}}) break m = Modder(actions=[DictActions, FileActions]) modified = [] for a in actions: if "dict" in a: modified.append(a["dict"]) vi[a["dict"]] = m.modify_object(a["action"], vi[a["dict"]]) elif "file" in a: m.modify(a["action"], a["file"]) for f in modified: vi[f].write_file(f) return {"errors": list(self.errors), "actions": actions}