From 6428deed7e5028ef059a4946172c52011de221b7 Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Thu, 26 Oct 2023 15:59:54 +0200 Subject: [PATCH 1/6] Added Info attributes to ascii class files Now all ascii files will contain the info attributes. This enables the class to dynamically add attributes based on the content of Sile._info_attributes_. Each of those entries will be converted to a regex Pattern and will process on each read line by patching the instance readline function. This is very crude, and might be slow for very big files with lots of attributes, perhaps it can be streamlined a bit by removing those attributes which are static and already found (or perhaps all non-updatable attributes will be removed for searching once the full file has been processed). It proves quite simple to use and makes the code look prettier and more readable. But the internals are a bit messy. Signed-off-by: Nick Papior --- src/sisl/io/orca/stdout.py | 83 +++-------- src/sisl/io/orca/tests/test_stdout.py | 2 + src/sisl/io/sile.py | 191 +++++++++++++++++++++++++- 3 files changed, 212 insertions(+), 64 deletions(-) diff --git a/src/sisl/io/orca/stdout.py b/src/sisl/io/orca/stdout.py index 41d68e7d29..18002a48b7 100644 --- a/src/sisl/io/orca/stdout.py +++ b/src/sisl/io/orca/stdout.py @@ -2,6 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. import numpy as np +from functools import partial from sisl._internal import set_module from sisl.messages import deprecation @@ -15,80 +16,39 @@ __all__ = ["outputSileORCA", "stdoutSileORCA"] +_A = partial(SileORCA._Attr, updatable=False) + + @set_module("sisl.io.orca") class stdoutSileORCA(SileORCA): """ Output file from ORCA """ - def _setup(self, *args, **kwargs): - """ Ensure the class has essential tags """ - super()._setup(*args, **kwargs) - self._completed = None - self._na = None - self._no = None - self._vdw = None - - def readline(self, *args, **kwargs): - line = super().readline(*args, **kwargs) - if self._completed is None and "ORCA TERMINATED NORMALLY" in line: - self._completed = True - elif self._completed is None and line == '': - self._completed = False - elif self._na is None and "Number of atoms" in line: - v = line.split() - self._na = int(v[-1]) - elif self._no is None and "Number of basis functions" in line: - v = line.split() - self._no = int(v[-1]) - elif self._vdw is None and "DFT DISPERSION CORRECTION" in line: - self._vdw = True - return line - - readline.__doc__ = SileORCA.readline.__doc__ + _info_attributes_ = [ + _A("na", r".*Number of atoms", + lambda attr, match: int(match.string.split()[-1])), + _A("no", r".*Number of basis functions", + lambda attr, match: int(match.string.split()[-1])), + _A("_vdw_", r".*DFT DISPERSION CORRECTION", + lambda attr, match: True, default=False), + _A("completed", r".*ORCA TERMINATED NORMALLY", + lambda attr, match: True, default=False), + ] def completed(self): """ True if the full file has been read and "ORCA TERMINATED NORMALLY" was found. """ - if self._completed is None: - with self: - completed = self.step_to("ORCA TERMINATED NORMALLY")[0] - else: - completed = self._completed - if completed: - self._completed = True - return completed + return self.info.completed @property + @deprecation("stdoutSileORCA.na is deprecated in favor of stdoutSileORCA.info.na", "0.16.0") def na(self): """ Number of atoms """ - if self._na is None: - with self: - f = self.step_to("Number of atoms") - if f[0]: - self._na = int(f[1].split()[-1]) - return self._na + return self.info.na @property + @deprecation("stdoutSileORCA.no is deprecated in favor of stdoutSileORCA.info.no", "0.16.0") def no(self): """ Number of orbitals (basis functions) """ - if self._no is None: - with self: - f = self.step_to("Number of basis functions") - if f[0]: - self._no = int(f[1].split()[-1]) - return self._no - - @property - def _vdw_(self): - """ Whether VDW dispersions are included """ - if self._vdw is None: - old_line = None - if hasattr(self, "fh"): - old_line = self.fh.tell() - with self: - f = self.step_to("DFT DISPERSION CORRECTION") - self._vdw = f[0] - if old_line is not None: - self.fh.seek(old_line) - return self._vdw + return self.info.no @SileBinder(postprocess=np.array) @sile_fh_open() @@ -306,9 +266,10 @@ def read_energy(self): E["embedding"] = float(v[-2]) * Ha2eV line = self.readline() - if self._vdw_: + if self.info._vdw_: self.step_to("DFT DISPERSION CORRECTION") v = self.step_to("Dispersion correction", allow_reread=False)[1].split() + print("vdW", v, self.info._vdw_) E["vdw"] = float(v[-1]) * Ha2eV return E @@ -355,7 +316,7 @@ def read_orbital_energies(self): return E -outputSileORCA = deprecation("outputSileORCA has been deprecated in favor of outSileOrca.", "0.15")(stdoutSileORCA) +outputSileORCA = deprecation("outputSileORCA has been deprecated in favor of stdoutSileOrca.", "0.15")(stdoutSileORCA) add_sile("output", stdoutSileORCA, gzip=True, case=False) add_sile("orca.out", stdoutSileORCA, gzip=True, case=False) diff --git a/src/sisl/io/orca/tests/test_stdout.py b/src/sisl/io/orca/tests/test_stdout.py index 7355613c52..331cca1298 100644 --- a/src/sisl/io/orca/tests/test_stdout.py +++ b/src/sisl/io/orca/tests/test_stdout.py @@ -244,6 +244,7 @@ def test_charge_orbital_reduced_unpol(sisl_files): assert C[0] == 0.315910 assert S is None +@pytest.mark.only def test_charge_orbital_full_unpol(sisl_files): f = sisl_files(_dir, 'molecule2.output') out = stdoutSileORCA(f) @@ -253,6 +254,7 @@ def test_charge_orbital_full_unpol(sisl_files): assert C is None assert S is None +@pytest.mark.only def test_read_energy(sisl_files): f = sisl_files(_dir, 'molecule.output') out = stdoutSileORCA(f) diff --git a/src/sisl/io/sile.py b/src/sisl/io/sile.py index cbae482889..28f1d1fbc9 100644 --- a/src/sisl/io/sile.py +++ b/src/sisl/io/sile.py @@ -8,11 +8,13 @@ from operator import and_, contains from os.path import basename, splitext from pathlib import Path -from typing import Any, Callable, Optional +from typing import Any, Callable, Optional, Union +from textwrap import dedent, indent +import re from sisl._environ import get_environ_variable from sisl._internal import set_module -from sisl.messages import SislInfo, SislWarning, deprecate +from sisl.messages import SislInfo, SislWarning, deprecate, info from sisl.utils.misc import str_spec from ._help import * @@ -716,7 +718,190 @@ def close(self): @set_module("sisl.io") -class Sile(BaseSile): +class Info: + """ An info class that creates .info with inherent properties + + These properties can be added at will. + """ + + # default to be empty + _info_attributes_ = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.info = _Info(self) + + class _Info: + """ The actual .info object that will attached to the instance. + + As of know this is problematic to document. + We should figure out a way to do that. + """ + + def __init__(self, instance): + # attach this info instance to the instance + self._instance = instance + self._attrs = [] + self._properties = [] + + # Patch once the properties has been created + + # Patch the readline of the instance + def patch(info): + # grab the function to be patched + instance = info._instance + properties = info._properties + func = instance.readline + + @wraps(func) + def readline(*args, **kwargs): + line = func(*args, **kwargs) + for prop in properties: + prop.process(line) + return line + return readline + + self._instance.readline = patch(self) + + # add the properties + for prop in instance._info_attributes_: + if isinstance(prop, dict): + prop = _Attr(**prop) + else: + prop = prop.copy() + self.add_property(prop) + + def add_property(self, prop): + """ Add a new property to be reachable from the .info """ + self._attrs.append(prop.attr) + self._properties.append(prop) + + def __str__(self): + """ Return a string of the contained attributes, with the values they currently contain """ + return "\n".join([p.documentation() for p in self._properties]) + + def __getattr__(self, attr): + """ Overwrite the attribute retrieval to be able to fetch the actual values from the information """ + inst = self._instance + if attr not in self._attrs: + raise AttributeError(f"{inst.__class__.__name__}.info.{attr} does not exist, did you mistype?") + + idx = self._attrs.index(attr) + prop = self._properties[idx] + if prop.found: + # only when hitting the new line will this change... + return prop.value + + # we need to parse the rest of the file + # This is not ideal, but... + loc = None + try: + loc = inst.fh.tell() + except AttributeError: + pass + with inst: + line = inst.readline() + while not (prop.found or line == ''): + line = inst.readline() + if loc is not None: + inst.fh.seek(loc) + + if not prop.found: + # TODO see if this should just be a warning? Perhaps it would be ok that it can't be + # found. + info(f"Attribute {attr} could not be found in {inst}") + + return prop.value + + + + class _Attr: + """ Holder for parsing lines and extracting information from text files + + This consists of: + + attr: + the name of the attribute + This will be the `sile.info.` access point. + regex: + the regular expression used to match a line. + If a `str`, it will be compiled *as is* to a regex pattern. + `regex.match(line)` will be used to check if the value should be updated. + parser: + if `regex.match(line)` returns a match that is true, then this parser will + be executed. + The parser *must* be a function accepting two arguments: + + def parser(attr, match) + + where `attr` is this object, and `match` is the match done on the line. + (Note that `match.string` will return the full line used to match against). + updatable: + control whether a new match on the line will update using `parser`. + If false, only the first match will update the value + default: + the default value of the attribute + found: + whether the value has been found in the file. + """ + __slots__ = ("attr", "regex", "parser", "updatable", "value", "found", "doc") + + def __init__(self, + attr: str, + regex: Union[str, re.Pattern], + parser, + doc: str="", + updatable: bool=True, + default: Optional[Any]=None, + found: bool=False, + ): + self.attr = attr + if isinstance(regex, str): + regex = re.compile(regex) + self.regex = regex + self.parser = parser + self.updatable = updatable + self.value = default + self.found = found + self.doc = doc + + def process(self, line): + if self.found and not self.updatable: + return False + + match = self.regex.match(line) + if match: + self.value = self.parser(self, match) + #print(f"found {self.attr}={self.value} with {line}") + self.found = True + return True + + return False + + def copy(self): + return self.__class__(attr=self.attr, + regex=self.regex, + parser=self.parser, + doc=self.doc, + updatable=self.updatable, + default=self.value, + found=self.found) + + def documentation(self): + """ Returns a documentation string for this object """ + if self.doc: + doc = "\n" + indent(dedent(self.doc), " " * 4) + else: + doc = "" + return f"{self.attr}[{self.value}]: r'{self.regex.pattern}'{doc}" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.info = self._Info(self) + + +@set_module("sisl.io") +class Sile(Info, BaseSile): """ Base class for ASCII files All ASCII files that needs to be added to the global lookup table can From d76ae4255c0d4bd773395abd7d4b0ea93c309008 Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Thu, 26 Oct 2023 20:47:11 +0200 Subject: [PATCH 2/6] Added to changelog and changed a few things Signed-off-by: Nick Papior --- CHANGELOG.md | 8 ++++++++ src/sisl/io/orca/stdout.py | 4 +--- src/sisl/io/sile.py | 12 +++++------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f28171fda3..1c1d9a52ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,17 @@ we hit release version 1.0.0. - added logging in some modules, to be added in more stuff to allow easier debugging. - marked all `toSphere|toEllipsoid|...` as deprecated +- a simple extensionable method to add `Sile.info.` by exposing + attributes through an object on each class. + The _info_attributes_ contains a list of attributes that can be + discovered while reading ascii files see #509 ### Changed - `Lattice` now holds the boundary conditions (not `Grid`), see #626 +- Some siles exposed certain properties containing basic information + about the content, say number of atoms/orbitals etc. + These will be moved to `sile.info.` instead to reduce + the number of methods exposed on each sile. ## [0.14.2] - 2023-10-04 diff --git a/src/sisl/io/orca/stdout.py b/src/sisl/io/orca/stdout.py index 18002a48b7..870505943a 100644 --- a/src/sisl/io/orca/stdout.py +++ b/src/sisl/io/orca/stdout.py @@ -2,7 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. import numpy as np -from functools import partial from sisl._internal import set_module from sisl.messages import deprecation @@ -16,7 +15,7 @@ __all__ = ["outputSileORCA", "stdoutSileORCA"] -_A = partial(SileORCA._Attr, updatable=False) +_A = SileORCA.InfoAttr @set_module("sisl.io.orca") @@ -269,7 +268,6 @@ def read_energy(self): if self.info._vdw_: self.step_to("DFT DISPERSION CORRECTION") v = self.step_to("Dispersion correction", allow_reread=False)[1].split() - print("vdW", v, self.info._vdw_) E["vdw"] = float(v[-1]) * Ha2eV return E diff --git a/src/sisl/io/sile.py b/src/sisl/io/sile.py index 28f1d1fbc9..855e8df49d 100644 --- a/src/sisl/io/sile.py +++ b/src/sisl/io/sile.py @@ -2,15 +2,15 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. import gzip +import re from functools import reduce, wraps from io import TextIOBase from itertools import product from operator import and_, contains from os.path import basename, splitext from pathlib import Path -from typing import Any, Callable, Optional, Union from textwrap import dedent, indent -import re +from typing import Any, Callable, Optional, Union from sisl._environ import get_environ_variable from sisl._internal import set_module @@ -766,7 +766,7 @@ def readline(*args, **kwargs): # add the properties for prop in instance._info_attributes_: if isinstance(prop, dict): - prop = _Attr(**prop) + prop = InfoAttr(**prop) else: prop = prop.copy() self.add_property(prop) @@ -813,9 +813,7 @@ def __getattr__(self, attr): return prop.value - - - class _Attr: + class InfoAttr: """ Holder for parsing lines and extracting information from text files This consists of: @@ -851,7 +849,7 @@ def __init__(self, regex: Union[str, re.Pattern], parser, doc: str="", - updatable: bool=True, + updatable: bool=False, default: Optional[Any]=None, found: bool=False, ): From 1af4ce06ed3bd7db6e1caf822f5c0068a217e79e Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Tue, 31 Oct 2023 08:59:18 +0100 Subject: [PATCH 3/6] fixed @tfrederiksen comments Changed _vdw_ -> vdw_correction for clarity Signed-off-by: Nick Papior --- src/sisl/io/orca/stdout.py | 20 ++++++++++---------- src/sisl/io/orca/tests/test_stdout.py | 8 ++++---- src/sisl/io/sile.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sisl/io/orca/stdout.py b/src/sisl/io/orca/stdout.py index 870505943a..7c3e27e944 100644 --- a/src/sisl/io/orca/stdout.py +++ b/src/sisl/io/orca/stdout.py @@ -27,7 +27,7 @@ class stdoutSileORCA(SileORCA): lambda attr, match: int(match.string.split()[-1])), _A("no", r".*Number of basis functions", lambda attr, match: int(match.string.split()[-1])), - _A("_vdw_", r".*DFT DISPERSION CORRECTION", + _A("vdw_correction", r".*DFT DISPERSION CORRECTION", lambda attr, match: True, default=False), _A("completed", r".*ORCA TERMINATED NORMALLY", lambda attr, match: True, default=False), @@ -120,8 +120,8 @@ def read_block(step_to): spin_block = False self.readline() # skip --- - A = np.empty(self.na, np.float64) - for ia in range(self.na): + A = np.empty(self.info.na, np.float64) + for ia in range(self.info.na): line = self.readline() v = line.split() if spin_block and not spin: @@ -177,7 +177,7 @@ def read_block(step_to): if orbitals is None: return D else: - Da = np.zeros(self.na, np.float64) + Da = np.zeros(self.info.na, np.float64) for (ia, orb), d in D.items(): if orb == orbitals: Da[ia] = d @@ -203,9 +203,9 @@ def read_block(step_to): if "MULLIKEN" in step_to: self.readline() # skip line "The uncorrected..." - Do = np.empty(self.no, np.float64) # orbital-resolved - Da = np.zeros(self.na, np.float64) # atom-resolved - for io in range(self.no): + Do = np.empty(self.info.no, np.float64) # orbital-resolved + Da = np.zeros(self.info.na, np.float64) # atom-resolved + for io in range(self.info.no): v = self.readline().split() # io, ia+element, orb, chg, (spin) # split atom number and element from v[1] @@ -265,7 +265,7 @@ def read_energy(self): E["embedding"] = float(v[-2]) * Ha2eV line = self.readline() - if self.info._vdw_: + if self.info.vdw_correction: self.step_to("DFT DISPERSION CORRECTION") v = self.step_to("Dispersion correction", allow_reread=False)[1].split() E["vdw"] = float(v[-1]) * Ha2eV @@ -288,10 +288,10 @@ def read_orbital_energies(self): self.readline() # skip --- if "SPIN UP ORBITALS" in self.readline(): spin = True - E = np.empty([self.no, 2], np.float64) + E = np.empty([self.info.no, 2], np.float64) else: spin = False - E = np.empty([self.no, 1], np.float64) + E = np.empty([self.info.no, 1], np.float64) self.readline() # Skip "NO OCC" header line diff --git a/src/sisl/io/orca/tests/test_stdout.py b/src/sisl/io/orca/tests/test_stdout.py index 331cca1298..d40230744d 100644 --- a/src/sisl/io/orca/tests/test_stdout.py +++ b/src/sisl/io/orca/tests/test_stdout.py @@ -17,8 +17,8 @@ def test_tags(sisl_files): f = sisl_files(_dir, 'molecule.output') out = stdoutSileORCA(f) - assert out.na == 2 - assert out.no == 62 + assert out.info.na == 2 + assert out.info.no == 62 assert out.completed() def test_read_electrons(sisl_files): @@ -289,7 +289,7 @@ def test_read_orbital_energies(sisl_files): assert pytest.approx(E[1][61, 1]) == 1173.6985 E = out.read_orbital_energies[-1]() - assert E.shape == (out.no, 2) + assert E.shape == (out.info.no, 2) assert pytest.approx(E[61, 0]) == 1173.4259 def test_read_orbital_energies_unpol(sisl_files): @@ -303,7 +303,7 @@ def test_read_orbital_energies_unpol(sisl_files): assert pytest.approx(E[1][61]) == 1171.5967 E = out.read_orbital_energies[-1]() - assert E.shape == (out.no,) + assert E.shape == (out.info.no,) assert pytest.approx(E[0]) == -513.0976 assert pytest.approx(E[61]) == 1171.5967 diff --git a/src/sisl/io/sile.py b/src/sisl/io/sile.py index 855e8df49d..3a7c46b1e0 100644 --- a/src/sisl/io/sile.py +++ b/src/sisl/io/sile.py @@ -734,7 +734,7 @@ def __init__(self, *args, **kwargs): class _Info: """ The actual .info object that will attached to the instance. - As of know this is problematic to document. + As of now this is problematic to document. We should figure out a way to do that. """ From c0b85d68a8e069a7490b4a1f24b23a1fadceedcd Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Tue, 31 Oct 2023 08:59:51 +0100 Subject: [PATCH 4/6] added .info. to stdoutSileSiesta Currently only on the completed attribute Signed-off-by: Nick Papior --- src/sisl/io/siesta/stdout.py | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/sisl/io/siesta/stdout.py b/src/sisl/io/siesta/stdout.py index 8e207c4365..64ffe25d93 100644 --- a/src/sisl/io/siesta/stdout.py +++ b/src/sisl/io/siesta/stdout.py @@ -22,6 +22,7 @@ Bohr2Ang = unit_convert('Bohr', 'Ang') +_A = SileSiesta.InfoAttr def _ensure_atoms(atoms): @@ -39,30 +40,16 @@ class stdoutSileSiesta(SileSiesta): This enables reading the output quantities from the Siesta output. """ + + _info_attributes_ = [ + _A("completed", r".*Job completed", + lambda attr, match: lambda : True, default=lambda : False), + ] - def _setup(self, *args, **kwargs): - """ Ensure the class has a _completed tag """ - super()._setup(*args, **kwargs) - self._completed = None - - def readline(self, *args, **kwargs): - line = super().readline(*args, **kwargs) - if 'Job completed' in line: - self._completed = True - return line - - readline.__doc__ = SileSiesta.readline.__doc__ - - @sile_fh_open() + @deprecation("stdoutSileSiesta.completed is deprecated in favor of stdoutSileSiesta.info.completed", "0.16.0") def completed(self): """ True if the full file has been read and "Job completed" was found. """ - if self._completed is None: - completed = self.step_to("Job completed")[0] - else: - completed = self._completed - if completed: - self._completed = True - return completed + return self.info.completed() @lru_cache(1) @sile_fh_open(True) @@ -439,10 +426,9 @@ def next_stress(): return _a.arrayd(S) - # list of all stresses - Ss = [] - if all or last: + # list of all stresses + Ss = [] while True: S = next_stress() if S is None: From a6a29c646aca2cfc5abb8571bc2b5cdc50c8906d Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Tue, 31 Oct 2023 09:14:18 +0100 Subject: [PATCH 5/6] added .info. to stdoutVasp Signed-off-by: Nick Papior --- src/sisl/io/vasp/stdout.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/sisl/io/vasp/stdout.py b/src/sisl/io/vasp/stdout.py index c0f0e3759e..1ccdb27af8 100644 --- a/src/sisl/io/vasp/stdout.py +++ b/src/sisl/io/vasp/stdout.py @@ -14,27 +14,22 @@ __all__ = ["stdoutSileVASP", "outSileVASP"] +_A = SileVASP.InfoAttr + + @set_module("sisl.io.vasp") class stdoutSileVASP(SileVASP): """ Output file from VASP """ - def _setup(self, *args, **kwargs): - """ Ensure the class has a _completed tag """ - super()._setup(*args, **kwargs) - self._completed = None + _info_attributes_ = [ + _A("completed", r".*General timing and accounting", + lambda attr, match: lambda : True, default=lambda : False), + ] - def readline(self, *args, **kwargs): - line = super().readline(*args, **kwargs) - if "General timing and accounting" in line: - self._completed = True - return line - - @sile_fh_open() + @deprecation("stdoutSileVASP.completed is deprecated in favor of stdoutSileVASP.info.completed", "0.16.0") def completed(self): """ True if the line "General timing and accounting" was found. """ - if self._completed is not True: - self._completed = self.step_to("General timing and accounting")[0] - return self._completed + return self.info.completed() @sile_fh_open() def cpu_time(self, flag="General timing and accounting"): From 71888471a6ceb25975900a1c15b6b981681c8f98 Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Tue, 31 Oct 2023 09:56:24 +0100 Subject: [PATCH 6/6] final commits for the VASP info attributes Signed-off-by: Nick Papior --- src/sisl/io/vasp/stdout.py | 13 +++++++------ src/sisl/io/vasp/tests/test_stdout.py | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sisl/io/vasp/stdout.py b/src/sisl/io/vasp/stdout.py index 1ccdb27af8..9b4c2d5aab 100644 --- a/src/sisl/io/vasp/stdout.py +++ b/src/sisl/io/vasp/stdout.py @@ -24,6 +24,8 @@ class stdoutSileVASP(SileVASP): _info_attributes_ = [ _A("completed", r".*General timing and accounting", lambda attr, match: lambda : True, default=lambda : False), + _A("accuracy_reached", r".*reached required accuracy", + lambda attr, match: lambda : True, default=lambda : False), ] @deprecation("stdoutSileVASP.completed is deprecated in favor of stdoutSileVASP.info.completed", "0.16.0") @@ -31,6 +33,11 @@ def completed(self): """ True if the line "General timing and accounting" was found. """ return self.info.completed() + @deprecation("stdoutSileVASP.accuracy_reached is deprecated in favor of stdoutSileVASP.info.accuracy_reached", "0.16.0") + def accuracy_reached(self): + """ True if the line "reached required accuracy" was found. """ + return self.info.accuracy_reached() + @sile_fh_open() def cpu_time(self, flag="General timing and accounting"): """ Returns the consumed cpu time (in seconds) from a given section """ @@ -41,17 +48,11 @@ def cpu_time(self, flag="General timing and accounting"): found = self.step_to(flag, allow_reread=False)[0] if found: - self._completed = True for _ in range(nskip): line = self.readline() return float(line.split()[iplace]) raise KeyError(f"{self.__class__.__name__}.cpu_time could not find flag '{flag}' in file") - @sile_fh_open() - def accuracy_reached(self): - """ True if the line "reached required accuracy" was found. """ - return self.step_to("reached required accuracy")[0] - @SileBinder() @sile_fh_open() @deprecate_argument("all", None, "use read_energy[:]() instead to get all entries", from_version="0.14") diff --git a/src/sisl/io/vasp/tests/test_stdout.py b/src/sisl/io/vasp/tests/test_stdout.py index 2b770c5ae1..77de8cb6e4 100644 --- a/src/sisl/io/vasp/tests/test_stdout.py +++ b/src/sisl/io/vasp/tests/test_stdout.py @@ -26,7 +26,7 @@ def test_diamond_outcar_energies(sisl_files): assert E0 == Eall[0] assert E == Eall[-1] assert len(Eall) > 1 - assert f.completed() + assert f.info.completed() def test_diamond_outcar_cputime(sisl_files): @@ -34,14 +34,14 @@ def test_diamond_outcar_cputime(sisl_files): f = stdoutSileVASP(f) assert f.cpu_time() > 0. - assert f.completed() + assert f.info.completed() def test_diamond_outcar_completed(sisl_files): f = sisl_files(_dir, 'diamond', 'OUTCAR') f = stdoutSileVASP(f) - assert f.completed() + assert f.info.completed() def test_diamond_outcar_trajectory(sisl_files):