From e6c92d6c50ffb1dbdeb6dce898fee510662b4ce3 Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 13:33:10 +0000 Subject: [PATCH 1/7] Tidy lobster workflow --- src/atomate2/lobster/jobs.py | 28 +++-- src/atomate2/lobster/run.py | 12 +- src/atomate2/lobster/schemas.py | 61 +++++----- src/atomate2/vasp/flows/lobster.py | 150 +++++++++++-------------- src/atomate2/vasp/jobs/lobster.py | 172 ++++++++++------------------- 5 files changed, 176 insertions(+), 247 deletions(-) diff --git a/src/atomate2/lobster/jobs.py b/src/atomate2/lobster/jobs.py index 93107939c5..a90ade6310 100644 --- a/src/atomate2/lobster/jobs.py +++ b/src/atomate2/lobster/jobs.py @@ -25,32 +25,30 @@ class LobsterMaker(Maker): """ LOBSTER job maker. - The maker copies the DFT output files - necessary for the LOBSTER run. - It will create all lobsterin files, run LOBSTER, - zip the outputs and parse the LOBSTER outputs. + + The maker copies DFT output files necessary for the LOBSTER run. + It will create all lobsterin files, run LOBSTER, zip the outputs and parse the + LOBSTER outputs. Parameters ---------- name : str Name of jobs produced by this maker. - resubmit : bool - Maybe useful. task_document_kwargs : dict Keyword arguments passed to :obj:`.LobsterTaskDocument.from_directory`. - user_lobsterin_settings: dict + user_lobsterin_settings : dict Dict including additional information on the Lobster settings. - additional_outputs: list[str] - A list including additional output files. - calculation_type: str - Type of calculation for the Lobster run. + run_lobster_kwargs : dict + Keyword arguments that will get passed to :obj:`.run_lobster`. + calculation_type : str + Type of calculation for the Lobster run that will get passed to + :obj:`.Lobsterin.standard_calculations_from_vasp_files`. """ name: str = "lobster" - resubmit: bool = False task_document_kwargs: dict = field(default_factory=dict) user_lobsterin_settings: dict | None = None - additional_outputs: list | None = None + run_lobster_kwargs: dict = field(default_factory=dict) calculation_type: str = "standard" @job(output_schema=LobsterTaskDocument, data=[CompleteCohp, LobsterCompleteDos]) @@ -84,9 +82,10 @@ def make( lobsterin[key] = parameter lobsterin.write_lobsterin("lobsterin") + # run lobster logger.info("Running LOBSTER") - run_lobster() + run_lobster(**self.run_lobster_kwargs) # gzip folder gzip_dir(".") @@ -95,6 +94,5 @@ def make( task_doc = LobsterTaskDocument.from_directory( Path.cwd(), **self.task_document_kwargs, - additional_fields=self.additional_outputs, ) return task_doc diff --git a/src/atomate2/lobster/run.py b/src/atomate2/lobster/run.py index 6a7bf89288..71e1b30b08 100644 --- a/src/atomate2/lobster/run.py +++ b/src/atomate2/lobster/run.py @@ -57,17 +57,17 @@ def run_lobster( Parameters ---------- - job_type: str or .JobType + job_type : str or .JobType The job type. - lobster_cmd: str + lobster_cmd : str Command to run lobster. - max_errors: + max_errors : int Maximum number of errors. - scratch_dir: str or Path + scratch_dir : str or Path Scratch directory. - validators: list of .Validator + validators : list of .Validator The validators handlers used by custodian. - lobster_job_kwargs: dict + lobster_job_kwargs : dict Keyword arguments that are passed to :obj:`.LosterJob`. custodian_kwargs : dict Keyword arguments that are passed to :obj:`.Custodian`. diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index 35f733b976..bed9da12ba 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -37,7 +37,7 @@ class LobsteroutModel(BaseModel): - """Collection to store computational settings from the LOBSTER computation.""" + """Definition of computational settings from the LOBSTER computation.""" restart_from_projection: bool = Field( None, @@ -91,7 +91,7 @@ class LobsteroutModel(BaseModel): class LobsterinModel(BaseModel): - """Collection to store input settings for the LOBSTER computation.""" + """Definition of input settings for the LOBSTER computation.""" cohpstartenergy: float = Field( None, description="Start energy for COHP computation" @@ -125,9 +125,7 @@ class LobsterinModel(BaseModel): class CondensedBondingAnalysis(BaseModel): - """Collection to store condensed bonding analysis - data from LobsterPy based on ICOHP. - """ + """Definition of condensed bonding analysis data from LobsterPy ICOHP.""" formula: str = Field(None, description="Pretty formula of the structure") max_considered_bond_length: Any = Field( @@ -185,9 +183,7 @@ class CondensedBondingAnalysis(BaseModel): class StrongestBonds(BaseModel): - """Collection to store strongest bonds extracted - from ICOHPLIST/ICOOPLIST/ICOBILIST data from LOBSTER runs. - """ + """Strongest bonds extracted from ICOHPLIST/ICOOPLIST/ICOBILIST from LOBSTER.""" which_bonds: str = Field( None, @@ -271,7 +267,8 @@ class LobsterTaskDocument(BaseModel): def from_directory( cls, dir_name: Union[Path, str], - additional_fields: list = None, + additional_fields: dict = None, + store_lso_dos: bool = False, save_cohp_plots: bool = True, plot_kwargs: dict = None, ): @@ -284,18 +281,20 @@ def from_directory( The path to the folder containing the calculation outputs. additional_fields : dict Dictionary of additional fields to add to output document. - save_cohp_plots: bool + store_lso_dos : bool + Whether to store the LSO DOS. + save_cohp_plots : bool Bool to indicate whether automatic cohp plots and jsons from lobsterpy will be generated. - plot_kwargs: dict - kwargs to change plotting options in lobsterpy + plot_kwargs : dict + kwargs to change plotting options in lobsterpy. Returns ------- LobsterTaskDocument A task document for the lobster calculation. """ - additional_fields = [] if additional_fields is None else additional_fields + additional_fields = {} if additional_fields is None else additional_fields dir_name = Path(dir_name) # do automatic analysis with lobsterpy and provide data @@ -468,14 +467,12 @@ def from_directory( # Read in LSO DOS lso_dos = None - if additional_fields and "DOSCAR.LSO.lobster" in additional_fields: - doscar_lso_path = dir_name / "DOSCAR.LSO.lobster" - - if doscar_lso_path.exists(): - doscar_lso_lobster = Doscar( - doscar=doscar_lso_path, structure_file=structure_path - ) - lso_dos = doscar_lso_lobster.completedos + doscar_lso_path = dir_name / "DOSCAR.LSO.lobster" + if store_lso_dos and doscar_lso_path.exists(): + doscar_lso_lobster = Doscar( + doscar=doscar_lso_path, structure_file=structure_path + ) + lso_dos = doscar_lso_lobster.completedos # Read in Madelung energies madelung_energies = None @@ -488,7 +485,7 @@ def from_directory( "Ewald_splitting": madelung_obj.ewald_splitting, } - return cls( + doc = cls( structure=struct, dir_name=dir_name, lobsterin=lobster_in, @@ -505,7 +502,9 @@ def from_directory( lso_dos=lso_dos, charges=charges, madelung_energies=madelung_energies, - ) # doc + ) + doc = doc.copy(update=additional_fields) + return doc @staticmethod def _identify_strongest_bonds( @@ -518,13 +517,13 @@ def _identify_strongest_bonds( Parameters ---------- - analyse: .Analysis + analyse : .Analysis Analysis object from lobsterpy automatic analysis - icobilist_path: Path or str + icobilist_path : Path or str Path to ICOBILIST.lobster - icohplist_path: Path or str + icohplist_path : Path or str Path to ICOHPLIST.lobster - icooplist_path: Path or str + icooplist_path : Path or str Path to ICOOPLIST.lobster Returns @@ -610,13 +609,13 @@ def _get_strng_bonds( Parameters ---------- - bondlist: dict + bondlist : dict dict including bonding information - are_cobis: bool + are_cobis : bool True if these are cobis - are_coops: bool + are_coops : bool True if these are coops - relevant_bonds: dict + relevant_bonds : dict Dict include all bonds that are considered. Returns diff --git a/src/atomate2/vasp/flows/lobster.py b/src/atomate2/vasp/flows/lobster.py index 5d53284311..5b59420a85 100644 --- a/src/atomate2/vasp/flows/lobster.py +++ b/src/atomate2/vasp/flows/lobster.py @@ -29,45 +29,48 @@ class VaspLobsterMaker(Maker): """ Maker to perform a Lobster computation. - Optional optimization. - Optional static computation with symmetry - to preconverge the wavefunction. - Static computation with ISYM=0 is performed - Several Lobster computations testing several basis sets are performed. - The basis sets can only be changed with yaml files. + The calculations performed are: + + 1. Optional optimization. + 2. Optional static computation with symmetry to preconverge the wavefunction. + 3. Static calculation with ISYM=0. + 4. Several Lobster computations testing several basis sets are performed. + + .. Note:: + + The basis sets can only be changed with yaml files. Parameters ---------- name : str Name of the flows produced by this maker. - bulk_relax_maker : .BaseVaspMaker or None - A maker to perform a relaxation on the bulk. - Set to ``None`` to skip the - bulk relaxation - additional_static_run_maker: .BaseVaspMaker or None - A maker to perform a preconvergence run - before the wavefunction computation without symmetry + relax_maker : .BaseVaspMaker or None + A maker to perform a relaxation on the bulk. Set to ``None`` to skip the + bulk relaxation. + preconverge_static_maker: .BaseVaspMaker or None + A maker to perform a preconvergence run before the wavefunction computation + without symmetry lobster_static_maker : .BaseVaspMaker A maker to perform the computation of the wavefunction before the static run. Cannot be skipped. - lobster_maker: .LobsterMaker + lobster_maker : .LobsterMaker A maker to perform the Lobster run. - delete_all_wavecars: bool - if true, all WAVECARs will be deleated after the run - address_min_basis: str - yaml file including basis set information + delete_all_wavecars : bool + If true, all WAVECARs will be deleted after the run. + address_min_basis : str + A path to a yaml file including basis set information. address_max_basis: str - yaml file including basis set information + A path to a yaml file including basis set information. """ name: str = "lobster" - bulk_relax_maker: BaseVaspMaker | None = field( + relax_maker: BaseVaspMaker | None = field( default_factory=lambda: DoubleRelaxMaker.from_relax_maker(RelaxMaker()) ) lobster_static_maker: BaseVaspMaker = field( default_factory=lambda: LobsterStaticMaker() ) - additional_static_run_maker: BaseVaspMaker | None = field( + preconverge_static_maker: BaseVaspMaker | None = field( default_factory=lambda: StaticMaker( input_set_generator=StaticSetGenerator( user_incar_settings={"LWAVE": True}, @@ -96,41 +99,31 @@ def make( have very strict settings! prev_vasp_dir : str or Path or None A previous vasp calculation directory to use for copying outputs. - """ jobs = [] - # do a relaxation step first - if self.bulk_relax_maker is not None: - # optionally relax the structure - bulk = self.bulk_relax_maker.make(structure, prev_vasp_dir=prev_vasp_dir) - jobs.append(bulk) - structure = bulk.output.structure - optimization_run_job_dir = bulk.output.dir_name - optimization_run_uuid = bulk.output.uuid - else: - optimization_run_job_dir = None - optimization_run_uuid = None - - # do a static WAVECAR computation with symmetry - # and standard number of bands first - # this preconverges the WAVECAR - if self.additional_static_run_maker is not None: - preconvergence_job = self.additional_static_run_maker.make( - structure=structure - ) - preconvergence_job.append_name(" preconvergence") - jobs.append(preconvergence_job) - prev_vasp_dir = preconvergence_job.output.dir_name - additional_static_run_job_dir = preconvergence_job.output.dir_name - additional_static_run_uuid = preconvergence_job.output.uuid - else: - if optimization_run_job_dir is not None: - prev_vasp_dir = optimization_run_job_dir - else: - prev_vasp_dir = None - additional_static_run_job_dir = None - additional_static_run_uuid = None + # optionally relax the structure + optimization_dir = None + optimization_uuid = None + if self.relax_maker is not None: + optimization = self.relax_maker.make(structure, prev_vasp_dir=prev_vasp_dir) + jobs.append(optimization) + structure = optimization.output.structure + optimization_dir = optimization.output.dir_name + optimization_uuid = optimization.output.uuid + prev_vasp_dir = optimization_dir + + # do a static WAVECAR computation with symmetry and standard number of bands + # first to preconverge the WAVECAR + preconverge_static_dir = None + preconverge_static_uuid = None + if self.preconverge_static_maker is not None: + preconverge = self.preconverge_static_maker.make(structure) + preconverge.append_name(" preconverge") + jobs.append(preconverge) + preconverge_static_dir = preconverge.output.dir_name + preconverge_static_uuid = preconverge.output.uuid + prev_vasp_dir = preconverge.output.dir_name # Information about the basis is collected basis_infos = get_basis_infos( @@ -141,49 +134,38 @@ def make( ) jobs.append(basis_infos) - # Maker needs to be updated here. If the job itself is updated, - # no further updates on the job are possible - vaspjob = update_user_incar_settings_maker( + # Maker needs to be updated here. If the job itself is updated, no further + # updates on the job are possible + lobster_static = update_user_incar_settings_maker( self.lobster_static_maker, basis_infos.output["nbands"], structure, prev_vasp_dir, ) + jobs.append(lobster_static) + lobster_static_dir = lobster_static.output.dir_name + lobster_static_uuid = lobster_static.output.uuid - jobs.append(vaspjob) - - static_run_job_dir = vaspjob.output.dir_name - static_run_uuid = vaspjob.output.uuid - - lobsterjobs = get_lobster_jobs( + lobster_jobs = get_lobster_jobs( lobster_maker=self.lobster_maker, basis_dict=basis_infos.output["basis_dict"], - wavefunction_dir=vaspjob.output.dir_name, - optimization_run_job_dir=optimization_run_job_dir, - optimization_run_uuid=optimization_run_uuid, - static_run_job_dir=static_run_job_dir, - static_run_uuid=static_run_uuid, - additional_static_run_job_dir=additional_static_run_job_dir, - additional_static_run_uuid=additional_static_run_uuid, + wavefunction_dir=lobster_static.output.dir_name, + optimization_dir=optimization_dir, + optimization_uuid=optimization_uuid, + static_dir=lobster_static_dir, + static_uuid=lobster_static_uuid, + preconverge_static_dir=preconverge_static_dir, + preconverge_static_uuid=preconverge_static_uuid, ) + jobs.append(lobster_jobs) - jobs.append(lobsterjobs) - - # will delete all WAVECARs that have been copied + # delete all WAVECARs that have been copied if self.delete_all_wavecars: - vasp_stat = vaspjob.output.dir_name - if self.additional_static_run_maker is not None: - vasp_add_stat = preconvergence_job.output.dir_name - if self.additional_static_run_maker is None: - vasp_add_stat = None - delete_wavecars = delete_lobster_wavecar( - dirs=lobsterjobs.output["lobster_dirs"], - dir_vasp=vasp_stat, - dir_preconverge=vasp_add_stat, + dirs=lobster_jobs.output["lobster_dirs"], + lobster_static_dir=lobster_static.output.dir_name, + preconverge_static_dir=preconverge_static_dir, ) - jobs.append(delete_wavecars) - flow = Flow(jobs, output=lobsterjobs.output) - return flow + return Flow(jobs, output=lobster_jobs.output) diff --git a/src/atomate2/vasp/jobs/lobster.py b/src/atomate2/vasp/jobs/lobster.py index a8c10b84df..09a0501686 100644 --- a/src/atomate2/vasp/jobs/lobster.py +++ b/src/atomate2/vasp/jobs/lobster.py @@ -5,6 +5,7 @@ import logging from dataclasses import dataclass, field from pathlib import Path +from typing import Any from jobflow import Flow, Response, job from pymatgen.core import Structure @@ -18,14 +19,16 @@ from atomate2.vasp.sets.base import VaspInputGenerator from atomate2.vasp.sets.core import StaticSetGenerator -__all__ = ["LobsterStaticMaker", "get_basis_infos", "get_lobster_jobs"] +__all__ = [ + "LobsterStaticMaker", + "get_basis_infos", + "get_lobster_jobs", + "delete_lobster_wavecar", +] logger = logging.getLogger(__name__) -# include a class where we can also add Lobster - - @dataclass class LobsterStaticMaker(BaseVaspMaker): """ @@ -110,31 +113,10 @@ def get_basis_infos( structure=structure, potcar_spec=True ) - # get data from Lobsterinput - # Lobster - 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, - ) + # get data from LobsterInput + list_basis_dict = Lobsterin.get_all_possible_basis_functions( + structure=structure, potcar_symbols=potcar_symbols + ) nband_list = [] for dict_for_basis in list_basis_dict: @@ -158,14 +140,14 @@ def update_user_incar_settings_maker( Parameters ---------- - vasp_maker: .BaseVaspMaker + vasp_maker : .BaseVaspMaker A maker for the static run with all parammeters relevant for Lobster. - nbands: int + nbands : int integer indicating the correct number of bands - structure: .Structure + structure : .Structure Structure object. - prev_vasp_dir: Path or str + prev_vasp_dir : Path or str Path or string to vasp files. Returns @@ -174,45 +156,44 @@ def update_user_incar_settings_maker( LobsterStaticMaker with correct number of bands. """ vasp_maker = update_user_incar_settings(vasp_maker, {"NBANDS": nbands}) - vaspjob = vasp_maker.make(structure=structure, prev_vasp_dir=prev_vasp_dir) - - return Response(replace=vaspjob) + vasp_job = vasp_maker.make(structure=structure, prev_vasp_dir=prev_vasp_dir) + return Response(replace=vasp_job) @job def get_lobster_jobs( - lobster_maker, - basis_dict, - wavefunction_dir, - optimization_run_job_dir, - optimization_run_uuid, - static_run_job_dir, - static_run_uuid, - additional_static_run_job_dir, - additional_static_run_uuid, + lobster_maker: LobsterMaker, + basis_dict: dict, + wavefunction_dir: Path | str, + optimization_dir: Path | str, + optimization_uuid: str, + static_dir: Path | str, + static_uuid: str, + preconverge_static_dir: Path | str, + preconverge_static_uuid: str, ): """ Create a list of Lobster jobs with different basis sets. Parameters ---------- - lobster_maker: .LobsterMaker + lobster_maker : .LobsterMaker maker for the Lobster jobs - basis_dict: dict + basis_dict : dict dict including basis set information. - wavefunction_dir: Path or str + wavefunction_dir : Path or str Path to VASP calculation with WAVECAR - optimization_run_job_dir: Path or str + optimization_dir : Path or str Path to optimization run. - optimization_run_uuid: str + optimization_uuid : str uuid of optimization run. - static_run_job_dir: Path or str + static_dir : Path or str Path to static VASP calculation. - static_run_uuid: str + static_uuid : str uuid of static run. - additional_static_run_job_dir: Path or str + preconverge_static_dir : Path or str Path to preconvergence step. - additional_static_run_uuid: str + preconverge_static_uuid : str uuid of preconvergence step. Returns @@ -221,16 +202,17 @@ def get_lobster_jobs( List of Lobster jobs. """ jobs = [] - outputs = {} - outputs["optimization_run_job_dir"] = optimization_run_job_dir - outputs["optimization_run_uuid"] = optimization_run_uuid - outputs["static_run_job_dir"] = static_run_job_dir - outputs["static_run_uuid"] = static_run_uuid - outputs["additional_static_run_dir"] = additional_static_run_job_dir - outputs["additional_static_uuid"] = additional_static_run_uuid - outputs["lobster_uuids"] = [] - outputs["lobster_dirs"] = [] - outputs["lobster_task_documents"] = [] + outputs: dict[str, Any] = { + "optimization_dir": optimization_dir, + "optimization_uuid": optimization_uuid, + "static_dir": static_dir, + "static_uuid": static_uuid, + "preconverge_static_dir": preconverge_static_dir, + "preconverge_static_uuid": preconverge_static_uuid, + "lobster_uuids": [], + "lobster_dirs": [], + "lobster_task_documents": [], + } if lobster_maker is None: lobster_maker = LobsterMaker() @@ -251,62 +233,30 @@ def get_lobster_jobs( @job def delete_lobster_wavecar( - dirs: list, dir_vasp: Path | str = None, dir_preconverge: Path | str = None + dirs: list[Path | str], + lobster_static_dir: Path | str = None, + preconverge_static_dir: Path | str = None, ): """ Delete all WAVECARs. Parameters ---------- - dirs: List[Path|str] + dirs : list of path or str Path to directories of lobster jobs. - dir_vasp: Path or str + lobster_static_dir : Path or str Path to directory of static VASP run. - dir_preconverge: Path or str + preconverge_static_dir : Path or str Path to directory of preconvergence run. """ - jobs = [] - outputs: dict = {} - outputs["lobster_dir_name"] = [] - outputs["add_static_dir_name"] = [] - outputs["static_dir_name"] = [] - dec_delete_files = job(delete_files) + if lobster_static_dir: + dirs.append(lobster_static_dir) + + if preconverge_static_dir: + dirs.append(preconverge_static_dir) + for dir_name in dirs: - jobs.append( - dec_delete_files( - dir_name, include_files=["WAVECAR", "WAVECAR.gz"], allow_missing=True - ) - ) - outputs["lobster_dir_name"].append(dir_name) - if dir_preconverge is None and dir_vasp is not None: - dir_vasp_stat = strip_hostname(dir_vasp) - jobs.append( - dec_delete_files( - dir_vasp_stat, - include_files=["WAVECAR", "WAVECAR.gz"], - allow_missing=True, - ) - ) - outputs["static_dir_name"].append(dir_vasp_stat) - if dir_preconverge is not None and dir_vasp is not None: - dir_vasp_add_stat = strip_hostname(dir_preconverge) - dir_vasp_stat = strip_hostname(dir_vasp) - jobs.append( - dec_delete_files( - dir_vasp_stat, - include_files=["WAVECAR", "WAVECAR.gz"], - allow_missing=True, - ) + dir_name = strip_hostname(dir_name) + delete_files( + dir_name, include_files=["WAVECAR", "WAVECAR.gz"], allow_missing=True ) - jobs.append( - dec_delete_files( - dir_vasp_add_stat, - include_files=["WAVECAR", "WAVECAR.gz"], - allow_missing=True, - ) - ) - outputs["add_static_dir_name"].append(dir_vasp_add_stat) - outputs["static_dir_name"].append(dir_vasp_stat) - - flow = Flow(jobs, output=outputs) - return Response(replace=flow) From bf90b828b086d67b4724e06bbcf0d17669303c85 Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 13:41:40 +0000 Subject: [PATCH 2/7] Re-enable lobster basis --- src/atomate2/vasp/jobs/lobster.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/atomate2/vasp/jobs/lobster.py b/src/atomate2/vasp/jobs/lobster.py index 09a0501686..957ecdd9e5 100644 --- a/src/atomate2/vasp/jobs/lobster.py +++ b/src/atomate2/vasp/jobs/lobster.py @@ -115,7 +115,10 @@ def get_basis_infos( # get data from LobsterInput list_basis_dict = Lobsterin.get_all_possible_basis_functions( - structure=structure, potcar_symbols=potcar_symbols + structure=structure, + potcar_symbols=potcar_symbols, + address_basis_file_max=address_max_basis, + address_basis_file_min=address_min_basis, ) nband_list = [] From abfa440ec3bda18affe0e6913d06dfe082ce17f5 Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 13:57:08 +0000 Subject: [PATCH 3/7] Fix tests --- src/atomate2/lobster/schemas.py | 98 ++++++------------ src/atomate2/vasp/schemas/lobster.py | 0 .../inputs/INCAR | 0 .../inputs/KPOINTS | 0 .../inputs/POSCAR | 0 .../inputs/POTCAR.spec | 0 .../outputs/CONTCAR.gz | Bin .../outputs/INCAR.gz | Bin .../outputs/INCAR.orig.gz | Bin .../outputs/KPOINTS.gz | Bin .../outputs/KPOINTS.orig.gz | Bin .../outputs/OUTCAR.gz | Bin .../outputs/POSCAR.gz | Bin .../outputs/POSCAR.orig.gz | Bin .../outputs/POTCAR.spec | 0 .../outputs/WAVECAR.gz | Bin .../outputs/custodian.json.gz | Bin .../outputs/vasprun.xml.gz | Bin .../{test_lobsterwf.py => test_lobster.py} | 8 +- 19 files changed, 34 insertions(+), 72 deletions(-) delete mode 100644 src/atomate2/vasp/schemas/lobster.py rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/inputs/INCAR (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/inputs/KPOINTS (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/inputs/POSCAR (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/inputs/POTCAR.spec (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/CONTCAR.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/INCAR.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/INCAR.orig.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/KPOINTS.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/KPOINTS.orig.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/OUTCAR.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/POSCAR.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/POSCAR.orig.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/POTCAR.spec (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/WAVECAR.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/custodian.json.gz (100%) rename tests/test_data/vasp/Si_lobster/{preconvergence_run => static_preconverge}/outputs/vasprun.xml.gz (100%) rename tests/vasp/lobster/flows/{test_lobsterwf.py => test_lobster.py} (96%) diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index bed9da12ba..adb12b7730 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -531,74 +531,36 @@ def _identify_strongest_bonds( Tuple[StrongestBonds] Tuple of StrongestBonds """ - if icohplist_path.exists(): - icohplist = Icohplist( - filename=icohplist_path, - are_cobis=False, - are_coops=False, - ) - icohp_dict = icohplist.icohpcollection.as_dict() - icohp_bond_dict = LobsterTaskDocument._get_strng_bonds( - icohp_dict, - are_cobis=False, - are_coops=False, - relevant_bonds=analyse.final_dict_bonds, - ) - sb_icohp = StrongestBonds( - are_cobis=False, - are_coops=False, - strongest_bonds=icohp_bond_dict, - which_bonds=analyse.whichbonds, - ) - else: - sb_icohp = None - if icobilist_path.exists(): - icobilist = Icohplist( - filename=icobilist_path, - are_cobis=True, - are_coops=False, - ) - icobi_dict = icobilist.icohpcollection.as_dict() - icobi_bond_dict = LobsterTaskDocument._get_strng_bonds( - icobi_dict, - are_cobis=True, - are_coops=False, - relevant_bonds=analyse.final_dict_bonds, - ) - sb_icobi = StrongestBonds( - are_cobis=True, - are_coops=False, - strongest_bonds=icobi_bond_dict, - which_bonds=analyse.whichbonds, - ) - else: - - sb_icobi = None - if icooplist_path.exists(): - icooplist = Icohplist( - filename=icooplist_path, - are_coops=True, - are_cobis=False, - ) - icoop_dict = icooplist.icohpcollection.as_dict() - - icoop_bond_dict = LobsterTaskDocument._get_strng_bonds( - icoop_dict, - are_cobis=False, - are_coops=True, - relevant_bonds=analyse.final_dict_bonds, - ) - - sb_icoop = StrongestBonds( - are_cobis=False, - are_coops=True, - strongest_bonds=icoop_bond_dict, - which_bonds=analyse.whichbonds, - ) - - else: - sb_icoop = None - return sb_icobi, sb_icohp, sb_icoop + data = [ + (icohplist_path, False, False), + (icobilist_path, True, False), + (icooplist_path, False, True), + ] + output = [] + for file, are_cobis, are_coops in data: + if file.exists(): + icohplist = Icohplist( + filename=icohplist_path, + are_cobis=are_cobis, + are_coops=are_coops, + ) + bond_dict = LobsterTaskDocument._get_strng_bonds( + icohplist.icohpcollection.as_dict(), + relevant_bonds=analyse.final_dict_bonds, + are_cobis=are_cobis, + are_coops=are_coops, + ) + output.append( + StrongestBonds( + are_cobis=are_cobis, + are_coops=are_coops, + strongest_bonds=bond_dict, + which_bonds=analyse.whichbonds, + ) + ) + else: + output.append(None) + return output @staticmethod def _get_strng_bonds( diff --git a/src/atomate2/vasp/schemas/lobster.py b/src/atomate2/vasp/schemas/lobster.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/INCAR b/tests/test_data/vasp/Si_lobster/static_preconverge/inputs/INCAR similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/INCAR rename to tests/test_data/vasp/Si_lobster/static_preconverge/inputs/INCAR diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/KPOINTS b/tests/test_data/vasp/Si_lobster/static_preconverge/inputs/KPOINTS similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/KPOINTS rename to tests/test_data/vasp/Si_lobster/static_preconverge/inputs/KPOINTS diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/POSCAR b/tests/test_data/vasp/Si_lobster/static_preconverge/inputs/POSCAR similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/POSCAR rename to tests/test_data/vasp/Si_lobster/static_preconverge/inputs/POSCAR diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/POTCAR.spec b/tests/test_data/vasp/Si_lobster/static_preconverge/inputs/POTCAR.spec similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/inputs/POTCAR.spec rename to tests/test_data/vasp/Si_lobster/static_preconverge/inputs/POTCAR.spec diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/CONTCAR.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/CONTCAR.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/CONTCAR.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/CONTCAR.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/INCAR.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/INCAR.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/INCAR.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/INCAR.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/INCAR.orig.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/INCAR.orig.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/INCAR.orig.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/INCAR.orig.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/KPOINTS.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/KPOINTS.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/KPOINTS.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/KPOINTS.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/KPOINTS.orig.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/KPOINTS.orig.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/KPOINTS.orig.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/KPOINTS.orig.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/OUTCAR.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/OUTCAR.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/OUTCAR.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/OUTCAR.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/POSCAR.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/POSCAR.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/POSCAR.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/POSCAR.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/POSCAR.orig.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/POSCAR.orig.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/POSCAR.orig.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/POSCAR.orig.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/POTCAR.spec b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/POTCAR.spec similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/POTCAR.spec rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/POTCAR.spec diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/WAVECAR.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/WAVECAR.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/WAVECAR.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/WAVECAR.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/custodian.json.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/custodian.json.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/custodian.json.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/custodian.json.gz diff --git a/tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/vasprun.xml.gz b/tests/test_data/vasp/Si_lobster/static_preconverge/outputs/vasprun.xml.gz similarity index 100% rename from tests/test_data/vasp/Si_lobster/preconvergence_run/outputs/vasprun.xml.gz rename to tests/test_data/vasp/Si_lobster/static_preconverge/outputs/vasprun.xml.gz diff --git a/tests/vasp/lobster/flows/test_lobsterwf.py b/tests/vasp/lobster/flows/test_lobster.py similarity index 96% rename from tests/vasp/lobster/flows/test_lobsterwf.py rename to tests/vasp/lobster/flows/test_lobster.py index 636c762ba0..028df363e4 100644 --- a/tests/vasp/lobster/flows/test_lobsterwf.py +++ b/tests/vasp/lobster/flows/test_lobster.py @@ -12,7 +12,7 @@ def test_lobstermaker(mock_vasp, mock_lobster, clean_dir, memory_jobstore): # mapping from job name to directory containing test files ref_paths = { - "static preconvergence": "Si_lobster/preconvergence_run", + "static preconverge": "Si_lobster/static_preconverge", "relax 1": "Si_lobster/relax_1", "relax 2": "Si_lobster/relax_2", "static_run": "Si_lobster/static_run", @@ -22,7 +22,7 @@ def test_lobstermaker(mock_vasp, mock_lobster, clean_dir, memory_jobstore): fake_run_vasp_kwargs = { "relax 1": {"incar_settings": ["NSW", "ISMEAR"]}, "relax 2": {"incar_settings": ["NSW", "ISMEAR"]}, - "static preconvergence": { + "static preconverge": { "incar_settings": ["NSW", "ISMEAR", "LWAVE", "ISYM"], }, "static_run": { @@ -77,7 +77,7 @@ def test_lobstermaker(mock_vasp, mock_lobster, clean_dir, memory_jobstore): def test_lobstermaker_delete(mock_vasp, mock_lobster, clean_dir, memory_jobstore): # mapping from job name to directory containing test files ref_paths = { - "static preconvergence": "Si_lobster/preconvergence_run", + "static preconverge": "Si_lobster/static_preconverge", "relax 1": "Si_lobster/relax_1", "relax 2": "Si_lobster/relax_2", "static_run": "Si_lobster/static_run", @@ -87,7 +87,7 @@ def test_lobstermaker_delete(mock_vasp, mock_lobster, clean_dir, memory_jobstore fake_run_vasp_kwargs = { "relax 1": {"incar_settings": ["NSW", "ISMEAR"]}, "relax 2": {"incar_settings": ["NSW", "ISMEAR"]}, - "preconvergence run": { + "static preconverge": { "incar_settings": ["NSW", "ISMEAR", "LWAVE", "ISYM"], }, "static_run": { From 39958600467d8d9e6d8c20a8b90165366bfe540d Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 14:02:51 +0000 Subject: [PATCH 4/7] Pass prev vasp dir --- src/atomate2/vasp/flows/lobster.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/atomate2/vasp/flows/lobster.py b/src/atomate2/vasp/flows/lobster.py index 5b59420a85..599b9e9210 100644 --- a/src/atomate2/vasp/flows/lobster.py +++ b/src/atomate2/vasp/flows/lobster.py @@ -118,7 +118,9 @@ def make( preconverge_static_dir = None preconverge_static_uuid = None if self.preconverge_static_maker is not None: - preconverge = self.preconverge_static_maker.make(structure) + preconverge = self.preconverge_static_maker.make( + structure, prev_vasp_dir=prev_vasp_dir + ) preconverge.append_name(" preconverge") jobs.append(preconverge) preconverge_static_dir = preconverge.output.dir_name From 93825f56e693ae754305811e1c31cf26f4e723e5 Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 15:19:52 +0000 Subject: [PATCH 5/7] Tidy and fix bugs in schema --- src/atomate2/lobster/schemas.py | 566 +++++++++++++++++--------------- 1 file changed, 302 insertions(+), 264 deletions(-) diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index adb12b7730..16e87b6e10 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -181,6 +181,134 @@ class CondensedBondingAnalysis(BaseModel): None, description="Time needed to run Lobsterpy condensed bonding analysis" ) + @classmethod + def from_directory( + cls, + dir_name: Union[str, Path], + save_cohp_plots: bool = True, + plot_kwargs: dict = None, + ): + """ + Create a task document from a directory containing LOBSTER files. + + Parameters + ---------- + dir_name : path or str + The path to the folder containing the calculation outputs. + save_cohp_plots : bool + Bool to indicate whether automatic cohp plots and jsons + from lobsterpy will be generated. + plot_kwargs : dict + kwargs to change plotting options in lobsterpy. + """ + plot_kwargs = {} if plot_kwargs is None else plot_kwargs + dir_name = Path(dir_name) + + cohpcar_path = dir_name / "COHPCAR.lobster.gz" + charge_path = dir_name / "CHARGE.lobster.gz" + structure_path = dir_name / "POSCAR.gz" + icohplist_path = dir_name / "ICOHPLIST.lobster.gz" + icobilist_path = dir_name / "ICOBILIST.lobster.gz" + icooplist_path = dir_name / "ICOOPLIST.lobster.gz" + + try: + # cation anion-mode + start = time.time() + analyse = Analysis( + path_to_poscar=structure_path, + path_to_icohplist=icohplist_path, + path_to_cohpcar=cohpcar_path, + path_to_charge=charge_path, + summed_spins=True, + cutoff_icohp=0.10, + whichbonds="cation-anion", + ) + cba_run_time = time.time() - start + except ValueError: + # all bonds + start = time.time() + analyse = Analysis( + path_to_poscar=structure_path, + path_to_icohplist=icohplist_path, + path_to_cohpcar=cohpcar_path, + path_to_charge=charge_path, + summed_spins=True, + cutoff_icohp=0.10, + whichbonds="all", + ) + cba_run_time = time.time() - start + + # initialize lobsterpy condensed bonding analysis + cba = analyse.condensed_bonding_analysis + cba_cohp_plot_data = {} # Initialize dict to store plot data + + set_cohps = analyse.set_cohps + set_labels_cohps = analyse.set_labels_cohps + set_inequivalent_cations = analyse.set_inequivalent_ions + struct = analyse.structure + for _iplot, (ication, labels, cohps) in enumerate( + zip(set_inequivalent_cations, set_labels_cohps, set_cohps) + ): + label_str = f"{str(struct[ication].specie)}{str(ication + 1)}: " + for label, cohp in zip(labels, cohps): + if label is not None: + cba_cohp_plot_data.update( + { + label_str + + label: { + "COHP": list(cohp.get_cohp()[Spin.up]), + "ICOHP": list(cohp.get_icohp()[Spin.up]), + "Energies": list(cohp.energies), + "Efermi": cohp.efermi, + } + } + ) + + describe = Description(analysis_object=analyse) + + condensed_bonding_analysis = CondensedBondingAnalysis( + formula=cba["formula"], + max_considered_bond_length=cba["max_considered_bond_length"], + limit_icohp=cba["limit_icohp"], + number_of_considered_ions=cba["number_of_considered_ions"], + sites=cba["sites"], + type_charges=analyse.type_charge, + cohp_plot_data=cba_cohp_plot_data, + cutoff_icohp=analyse.cutoff_icohp, + summed_spins=True, + which_bonds=analyse.whichbonds, + final_dict_bonds=analyse.final_dict_bonds, + final_dict_ions=analyse.final_dict_ions, + run_time=cba_run_time, + ) + if save_cohp_plots: + describe.plot_cohps( + save=True, + filename="automatic_cohp_plots.pdf", + skip_show=True, + **plot_kwargs, + ) + import json + + with open(dir_name / "condensed_bonding_analysis.json", "w") as fp: + json.dump(analyse.condensed_bonding_analysis, fp) + with open(dir_name / "condensed_bonding_analysis.txt", "w") as fp: + for line in describe.text: + fp.write(line + "\n") + + # Read in strongest icohp values + sb_icobi, sb_icohp, sb_icoop = _identify_strongest_bonds( + analyse, icobilist_path, icohplist_path, icooplist_path + ) + return ( + condensed_bonding_analysis, + struct, + describe, + sb_icobi, + sb_icohp, + sb_icoop, + ) + class StrongestBonds(BaseModel): """Strongest bonds extracted from ICOHPLIST/ICOOPLIST/ICOBILIST from LOBSTER.""" @@ -296,19 +424,13 @@ def from_directory( """ additional_fields = {} if additional_fields is None else additional_fields dir_name = Path(dir_name) - # do automatic analysis with lobsterpy and provide data - - lobsterout_here = Lobsterout(dir_name / "lobsterout.gz") - lobsterout_doc = lobsterout_here.get_doc() - lobsterin_here = Lobsterin.from_file(dir_name / "lobsterin.gz") # Read in lobsterout and lobsterin + lobsterout_doc = Lobsterout(dir_name / "lobsterout.gz").get_doc() lobster_out = LobsteroutModel(**lobsterout_doc) - lobster_in = LobsterinModel(**lobsterin_here) + lobster_in = LobsterinModel(**Lobsterin.from_file(dir_name / "lobsterin.gz")) icohplist_path = dir_name / "ICOHPLIST.lobster.gz" - icobilist_path = dir_name / "ICOBILIST.lobster.gz" - icooplist_path = dir_name / "ICOOPLIST.lobster.gz" cohpcar_path = dir_name / "COHPCAR.lobster.gz" cobicar_path = dir_name / "COBICAR.lobster.gz" coopcar_path = dir_name / "COOPCAR.lobster.gz" @@ -319,115 +441,28 @@ def from_directory( # Do automatic bonding analysis with LobsterPy condensed_bonding_analysis_data = None + sb_icobi = None + sb_icohp = None + sb_icoop = None + struct = None + describe = None if icohplist_path.exists() and cohpcar_path.exists() and charge_path.exists(): - try: - # cation anion-mode - - start = time.time() - analyse = Analysis( - path_to_poscar=structure_path, - path_to_icohplist=icohplist_path, - path_to_cohpcar=cohpcar_path, - path_to_charge=charge_path, - summed_spins=True, - cutoff_icohp=0.10, - whichbonds="cation-anion", - ) - - cba_run_time = time.time() - start - except ValueError: - # all bonds - start = time.time() - analyse = Analysis( - path_to_poscar=structure_path, - path_to_icohplist=icohplist_path, - path_to_cohpcar=cohpcar_path, - path_to_charge=charge_path, - summed_spins=True, - cutoff_icohp=0.10, - whichbonds="all", - ) - - cba_run_time = time.time() - start - - cba = ( - analyse.condensed_bonding_analysis - ) # initialize lobsterpy condensed bonding analysis - cba_cohp_plot_data = {} # Initialize dict to store plot data - - set_cohps = analyse.set_cohps - set_labels_cohps = analyse.set_labels_cohps - set_inequivalent_cations = analyse.set_inequivalent_ions - struct = analyse.structure - for _iplot, (ication, labels, cohps) in enumerate( - zip(set_inequivalent_cations, set_labels_cohps, set_cohps) - ): - namecation = str(struct[ication].specie) - for label, cohp in zip(labels, cohps): - if label is not None: - cba_cohp_plot_data.update( - { - namecation - + str(ication + 1) - + ": " - + label: { - "COHP": list(cohp.get_cohp()[Spin.up]), - "ICOHP": list(cohp.get_icohp()[Spin.up]), - "Energies": list(cohp.energies), - "Efermi": cohp.efermi, - } - } - ) - - describe = Description(analysis_object=analyse) - - # TODO: add automatic plots from lobsterpy - condensed_bonding_analysis_data = CondensedBondingAnalysis( - formula=cba["formula"], - max_considered_bond_length=cba["max_considered_bond_length"], - limit_icohp=cba["limit_icohp"], - number_of_considered_ions=cba["number_of_considered_ions"], - sites=cba["sites"], - type_charges=analyse.type_charge, - cohp_plot_data=cba_cohp_plot_data, - cutoff_icohp=analyse.cutoff_icohp, - summed_spins=True, - which_bonds=analyse.whichbonds, - final_dict_bonds=analyse.final_dict_bonds, - final_dict_ions=analyse.final_dict_ions, - run_time=cba_run_time, - ) - if save_cohp_plots: - if plot_kwargs is None: - describe.plot_cohps( - save=True, filename="automatic_cohp_plots.pdf", skip_show=True - ) - else: - describe.plot_cohps( - save=True, - filename="automatic_cohp_plots.pdf", - skip_show=True, - **plot_kwargs - ) - import json - - with open(dir_name / "condensed_bonding_analysis.json", "w") as fp: - json.dump(analyse.condensed_bonding_analysis, fp) - with open(dir_name / "condensed_bonding_analysis.txt", "w") as fp: - for line in describe.text: - fp.write(line + "\n") - - # Read in strongest icohp values - sb_icobi, sb_icohp, sb_icoop = cls._identify_strongest_bonds( - analyse, icobilist_path, icohplist_path, icooplist_path + ( + condensed_bonding_analysis, + struct, + describe, + sb_icobi, + sb_icohp, + sb_icoop, + ) = CondensedBondingAnalysis.from_directory( + dir_name, save_cohp_plots=save_cohp_plots, plot_kwargs=plot_kwargs ) # Read in charges + charges = None if charge_path.exists(): charge = Charge(charge_path) charges = {"Mulliken": charge.Mulliken, "Loewdin": charge.Loewdin} - else: - charges = None # Read in COHP, COBI, COOP plots cohp_obj = None @@ -439,6 +474,7 @@ def from_directory( are_coops=False, are_cobis=False, ) + coop_obj = None if coopcar_path.exists(): coop_obj = CompleteCohp.from_file( @@ -448,6 +484,7 @@ def from_directory( are_coops=True, are_cobis=False, ) + cobi_obj = None if cobicar_path.exists(): cobi_obj = CompleteCohp.from_file( @@ -457,6 +494,7 @@ def from_directory( are_coops=False, are_cobis=True, ) + # Read in DOS dos = None if doscar_path.exists(): @@ -506,165 +544,165 @@ def from_directory( doc = doc.copy(update=additional_fields) return doc - @staticmethod - def _identify_strongest_bonds( - analyse: Analysis, - icobilist_path: Path, - icohplist_path: Path, - icooplist_path: Path, - ): - """ - Parameters - ---------- - analyse : .Analysis - Analysis object from lobsterpy automatic analysis - icobilist_path : Path or str - Path to ICOBILIST.lobster - icohplist_path : Path or str - Path to ICOHPLIST.lobster - icooplist_path : Path or str - Path to ICOOPLIST.lobster - - Returns - ------- - Tuple[StrongestBonds] - Tuple of StrongestBonds - """ - data = [ - (icohplist_path, False, False), - (icobilist_path, True, False), - (icooplist_path, False, True), - ] - output = [] - for file, are_cobis, are_coops in data: - if file.exists(): - icohplist = Icohplist( - filename=icohplist_path, - are_cobis=are_cobis, - are_coops=are_coops, - ) - bond_dict = LobsterTaskDocument._get_strng_bonds( - icohplist.icohpcollection.as_dict(), - relevant_bonds=analyse.final_dict_bonds, +def _identify_strongest_bonds( + analyse: Analysis, + icobilist_path: Path, + icohplist_path: Path, + icooplist_path: Path, +): + """ + + Parameters + ---------- + analyse : .Analysis + Analysis object from lobsterpy automatic analysis + icobilist_path : Path or str + Path to ICOBILIST.lobster + icohplist_path : Path or str + Path to ICOHPLIST.lobster + icooplist_path : Path or str + Path to ICOOPLIST.lobster + + Returns + ------- + Tuple[StrongestBonds] + Tuple of StrongestBonds + """ + data = [ + (icohplist_path, False, False), + (icobilist_path, True, False), + (icooplist_path, False, True), + ] + output = [] + for file, are_cobis, are_coops in data: + if file.exists(): + icohplist = Icohplist( + filename=icohplist_path, + are_cobis=are_cobis, + are_coops=are_coops, + ) + bond_dict = _get_strong_bonds( + icohplist.icohpcollection.as_dict(), + relevant_bonds=analyse.final_dict_bonds, + are_cobis=are_cobis, + are_coops=are_coops, + ) + output.append( + StrongestBonds( are_cobis=are_cobis, are_coops=are_coops, + strongest_bonds=bond_dict, + which_bonds=analyse.whichbonds, ) - output.append( - StrongestBonds( - are_cobis=are_cobis, - are_coops=are_coops, - strongest_bonds=bond_dict, - which_bonds=analyse.whichbonds, - ) - ) - else: - output.append(None) - return output - - @staticmethod - def _get_strng_bonds( - bondlist: dict, are_cobis: bool, are_coops: bool, relevant_bonds: dict + ) + else: + output.append(None) + return output + + +def _get_strong_bonds( + bondlist: dict, are_cobis: bool, are_coops: bool, relevant_bonds: dict +): + """ + Identify the strongest bonds from a list of bonds. + + Parameters + ---------- + bondlist : dict + dict including bonding information + are_cobis : bool + True if these are cobis + are_coops : bool + True if these are coops + relevant_bonds : dict + Dict include all bonds that are considered. + + Returns + ------- + dict + Dictionary including strongest bonds. + """ + bonds = [] + icohp_all = [] + lengths = [] + for a, b, c, l in zip( + bondlist["list_atom1"], + bondlist["list_atom2"], + bondlist["list_icohp"], + bondlist["list_length"], ): - """ - Identify the strongest bonds from a list of bonds. - - Parameters - ---------- - bondlist : dict - dict including bonding information - are_cobis : bool - True if these are cobis - are_coops : bool - True if these are coops - relevant_bonds : dict - Dict include all bonds that are considered. - - Returns - ------- - dict - Dictionary including strongest bonds. - """ - bonds = [] - icohp_all = [] - lengths = [] - for a, b, c, l in zip( - bondlist["list_atom1"], - bondlist["list_atom2"], - bondlist["list_icohp"], - bondlist["list_length"], - ): - bonds.append(a.rstrip("0123456789") + "-" + b.rstrip("0123456789")) - icohp_all.append(sum(c.values())) - lengths.append(l) - - bond_labels_unique = list(set(bonds)) - sep_blabels: List[List[str]] = [[] for _ in range(len(bond_labels_unique))] - sep_icohp: List[List[float]] = [[] for _ in range(len(bond_labels_unique))] - sep_lengths: List[List[float]] = [[] for _ in range(len(bond_labels_unique))] - - for i, val in enumerate(bond_labels_unique): - for j, val2 in enumerate(bonds): - if val == val2: - sep_blabels[i].append(val2) - sep_icohp[i].append(icohp_all[j]) - sep_lengths[i].append(lengths[j]) - if not are_cobis and not are_coops: - bond_dict = {} - for i, lab in enumerate(bond_labels_unique): - label = lab.split("-") - label.sort() - for rel_bnd in relevant_bonds: - rel_bnd_list = rel_bnd.split("-") - rel_bnd_list.sort() - if label == rel_bnd_list: - index = np.argmin(sep_icohp[i]) - bond_dict.update( - { - rel_bnd: { - "ICOHP": min(sep_icohp[i]), - "length": sep_lengths[i][index], - } + bonds.append(a.rstrip("0123456789") + "-" + b.rstrip("0123456789")) + icohp_all.append(sum(c.values())) + lengths.append(l) + + bond_labels_unique = list(set(bonds)) + sep_blabels: List[List[str]] = [[] for _ in range(len(bond_labels_unique))] + sep_icohp: List[List[float]] = [[] for _ in range(len(bond_labels_unique))] + sep_lengths: List[List[float]] = [[] for _ in range(len(bond_labels_unique))] + + for i, val in enumerate(bond_labels_unique): + for j, val2 in enumerate(bonds): + if val == val2: + sep_blabels[i].append(val2) + sep_icohp[i].append(icohp_all[j]) + sep_lengths[i].append(lengths[j]) + if not are_cobis and not are_coops: + bond_dict = {} + for i, lab in enumerate(bond_labels_unique): + label = lab.split("-") + label.sort() + for rel_bnd in relevant_bonds: + rel_bnd_list = rel_bnd.split("-") + rel_bnd_list.sort() + if label == rel_bnd_list: + index = np.argmin(sep_icohp[i]) + bond_dict.update( + { + rel_bnd: { + "ICOHP": min(sep_icohp[i]), + "length": sep_lengths[i][index], } - ) - return bond_dict - - if are_cobis and not are_coops: - bond_dict = {} - for i, lab in enumerate(bond_labels_unique): - label = lab.split("-") - label.sort() - for rel_bnd in relevant_bonds: - rel_bnd_list = rel_bnd.split("-") - rel_bnd_list.sort() - if label == rel_bnd_list: - index = np.argmax(sep_icohp[i]) - bond_dict.update( - { - rel_bnd: { - "ICOBI": max(sep_icohp[i]), - "length": sep_lengths[i][index], - } + } + ) + return bond_dict + + if are_cobis and not are_coops: + bond_dict = {} + for i, lab in enumerate(bond_labels_unique): + label = lab.split("-") + label.sort() + for rel_bnd in relevant_bonds: + rel_bnd_list = rel_bnd.split("-") + rel_bnd_list.sort() + if label == rel_bnd_list: + index = np.argmax(sep_icohp[i]) + bond_dict.update( + { + rel_bnd: { + "ICOBI": max(sep_icohp[i]), + "length": sep_lengths[i][index], } - ) - return bond_dict - - if not are_cobis and are_coops: - bond_dict = {} - for i, lab in enumerate(bond_labels_unique): - label = lab.split("-") - label.sort() - for rel_bnd in relevant_bonds: - rel_bnd_list = rel_bnd.split("-") - rel_bnd_list.sort() - if label == rel_bnd_list: - index = np.argmax(sep_icohp[i]) - bond_dict.update( - { - rel_bnd: { - "ICOOP": max(sep_icohp[i]), - "length": sep_lengths[i][index], - } + } + ) + return bond_dict + + if not are_cobis and are_coops: + bond_dict = {} + for i, lab in enumerate(bond_labels_unique): + label = lab.split("-") + label.sort() + for rel_bnd in relevant_bonds: + rel_bnd_list = rel_bnd.split("-") + rel_bnd_list.sort() + if label == rel_bnd_list: + index = np.argmax(sep_icohp[i]) + bond_dict.update( + { + rel_bnd: { + "ICOOP": max(sep_icohp[i]), + "length": sep_lengths[i][index], } - ) - return bond_dict + } + ) + return bond_dict From 59e6c47a2421de655afadfe1dbd8a4be6132d3f2 Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 15:22:18 +0000 Subject: [PATCH 6/7] Documentation --- src/atomate2/lobster/schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index 16e87b6e10..4187668ee9 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -552,6 +552,7 @@ def _identify_strongest_bonds( icooplist_path: Path, ): """ + Identify the strongest bonds and convert them into StrongestBonds objects. Parameters ---------- From bf5dbf1c51fe6a571209c12a1485778ddae6f033 Mon Sep 17 00:00:00 2001 From: Alex Ganose Date: Sun, 12 Mar 2023 16:53:13 +0000 Subject: [PATCH 7/7] More tidying --- src/atomate2/lobster/jobs.py | 8 +-- src/atomate2/lobster/run.py | 5 +- src/atomate2/lobster/schemas.py | 91 ++++++++---------------- src/atomate2/vasp/flows/lobster.py | 11 ++- src/atomate2/vasp/jobs/lobster.py | 13 ++-- tests/vasp/lobster/flows/test_lobster.py | 4 +- 6 files changed, 46 insertions(+), 86 deletions(-) diff --git a/src/atomate2/lobster/jobs.py b/src/atomate2/lobster/jobs.py index a90ade6310..bb716e07e8 100644 --- a/src/atomate2/lobster/jobs.py +++ b/src/atomate2/lobster/jobs.py @@ -26,9 +26,8 @@ class LobsterMaker(Maker): """ LOBSTER job maker. - The maker copies DFT output files necessary for the LOBSTER run. - It will create all lobsterin files, run LOBSTER, zip the outputs and parse the - LOBSTER outputs. + The maker copies DFT output files necessary for the LOBSTER run. It will create all + lobsterin files, run LOBSTER, zip the outputs and parse the LOBSTER outputs. Parameters ---------- @@ -91,8 +90,7 @@ def make( gzip_dir(".") # parse lobster outputs - task_doc = LobsterTaskDocument.from_directory( + return LobsterTaskDocument.from_directory( Path.cwd(), **self.task_document_kwargs, ) - return task_doc diff --git a/src/atomate2/lobster/run.py b/src/atomate2/lobster/run.py index 71e1b30b08..a78df268b3 100644 --- a/src/atomate2/lobster/run.py +++ b/src/atomate2/lobster/run.py @@ -19,10 +19,7 @@ from atomate2 import SETTINGS -__all__ = [ - "JobType", - "run_lobster", -] +__all__ = ["JobType", "run_lobster"] _DEFAULT_VALIDATORS = (LobsterFilesValidator(), EnoughBandsValidator()) _DEFAULT_HANDLERS = () diff --git a/src/atomate2/lobster/schemas.py b/src/atomate2/lobster/schemas.py index 4187668ee9..a9eca8010c 100644 --- a/src/atomate2/lobster/schemas.py +++ b/src/atomate2/lobster/schemas.py @@ -31,7 +31,13 @@ Analysis = None Description = None -__all__ = ["LobsterTaskDocument"] +__all__ = [ + "LobsterTaskDocument", + "LobsteroutModel", + "LobsterinModel", + "CondensedBondingAnalysis", + "StrongestBonds", +] logger = logging.getLogger(__name__) @@ -432,9 +438,9 @@ def from_directory( icohplist_path = dir_name / "ICOHPLIST.lobster.gz" cohpcar_path = dir_name / "COHPCAR.lobster.gz" + charge_path = dir_name / "CHARGE.lobster.gz" cobicar_path = dir_name / "COBICAR.lobster.gz" coopcar_path = dir_name / "COOPCAR.lobster.gz" - charge_path = dir_name / "CHARGE.lobster.gz" doscar_path = dir_name / "DOSCAR.lobster.gz" structure_path = dir_name / "POSCAR.gz" madelung_energies_path = dir_name / "MadelungEnergies.lobster.gz" @@ -638,72 +644,37 @@ def _get_strong_bonds( lengths.append(l) bond_labels_unique = list(set(bonds)) - sep_blabels: List[List[str]] = [[] for _ in range(len(bond_labels_unique))] sep_icohp: List[List[float]] = [[] for _ in range(len(bond_labels_unique))] sep_lengths: List[List[float]] = [[] for _ in range(len(bond_labels_unique))] for i, val in enumerate(bond_labels_unique): for j, val2 in enumerate(bonds): if val == val2: - sep_blabels[i].append(val2) sep_icohp[i].append(icohp_all[j]) sep_lengths[i].append(lengths[j]) - if not are_cobis and not are_coops: - bond_dict = {} - for i, lab in enumerate(bond_labels_unique): - label = lab.split("-") - label.sort() - for rel_bnd in relevant_bonds: - rel_bnd_list = rel_bnd.split("-") - rel_bnd_list.sort() - if label == rel_bnd_list: - index = np.argmin(sep_icohp[i]) - bond_dict.update( - { - rel_bnd: { - "ICOHP": min(sep_icohp[i]), - "length": sep_lengths[i][index], - } - } - ) - return bond_dict if are_cobis and not are_coops: - bond_dict = {} - for i, lab in enumerate(bond_labels_unique): - label = lab.split("-") - label.sort() - for rel_bnd in relevant_bonds: - rel_bnd_list = rel_bnd.split("-") - rel_bnd_list.sort() - if label == rel_bnd_list: - index = np.argmax(sep_icohp[i]) - bond_dict.update( - { - rel_bnd: { - "ICOBI": max(sep_icohp[i]), - "length": sep_lengths[i][index], - } + prop = "ICOBI" + elif not are_cobis and are_coops: + prop = "ICOOP" + else: + prop = "ICOHP" + + bond_dict = {} + for i, lab in enumerate(bond_labels_unique): + label = lab.split("-") + label.sort() + for rel_bnd in relevant_bonds: + rel_bnd_list = rel_bnd.split("-") + rel_bnd_list.sort() + if label == rel_bnd_list: + index = np.argmin(sep_icohp[i]) + bond_dict.update( + { + rel_bnd: { + prop: min(sep_icohp[i]), + "length": sep_lengths[i][index], } - ) - return bond_dict - - if not are_cobis and are_coops: - bond_dict = {} - for i, lab in enumerate(bond_labels_unique): - label = lab.split("-") - label.sort() - for rel_bnd in relevant_bonds: - rel_bnd_list = rel_bnd.split("-") - rel_bnd_list.sort() - if label == rel_bnd_list: - index = np.argmax(sep_icohp[i]) - bond_dict.update( - { - rel_bnd: { - "ICOOP": max(sep_icohp[i]), - "length": sep_lengths[i][index], - } - } - ) - return bond_dict + } + ) + return bond_dict diff --git a/src/atomate2/vasp/flows/lobster.py b/src/atomate2/vasp/flows/lobster.py index 599b9e9210..000ea36036 100644 --- a/src/atomate2/vasp/flows/lobster.py +++ b/src/atomate2/vasp/flows/lobster.py @@ -47,7 +47,7 @@ class VaspLobsterMaker(Maker): relax_maker : .BaseVaspMaker or None A maker to perform a relaxation on the bulk. Set to ``None`` to skip the bulk relaxation. - preconverge_static_maker: .BaseVaspMaker or None + preconverge_static_maker : .BaseVaspMaker or None A maker to perform a preconvergence run before the wavefunction computation without symmetry lobster_static_maker : .BaseVaspMaker @@ -55,11 +55,11 @@ class VaspLobsterMaker(Maker): Cannot be skipped. lobster_maker : .LobsterMaker A maker to perform the Lobster run. - delete_all_wavecars : bool + delete_wavecars : bool If true, all WAVECARs will be deleted after the run. address_min_basis : str A path to a yaml file including basis set information. - address_max_basis: str + address_max_basis : str A path to a yaml file including basis set information. """ @@ -79,7 +79,7 @@ class VaspLobsterMaker(Maker): ) ) lobster_maker: LobsterMaker | None = field(default_factory=lambda: LobsterMaker()) - delete_all_wavecars: bool = True + delete_wavecars: bool = True address_min_basis: str | None = None address_max_basis: str | None = None @@ -151,7 +151,6 @@ def make( lobster_jobs = get_lobster_jobs( lobster_maker=self.lobster_maker, basis_dict=basis_infos.output["basis_dict"], - wavefunction_dir=lobster_static.output.dir_name, optimization_dir=optimization_dir, optimization_uuid=optimization_uuid, static_dir=lobster_static_dir, @@ -162,7 +161,7 @@ def make( jobs.append(lobster_jobs) # delete all WAVECARs that have been copied - if self.delete_all_wavecars: + if self.delete_wavecars: delete_wavecars = delete_lobster_wavecar( dirs=lobster_jobs.output["lobster_dirs"], lobster_static_dir=lobster_static.output.dir_name, diff --git a/src/atomate2/vasp/jobs/lobster.py b/src/atomate2/vasp/jobs/lobster.py index 957ecdd9e5..b13ef7b228 100644 --- a/src/atomate2/vasp/jobs/lobster.py +++ b/src/atomate2/vasp/jobs/lobster.py @@ -133,7 +133,7 @@ def get_basis_infos( @job def update_user_incar_settings_maker( - vasp_maker: LobsterStaticMaker, + vasp_maker: BaseVaspMaker, nbands: int, structure: Structure, prev_vasp_dir: Path | str, @@ -167,7 +167,6 @@ def update_user_incar_settings_maker( def get_lobster_jobs( lobster_maker: LobsterMaker, basis_dict: dict, - wavefunction_dir: Path | str, optimization_dir: Path | str, optimization_uuid: str, static_dir: Path | str, @@ -184,16 +183,14 @@ def get_lobster_jobs( maker for the Lobster jobs basis_dict : dict dict including basis set information. - wavefunction_dir : Path or str - Path to VASP calculation with WAVECAR optimization_dir : Path or str Path to optimization run. optimization_uuid : str uuid of optimization run. static_dir : Path or str - Path to static VASP calculation. + Path to static VASP calculation containing the WAVECAR. static_uuid : str - uuid of static run. + Uuid of static run. preconverge_static_dir : Path or str Path to preconvergence step. preconverge_static_uuid : str @@ -221,9 +218,7 @@ def get_lobster_jobs( lobster_maker = LobsterMaker() for i, basis in enumerate(basis_dict): - lobsterjob = lobster_maker.make( - wavefunction_dir=wavefunction_dir, basis_dict=basis - ) + lobsterjob = lobster_maker.make(wavefunction_dir=static_dir, basis_dict=basis) lobsterjob.append_name(f"_run_{i}") outputs["lobster_uuids"].append(lobsterjob.output.uuid) outputs["lobster_dirs"].append(lobsterjob.output.dir_name) diff --git a/tests/vasp/lobster/flows/test_lobster.py b/tests/vasp/lobster/flows/test_lobster.py index 028df363e4..14f8834bc8 100644 --- a/tests/vasp/lobster/flows/test_lobster.py +++ b/tests/vasp/lobster/flows/test_lobster.py @@ -57,7 +57,7 @@ def test_lobstermaker(mock_vasp, mock_lobster, clean_dir, memory_jobstore): "cohpGenerator": "from 0.1 to 3.0 orbitalwise", } ), - delete_all_wavecars=False, + delete_wavecars=False, ).make(si_structure) job = update_user_incar_settings(job, {"NPAR": 4}) @@ -123,7 +123,7 @@ def test_lobstermaker_delete(mock_vasp, mock_lobster, clean_dir, memory_jobstore "cohpGenerator": "from 0.1 to 3.0 orbitalwise", } ), - delete_all_wavecars=True, + delete_wavecars=True, ).make(si_structure) job = update_user_incar_settings(job, {"NPAR": 4})