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

1# fmt: off 

2 

3""" 

4This module defines abstract helper classes with the objective of reducing 

5boilerplace method definitions (i.e. duplication) in calculators. 

6""" 

7 

8from abc import ABC, abstractmethod 

9from typing import Any, Mapping 

10 

11 

12class GetPropertiesMixin(ABC): 

13 """Mixin class which provides get_forces(), get_stress() and so on. 

14 

15 Inheriting class must implement get_property().""" 

16 

17 @abstractmethod 

18 def get_property(self, name, atoms=None, allow_calculation=True): 

19 """Get the named property.""" 

20 

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) 

27 

28 def get_potential_energies(self, atoms=None): 

29 return self.get_property('energies', atoms) 

30 

31 def get_forces(self, atoms=None): 

32 return self.get_property('forces', atoms) 

33 

34 def get_stress(self, atoms=None): 

35 return self.get_property('stress', atoms) 

36 

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) 

42 

43 def get_dipole_moment(self, atoms=None): 

44 return self.get_property('dipole', atoms) 

45 

46 def get_charges(self, atoms=None): 

47 return self.get_property('charges', atoms) 

48 

49 def get_magnetic_moment(self, atoms=None): 

50 return self.get_property('magmom', atoms) 

51 

52 def get_magnetic_moments(self, atoms=None): 

53 """Calculate magnetic moments projected onto atoms.""" 

54 return self.get_property('magmoms', atoms) 

55 

56 

57class GetOutputsMixin(ABC): 

58 """Mixin class for providing get_fermi_level() and others. 

59 

60 Effectively this class expresses data in calc.results as 

61 methods such as get_fermi_level(). 

62 

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. 

70 

71 This may be called many times and should hence not be 

72 expensive (except possibly the first time).""" 

73 

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) 

82 

83 def get_fermi_level(self): 

84 return self._get('fermi_level') 

85 

86 def get_ibz_k_points(self): 

87 return self._get('ibz_kpoints') 

88 

89 def get_k_point_weights(self): 

90 return self._get('kpoint_weights') 

91 

92 def get_eigenvalues(self, kpt=0, spin=0): 

93 eigs = self._get('eigenvalues') 

94 return eigs[spin, kpt] 

95 

96 def _eigshape(self): 

97 # We don't need this if we already have a Properties object. 

98 return self._get('eigenvalues').shape 

99 

100 def get_occupation_numbers(self, kpt=0, spin=0): 

101 occs = self._get('occupations') 

102 return occs[spin, kpt] 

103 

104 def get_number_of_bands(self): 

105 return self._eigshape()[2] 

106 

107 def get_number_of_spins(self): 

108 nspins = self._eigshape()[0] 

109 assert nspins in [1, 2] 

110 return nspins 

111 

112 def get_spin_polarized(self): 

113 return self.get_number_of_spins() == 2