From 884f00ffe6c963fe3d120e8fbc99795530608a01 Mon Sep 17 00:00:00 2001 From: Marvin Poul Date: Fri, 19 Apr 2024 11:46:53 +0200 Subject: [PATCH 1/3] Add OutcarCollectError and catch it in Vasp.collect If an OUTCAR is present but does not contain NIONS the collect method breaks the Outcar objects raises an unexpected ValueError. This changes the error to a more specific one and correctly catches the error during collect. --- pyiron_atomistics/vasp/base.py | 10 +++++++--- pyiron_atomistics/vasp/parser/outcar.py | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pyiron_atomistics/vasp/base.py b/pyiron_atomistics/vasp/base.py index cd498e8fe..fc543f83e 100644 --- a/pyiron_atomistics/vasp/base.py +++ b/pyiron_atomistics/vasp/base.py @@ -23,7 +23,7 @@ dict_group_to_hdf, ) from pyiron_base import state, GenericParameters, deprecate -from pyiron_atomistics.vasp.parser.outcar import Outcar +from pyiron_atomistics.vasp.parser.outcar import Outcar, OutcarCollectError from pyiron_atomistics.vasp.parser.oszicar import Oszicar from pyiron_atomistics.vasp.procar import Procar from pyiron_atomistics.vasp.structure import read_atoms, write_poscar, vasp_sorter @@ -2003,8 +2003,12 @@ def collect(self, directory=os.getcwd(), sorted_indices=None): if "OSZICAR" in files_present: self.oszicar.from_file(filename=posixpath.join(directory, "OSZICAR")) if "OUTCAR" in files_present: - self.outcar.from_file(filename=posixpath.join(directory, "OUTCAR")) - outcar_working = True + try: + self.outcar.from_file(filename=posixpath.join(directory, "OUTCAR")) + outcar_working = True + except OutcarCollectError as e: + state.logger.warning(f"OUTCAR present, but could not be parsed: {e}!") + outcar_working = False if "vasprun.xml" in files_present: try: with warnings.catch_warnings(record=True) as w: diff --git a/pyiron_atomistics/vasp/parser/outcar.py b/pyiron_atomistics/vasp/parser/outcar.py index 4d573f56b..8efdf3b83 100644 --- a/pyiron_atomistics/vasp/parser/outcar.py +++ b/pyiron_atomistics/vasp/parser/outcar.py @@ -23,6 +23,9 @@ scipy.constants.physical_constants["joule-electron volt relationship"][0] / 1e22 ) +# derives from ValueError, because that was the exception previously raised +class OutcarCollectError(ValueError): + pass class Outcar(object): """ @@ -934,7 +937,7 @@ def get_number_of_atoms(filename="OUTCAR", lines=None): if len(trigger_indices) != 0: return int(lines[trigger_indices[0]].split(ions_trigger)[-1]) else: - raise ValueError() + raise OutcarCollectError("Failed to read number of atoms, can't find NIONS!") @staticmethod def get_band_properties(filename="OUTCAR", lines=None): From 508ff1489c7eeeb4009d4a67c6f47ae0e9a17b77 Mon Sep 17 00:00:00 2001 From: pyiron-runner Date: Fri, 19 Apr 2024 10:39:23 +0000 Subject: [PATCH 2/3] Format black --- pyiron_atomistics/vasp/parser/outcar.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyiron_atomistics/vasp/parser/outcar.py b/pyiron_atomistics/vasp/parser/outcar.py index 8efdf3b83..27a2f8cef 100644 --- a/pyiron_atomistics/vasp/parser/outcar.py +++ b/pyiron_atomistics/vasp/parser/outcar.py @@ -23,10 +23,12 @@ scipy.constants.physical_constants["joule-electron volt relationship"][0] / 1e22 ) + # derives from ValueError, because that was the exception previously raised class OutcarCollectError(ValueError): pass + class Outcar(object): """ This module is used to parse VASP OUTCAR files. @@ -937,7 +939,9 @@ def get_number_of_atoms(filename="OUTCAR", lines=None): if len(trigger_indices) != 0: return int(lines[trigger_indices[0]].split(ions_trigger)[-1]) else: - raise OutcarCollectError("Failed to read number of atoms, can't find NIONS!") + raise OutcarCollectError( + "Failed to read number of atoms, can't find NIONS!" + ) @staticmethod def get_band_properties(filename="OUTCAR", lines=None): From fe1f9d26daf340d5052c426a5c4c3f356a976853 Mon Sep 17 00:00:00 2001 From: Marvin Poul Date: Fri, 24 May 2024 16:00:59 +0200 Subject: [PATCH 3/3] Add tests --- .../outcar_without_nions/OUTCAR | 185 ++++++++++++++++++ .../outcar_without_nions/README.md | 3 + tests/vasp/test_outcar.py | 28 ++- 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 tests/static/vasp_test_files/outcar_without_nions/OUTCAR create mode 100644 tests/static/vasp_test_files/outcar_without_nions/README.md diff --git a/tests/static/vasp_test_files/outcar_without_nions/OUTCAR b/tests/static/vasp_test_files/outcar_without_nions/OUTCAR new file mode 100644 index 000000000..883d66e0a --- /dev/null +++ b/tests/static/vasp_test_files/outcar_without_nions/OUTCAR @@ -0,0 +1,185 @@ + vasp.5.4.4.18Apr17-6-g9f103f2a35 (build Feb 19 2020 16:12:31) complex + + executed on LinuxIFC date 2024.05.24 14:55:29 + running on 20 total cores + distrk: each k-point on 20 cores, 1 groups + distr: one band on NCORES_PER_BAND= 20 cores, 1 groups + + +-------------------------------------------------------------------------------------------------------- + + + INCAR: + POTCAR: PAW_PBE Mg 13Apr2007 + + ----------------------------------------------------------------------------- +| | +| W W AA RRRRR N N II N N GGGG !!! | +| W W A A R R NN N II NN N G G !!! | +| W W A A R R N N N II N N N G !!! | +| W WW W AAAAAA RRRRR N N N II N N N G GGG ! | +| WW WW A A R R N NN II N NN G G | +| W W A A R R N N II N N GGGG !!! | +| | +| For optimal performance we recommend to set | +| NCORE= 4 - approx SQRT( number of cores) | +| NCORE specifies how many cores store one orbital (NPAR=cpu/NCORE). | +| This setting can greatly improve the performance of VASP for DFT. | +| The default, NCORE=1 might be grossly inefficient | +| on modern multi-core architectures or massively parallel machines. | +| Do your own testing !!!! | +| Unfortunately you need to use the default for GW and RPA calculations. | +| (for HF NCORE is supported but not extensively tested yet) | +| | + ----------------------------------------------------------------------------- + + POTCAR: PAW_PBE Mg 13Apr2007 + SHA256 = a90315392b5cf46bd986ff2010bfd53a40dec98e36f8d1f6277412e9d0e81a9f Mg + COPYR = (c) Copyright 13Apr2007 Georg Kresse + COPYR = This file is part of the software VASP. Any use, copying, and all ot + COPYR = If you do not have a valid VASP license, you may not use, copy or di + VRHFIN =Mg: s2p0 + LEXCH = PE + EATOM = 23.0369 eV, 1.6932 Ry + + TITEL = PAW_PBE Mg 13Apr2007 + LULTRA = F use ultrasoft PP ? + IUNSCR = 1 unscreen: 0-lin 1-nonlin 2-no + RPACOR = 1.500 partial core radius + POMASS = 24.305; ZVAL = 2.000 mass and valenz + RCORE = 2.000 outmost cutoff radius + RWIGS = 2.880; RWIGS = 1.524 wigner-seitz radius (au A) + ENMAX = 200.000; ENMIN = 100.000 eV + RCLOC = 1.506 cutoff for local pot + LCOR = T correct aug charges + LPAW = T paw PP + EAUG = 454.734 + DEXC = 0.000 + RMAX = 2.045 core radius for proj-oper + RAUG = 1.300 factor for augmentation sphere + RDEP = 2.025 radius for radial grids + RDEPT = 1.942 core radius for aug-charge + + Atomic configuration + 6 entries + n l j E occ. + 1 0 0.50 -1259.6230 2.0000 + 2 0 0.50 -79.8442 2.0000 + 2 1 1.50 -46.6121 6.0000 + 3 0 0.50 -4.7055 2.0000 + 3 1 0.50 -1.3660 0.0000 + 3 2 1.50 -1.3606 0.0000 + Description + l E TYP RCUT TYP RCUT + 0 -4.7054661 23 2.000 + 0 27.2116520 23 2.000 + 1 1.3605826 23 2.000 + 1 27.2116520 23 2.000 + 2 -1.3605826 23 2.000 + local pseudopotential read in + partial core-charges read in + partial kinetic energy density read in + atomic valenz-charges read in + non local Contribution for L= 0 read in + real space projection operators read in + non local Contribution for L= 0 read in + real space projection operators read in + non local Contribution for L= 1 read in + real space projection operators read in + non local Contribution for L= 1 read in + real space projection operators read in + non local Contribution for L= 2 read in + real space projection operators read in + PAW grid and wavefunctions read in + + number of l-projection operators is LMAX = 5 + number of lm-projection operators is LMMAX = 13 + + PAW_PBE Mg 13Apr2007 : + energy of atom 1 EATOM= -23.0369 + kinetic energy error for atom= 0.0001 (will be added to EATOM!!) + + + POSCAR: Poscar file generated with pyiron + positions in cartesian coordinates + No initial velocities read in + exchange correlation table for LEXCH = 8 + RHO(1)= 0.500 N(1) = 2000 + RHO(2)= 100.500 N(2) = 4000 + + + +-------------------------------------------------------------------------------------------------------- + + + ion position nearest neighbor table + 1 0.500 0.500 0.250- + 2 0.500 0.500 0.750- + + LATTYP: Found a simple tetragonal cell. + ALAT = 2.8632343400 + C/A-ratio = 2.8377866025 + + Lattice vectors: + + A1 = ( 2.8632343400, 0.0000001000, -0.0000006600) + A2 = ( 0.0000001000, 2.8632342300, 0.0000025400) + A3 = ( -0.0000027600, 0.0000097000, 8.1252480500) + + +Analysis of symmetry for initial positions (statically): +===================================================================== + Subroutine PRICEL returns following result: + + LATTYP: Found a simple tetragonal cell. + ALAT = 2.8632343400 + C/A-ratio = 1.4188933013 + + Lattice vectors: + + A1 = ( 2.8632343400, 0.0000001000, -0.0000006600) + A2 = ( 0.0000001000, 2.8632342300, 0.0000025400) + A3 = ( -0.0000013800, 0.0000048500, 4.0626240250) + + 2 primitive cells build up your supercell. + + + Routine SETGRP: Setting up the symmetry group for a + simple tetragonal supercell. + + + Subroutine GETGRP returns: Found 16 space group operations + (whereof 16 operations were pure point group operations) + out of a pool of 16 trial point group operations. + + +The static configuration has the point symmetry D_4h. + + +Analysis of symmetry for dynamics (positions and initial velocities): +===================================================================== + Subroutine PRICEL returns following result: + + LATTYP: Found a simple tetragonal cell. + ALAT = 2.8632343400 + C/A-ratio = 1.4188933013 + + Lattice vectors: + + A1 = ( 2.8632343400, 0.0000001000, -0.0000006600) + A2 = ( 0.0000001000, 2.8632342300, 0.0000025400) + A3 = ( -0.0000013800, 0.0000048500, 4.0626240250) + + 2 primitive cells build up your supercell. + + + Routine SETGRP: Setting up the symmetry group for a + simple tetragonal supercell. + + + Subroutine GETGRP returns: Found 16 space group operations + (whereof 16 operations were pure point group operations) + out of a pool of 16 trial point group operations. + + +The dynamic configuration has the point symmetry D_4h. diff --git a/tests/static/vasp_test_files/outcar_without_nions/README.md b/tests/static/vasp_test_files/outcar_without_nions/README.md new file mode 100644 index 000000000..32933858b --- /dev/null +++ b/tests/static/vasp_test_files/outcar_without_nions/README.md @@ -0,0 +1,3 @@ +Simulates output directory of a VASP job that crashed early in its run. +In particular too early for the OUTCAR to contain the NIONS tag, which the +outcar parser implicitly relies on. diff --git a/tests/vasp/test_outcar.py b/tests/vasp/test_outcar.py index 0da288151..73aa993e1 100644 --- a/tests/vasp/test_outcar.py +++ b/tests/vasp/test_outcar.py @@ -6,7 +6,8 @@ import os import posixpath import numpy as np -from pyiron_atomistics.vasp.parser.outcar import Outcar +from pyiron_atomistics.vasp.base import Output, VaspCollectError +from pyiron_atomistics.vasp.parser.outcar import Outcar, OutcarCollectError class TestOutcar(unittest.TestCase): @@ -1204,5 +1205,30 @@ def test_get_band_properties(self): self.assertTrue(np.allclose(np.array(cbm_list), np.array([[-0.1332], [0.0219]]))) + def test_error_on_parse(self): + """OutcarCollectError should be raised when vital information cannot be read.""" + with self.assertRaises(OutcarCollectError): + Outcar().from_file(os.path.join(self.file_location, "../static/vasp_test_files/outcar_without_nions/OUTCAR")) + + def test_no_error_on_incomplete_collect(self): + """Do not raise an error when collecting output and the OUTCAR is incomplete.""" + # the reason being: VaspBase.restart() tries to collect the job before creating a new copy, but if collect fails + # when the OUTCAR is not complete, that would mean this kind of job could not be restarted to apply some fix to + # the input to avoid the crash. + # We want to be able to restart any job, so errors on collect must be silenced or be of the VaspCollectError, + # which is caught during restart + test_folders = ["../static/vasp_test_files/full_job_aborted", "../static/vasp_test_files/outcar_without_nions"] + for folder in test_folders: + with self.subTest(folder=folder): + try: + Output().collect( + os.path.join(self.file_location, folder), + sorted_indices=[] # necessary only because Output object otherwise needs to have a structure defined + ) + except VaspCollectError: + pass + except Exception as e: + self.fail(f"collect_output_parser raised {e}, but should be silent!") + if __name__ == "__main__": unittest.main()