Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import numpy as np
3from ase.calculators.calculator import Calculator, all_properties
4from ase.calculators.calculator import PropertyNotImplementedError
7class SinglePointCalculator(Calculator):
8 """Special calculator for a single configuration.
10 Used to remember the energy, force and stress for a given
11 configuration. If the positions, atomic numbers, unit cell, or
12 boundary conditions are changed, then asking for
13 energy/forces/stress will raise an exception."""
15 name = 'unknown'
17 def __init__(self, atoms, **results):
18 """Save energy, forces, stress, ... for the current configuration."""
19 Calculator.__init__(self)
20 self.results = {}
21 for property, value in results.items():
22 assert property in all_properties
23 if value is None:
24 continue
25 if property in ['energy', 'magmom', 'free_energy']:
26 self.results[property] = value
27 else:
28 self.results[property] = np.array(value, float)
29 self.atoms = atoms.copy()
31 def __str__(self):
32 tokens = []
33 for key, val in sorted(self.results.items()):
34 if np.isscalar(val):
35 txt = '{}={}'.format(key, val)
36 else:
37 txt = '{}=...'.format(key)
38 tokens.append(txt)
39 return '{}({})'.format(self.__class__.__name__, ', '.join(tokens))
41 def get_property(self, name, atoms=None, allow_calculation=True):
42 if atoms is None:
43 atoms = self.atoms
44 if name not in self.results or self.check_state(atoms):
45 if allow_calculation:
46 raise PropertyNotImplementedError(
47 'The property "{0}" is not available.'.format(name))
48 return None
50 result = self.results[name]
51 if isinstance(result, np.ndarray):
52 result = result.copy()
53 return result
56class SinglePointKPoint:
57 def __init__(self, weight, s, k, eps_n=[], f_n=[]):
58 self.weight = weight
59 self.s = s # spin index
60 self.k = k # k-point index
61 self.eps_n = eps_n
62 self.f_n = f_n
65def arrays_to_kpoints(eigenvalues, occupations, weights):
66 """Helper function for building SinglePointKPoints.
68 Convert eigenvalue, occupation, and weight arrays to list of
69 SinglePointKPoint objects."""
70 nspins, nkpts, nbands = eigenvalues.shape
71 assert eigenvalues.shape == occupations.shape
72 assert len(weights) == nkpts
73 kpts = []
74 for s in range(nspins):
75 for k in range(nkpts):
76 kpt = SinglePointKPoint(
77 weight=weights[k], s=s, k=k,
78 eps_n=eigenvalues[s, k], f_n=occupations[s, k])
79 kpts.append(kpt)
80 return kpts
83class SinglePointDFTCalculator(SinglePointCalculator):
84 def __init__(self, atoms,
85 efermi=None, bzkpts=None, ibzkpts=None, bz2ibz=None,
86 **results):
87 self.bz_kpts = bzkpts
88 self.ibz_kpts = ibzkpts
89 self.bz2ibz = bz2ibz
90 self.eFermi = efermi
92 SinglePointCalculator.__init__(self, atoms, **results)
93 self.kpts = None
95 def get_fermi_level(self):
96 """Return the Fermi-level(s)."""
97 return self.eFermi
99 def get_bz_to_ibz_map(self):
100 return self.bz2ibz
102 def get_bz_k_points(self):
103 """Return the k-points."""
104 return self.bz_kpts
106 def get_number_of_spins(self):
107 """Return the number of spins in the calculation.
109 Spin-paired calculations: 1, spin-polarized calculation: 2."""
110 if self.kpts is not None:
111 nspin = set()
112 for kpt in self.kpts:
113 nspin.add(kpt.s)
114 return len(nspin)
115 return None
117 def get_spin_polarized(self):
118 """Is it a spin-polarized calculation?"""
119 nos = self.get_number_of_spins()
120 if nos is not None:
121 return nos == 2
122 return None
124 def get_ibz_k_points(self):
125 """Return k-points in the irreducible part of the Brillouin zone."""
126 return self.ibz_kpts
128 def get_kpt(self, kpt=0, spin=0):
129 if self.kpts is not None:
130 counter = 0
131 for kpoint in self.kpts:
132 if kpoint.s == spin:
133 if kpt == counter:
134 return kpoint
135 counter += 1
136 return None
138 def get_k_point_weights(self):
139 """ Retunrs the weights of the k points """
140 if self.kpts is not None:
141 weights = []
142 for kpoint in self.kpts:
143 if kpoint.s == 0:
144 weights.append(kpoint.weight)
145 return np.array(weights)
146 return None
148 def get_occupation_numbers(self, kpt=0, spin=0):
149 """Return occupation number array."""
150 kpoint = self.get_kpt(kpt, spin)
151 if kpoint is not None:
152 return kpoint.f_n
153 return None
155 def get_eigenvalues(self, kpt=0, spin=0):
156 """Return eigenvalue array."""
157 kpoint = self.get_kpt(kpt, spin)
158 if kpoint is not None:
159 return kpoint.eps_n
160 return None
162 def get_homo_lumo(self):
163 """Return HOMO and LUMO energies."""
164 if self.kpts is None:
165 raise RuntimeError('No kpts')
166 eHs = []
167 eLs = []
168 for kpt in self.kpts:
169 eH, eL = self.get_homo_lumo_by_spin(kpt.s)
170 eHs.append(eH)
171 eLs.append(eL)
172 return np.array(eHs).max(), np.array(eLs).min()
174 def get_homo_lumo_by_spin(self, spin=0):
175 """Return HOMO and LUMO energies for a given spin."""
176 if self.kpts is None:
177 raise RuntimeError('No kpts')
178 for kpt in self.kpts:
179 if kpt.s == spin:
180 break
181 else:
182 raise RuntimeError('No k-point with spin {0}'.format(spin))
183 if self.eFermi is None:
184 raise RuntimeError('Fermi level is not available')
185 eH = -1.e32
186 eL = 1.e32
187 for kpt in self.kpts:
188 if kpt.s == spin:
189 for e in kpt.eps_n:
190 if e <= self.eFermi:
191 eH = max(eH, e)
192 else:
193 eL = min(eL, e)
194 return eH, eL