def get_relax_static_wf(structures, vasp_cmd=">>vasp_cmd<<", db_file=">>db_file<<", name="regular_relax", **kwargs): """ :param structures: :param vasp_cmd: :param db_file: :param name: :param kwargs: :return: """ wfs = [] for s in structures: fw1 = OptimizeFW(s, vasp_cmd=vasp_cmd, db_file=db_file, parents=[], **kwargs) fw2 = StaticFW(s, vasp_cmd=vasp_cmd, db_file=db_file, parents=[fw1]) wfs.append( Workflow([fw1, fw2], name=name + str(s.composition.reduced_formula))) return wfs
def get_wf_molecules(molecules, vasp_input_set=None, db_file=None, vasp_cmd="vasp", name=""): """ Args: molecules (Molecules): list of molecules to calculate vasp_input_set (DictSet): VaspInputSet for molecules db_file (string): database file path vasp_cmd (string): VASP command name (string): name for workflow Returns: workflow consisting of molecule calculations """ fws = [] for molecule in molecules: # molecule in box m_struct = molecule.get_boxed_structure(10, 10, 10, offset=np.array([5, 5, 5])) vis = vasp_input_set or MPSurfaceSet(m_struct) fws.append( OptimizeFW(structure=molecule, job_type="normal", vasp_input_set=vis, db_file=db_file, vasp_cmd=vasp_cmd)) name = name or "molecules workflow" return Workflow(fws, name=name)
def get_wf_structure_sampler(xdatcar_file, n=10, steps_skip_first=1000, vasp_cmd=">>vasp_cmd<<", db_file=">>db_file<<", name="structure_sampler", **kwargs): """ :param xdatcar_file: :param n: :param steps_skip_first: :param vasp_cmd: :param db_file: :param name: :param kwargs: :return: """ structures = get_sample_structures(xdatcar_path=xdatcar_file, n=n, steps_skip_first=steps_skip_first) wfs = [] for s in structures: fw1 = OptimizeFW(s, vasp_cmd=vasp_cmd, db_file=db_file, parents=[], **kwargs) fw2 = StaticFW(s, vasp_cmd=vasp_cmd, db_file=db_file, parents=[fw1]) wfs.append( Workflow([fw1, fw2], name=name + str(s.composition.reduced_formula))) return wfs
def run_task(self, fw_spec): #create structure from CONTCAR struct = Poscar.from_file('CONTCAR').structure #repeat layers of bulk to create supercell struct.make_supercell([1, 1, self.get("num_layers", 2)]) #add vacuum to create slab struct = add_vacuum(struct, self.get("vacuum", 15)) #add selective dynamics selective_dynamics = [] """ min_bulk = self.get("surf_layers_to_relax",3)/(self.get("atomic_thickness")*self.get("num_layers",2)) * max([site.z for site in struct.sites]) max_bulk = (self.get("atomic_thickness")*self.get("num_layers",2) - self.get("surf_layers_to_relax",3))/(self.get("atomic_thickness")*self.get("num_layers",2)) * max([site.z for site in struct.sites]) for site in struct.sites: if site.z > min_bulk and site.z <= max_bulk: selective_dynamics.append([False, False, False]) else: selective_dynamics.append([True, True, True]) struct.add_site_property("selective_dynamics", selective_dynamics) """ #create optimize and static fireworks using the newly created slab slab_optimize = OptimizeFW(struct, name=name + '_slab_optimization' + time, vasp_cmd=">>vasp_cmd<<", db_file=">>db_file<<", parents=[bulk_optimize]) slab_optimize = Workflow(slab_optimize) optimize_incar_settings = {"ISIF": 2} optimize_update = {"incar_update": optimize_incar_settings} slab_optimize = add_modify_incar(slab_optimize, modify_incar_params=optimize_update, fw_name_constraint='optimization') slab_static = StaticFW(struct, name=name + '_slab_static_' + time, parents=[slab_optimize], prev_calc_loc=True, vasp_cmd=">>vasp_cmd<<", db_file=">>db_file<<") #slab_static.tasks.append(Slab_energy_and_SA()) #surface_energy_calc_fw = Firework(Surface_energy_calc(), parents = [bulk_static, slab_static]) #return FWAction(additions = [slab_optimize, slab_static, surface_energy_calc_fw]) return FWAction(additions=[slab_optimize, slab_static])
def get_wf_ferroelectric(polar_structure, nonpolar_structure, vasp_cmd="vasp", db_file=None, vasp_input_set_polar="MPStaticSet", vasp_input_set_nonpolar="MPStaticSet", relax=False, vasp_relax_input_set_polar=None, vasp_relax_input_set_nonpolar=None, nimages=9, hse=False, add_analysis_task=False, wfid=None, tags=None): """ Returns a workflow to calculate the spontaneous polarization of polar_structure using a nonpolar reference phase structure and linear interpolations between the polar and nonpolar structure. The nonpolar and polar structures must be in the same space group setting and atoms ordered such that a linear interpolation can be performed to create intermediate structures along the distortion. For example, to calculate the polarization of orthorhombic BaTiO3 (space group 38) using the cubic structure (space group 221) as the nonpolar reference phase, we must transform the cubic to the orthorhombic setting. This can be accomplished using Bilbao Crystallographic Server's Structure Relations tool. (http://www.cryst.ehu.es/cryst/rel.html) Args: polar_structure (Structure): polar structure of candidate ferroelectric nonpolar_structure (Structure): nonpolar reference structure in polar setting vasp_input_set_polar (DictVaspInputSet): VASP polar input set. Defaults to MPStaticSet. vasp_input_set_nonpolar (DictVaspInputSet): VASP nonpolar input set. Defaults to MPStaticSet. vasp_relax_input_set_polar (DictVaspInputSet): VASP polar input set. Defaults to MPRelaxSet. vasp_relax_input_set_nonpolar (DictVaspInputSet): VASP nonpolar input set. Defaults to MPRelaxSet. vasp_cmd (str): command to run db_file (str): path to file containing the database credentials. nimages: Number of interpolations calculated from polar to nonpolar structures, including the nonpolar. For example, nimages = 9 will calculate 8 interpolated structures. 8 interpolations + nonpolar = 9. add_analysis_task: Analyze polarization and energy trends as part of workflow. Default False. wfid (string): Unique worfklow id starting with "wfid_". If None this is atomatically generated (recommended). tags (list of strings): Additional tags to add such as identifiers for structures. Returns: """ wf = [] if wfid is None: wfid = 'wfid_' + get_a_unique_id() if tags is None: tags = [] if relax: polar_relax = OptimizeFW(structure=polar_structure, name="_polar_relaxation", vasp_cmd=vasp_cmd, db_file=db_file, vasp_input_set=vasp_relax_input_set_polar) nonpolar_relax = OptimizeFW( structure=nonpolar_structure, name="_nonpolar_relaxation", vasp_cmd=vasp_cmd, db_file=db_file, vasp_input_set=vasp_relax_input_set_nonpolar) wf.append(polar_relax) wf.append(nonpolar_relax) parents_polar = polar_relax parents_nonpolar = nonpolar_relax else: parents_polar = None parents_nonpolar = None # Run polarization calculation on polar structure. # Defuse workflow if polar structure is metallic. polar = LcalcpolFW(structure=polar_structure, name="_polar_polarization", static_name="_polar_static", parents=parents_polar, vasp_cmd=vasp_cmd, db_file=db_file, vasp_input_set=vasp_input_set_polar) # Run polarization calculation on nonpolar structure. # Defuse workflow if nonpolar structure is metallic. nonpolar = LcalcpolFW(structure=nonpolar_structure, name="_nonpolar_polarization", static_name="_nonpolar_static", parents=parents_nonpolar, vasp_cmd=vasp_cmd, db_file=db_file, vasp_input_set=vasp_input_set_nonpolar) # Interpolation polarization interpolation = [] # Interpolations start from one increment after polar and end prior to nonpolar. # The Structure.interpolate method adds an additional image for the nonpolar endpoint. # Defuse children fireworks if metallic. for i in range(1, nimages): # nonpolar_structure is being used as a dummy structure. # The structure will be replaced by the interpolated structure generated by # StaticInterpolatedFW. # Defuse workflow if interpolated structure is metallic. interpolation.append( LcalcpolFW(structure=polar_structure, name="_interpolation_{}_polarization".format(str(i)), static_name="_interpolation_{}_static".format(str(i)), vasp_cmd=vasp_cmd, db_file=db_file, vasp_input_set=vasp_input_set_polar, interpolate=True, start="_polar_static", end="_nonpolar_static", nimages=nimages, this_image=i, parents=[polar, nonpolar])) wf.append(polar) wf.append(nonpolar) wf += interpolation # Add FireTask that uses Polarization object to store spontaneous polarization information if add_analysis_task: fw_analysis = Firework(PolarizationToDb(db_file=db_file), parents=interpolation, name="_polarization_post_processing") wf.append(fw_analysis) # Run HSE band gap calculation if hse: # Run HSE calculation at band gap for polar calculation if polar structure is not metallic hse = HSEBSFW(structure=polar_structure, parents=polar, name="_polar_hse_gap", vasp_cmd=vasp_cmd, db_file=db_file, calc_loc="_polar_polarization") wf.append(hse) # Create Workflow task and add tags to workflow workflow = Workflow(wf) workflow = add_tags(workflow, [wfid] + tags) return workflow
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 get_wf_slab(slab, include_bulk_opt=False, adsorbates=None, ads_structures_params=None, ads_site_finder_params=None, vasp_cmd="vasp", db_file=None, add_molecules_in_box=False, user_incar_settings=None): """ Gets a workflow corresponding to a slab calculation along with optional adsorbate calcs and precursor oriented unit cell optimization Args: slabs (list of Slabs or Structures): slabs to calculate include_bulk_opt (bool): whether to include bulk optimization, this flag sets the slab fireworks to be TransmuterFWs based on bulk optimization of oriented unit cells adsorbates ([Molecule]): list of molecules to place as adsorbates ads_structures_params (dict): parameters to be supplied as kwargs to AdsorbateSiteFinder.generate_adsorption_structures add_molecules_in_box (boolean): flag to add calculation of adsorbate molecule energies to the workflow db_file (string): path to database file vasp_cmd (string): vasp command Returns: Workflow """ fws, parents = [], [] if adsorbates is None: adsorbates = [] if ads_structures_params is None: ads_structures_params = {} # Add bulk opt firework if specified if include_bulk_opt: oriented_bulk = slab.oriented_unit_cell vis = MPSurfaceSet(oriented_bulk, bulk=True) fws.append( OptimizeFW(structure=oriented_bulk, vasp_input_set=vis, vasp_cmd=vasp_cmd, db_file=db_file)) parents = fws[-1] name = slab.composition.reduced_formula if getattr(slab, "miller_index", None): name += "_{}".format(slab.miller_index) # Create slab fw and add it to list of fws slab_fw = get_slab_fw(slab, include_bulk_opt, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, name="{} slab optimization".format(name)) fws.append(slab_fw) for adsorbate in adsorbates: ads_slabs = AdsorbateSiteFinder( slab, **ads_site_finder_params).generate_adsorption_structures( adsorbate, **ads_structures_params) for n, ads_slab in enumerate(ads_slabs): # Create adsorbate fw ads_name = "{}-{} adsorbate optimization {}".format( adsorbate.composition.formula, name, n) adsorbate_fw = get_slab_fw(ads_slab, include_bulk_opt, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, name=ads_name, user_incar_settings=user_incar_settings) fws.append(adsorbate_fw) if isinstance(slab, Slab): name = "{}_{} slab workflow".format( slab.composition.reduced_composition, slab.miller_index) else: name = "{} slab workflow".format(slab.composition.reduced_composition) wf = Workflow(fws, name=name) # Add optional molecules workflow if add_molecules_in_box: molecule_wf = get_wf_molecules(adsorbates, db_file=db_file, vasp_cmd=vasp_cmd) wf.append_wf(molecule_wf) return wf
def generate_lattconst_wf(list_elt_sets, functional='PBE', vasp_cmd='>>vasp_cmd<<', db_file='>>db_file<<', submit=False, scan_smart_lattice=None): """Generates a workflow which calculates lattice constants through optimization fireworks for a given functional type NOTE: that the SCAN functionality might be reliant on some Custodian features from Danny's Custodian """ if functional in ['PBE', 'LDA']: job_type = 'double_relaxation_run' potcar_type = functional incar_settings = {"ADDGRID": True, 'EDIFF': 1e-8} elif functional in ['SCAN']: job_type = 'metagga_opt_run' potcar_type = 'PBE' #this is the POTCAR that needs to be used for SCAN... incar_settings = {'EDIFF': 1e-8, 'ISIF': 7} if scan_smart_lattice is None: raise ValueError("Need to provide a smarter starting point " "for SCAN lattice constants...") fws = [] for elt_set in list_elt_sets: if functional == 'SCAN': compkey = Composition({ elt_set[0]: 1, elt_set[1]: 1, elt_set[2]: 3 }) lattconst = scan_smart_lattice[compkey] else: lattconst = None pp = PerfectPerovskite(Asite=elt_set[0], Bsite=elt_set[1], Osite=elt_set[2], lattconst=lattconst) s = pp.get_111_struct() vis = MPRelaxSet(s, user_incar_settings=incar_settings, potcar_functional=potcar_type) fw = OptimizeFW(s, name="{} {} structure optimization".format( s.composition.reduced_formula, functional), vasp_input_set=vis, vasp_cmd=vasp_cmd, db_file=db_file, job_type=job_type, auto_npar=">>auto_npar<<") fws.append(fw) wf = Workflow(fws, name='{} latt const workflow'.format(functional)) if submit: print('Submitting workflow with {} fws for {}'.format( len(list_elt_sets), functional)) lpad = LaunchPad().from_file(lpad_file_path) lpad.add_wf(wf) else: print('Workflow created with {} fws for {}'.format( len(list_elt_sets), functional)) return wf
def get_wf(self, scan=False, perform_bader=True, num_orderings_hard_limit=16, c=None): """ Retrieve the FireWorks workflow. Args: scan (bool): if True, use the SCAN functional instead of GGA+U, since the SCAN functional has shown to have improved performance for magnetic systems in some cases perform_bader (bool): if True, make sure the "bader" binary is in your path, will use Bader analysis to calculate atom-projected magnetic moments num_orderings_hard_limit (int): will make sure total number of magnetic orderings does not exceed this number even if there are extra orderings of equivalent symmetry c (dict): additional config dict (as used elsewhere in atomate) Returns: FireWorks Workflow """ c_defaults = {"VASP_CMD": VASP_CMD, "DB_FILE": DB_FILE} additional_fields = {"relax": not self.static} c = c or {} for k, v in c_defaults.items(): if k not in c: c[k] = v fws = [] analysis_parents = [] # trim total number of orderings (useful in high-throughput context) # this is somewhat course, better to reduce num_orderings kwarg and/or # change enumeration strategies ordered_structures = self.ordered_structures ordered_structure_origins = self.ordered_structure_origins def _add_metadata(structure): """ For book-keeping, store useful metadata with the Structure object for later database ingestion including workflow version and a UUID for easier querying of all tasks generated from the workflow. Args: structure: Structure Returns: TransformedStructure """ # this could be further improved by storing full transformation # history, but would require an improved transformation pipeline return TransformedStructure( structure, other_parameters={"wf_meta": self.wf_meta}) ordered_structures = [ _add_metadata(struct) for struct in ordered_structures ] if (num_orderings_hard_limit and len(self.ordered_structures) > num_orderings_hard_limit): ordered_structures = self.ordered_structures[ 0:num_orderings_hard_limit] ordered_structure_origins = self.ordered_structure_origins[ 0:num_orderings_hard_limit] logger.warning("Number of ordered structures exceeds hard limit, " "removing last {} structures.".format( len(self.ordered_structures) - len(ordered_structures))) # always make sure input structure is included if self.input_index and self.input_index > num_orderings_hard_limit: ordered_structures.append( self.ordered_structures[self.input_index]) ordered_structure_origins.append( self.ordered_structure_origins[self.input_index]) # default incar settings user_incar_settings = {"ISYM": 0, "LASPH": True, "EDIFFG": -0.05} if scan: # currently, using SCAN relaxation as a static calculation also # since it is typically high quality enough, but want to make # sure we are also writing the AECCAR* files user_incar_settings.update({"LAECHG": True}) user_incar_settings.update(c.get("user_incar_settings", {})) c["user_incar_settings"] = user_incar_settings for idx, ordered_structure in enumerate(ordered_structures): analyzer = CollinearMagneticStructureAnalyzer(ordered_structure) name = f" ordering {idx} {analyzer.ordering.value} -" if not scan: vis = MPRelaxSet(ordered_structure, user_incar_settings=user_incar_settings) if not self.static: # relax fws.append( OptimizeFW( ordered_structure, vasp_input_set=vis, vasp_cmd=c["VASP_CMD"], db_file=c["DB_FILE"], max_force_threshold=0.05, half_kpts_first_relax=False, name=name + " optimize", )) # static fws.append( StaticFW( ordered_structure, vasp_cmd=c["VASP_CMD"], db_file=c["DB_FILE"], name=name + " static", prev_calc_loc=True, parents=fws[-1], vasptodb_kwargs={ "parse_chgcar": True, "parse_aeccar": True }, )) if not self.static: # so a failed optimize doesn't crash workflow fws[-1].spec["_allow_fizzled_parents"] = True elif scan: # wf_scan_opt is just a single FireWork so can append it directly scan_fws = wf_scan_opt(ordered_structure, c=c).fws # change name for consistency with non-SCAN new_name = scan_fws[0].name.replace("structure optimization", name + " optimize") scan_fws[0].name = new_name scan_fws[0].tasks[-1]["additional_fields"][ "task_label"] = new_name fws += scan_fws analysis_parents.append(fws[-1]) fw_analysis = Firework( MagneticOrderingsToDb( db_file=c["DB_FILE"], wf_uuid=self.uuid, parent_structure=self.sanitized_structure, origins=ordered_structure_origins, input_index=self.input_index, perform_bader=perform_bader, scan=scan, additional_fields=additional_fields, ), name="Magnetic Orderings Analysis", parents=analysis_parents, spec={"_allow_fizzled_parents": True}, ) fws.append(fw_analysis) formula = self.sanitized_structure.composition.reduced_formula wf_name = f"{formula} - magnetic orderings" if scan: wf_name += " - SCAN" wf = Workflow(fws, name=wf_name) wf = add_additional_fields_to_taskdocs(wf, {"wf_meta": self.wf_meta}) tag = f"magnetic_orderings group: >>{self.uuid}<<" wf = add_tags(wf, [tag, ordered_structure_origins]) return wf
def get_quench_wf(structures, temperatures={}, priority=None, quench_type="slow_quench", cool_args={}, hold_args={}, quench_args={}, descriptor="", **kwargs): fw_list = [] temp = {"start_temp": 3000, "end_temp": 500, "temp_step": 500} if temp is None else temp cool_args = {"md_params": {"nsteps": 200}} if cool_args is None else cool_args hold_args = {"md_params": {"nsteps": 500}} if hold_args is None else hold_args quench_args = {} if quench_args is None else quench_args for (i, structure) in enumerate(structures): _fw_list = [] if quench_type == "slow_quench": for temp in np.arange(temperatures["start_temp"], temperatures["end_temp"], -temperatures["temp_step"]): # get fw for cool step use_prev_structure = False if len(_fw_list) > 0: use_prev_structure = True _fw = get_MDFW(structure, t, t - temp["temp_step"], name="snap_" + str(i) + "_cool_" + str(t - temp["temp_step"]), args=cool_args, parents=[_fw_list[-1]] if len(_fw_list) > 0 else [], priority=priority, previous_structure=use_prev_structure, insert_db=True, **kwargs) _fw_list.append(_fw) # get fw for hold step _fw = get_MDFW(structure, t - temp["temp_step"], t - temp["temp_step"], name="snap_" + str(i) + "_hold_" + str(t - temp["temp_step"]), args=hold_args, parents=[_fw_list[-1]], priority=priority, previous_structure=True, insert_db=True, **kwargs) _fw_list.append(_fw) if quench_type in ["slow_quench", "mp_quench"]: # Relax OptimizeFW and StaticFW run_args = {"run_specs": {"vasp_input_set": None, "vasp_cmd": ">>vasp_cmd<<", "db_file": ">>db_file<<", "spec": {"_priority": priority} }, "optional_fw_params": {"override_default_vasp_params": {}} } run_args = recursive_update(run_args, quench_args) _name = "snap_" + str(i) fw1 = OptimizeFW(structure=structure, name=_name + descriptor + "_optimize", parents=[_fw_list[-1]] if len(_fw_list) > 0 else [], **run_args["run_specs"], **run_args["optional_fw_params"], max_force_threshold=None) if len(_fw_list) > 0: fw1 = powerups.add_cont_structure(fw1) fw1 = powerups.add_pass_structure(fw1) fw2 = StaticFW(structure=structure, name=_name + descriptor + "_static", parents=[fw1], **run_args["run_specs"], **run_args["optional_fw_params"]) fw2 = powerups.add_cont_structure(fw2) fw2 = powerups.add_pass_structure(fw2) _fw_list.extend([fw1, fw2]) fw_list.extend(_fw_list) name = structure.composition.reduced_formula + descriptor + "_quench" wf = Workflow(fw_list, name=name) return wf
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 get_wf_surface(slabs, molecules=[], bulk_structure=None, slab_gen_params=None, vasp_cmd="vasp", db_file=None, ads_structures_params={}, add_molecules_in_box=False): """ Args: slabs (list of Slabs or Structures): slabs to calculate molecules (list of Molecules): molecules to place as adsorbates bulk_structure (Structure): bulk structure from which generate slabs after reoptimization. If supplied, workflow will begin with bulk structure optimization. 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 if in that mode ads_structures_params (dict): parameters to be supplied as kwargs to AdsorbateSiteFinder.generate_adsorption_structures add_molecules_in_box (boolean): flag to add calculation of molecule energies to the workflow db_file (string): path to database file vasp_cmd (string): vasp command Returns: Workflow """ fws, parents = [], [] if bulk_structure: vis = MVLSlabSet(bulk_structure, bulk=True) fws.append( OptimizeFW(bulk_structure, vasp_input_set=vis, vasp_cmd="vasp", db_file=db_file)) parents = fws[0] for slab in slabs: name = slab.composition.reduced_formula if getattr(slab, "miller_index", None): name += "_{}".format(slab.miller_index) fws.append( get_slab_fw(slab, bulk_structure, slab_gen_params, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, name=name + " slab optimization")) for molecule in molecules: ads_slabs = AdsorbateSiteFinder( slab).generate_adsorption_structures(molecule, **ads_structures_params) for n, ads_slab in enumerate(ads_slabs): ads_name = "{}-{} adsorbate optimization {}".format( molecule.composition.formula, name, n) fws.append( get_slab_fw(ads_slab, bulk_structure, slab_gen_params, db_file=db_file, vasp_cmd=vasp_cmd, parents=parents, name=ads_name)) if add_molecules_in_box: for molecule in molecules: # molecule in box m_struct = Structure(Lattice.cubic(10), molecule.species_and_occu, molecule.cart_coords, coords_are_cartesian=True) m_struct.translate_sites( list(range(len(m_struct))), np.array([0.5] * 3) - np.average(m_struct.frac_coords, axis=0)) vis = MVLSlabSet(m_struct) fws.append( OptimizeFW(molecule, job_type="normal", vasp_input_set=vis, db_file=db_file, vasp_cmd=vasp_cmd)) # TODO: add analysis framework return Workflow(fws, name="")
def get_wf(self, c=None): """ Get the workflow. Returns: Workflow """ c = c or {"VASP_CMD": VASP_CMD, "DB_FILE": DB_FILE} vasp_cmd = c.get("VASP_CMD", VASP_CMD) db_file = c.get("DB_FILE", DB_FILE) nsites = len(self.structure.sites) vis = MPRelaxSet(self.structure, potcar_functional="PBE_54", force_gamma=True) opt_fw = OptimizeFW( self.structure, vasp_input_set=vis, vasp_cmd=c["VASP_CMD"], db_file=c["DB_FILE"], ) vis = MPStaticSet(self.structure, potcar_functional="PBE_54", force_gamma=True) static_fw = StaticFW( self.structure, vasp_input_set=vis, vasp_cmd=c["VASP_CMD"], db_file=c["DB_FILE"], parents=[opt_fw], ) # Separate FW for each BZ surface calc # Run Z2Pack on unique TRIM planes in the BZ surfaces = ["kx_0", "kx_1"] equiv_planes = self.get_equiv_planes() # Only run calcs on inequivalent BZ surfaces if self.symmetry_reduction: for add_surface in equiv_planes.keys(): mark = True for surface in surfaces: if surface in equiv_planes[add_surface]: mark = False if mark and add_surface not in surfaces: surfaces.append(add_surface) else: # 4 TRI surfaces define Z2 in 3D surfaces = ["kx_1", "ky_1", "kz_0", "kz_1"] z2pack_fws = [] for surface in surfaces: z2pack_fw = Z2PackFW( parents=[static_fw], structure=self.structure, surface=surface, uuid=self.uuid, name="z2pack", vasp_cmd=c["VASP_CMD"], db_file=c["DB_FILE"], ) z2pack_fws.append(z2pack_fw) analysis_fw = InvariantFW( parents=z2pack_fws, structure=self.structure, symmetry_reduction=self.symmetry_reduction, equiv_planes=equiv_planes, uuid=self.uuid, name="invariant", db_file=c["DB_FILE"], ) fws = [opt_fw, static_fw] + z2pack_fws + [analysis_fw] wf = Workflow(fws) wf = add_additional_fields_to_taskdocs(wf, {"wf_meta": self.wf_meta}) # Add vdW corrections if structure is layered dim_data = StructureDimensionality(self.structure) if np.any( [ dim == 2 for dim in [dim_data.larsen_dim, dim_data.cheon_dim, dim_data.gorai_dim] ] ): wf = add_modify_incar( wf, modify_incar_params={ "incar_update": { "IVDW": 11, "EDIFFG": 0.005, "IBRION": 2, "NSW": 100, } }, fw_name_constraint="structure optimization", ) wf = add_modify_incar( wf, modify_incar_params={"incar_update": {"IVDW": 11}}, fw_name_constraint="static", ) wf = add_modify_incar( wf, modify_incar_params={"incar_update": {"IVDW": 11}}, fw_name_constraint="z2pack", ) else: wf = add_modify_incar( wf, modify_incar_params={ "incar_update": {"EDIFFG": 0.005, "IBRION": 2, "NSW": 100} }, fw_name_constraint="structure optimization", ) # Helpful vasp settings and no parallelization wf = add_modify_incar( wf, modify_incar_params={ "incar_update": { "ADDGRID": ".TRUE.", "LASPH": ".TRUE.", "GGA": "PS", "NCORE": 1, } }, ) # Generate inputs for Z2Pack with a static calc wf = add_modify_incar( wf, modify_incar_params={"incar_update": {"PREC": "Accurate"}}, fw_name_constraint="static", ) wf = add_common_powerups(wf, c) wf.name = "{} {}".format(self.structure.composition.reduced_formula, "Z2Pack") if c.get("STABILITY_CHECK", STABILITY_CHECK): wf = add_stability_check(wf, fw_name_constraint="structure optimization") if c.get("ADD_WF_METADATA", ADD_WF_METADATA): wf = add_wf_metadata(wf, self.structure) tag = "z2pack: {}".format(self.uuid) wf = add_tags(wf, [tag]) return wf
for composition in comp_list: tilt_count = 15 temp_structs = copy.deepcopy(tilts_and_orders) comp_tag = composition[0] + composition[1] + 'N' + 'O' + str( composition[2]) struct_tag = 'tetra' for structure in temp_structs: if composition[2] == 1: structure.replace_species({'O': 'N', 'N': 'O'}) structure.replace_species({ default_B: composition[1] }) #this will break if B-site is default_a but that shouldn't happen structure.replace_species({default_A: composition[0]}) poscar_key = tetra_dict[tilt_count] opt = OptimizeFW(structure, vasp_cmd='ibrun tacc_affinity vasp_std') stat = StaticFW(parents=opt, vasp_cmd='ibrun tacc_affinity vasp_std') wf = Workflow([opt, stat]) wf = add_modify_incar(wf, modify_incar_params={ 'incar_update': { 'KPAR': 2, 'NCORE': 4, 'NSIM': 8, 'EDIFF': 0.000002, 'LMAXMIX': 6, 'LSCALAPACK': '.FALSE.', 'ALGO': 'All' } }) wf = add_modify_incar(wf,
def get_wf_raman_spectra( structure, modes=None, step_size=0.005, vasp_cmd="vasp", db_file=None ): """ Raman susceptibility tensor workflow: Calculation of phonon normal modes followed by the computation of dielectric tensor for structures displaced along the normal modes. Finally the dielectric tensors corresponding to each mode are used to compute the Raman susceptibility tensor using finite difference (central difference scheme). Args: structure (Structure): Input structure. modes (tuple/list): list of modes for which the Raman spectra need to be calculated. The default is to use all the 3N modes. step_size (float): site displacement along the normal mode in Angstroms. Used to compute the finite difference(central difference scheme) first derivative of the dielectric constant along the normal modes. vasp_cmd (str): vasp command to run. db_file (str): path to file containing the database credentials. Returns: Workflow """ modes = modes or range(3 * len(structure)) vis = MPRelaxSet(structure, force_gamma=True) # displacements in + and - direction along the normal mode so that the central difference scheme # can be used for the evaluation of Raman tensor (derivative of epsilon wrt displacement) displacements = [-step_size, step_size] fws = [] # Structure optimization fw_opt = OptimizeFW( structure=structure, vasp_input_set=vis, ediffg=None, vasp_cmd=vasp_cmd, db_file=db_file, ) fws.append(fw_opt) # Static run: compute the normal modes and pass fw_leps = DFPTFW( structure=structure, vasp_cmd=vasp_cmd, db_file=db_file, parents=fw_opt, pass_nm_results=True, ) fws.append(fw_leps) # Static runs to compute epsilon for each mode and displacement along that mode. fws_nm_disp = [] for mode in modes: for disp in displacements: fws_nm_disp.append( RamanFW( mode, disp, structure=structure, parents=fw_leps, vasp_cmd=vasp_cmd, db_file=db_file, ) ) fws.extend(fws_nm_disp) # Compute the Raman susceptibility tensor fw_analysis = Firework( RamanTensorToDb(db_file=db_file), parents=fws[:], name="{}-{}".format(structure.composition.reduced_formula, "raman analysis"), ) fws.append(fw_analysis) wfname = "{}:{}".format(structure.composition.reduced_formula, "raman spectra") return Workflow(fws, name=wfname)
def run_task(self, fw_spec): insert_sites = fw_spec.get("insert_sites", []) base_task_id = fw_spec.get("base_task_id") base_structure = Structure.from_dict(fw_spec.get("base_structure")) working_ion = fw_spec.get("working_ion") allow_fizzled_parents = fw_spec.get("allow_fizzled_parents", False) optimizefw_kwargs = fw_spec.get("optimizefw_kwargs", {}) n_ion = int( base_structure.composition.element_composition[working_ion]) + 1 new_fws = [] for itr, isite in enumerate(insert_sites): inserted_structure = base_structure.copy() fpos = isite inserted_structure.insert(0, working_ion, fpos, properties={"magmom": 1.0}) additional_fields = { "insertion_fpos": fpos, "base_task_id": base_task_id } # Create new fw fw = OptimizeFW( inserted_structure, name= f"structure optimization-{itr}", # structure are rough guesses **optimizefw_kwargs, ) fw.tasks[-1]["additional_fields"].update( additional_fields) # easy way to update a field pass_dict = { "structure": ">>output.ionic_steps.-1.structure", "energy": "a>>final_energy", } pass_task = pass_vasp_result( filename="vasprun.xml.relax2.gz", pass_dict=pass_dict, mod_spec_cmd="_push", mod_spec_key="inserted_results", ) fw.tasks.append(pass_task) new_fws.append(fw) if len(new_fws) == 0: return check_fw = Firework( [CollectInsertedCalcs(), SubmitMostStable()], parents=new_fws, name=f"Collect Inserted Calcs-{n_ion}", ) # Allow fizzled parent check_fw.spec["_allow_fizzled_parents"] = allow_fizzled_parents wf = Workflow(new_fws + [check_fw]) wf = get_powerup_wf(wf, fw_spec) update_wf_keys(wf, fw_spec) return FWAction(additions=[wf])