def __init__( self, structure, name="static dipole moment", static_name="static", vasp_cmd="vasp", vasp_input_set=None, vasp_input_set_params=None, db_file=None, parents=None, gap_threshold=0.010, interpolate=False, start=None, end=None, this_image=0, nimages=5, **kwargs, ): """ Static Firework that calculates the dipole moment of structure or a single interpolated image of structures output by two calculations specified by PassCalcLoc names start and end. This Firework uses three steps to calculate the dipole moment. 1. A StaticFW or StaticInterpolatFW calculation is performed to compute the band gap of the structure. 2. Because VASP cannot compute the dipole moment of metallic structures, CheckBandgap checks that the structure has a band gap greater than gap_threshold. If the structure has a band gap less than gap_threshold, the Firework defuses. Otherwise, 3. a polarization calculation (LCALCPOL=TRUE) is performed to calculate the dipole moment. If interpolate is equal to True, the keywords start and end are PassCalcLoc names used to locate the output structures (CONTCAR) of two previous calculations. These two structures must have the same number and type of atoms given in identical order in the structures. These structures are used to create nimages structures interpolations. this_image specifies which of the interpolated structures to use in this Firework. For example in the ferroelectric workflow, two structures that can be distorted into one another (a high-symmetry nonpolar structure and a low-symmetry polar structure) are relaxed using OptimizeFW. Then the dipole moment is calculated for nimages intermediate structures generated by interpolating between these two relaxed structures. Args: structure (Structure): Input structure. For an interpolation, this is a dummy structure. See interpolate arg description. name (str): Name for the polarization FireWork. static_name (str): Name for the SCF run to be used in PassCalcLoc if copy_vasp_outputs != True. vasp_cmd (str): Command to run vasp. vasp_input_set (str): string name for the VASP input set (e.g., "MITMDVaspInputSet"). vasp_input_set_params (dict): Dict of vasp_input_set_kwargs. db_file (str): Path to file specifying db credentials. parents (Firework): Parents of this particular Firework. FW or list of FWS. gap_threshold: Band gap cutoff for determining whether polarization calculation will proceed from SCF band gap. interpolate (bool): use structure created by interpolating CONTCAR structure from calculations indicated by start and end PassCalcLoc names. If interpolate is False, start and end do not need to be set. start (str): PassCalcLoc name of StaticFW or RelaxFW run of starting structure end (str): PassCalcLoc name of StaticFW or RelaxFW run of ending structure this_image (int): which interpolation to use for this run of the nimage interpolations. nimages (int): number of interpolations between CONTCAR structures from calculations indicated by start and end args. """ t = [] # Ensure that LWAVE is set to true so we can use WAVECAR for # polarization calculation. vasp_input_set_params = vasp_input_set_params or {} vasp_input_set_params = vasp_input_set_params.copy() if not vasp_input_set_params.get("user_incar_settings", None): vasp_input_set_params.update({"user_incar_settings": {}}) vasp_input_set_params["user_incar_settings"].update({"LWAVE": True}) vasp_input_set = vasp_input_set or "MPStaticSet" if interpolate: static = StaticInterpolateFW( structure, start, end, name=static_name, vasp_input_set=vasp_input_set, vasp_input_set_params=vasp_input_set_params, vasp_cmd=vasp_cmd, db_file=db_file, parents=parents, this_image=this_image, nimages=nimages, **kwargs, ) else: vasp_input_set = MPStaticSet(structure, **vasp_input_set_params) static = StaticFW( structure, name=static_name, vasp_input_set=vasp_input_set, vasp_input_set_params=vasp_input_set_params, vasp_cmd=vasp_cmd, db_file=db_file, parents=parents, **kwargs, ) t.extend(static.tasks) # Defuse workflow if bandgap is less than gap_threshold. t.append(CheckBandgap(min_gap=gap_threshold)) # Create new directory and move to that directory to perform # polarization calculation t.append(CreateFolder(folder_name="polarization", change_dir=True)) # Copy VASP Outputs from static calculation t.extend([ CopyVaspOutputs( calc_loc=static_name, additional_files=["CHGCAR", "WAVECAR"], contcar_to_poscar=True, ), ModifyIncar(incar_update={"lcalcpol": True}), RunVaspCustodian(vasp_cmd=vasp_cmd), PassCalcLocs(name=name), VaspToDb(db_file=db_file, additional_fields={"task_label": name}), ]) # Note, Outcar must have read_lcalcpol method for polarization # information to be processed. ...assuming VaspDrone will automatically # assimilate all properties of the Outcar. super().__init__( t, parents=parents, name=f"{structure.composition.reduced_formula}-{name}", **kwargs, )
def get_wf_lobster_test_basis( structure: Structure, calculation_type: str = "standard", delete_all_wavecars: bool = True, c: dict = None, address_max_basis: Optional[str] = None, address_min_basis: Optional[str] = None, user_lobsterin_settings: dict = None, user_incar_settings: dict = None, user_kpoints_settings: dict = None, isym: int = 0, additional_outputs: List[str] = None, ) -> Workflow: """ creates workflow where all possible basis functions for one compound are tested at the end, the user has to decide which projection worked best (e.g., based on chargespilling) this is the recommended workflow at the moment! Args: structure (Structure): structure object that will be used during the run calculation_type (str): only "standard" is implemented so far delete_all_wavecars (bool): all wavecars wil be deleted if True c (dict): specifications for wf, e.g. VASP_CMD, LOBSTER_CMD etc. address_max_basis (str): address to yaml file including maximum basis set (otherwise predefined file) address_min_basis (str): address to yaml file including minimum basis set (otherwise predefined file) user_lobsterin_settings (dict): change lobsterin settings here user_incar_settings (dict): change incar settings with this dict user_kpoints_settings (dict): change kpoint settings with this dict isym (int): isym setting during the VASP calculation, currently lobster can only deal with isym=-1 and isym=0 additional_outputs (list): list of additional files to be stored in the results DB. They will be stored as files in gridfs. Examples are: "ICOHPLIST.lobster" or "DOSCAR.lobster". Note that the file name should be given with the full name and the correct capitalization. Returns: """ c = c or {} vasp_cmd = c.get("VASP_CMD", VASP_CMD) lobster_cmd = c.get("LOBSTER_CMD", LOBSTER_CMD) db_file = c.get("DB_FILE", DB_FILE) fws = [] # get the relevant potcar files! inputset = LobsterSet( structure, address_basis_file=address_max_basis, user_incar_settings=user_incar_settings, user_kpoints_settings=user_kpoints_settings, isym=isym, ) # get the basis from dict_max_basis potcar_symbols = inputset.potcar_symbols # will get all possible basis functions that have to be tested if address_max_basis is None and address_min_basis is None: list_basis_dict = Lobsterin.get_all_possible_basis_functions( structure=structure, potcar_symbols=potcar_symbols) elif address_max_basis is not None and address_min_basis is None: list_basis_dict = Lobsterin.get_all_possible_basis_functions( structure=structure, potcar_symbols=potcar_symbols, address_basis_file_max=address_max_basis, ) elif address_min_basis is not None and address_max_basis is None: list_basis_dict = Lobsterin.get_all_possible_basis_functions( structure=structure, potcar_symbols=potcar_symbols, address_basis_file_min=address_min_basis, ) elif address_min_basis is not None and address_max_basis is not None: list_basis_dict = Lobsterin.get_all_possible_basis_functions( structure=structure, potcar_symbols=potcar_symbols, address_basis_file_max=address_max_basis, address_basis_file_min=address_min_basis, ) staticfw = StaticFW( structure=structure, vasp_input_set=inputset, vasp_cmd=vasp_cmd, db_file=db_file, name="static", ) fws.append(staticfw) # append all lobster calculations that need to be done fws_lobster = [] for ibasis, basis_dict in enumerate(list_basis_dict): fws_lobster.append( LobsterFW( structure=structure, parents=staticfw, calculation_type=calculation_type, delete_wavecar=delete_all_wavecars, delete_wavecar_previous_fw=False, lobster_cmd=lobster_cmd, db_file=db_file, user_supplied_basis=basis_dict, lobsterin_key_dict=user_lobsterin_settings, handler_group="default", validator_group="strict", name="lobster_calculation_{}".format(ibasis), lobstertodb_kwargs={ "basis_id": ibasis, "number_lobster_runs": len(list_basis_dict), }, additional_outputs=additional_outputs, )) fws.extend(fws_lobster) # wavecar from static run is deleted, WAVECARs without symmetry can be huge! if delete_all_wavecars: t = DeleteFilesPrevFolder(files=["WAVECAR", "WAVECAR.gz"], calc_loc="static") final_fw = Firework([t], parents=fws_lobster, name="DelteWavecar") fws.append(final_fw) workflow = Workflow(fws, name="LobsterWorkflow") return workflow
def get_wf_lobster( structure: Structure, calculation_type: str = "standard", delete_all_wavecars: bool = True, user_lobsterin_settings: dict = None, user_incar_settings: dict = None, user_kpoints_settings: dict = None, user_supplied_basis: dict = None, isym: int = 0, c: dict = None, additional_outputs: List[str] = None, ) -> Workflow: """ Creates a workflow for a static Vasp calculation followed by a Lobster calculation. Args: structure: Structure object calculation_type: type of the Lobster calculation delete_all_wavecars: if True, all WAVECARs are deleted user_lobsterin_settings (dict): dict to set additional lobsterin settings user_incar_settings (dict): dict to set additional things in INCAR user_kpoints_settings (dict): dict to set additional things in KPOINTS user_supplied_basis (dict): dict to supply basis functions for each element type isym (int): isym setting during the vasp calculation, currently lobster can only deal with isym=-1 c (dict): configurations dict which can include "VASP_CMD", "LOBSTER_CMD", "DB_FILE" additional_outputs (list): list of additional files to be stored in the results DB. They will be stored as files in gridfs. Examples are: "ICOHPLIST.lobster" or "DOSCAR.lobster". Note that the file name should be given with the full name and the correct capitalization. Returns: Workflow """ c = c or {} vasp_cmd = c.get("VASP_CMD", VASP_CMD) lobster_cmd = c.get("LOBSTER_CMD", LOBSTER_CMD) db_file = c.get("DB_FILE", DB_FILE) fws = [] staticfw = StaticFW( structure=structure, vasp_input_set=LobsterSet( structure, user_incar_settings=user_incar_settings, user_kpoints_settings=user_kpoints_settings, isym=isym, ), vasp_cmd=vasp_cmd, db_file=db_file, ) fws.append(staticfw) fws.append( LobsterFW( structure=structure, parents=staticfw, calculation_type=calculation_type, delete_wavecar=delete_all_wavecars, delete_wavecar_previous_fw=delete_all_wavecars, lobster_cmd=lobster_cmd, db_file=db_file, lobsterin_key_dict=user_lobsterin_settings, user_supplied_basis=user_supplied_basis, handler_group="default", validator_group="strict", additional_outputs=additional_outputs, )) workflow = Workflow(fws, name="LobsterWorkflow") return workflow
def get_ion_insertion_wf( structure: Structure, working_ion: str, structure_matcher: StructureMatcher = None, db_file: str = DB_FILE, vasptodb_kwargs: dict = None, volumetric_data_type: str = "CHGCAR", vasp_powerups: List[dict] = None, attempt_insertions: int = 4, max_inserted_atoms: int = None, allow_fizzled_parents: bool = True, optimizefw_kwargs: dict = None, staticfw_kwargs: dict = None, ): """ Take the output static worflow and iteratively insert working ions based on charge density analysis. The workflow performs the following tasks. (StaticFW) <- Recieved dat inserted task_id from this workflow (AnalyzeChgcar) <- Obtain the set of possible unique insertions using the stored charge density (GetInsertionCalcs) <- This task contains the dynamic workflow creation that will keep inserting working ions Args: structure: The host structure to begin inserting on working_ion: The working ion to be inserted at each step structure_matcher: StructureMatcher object used to define topotactic insertion db_file: The db_file that defines the VASP output database vasptodb_kwargs: vasptodb_kwargs for the static workflow volumetric_data_type: the type of volumetric data used to determine the insertion sites vasp_powerups: additional powerups given to all the dynamically created workflows optimizefw_kwargs: additional kwargs for all the OptimizeFWs max_inserted_atoms: the limit on the total number of ions to insert attempt_insertions: number of insertions sites to run at each insertion step staticfw_kwargs: additional kwargs for all the StaticFWs """ if not structure.is_ordered: raise ValueError( "Please obtain an ordered approximation of the input structure.") if optimizefw_kwargs is None: optimizefw_kwargs = {} if staticfw_kwargs is None: staticfw_kwargs = {} # Configured the optimization and static FWs for the base material vasptodb_kwargs = vasptodb_kwargs if vasptodb_kwargs is not None else {} vasptodb_kwargs_vol_data = { "CHGCAR": ["CHGCAR"], "AECCAR": ["AECCAR0", "AECCAR2"] } vasptodb_kwargs.update({ "store_volumetric_data": vasptodb_kwargs_vol_data[volumetric_data_type], "task_fields_to_push": { "base_task_id": "task_id" }, }) opt_wf = OptimizeFW(structure=structure, db_file=db_file, **optimizefw_kwargs) pass_task = pass_vasp_result( filename="vasprun.xml.relax2.gz", pass_dict=">>output.ionic_steps.-1.structure", mod_spec_key="prev_calc_structure", ) opt_wf.tasks.append(pass_task) static_wf = StaticFW(structure=structure, vasptodb_kwargs=vasptodb_kwargs, db_file=db_file, parents=[opt_wf], spec_structure_key="prev_calc_structure", **staticfw_kwargs) wf_name = "{}-{}".format( structure.composition.reduced_formula if structure else "unknown", "insertion", ) # Configure the analysis FW analysis_wf = Firework( [AnalyzeChgcar(), GetInsertionCalcs()], parents=[static_wf], name="Charge Density Analysis-0", ) analysis_wf.spec["working_ion"] = working_ion # Crate the initial workflow wf = Workflow([opt_wf, static_wf, analysis_wf], name=wf_name) # Apply the vasp powerup if present if vasp_powerups is not None: wf = powerup_by_kwargs(wf, vasp_powerups) for fw in wf.fws: fw.spec["vasp_powerups"] = vasp_powerups if structure_matcher is not None: sm_dict = structure_matcher.as_dict() for fw in wf.fws: fw.spec["structure_matcher"] = sm_dict # write the persistent specs to all the fws # Note this is probably redundant but is easier for fw in wf.fws: fw.spec["db_file"] = db_file fw.spec["attempt_insertions"] = attempt_insertions fw.spec["vasptodb_kwargs"] = vasptodb_kwargs fw.spec["staticfw_kwargs"] = staticfw_kwargs fw.spec["optimizefw_kwargs"] = optimizefw_kwargs fw.spec["allow_fizzled_parents"] = allow_fizzled_parents fw.spec["volumetric_data_type"] = volumetric_data_type fw.spec["max_inserted_atoms"] = max_inserted_atoms return wf