Coverage for ase / calculators / abc.py: 96.61%

59 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-30 08:22 +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 collections.abc import Mapping 

10from typing import Any 

11 

12 

13class GetPropertiesMixin(ABC): 

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

15 

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

17 

18 @abstractmethod 

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

20 """Get the named property.""" 

21 

22 def get_potential_energy(self, atoms=None, force_consistent=False): 

23 if force_consistent: 

24 name = 'free_energy' 

25 else: 

26 name = 'energy' 

27 return self.get_property(name, atoms) 

28 

29 def get_potential_energies(self, atoms=None): 

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

31 

32 def get_forces(self, atoms=None): 

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

34 

35 def get_stress(self, atoms=None): 

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

37 

38 def get_stresses(self, atoms=None): 

39 """the calculator should return intensive stresses, i.e., such that 

40 stresses.sum(axis=0) == stress 

41 """ 

42 return self.get_property('stresses', atoms) 

43 

44 def get_dipole_moment(self, atoms=None): 

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

46 

47 def get_charges(self, atoms=None): 

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

49 

50 def get_magnetic_moment(self, atoms=None): 

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

52 

53 def get_magnetic_moments(self, atoms=None): 

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

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

56 

57 

58class GetOutputsMixin(ABC): 

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

60 

61 Effectively this class expresses data in calc.results as 

62 methods such as get_fermi_level(). 

63 

64 Inheriting class must implement _outputmixin_get_results(), 

65 typically returning self.results, which must be a mapping 

66 using the naming defined in ase.outputs.Properties. 

67 """ 

68 @abstractmethod 

69 def _outputmixin_get_results(self) -> Mapping[str, Any]: 

70 """Return Mapping of names to result value. 

71 

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

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

74 

75 def _get(self, name): 

76 # Cyclic import, should restructure. 

77 from ase.calculators.calculator import PropertyNotPresent 

78 dct = self._outputmixin_get_results() 

79 try: 

80 return dct[name] 

81 except KeyError: 

82 raise PropertyNotPresent(name) 

83 

84 def get_fermi_level(self): 

85 return self._get('fermi_level') 

86 

87 def get_ibz_k_points(self): 

88 return self._get('ibz_kpoints') 

89 

90 def get_k_point_weights(self): 

91 return self._get('kpoint_weights') 

92 

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

94 eigs = self._get('eigenvalues') 

95 return eigs[spin, kpt] 

96 

97 def _eigshape(self): 

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

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

100 

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

102 occs = self._get('occupations') 

103 return occs[spin, kpt] 

104 

105 def get_number_of_bands(self): 

106 return self._eigshape()[2] 

107 

108 def get_number_of_spins(self): 

109 nspins = self._eigshape()[0] 

110 assert nspins in [1, 2] 

111 return nspins 

112 

113 def get_spin_polarized(self): 

114 return self.get_number_of_spins() == 2