Skip to content

Commit

Permalink
Merge pull request #631 from zerothi/509-info-attributes
Browse files Browse the repository at this point in the history
Added Info attributes to ascii class files
  • Loading branch information
zerothi authored Oct 31, 2023
2 parents 00e0923 + 7188847 commit a6344c4
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 123 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,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.<attr>` 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.<attr>` instead to reduce
the number of methods exposed on each sile.


## [0.14.2] - 2023-10-04
Expand Down
97 changes: 28 additions & 69 deletions src/sisl/io/orca/stdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,80 +15,39 @@
__all__ = ["outputSileORCA", "stdoutSileORCA"]


_A = SileORCA.InfoAttr


@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_correction", 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()
Expand Down Expand Up @@ -161,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:
Expand Down Expand Up @@ -218,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
Expand All @@ -244,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]
Expand Down Expand Up @@ -306,7 +265,7 @@ def read_energy(self):
E["embedding"] = float(v[-2]) * Ha2eV
line = self.readline()

if self._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
Expand All @@ -329,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

Expand All @@ -355,7 +314,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)
Expand Down
10 changes: 6 additions & 4 deletions src/sisl/io/orca/tests/test_stdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -287,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):
Expand All @@ -301,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

Expand Down
34 changes: 10 additions & 24 deletions src/sisl/io/siesta/stdout.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@


Bohr2Ang = unit_convert('Bohr', 'Ang')
_A = SileSiesta.InfoAttr


def _ensure_atoms(atoms):
Expand All @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit a6344c4

Please sign in to comment.