Coverage for /builds/ase/ase/ase/calculators/abc.py: 96.55%
58 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3"""
4This module defines abstract helper classes with the objective of reducing
5boilerplace method definitions (i.e. duplication) in calculators.
6"""
8from abc import ABC, abstractmethod
9from typing import Any, Mapping
12class GetPropertiesMixin(ABC):
13 """Mixin class which provides get_forces(), get_stress() and so on.
15 Inheriting class must implement get_property()."""
17 @abstractmethod
18 def get_property(self, name, atoms=None, allow_calculation=True):
19 """Get the named property."""
21 def get_potential_energy(self, atoms=None, force_consistent=False):
22 if force_consistent:
23 name = 'free_energy'
24 else:
25 name = 'energy'
26 return self.get_property(name, atoms)
28 def get_potential_energies(self, atoms=None):
29 return self.get_property('energies', atoms)
31 def get_forces(self, atoms=None):
32 return self.get_property('forces', atoms)
34 def get_stress(self, atoms=None):
35 return self.get_property('stress', atoms)
37 def get_stresses(self, atoms=None):
38 """the calculator should return intensive stresses, i.e., such that
39 stresses.sum(axis=0) == stress
40 """
41 return self.get_property('stresses', atoms)
43 def get_dipole_moment(self, atoms=None):
44 return self.get_property('dipole', atoms)
46 def get_charges(self, atoms=None):
47 return self.get_property('charges', atoms)
49 def get_magnetic_moment(self, atoms=None):
50 return self.get_property('magmom', atoms)
52 def get_magnetic_moments(self, atoms=None):
53 """Calculate magnetic moments projected onto atoms."""
54 return self.get_property('magmoms', atoms)
57class GetOutputsMixin(ABC):
58 """Mixin class for providing get_fermi_level() and others.
60 Effectively this class expresses data in calc.results as
61 methods such as get_fermi_level().
63 Inheriting class must implement _outputmixin_get_results(),
64 typically returning self.results, which must be a mapping
65 using the naming defined in ase.outputs.Properties.
66 """
67 @abstractmethod
68 def _outputmixin_get_results(self) -> Mapping[str, Any]:
69 """Return Mapping of names to result value.
71 This may be called many times and should hence not be
72 expensive (except possibly the first time)."""
74 def _get(self, name):
75 # Cyclic import, should restructure.
76 from ase.calculators.calculator import PropertyNotPresent
77 dct = self._outputmixin_get_results()
78 try:
79 return dct[name]
80 except KeyError:
81 raise PropertyNotPresent(name)
83 def get_fermi_level(self):
84 return self._get('fermi_level')
86 def get_ibz_k_points(self):
87 return self._get('ibz_kpoints')
89 def get_k_point_weights(self):
90 return self._get('kpoint_weights')
92 def get_eigenvalues(self, kpt=0, spin=0):
93 eigs = self._get('eigenvalues')
94 return eigs[spin, kpt]
96 def _eigshape(self):
97 # We don't need this if we already have a Properties object.
98 return self._get('eigenvalues').shape
100 def get_occupation_numbers(self, kpt=0, spin=0):
101 occs = self._get('occupations')
102 return occs[spin, kpt]
104 def get_number_of_bands(self):
105 return self._eigshape()[2]
107 def get_number_of_spins(self):
108 nspins = self._eigshape()[0]
109 assert nspins in [1, 2]
110 return nspins
112 def get_spin_polarized(self):
113 return self.get_number_of_spins() == 2