Révision 1
ase/xrdebye.py (revision 1) | ||
---|---|---|
1 |
from math import exp, pi, sin, sqrt, cos, acos |
|
2 |
import numpy as np |
|
3 |
|
|
4 |
from ase.data import atomic_numbers |
|
5 |
|
|
6 |
# Table (1) of |
|
7 |
# D. WAASMAIER AND A. KIRFEL, Acta Cryst. (1995). A51, 416-431 |
|
8 |
waasmaier = { |
|
9 |
# a1 b1 a2 b2 a3 b3 a4 b4 a5 b5 c |
|
10 |
'C' : [2.657506, 14.780758, 1.078079, 0.776775, 1.490909, 42.086843, -4.241070, -0.000294, 0.713791, 0.239535, 4.297983], |
|
11 |
'S' : [6.372157, 1.514347, 5.154568, 22.092528, 1.473732, 0.061373, 1.635073, 55.445176, 1.209372, 0.646925, 0.154722], |
|
12 |
'Pd': [6.121511, 0.062549, 4.784063, 0.784031, 16.631683, 8.751391, 4.318258, 34.489983, 13.246773, 0.784031, 0.883099], |
|
13 |
'Ag': [6.073874, 0.055333, 17.155437, 7.896512, 4.173344, 28.443739, 0.852238, 110.376108, 17.988685, 0.716809, 0.756603], |
|
14 |
'Au': [16.777389, 0.122737, 19.317156, 8.621570, 32.979682, 1.256902, 5.595453, 38.008821, 10.576854, 0.000601, -6.279078], |
|
15 |
'P' : [1.950541, 0.908139, 4.146930, 27.044953, 1.494560, 0.071280, 1.522042, 67.520190, 5.729711, 1.981173, 0.155233], |
|
16 |
'Cl': [1.446071, 0.052357, 6.870609, 1.193165, 6.151801, 18.343416, 1.750347, 46.398394, 0.634168, 0.401005, 0.146773], |
|
17 |
} |
|
18 |
|
|
19 |
class XrDebye: |
|
20 |
def __init__(self, wavelength, alpha=1.01, damping=0.04, warn=True, |
|
21 |
method='Iwasa'): |
|
22 |
""" |
|
23 |
Obtain powder x-ray spectra. |
|
24 |
|
|
25 |
wavelength in Angstrom |
|
26 |
damping in Angstrom**2 |
|
27 |
""" |
|
28 |
self.wavelength = wavelength |
|
29 |
self.damping = damping |
|
30 |
self.alpha = alpha |
|
31 |
self.warn = warn |
|
32 |
self.method = method |
|
33 |
|
|
34 |
def set_damping(self, damping): |
|
35 |
self.damping = damping |
|
36 |
|
|
37 |
def get(self, atoms, s): |
|
38 |
"""Get the powder x-ray (XRD) pattern using the Debye-Formula. |
|
39 |
|
|
40 |
After: T. Iwasa and K. Nobusada, J. Phys. Chem. C 111 (2007) 45 |
|
41 |
s is assumed to be in 1/Angstrom |
|
42 |
""" |
|
43 |
|
|
44 |
sinth = self.wavelength * s / 2. |
|
45 |
costh = sqrt(1. - sinth**2) |
|
46 |
cos2th = cos(2. * acos(costh)) |
|
47 |
pre = exp(- self.damping * s**2 / 2) |
|
48 |
|
|
49 |
if self.method == 'Iwasa': |
|
50 |
pre *= costh / (1. + self.alpha * cos2th**2) |
|
51 |
|
|
52 |
f = {} |
|
53 |
def atomic(symbol): |
|
54 |
if not f.has_key(symbol): |
|
55 |
if self.method == 'Iwasa': |
|
56 |
f[symbol] = self.get_waasmaier(symbol, s) |
|
57 |
else: |
|
58 |
f[symbol] = atomic_numbers[symbol] |
|
59 |
return f[symbol] |
|
60 |
|
|
61 |
def sinc(x): |
|
62 |
if x < 1.e-6: |
|
63 |
x2 = x * x |
|
64 |
return 1 - x2 / 6. + x2 * x2 / 120. |
|
65 |
else: |
|
66 |
return sin(x) / x |
|
67 |
|
|
68 |
I = 0. |
|
69 |
for a in atoms: |
|
70 |
fa = atomic(a.symbol) |
|
71 |
# print a.symbol, fa |
|
72 |
for b in atoms: |
|
73 |
fb = atomic(b.symbol) |
|
74 |
|
|
75 |
if a == b: |
|
76 |
twopis = 0. |
|
77 |
else: |
|
78 |
vrij = a.position - b.position |
|
79 |
rij = np.sqrt(np.dot(vrij, vrij)) |
|
80 |
twopisr = 2 * pi * s * rij |
|
81 |
|
|
82 |
I += fa * fb * sinc(twopisr) |
|
83 |
|
|
84 |
return pre * I |
|
85 |
|
|
86 |
def get_waasmaier(self, symbol, s): |
|
87 |
"""Scattering factor for free atoms.""" |
|
88 |
if symbol == 'H': |
|
89 |
# XXXX implement analytical H |
|
90 |
return 0 |
|
91 |
elif waasmaier.has_key(symbol): |
|
92 |
abc = waasmaier[symbol] |
|
93 |
f = abc[10] |
|
94 |
s2 = s*s |
|
95 |
for i in range(5): |
|
96 |
f += abc[2 * i] * exp(-abc[2 * i + 1] * s2) |
|
97 |
return f |
|
98 |
if self.warn: |
|
99 |
print '<xrdebye::get_atomic> Element', symbol, 'not available' |
|
100 |
return 0 |
|
101 |
|
ase/units.py (revision 1) | ||
---|---|---|
1 |
from math import pi, sqrt |
|
2 |
|
|
3 |
# Constants from Konrad Hinsen's PhysicalQuantities module: |
|
4 |
_c = 299792458. # speed of light, m/s |
|
5 |
_mu0 = 4.e-7 * pi # permeability of vacuum |
|
6 |
_eps0 = 1 / _mu0 / _c**2 # permittivity of vacuum |
|
7 |
_Grav = 6.67259e-11 # gravitational constant |
|
8 |
_hplanck = 6.6260755e-34 # Planck constant, J s |
|
9 |
_hbar = _hplanck / (2 * pi) # Planck constant / 2pi, J s |
|
10 |
_e = 1.60217733e-19 # elementary charge |
|
11 |
_me = 9.1093897e-31 # electron mass |
|
12 |
_mp = 1.6726231e-27 # proton mass |
|
13 |
_Nav = 6.0221367e23 # Avogadro number |
|
14 |
_k = 1.380658e-23 # Boltzmann constant, J/K |
|
15 |
_amu = 1.6605402e-27 # atomic mass unit, kg |
|
16 |
|
|
17 |
Ang = Angstrom = 1.0 |
|
18 |
nm = 10.0 |
|
19 |
Bohr = 4e10 * pi * _eps0 * _hbar**2 / _me / _e**2 # Bohr radius |
|
20 |
|
|
21 |
eV = 1.0 |
|
22 |
Hartree = _me * _e**3 / 16 / pi**2 / _eps0**2 / _hbar**2 |
|
23 |
kJ = 1000.0 / _e |
|
24 |
kcal = 4.184 * kJ |
|
25 |
mol = _Nav |
|
26 |
Rydberg = 0.5 * Hartree |
|
27 |
Ry = Rydberg |
|
28 |
Ha = Hartree |
|
29 |
|
|
30 |
second = 1e10 * sqrt(_e / _amu) |
|
31 |
fs = 1e-15 * second |
|
32 |
|
|
33 |
kB = _k / _e # Boltzmann constant, eV/K |
|
34 |
|
|
35 |
Pascal = (1 / _e) / 1e30 # J/m^3 |
|
36 |
GPa = 1e9 * Pascal |
|
37 |
|
|
38 |
Debye = 1e11 *_e * _c |
|
39 |
|
|
40 |
del pi, sqrt |
ase/svnversion_io.py (revision 1) | ||
---|---|---|
1 |
# Copyright (C) 2003 CAMP |
|
2 |
# Please see the accompanying LICENSE file for further information. |
|
3 |
|
|
4 |
from os import path |
|
5 |
try: |
|
6 |
from subprocess import Popen, PIPE |
|
7 |
except ImportError: |
|
8 |
from os import popen3 |
|
9 |
else: |
|
10 |
def popen3(cmd): |
|
11 |
p = Popen(cmd, shell=True, close_fds=True, |
|
12 |
stdin=PIPE, stdout=PIPE, stderr=PIPE) |
|
13 |
return p.stdin, p.stdout, p.stderr |
|
14 |
|
|
15 |
def write_svnversion(svnversion, dir): |
|
16 |
svnversionfile = path.join(dir, 'svnversion.py') |
|
17 |
f = open(svnversionfile,'w') |
|
18 |
f.write('svnversion = "%s"\n' % svnversion) |
|
19 |
f.close() |
|
20 |
print 'svnversion = ' +svnversion+' written to '+svnversionfile |
|
21 |
# assert svn:ignore property if the installation is under svn control |
|
22 |
# because svnversion.py has to be ignored by svn! |
|
23 |
cmd = popen3('svn propset svn:ignore svnversion.py '+dir)[1] |
|
24 |
output = cmd.read() |
|
25 |
cmd.close() |
|
26 |
|
|
27 |
def get_svnversion_from_svn(dir): |
|
28 |
# try to get the last svn version number from svnversion |
|
29 |
cmd = popen3('svnversion -n '+dir)[1] # assert we are in the project dir |
|
30 |
output = cmd.read().strip() |
|
31 |
cmd.close() |
|
32 |
if output.startswith('exported'): |
|
33 |
# we build from exported source (e.g. rpmbuild) |
|
34 |
output = None |
|
35 |
return output |
|
36 |
|
|
37 |
svnversion = get_svnversion_from_svn(dir='ase') |
|
38 |
if svnversion: |
|
39 |
write_svnversion(svnversion, dir='ase') |
ase/neb.py (revision 1) | ||
---|---|---|
1 |
from math import sqrt |
|
2 |
|
|
3 |
import numpy as np |
|
4 |
|
|
5 |
from ase.parallel import world, rank, size |
|
6 |
from ase.calculators.singlepoint import SinglePointCalculator |
|
7 |
from ase.io import read |
|
8 |
|
|
9 |
class NEB: |
|
10 |
def __init__(self, images, k=0.1, climb=False, parallel=False): |
|
11 |
self.images = images |
|
12 |
self.k = k |
|
13 |
self.climb = climb |
|
14 |
self.parallel = parallel |
|
15 |
self.natoms = len(images[0]) |
|
16 |
self.nimages = len(images) |
|
17 |
self.emax = np.nan |
|
18 |
|
|
19 |
def interpolate(self): |
|
20 |
pos1 = self.images[0].get_positions() |
|
21 |
pos2 = self.images[-1].get_positions() |
|
22 |
d = (pos2 - pos1) / (self.nimages - 1.0) |
|
23 |
for i in range(1, self.nimages - 1): |
|
24 |
self.images[i].set_positions(pos1 + i * d) |
|
25 |
|
|
26 |
def get_positions(self): |
|
27 |
positions = np.empty(((self.nimages - 2) * self.natoms, 3)) |
|
28 |
n1 = 0 |
|
29 |
for image in self.images[1:-1]: |
|
30 |
n2 = n1 + self.natoms |
|
31 |
positions[n1:n2] = image.get_positions() |
|
32 |
n1 = n2 |
|
33 |
return positions |
|
34 |
|
|
35 |
def set_positions(self, positions): |
|
36 |
n1 = 0 |
|
37 |
for image in self.images[1:-1]: |
|
38 |
n2 = n1 + self.natoms |
|
39 |
image.set_positions(positions[n1:n2]) |
|
40 |
n1 = n2 |
|
41 |
|
|
42 |
def get_forces(self): |
|
43 |
"""Evaluate and return the forces.""" |
|
44 |
images = self.images |
|
45 |
forces = np.empty(((self.nimages - 2), self.natoms, 3)) |
|
46 |
energies = np.empty(self.nimages - 2) |
|
47 |
|
|
48 |
if not self.parallel: |
|
49 |
# Do all images - one at a time: |
|
50 |
for i in range(1, self.nimages - 1): |
|
51 |
energies[i - 1] = images[i].get_potential_energy() |
|
52 |
forces[i - 1] = images[i].get_forces() |
|
53 |
else: |
|
54 |
# Parallelize over images: |
|
55 |
i = rank * (self.nimages - 2) // size + 1 |
|
56 |
try: |
|
57 |
energies[i - 1] = images[i].get_potential_energy() |
|
58 |
forces[i - 1] = images[i].get_forces() |
|
59 |
except: |
|
60 |
# Make sure other images also fail: |
|
61 |
error = world.sum(1.0) |
|
62 |
raise |
|
63 |
else: |
|
64 |
error = world.sum(0.0) |
|
65 |
if error: |
|
66 |
raise RuntimeError('Parallel NEB failed!') |
|
67 |
|
|
68 |
for i in range(1, self.nimages - 1): |
|
69 |
root = (i - 1) * size // (self.nimages - 2) |
|
70 |
world.broadcast(energies[i - 1:i], root) |
|
71 |
world.broadcast(forces[i - 1], root) |
|
72 |
|
|
73 |
imax = 1 + np.argsort(energies)[-1] |
|
74 |
self.emax = energies[imax - 1] |
|
75 |
|
|
76 |
tangent1 = images[1].get_positions() - images[0].get_positions() |
|
77 |
for i in range(1, self.nimages - 1): |
|
78 |
tangent2 = (images[i + 1].get_positions() - |
|
79 |
images[i].get_positions()) |
|
80 |
if i < imax: |
|
81 |
tangent = tangent2 |
|
82 |
elif i > imax: |
|
83 |
tangent = tangent1 |
|
84 |
else: |
|
85 |
tangent = tangent1 + tangent2 |
|
86 |
|
|
87 |
tt = np.vdot(tangent, tangent) |
|
88 |
f = forces[i - 1] |
|
89 |
ft = np.vdot(f, tangent) |
|
90 |
if i == imax and self.climb: |
|
91 |
f -= 2 * ft / tt * tangent |
|
92 |
else: |
|
93 |
f -= ft / tt * tangent |
|
94 |
f -= (np.vdot(tangent1 - tangent2, tangent) * |
|
95 |
self.k / tt * tangent) |
|
96 |
|
|
97 |
tangent1 = tangent2 |
|
98 |
|
|
99 |
return forces.reshape((-1, 3)) |
|
100 |
|
|
101 |
def get_potential_energy(self): |
|
102 |
return self.emax |
|
103 |
|
|
104 |
def __len__(self): |
|
105 |
return (self.nimages - 2) * self.natoms |
|
106 |
|
|
107 |
class SingleCalculatorNEB(NEB): |
|
108 |
def __init__(self, images, k=0.1, climb=False): |
|
109 |
if isinstance(images, str): |
|
110 |
# this is a filename |
|
111 |
traj = read(images, '0:') |
|
112 |
images = [] |
|
113 |
for atoms in traj: |
|
114 |
images.append(atoms) |
|
115 |
|
|
116 |
NEB.__init__(self, images, k, climb, False) |
|
117 |
self.calculators = [None] * self.nimages |
|
118 |
self.energies_ok = False |
|
119 |
|
|
120 |
def interpolate(self, initial=0, final=-1): |
|
121 |
"""Interpolate linearly between initial and final images.""" |
|
122 |
if final < 0: |
|
123 |
final = self.nimages + final |
|
124 |
n = final - initial |
|
125 |
pos1 = self.images[initial].get_positions() |
|
126 |
pos2 = self.images[final].get_positions() |
|
127 |
d = (pos2 - pos1) / n |
|
128 |
for i in range(1, n): |
|
129 |
self.images[initial + i].set_positions(pos1 + i * d) |
|
130 |
|
|
131 |
def refine(self, steps=1, begin=0, end=-1): |
|
132 |
"""Refine the NEB trajectory.""" |
|
133 |
if end < 0: |
|
134 |
end = self.nimages + end |
|
135 |
j = begin |
|
136 |
n = end - begin |
|
137 |
for i in range(n): |
|
138 |
for k in range(steps): |
|
139 |
self.images.insert(j + 1, self.images[j].copy()) |
|
140 |
self.calculators.insert(j + 1, None) |
|
141 |
self.nimages = len(self.images) |
|
142 |
self.interpolate(j, j + steps + 1) |
|
143 |
j += steps + 1 |
|
144 |
|
|
145 |
def set_positions(self, positions): |
|
146 |
# new positions -> new forces |
|
147 |
if self.energies_ok: |
|
148 |
# restore calculators |
|
149 |
self.set_calculators(self.calculators[1:-1]) |
|
150 |
NEB.set_positions(self, positions) |
|
151 |
|
|
152 |
def get_calculators(self): |
|
153 |
"""Return the original calculators.""" |
|
154 |
calculators = [] |
|
155 |
for i, image in enumerate(self.images): |
|
156 |
if self.calculators[i] is None: |
|
157 |
calculators.append(image.get_calculator()) |
|
158 |
else: |
|
159 |
calculators.append(self.calculators[i]) |
|
160 |
return calculators |
|
161 |
|
|
162 |
def set_calculators(self, calculators): |
|
163 |
"""Set new calculators to the images.""" |
|
164 |
self.energies_ok = False |
|
165 |
|
|
166 |
if not isinstance(calculators, list): |
|
167 |
calculators = [calculators] * self.nimages |
|
168 |
|
|
169 |
n = len(calculators) |
|
170 |
if n == self.nimages: |
|
171 |
for i in range(self.nimages): |
|
172 |
self.images[i].set_calculator(calculators[i]) |
|
173 |
elif n == self.nimages - 2: |
|
174 |
for i in range(1, self.nimages -1): |
|
175 |
self.images[i].set_calculator(calculators[i-1]) |
|
176 |
else: |
|
177 |
raise RuntimeError( |
|
178 |
'len(calculators)=%d does not fit to len(images)=%d' |
|
179 |
% (n, self.nimages)) |
|
180 |
|
|
181 |
def get_energies_and_forces(self, all=False): |
|
182 |
"""Evaluate energies and forces and hide the calculators""" |
|
183 |
if self.energies_ok: |
|
184 |
return |
|
185 |
|
|
186 |
images = self.images |
|
187 |
forces = np.zeros(((self.nimages - 2), self.natoms, 3)) |
|
188 |
energies = np.zeros(self.nimages - 2) |
|
189 |
self.emax = -1.e32 |
|
190 |
|
|
191 |
def calculate_and_hide(i): |
|
192 |
image = self.images[i] |
|
193 |
calc = image.get_calculator() |
|
194 |
if self.calculators[i] is None: |
|
195 |
self.calculators[i] = calc |
|
196 |
if calc is not None: |
|
197 |
if not isinstance(calc, SinglePointCalculator): |
|
198 |
self.images[i].set_calculator( |
|
199 |
SinglePointCalculator(image.get_potential_energy(), |
|
200 |
image.get_forces(), |
|
201 |
None, |
|
202 |
None, |
|
203 |
image)) |
|
204 |
self.emax = min(self.emax, image.get_potential_energy()) |
|
205 |
|
|
206 |
if all and self.calculators[0] is None: |
|
207 |
calculate_and_hide(0) |
|
208 |
|
|
209 |
# Do all images - one at a time: |
|
210 |
for i in range(1, self.nimages - 1): |
|
211 |
calculate_and_hide(i) |
|
212 |
|
|
213 |
if all and self.calculators[-1] is None: |
|
214 |
calculate_and_hide(-1) |
|
215 |
|
|
216 |
self.energies_ok = True |
|
217 |
|
|
218 |
def get_forces(self): |
|
219 |
self.get_energies_and_forces() |
|
220 |
return NEB.get_forces(self) |
|
221 |
|
|
222 |
def n(self): |
|
223 |
return self.nimages |
|
224 |
|
|
225 |
def write(self, filename): |
|
226 |
from ase.io.trajectory import PickleTrajectory |
|
227 |
traj = PickleTrajectory(filename, 'w', self) |
|
228 |
traj.write() |
|
229 |
traj.close() |
|
230 |
|
|
231 |
def __add__(self, other): |
|
232 |
for image in other: |
|
233 |
self.images.append(image) |
|
234 |
return self |
|
235 |
|
|
236 |
def fit(images): |
|
237 |
E = [i.get_potential_energy() for i in images] |
|
238 |
F = [i.get_forces() for i in images] |
|
239 |
R = [i.get_positions() for i in images] |
|
240 |
return fit0(E, F, R) |
|
241 |
|
|
242 |
def fit0(E, F, R): |
|
243 |
E = np.array(E) - E[0] |
|
244 |
n = len(E) |
|
245 |
Efit = np.empty((n - 1) * 20 + 1) |
|
246 |
Sfit = np.empty((n - 1) * 20 + 1) |
|
247 |
|
|
248 |
s = [0] |
|
249 |
for i in range(n - 1): |
|
250 |
s.append(s[-1] + sqrt(((R[i + 1] - R[i])**2).sum())) |
|
251 |
|
|
252 |
lines = [] |
|
253 |
for i in range(n): |
|
254 |
if i == 0: |
|
255 |
d = R[1] - R[0] |
|
256 |
ds = 0.5 * s[1] |
|
257 |
elif i == n - 1: |
|
258 |
d = R[-1] - R[-2] |
|
259 |
ds = 0.5 * (s[-1] - s[-2]) |
|
260 |
else: |
|
261 |
d = R[i + 1] - R[i - 1] |
|
262 |
ds = 0.25 * (s[i + 1] - s[i - 1]) |
|
263 |
|
|
264 |
d = d / sqrt((d**2).sum()) |
|
265 |
dEds = -(F[i] * d).sum() |
|
266 |
x = np.linspace(s[i] - ds, s[i] + ds, 3) |
|
267 |
y = E[i] + dEds * (x - s[i]) |
|
268 |
lines.append((x, y)) |
|
269 |
|
|
270 |
if i > 0: |
|
271 |
s0 = s[i - 1] |
|
272 |
s1 = s[i] |
|
273 |
x = np.linspace(s0, s1, 20, endpoint=False) |
|
274 |
c = np.linalg.solve(np.array([(1, s0, s0**2, s0**3), |
|
275 |
(1, s1, s1**2, s1**3), |
|
276 |
(0, 1, 2 * s0, 3 * s0**2), |
|
277 |
(0, 1, 2 * s1, 3 * s1**2)]), |
|
278 |
np.array([E[i - 1], E[i], dEds0, dEds])) |
|
279 |
y = c[0] + x * (c[1] + x * (c[2] + x * c[3])) |
|
280 |
Sfit[(i - 1) * 20:i * 20] = x |
|
281 |
Efit[(i - 1) * 20:i * 20] = y |
|
282 |
|
|
283 |
dEds0 = dEds |
|
284 |
|
|
285 |
Sfit[-1] = s[-1] |
|
286 |
Efit[-1] = E[-1] |
|
287 |
return s, E, Sfit, Efit, lines |
ase/transport/__init__.py (revision 1) | ||
---|---|---|
1 |
from calculators import TransportCalculator |
ase/transport/tools.py (revision 1) | ||
---|---|---|
1 |
import numpy as np |
|
2 |
from math import sqrt, exp |
|
3 |
|
|
4 |
def tri2full(H_nn, UL='L'): |
|
5 |
"""Fill in values of hermitian matrix. |
|
6 |
|
|
7 |
Fill values in lower or upper triangle of H_nn based on the opposite |
|
8 |
triangle, such that the resulting matrix is symmetric/hermitian. |
|
9 |
|
|
10 |
UL='U' will copy (conjugated) values from upper triangle into the |
|
11 |
lower triangle. |
|
12 |
|
|
13 |
UL='L' will copy (conjugated) values from lower triangle into the |
|
14 |
upper triangle. |
|
15 |
""" |
|
16 |
N, tmp = H_nn.shape |
|
17 |
assert N == tmp, 'Matrix must be square' |
|
18 |
#assert np.isreal(H_nn.diagonal()).all(), 'Diagonal should be real' |
|
19 |
if UL != 'L': |
|
20 |
H_nn = H_nn.T |
|
21 |
|
|
22 |
for n in range(N - 1): |
|
23 |
H_nn[n, n + 1:] = H_nn[n + 1:, n].conj() |
|
24 |
|
|
25 |
def dagger(matrix): |
|
26 |
return np.conj(matrix.T) |
|
27 |
|
|
28 |
def rotate_matrix(h, u): |
|
29 |
return np.dot(u.T.conj(), np.dot(h, u)) |
|
30 |
|
|
31 |
def get_subspace(matrix, index): |
|
32 |
"""Get the subspace spanned by the basis function listed in index""" |
|
33 |
assert matrix.ndim == 2 and matrix.shape[0] == matrix.shape[1] |
|
34 |
return matrix.take(index, 0).take(index, 1) |
|
35 |
|
|
36 |
permute_matrix = get_subspace |
|
37 |
|
|
38 |
def normalize(matrix, S=None): |
|
39 |
"""Normalize column vectors. |
|
40 |
|
|
41 |
:: |
|
42 |
|
|
43 |
<matrix[:,i]| S |matrix[:,i]> = 1 |
|
44 |
|
|
45 |
""" |
|
46 |
for col in matrix.T: |
|
47 |
if S is None: |
|
48 |
col /= np.linalg.norm(col) |
|
49 |
else: |
|
50 |
col /= np.sqrt(np.dot(col.conj(), np.dot(S, col))) |
|
51 |
|
|
52 |
def subdiagonalize(h_ii, s_ii, index_j): |
|
53 |
nb = h_ii.shape[0] |
|
54 |
nb_sub = len(index_j) |
|
55 |
h_sub_jj = get_subspace(h_ii, index_j) |
|
56 |
s_sub_jj = get_subspace(s_ii, index_j) |
|
57 |
e_j, v_jj = np.linalg.eig(np.linalg.solve(s_sub_jj, h_sub_jj)) |
|
58 |
normalize(v_jj, s_sub_jj) # normalize: <v_j|s|v_j> = 1 |
|
59 |
permute_list = np.argsort(e_j.real) |
|
60 |
e_j = np.take(e_j, permute_list) |
|
61 |
v_jj = np.take(v_jj, permute_list, axis=1) |
|
62 |
|
|
63 |
#setup transformation matrix |
|
64 |
c_ii = np.identity(nb, complex) |
|
65 |
for i in xrange(nb_sub): |
|
66 |
for j in xrange(nb_sub): |
|
67 |
c_ii[index_j[i], index_j[j]] = v_jj[i, j] |
|
68 |
|
|
69 |
h1_ii = rotate_matrix(h_ii, c_ii) |
|
70 |
s1_ii = rotate_matrix(s_ii, c_ii) |
|
71 |
|
|
72 |
return h1_ii, s1_ii, c_ii, e_j |
|
73 |
|
|
74 |
def cutcoupling(h, s, index_n): |
|
75 |
for i in index_n: |
|
76 |
s[:, i] = 0.0 |
|
77 |
s[i, :] = 0.0 |
|
78 |
s[i, i] = 1.0 |
|
79 |
Ei = h[i, i] |
|
80 |
h[:, i] = 0.0 |
|
81 |
h[i, :] = 0.0 |
|
82 |
h[i, i] = Ei |
|
83 |
|
|
84 |
def fermidistribution(energy, kt): |
|
85 |
#fermi level is fixed to zero |
|
86 |
return 1.0 / (1.0 + np.exp(energy / kt) ) |
|
87 |
|
|
88 |
def fliplr(a): |
|
89 |
length=len(a) |
|
90 |
b = [0] * length |
|
91 |
for i in range(length): |
|
92 |
b[i] = a[length - i - 1] |
|
93 |
return b |
|
94 |
|
|
95 |
def plot_path(energy): |
|
96 |
import pylab |
|
97 |
pylab.plot(np.real(energy), np.imag(energy), 'b--o') |
|
98 |
pylab.show() |
|
99 |
|
|
100 |
|
|
101 |
def function_integral(function, calcutype): |
|
102 |
#return the integral of the 'function' on 'intrange' |
|
103 |
#the function can be a value or a matrix, arg1,arg2 are the possible |
|
104 |
#parameters of the function |
|
105 |
|
|
106 |
intctrl = function.intctrl |
|
107 |
if calcutype == 'eqInt': |
|
108 |
intrange = intctrl.eqintpath |
|
109 |
tol = intctrl.eqinttol |
|
110 |
if hasattr(function.intctrl, 'eqpath_radius'): |
|
111 |
radius = function.intctrl.eqpath_radius |
|
112 |
else: |
|
113 |
radius = -1 |
|
114 |
if hasattr(function.intctrl, 'eqpath_origin'): |
|
115 |
origin = function.intctrl.eqpath_origin |
|
116 |
else: |
|
117 |
origin = 1000 |
|
118 |
elif calcutype == 'neInt': |
|
119 |
intrange = intctrl.neintpath |
|
120 |
tol = intctrl.neinttol |
|
121 |
radius = -1 |
|
122 |
origin = 1000 |
|
123 |
elif calcutype == 'locInt': |
|
124 |
intrange = intctrl.locintpath |
|
125 |
tol = intctrl.locinttol |
|
126 |
if hasattr(function.intctrl, 'locpath_radius'): |
|
127 |
radius = function.intctrl.locpath_radius |
|
128 |
else: |
|
129 |
radius = -1 |
|
130 |
if hasattr(function.intctrl, 'locpath_origin'): |
|
131 |
origin = function.intctrl.locpath_origin |
|
132 |
else: |
|
133 |
origin = 1000 |
|
134 |
trace = 0 |
|
135 |
a = 0. |
|
136 |
b = 1. |
|
137 |
|
|
138 |
#Initialize with 13 function evaluations. |
|
139 |
c = (a + b) / 2 |
|
140 |
h = (b - a) / 2 |
|
141 |
realmin = 2e-17 |
|
142 |
|
|
143 |
s = [.942882415695480, sqrt(2.0/3), |
|
144 |
.641853342345781, 1/sqrt(5.0), .236383199662150] |
|
145 |
s1 = [0] * len(s) |
|
146 |
s2 = [0] * len(s) |
|
147 |
for i in range(len(s)): |
|
148 |
s1[i] = c - s[i] * h |
|
149 |
s2[i] = c + fliplr(s)[i] * h |
|
150 |
x0 = [a] + s1 + [c] + s2 + [b] |
|
151 |
|
|
152 |
s0 = [.0158271919734802, .094273840218850, .155071987336585, |
|
153 |
.188821573960182, .199773405226859, .224926465333340] |
|
154 |
w0 = s0 + [.242611071901408] + fliplr(s0) |
|
155 |
w1 = [1, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 1] |
|
156 |
w2 = [77, 0, 432, 0, 625, 0, 672, 0, 625, 0, 432, 0, 77] |
|
157 |
for i in range(len(w1)): |
|
158 |
w1[i] = w1[i] / 6.0 |
|
159 |
w2[i] = w2[i] / 1470.0 |
|
160 |
|
|
161 |
dZ = [intrange[:len(intrange) - 1], intrange[1:]] |
|
162 |
hmin = [0] * len(dZ[1]) |
|
163 |
|
|
164 |
path_type = [] |
|
165 |
for i in range(len(intrange) - 1): |
|
166 |
rs = np.abs(dZ[0][i] - origin) |
|
167 |
re = np.abs(dZ[1][i] - origin) |
|
168 |
if abs(rs - radius) < 1.0e-8 and abs(re - radius) < 1.0e-8: |
|
169 |
path_type.append('half_circle') |
|
170 |
else: |
|
171 |
path_type.append('line') |
|
172 |
|
|
173 |
for i in range(len(dZ[1])): |
|
174 |
if path_type[i] == 'half_circle': |
|
175 |
dZ[0][i] = 0 |
|
176 |
dZ[1][i] = np.pi |
|
177 |
for i in range(len(dZ[1])): |
|
178 |
dZ[1][i] = dZ[1][i] - dZ[0][i] |
|
179 |
hmin[i] = realmin / 1024 * abs(dZ[1][i]) |
|
180 |
|
|
181 |
|
|
182 |
temp = np.array([[1] * 13, x0]).transpose() |
|
183 |
|
|
184 |
Zx = np.dot(temp, np.array(dZ)) |
|
185 |
|
|
186 |
Zxx = [] |
|
187 |
for i in range(len(intrange) - 1): |
|
188 |
for j in range(13): |
|
189 |
Zxx.append(Zx[j][i]) |
|
190 |
|
|
191 |
ns = 0 |
|
192 |
ne = 12 |
|
193 |
if path_type[0] == 'line': |
|
194 |
yns = function.calgfunc(Zxx[ns], calcutype) |
|
195 |
elif path_type[0] == 'half_circle': |
|
196 |
energy = origin + radius * np.exp((np.pi - Zxx[ns + i]) * 1.j) |
|
197 |
yns = -1.j * radius * np.exp(-1.j* Zxx[ns +i])* function.calgfunc(energy, calcutype) |
|
198 |
fcnt = 0 |
|
199 |
|
|
200 |
|
|
201 |
for n in range(len(intrange)-1): |
|
202 |
# below evaluate the integral and adjust the tolerance |
|
203 |
Q1pQ0 = yns * (w1[0] - w0[0]) |
|
204 |
Q2pQ0 = yns * (w2[0] - w0[0]) |
|
205 |
fcnt = fcnt + 12 |
|
206 |
for i in range(1,12): |
|
207 |
if path_type[n] == 'line': |
|
208 |
yne = function.calgfunc(Zxx[ns + i], calcutype) |
|
209 |
elif path_type[n] == 'half_circle': |
|
210 |
energy = origin + radius * np.exp((np.pi -Zxx[ns + i]) * 1.j) |
|
211 |
yne = -1.j * radius * np.exp(-1.j * Zxx[ns + i])* function.calgfunc(energy, calcutype) |
|
212 |
Q1pQ0 += yne * (w1[i] - w0[i]) |
|
213 |
Q2pQ0 += yne * (w2[i] - w0[i]) |
|
214 |
|
|
215 |
# Increase the tolerance if refinement appears to be effective |
|
216 |
r = np.abs(Q2pQ0) / (np.abs(Q1pQ0) + np.abs(realmin)) |
|
217 |
dim = np.product(r.shape) |
|
218 |
r = np.sum(r) / dim |
|
219 |
if r > 0 and r < 1: |
|
220 |
thistol = tol / r |
|
221 |
else: |
|
222 |
thistol = tol |
|
223 |
if path_type[n] == 'line': |
|
224 |
yne = function.calgfunc(Zxx[ne], calcutype) |
|
225 |
elif path_type[n] == 'half_circle': |
|
226 |
energy = origin + radius * np.exp((np.pi -Zxx[ne]) * 1.j) |
|
227 |
yne = -1.j * radius * np.exp(-1.j * Zxx[ne])* function.calgfunc(energy, calcutype) |
|
228 |
#Call the recursive core integrator |
|
229 |
|
|
230 |
Qk, xpk, wpk, fcnt, warn = quadlstep(function, Zxx[ns], |
|
231 |
Zxx[ne], yns, yne, |
|
232 |
thistol, trace, fcnt, |
|
233 |
hmin[n], calcutype, path_type[n], |
|
234 |
origin, radius) |
|
235 |
if n == 0: |
|
236 |
Q = np.copy(Qk) |
|
237 |
Xp = xpk[:] |
|
238 |
Wp = wpk[:] |
|
239 |
else: |
|
240 |
Q += Qk |
|
241 |
Xp = Xp[:-1] + xpk |
|
242 |
Wp = Wp[:-1] + [Wp[-1] + wpk[0]] + wpk[1:] |
|
243 |
if warn == 1: |
|
244 |
print 'warning: Minimum step size reached,singularity possible' |
|
245 |
elif warn == 2: |
|
246 |
print 'warning: Maximum function count excced; singularity likely' |
|
247 |
elif warn == 3: |
|
248 |
print 'warning: Infinite or Not-a-Number function value encountered' |
|
249 |
else: |
|
250 |
pass |
|
251 |
|
|
252 |
ns += 13 |
|
253 |
ne += 13 |
|
254 |
yns = np.copy(yne) |
|
255 |
|
|
256 |
return Q,Xp,Wp,fcnt |
|
257 |
|
|
258 |
def quadlstep(f, Za, Zb, fa, fb, tol, trace, fcnt, hmin, calcutype, |
|
259 |
path_type, origin, radius): |
|
260 |
#Gaussian-Lobatto and Kronrod method |
|
261 |
#QUADLSTEP Recursive core routine for integral |
|
262 |
#input parameters: |
|
263 |
# f ---------- function, here we just use the module calgfunc |
|
264 |
# to return the value, if wanna use it for |
|
265 |
# another one, change it |
|
266 |
# Za, Zb ---------- the start and end point of the integral |
|
267 |
# fa, fb ---------- the function value on Za and Zb |
|
268 |
# fcnt ---------- the number of the funtion recalled till now |
|
269 |
#output parameters: |
|
270 |
# Q ---------- integral |
|
271 |
# Xp ---------- selected points |
|
272 |
# Wp ---------- weight |
|
273 |
# fcnt ---------- the number of the function recalled till now |
|
274 |
|
|
275 |
maxfcnt = 10000 |
|
276 |
|
|
277 |
# Evaluate integrand five times in interior of subintrval [a,b] |
|
278 |
Zh = (Zb - Za) / 2.0 |
|
279 |
if abs(Zh) < hmin: |
|
280 |
# Minimun step size reached; singularity possible |
|
281 |
Q = Zh * (fa + fb) |
|
282 |
if path_type == 'line': |
|
283 |
Xp = [Za, Zb] |
|
284 |
elif path_type == 'half_circle': |
|
285 |
Xp = [origin + radius * np.exp((np.pi - Za) * 1.j), |
|
286 |
origin + radius * np.exp((np.pi - Zb) * 1.j)] |
|
287 |
Wp = [Zh, Zh] |
|
288 |
warn = 1 |
|
289 |
return Q, Xp, Wp, fcnt, warn |
|
290 |
fcnt += 5 |
|
291 |
if fcnt > maxfcnt: |
|
292 |
#Maximum function count exceed; singularity likely |
|
293 |
Q = Zh * (fa + fb) |
|
294 |
if path_type == 'line': |
|
295 |
Xp = [Za, Zb] |
|
296 |
elif path_type == 'half_circle': |
|
297 |
Xp = [origin + radius * np.exp((np.pi - Za) * 1.j), |
|
298 |
origin + radius * np.exp((np.pi - Zb) * 1.j)] |
|
299 |
Wp = [Zh, Zh] |
|
300 |
warn = 2 |
|
301 |
return Q, Xp, Wp, fcnt, warn |
|
302 |
x = [0.18350341907227, 0.55278640450004, 1.0, |
|
303 |
1.44721359549996, 1.81649658092773]; |
|
304 |
Zx = [0] * len(x) |
|
305 |
y = [0] * len(x) |
|
306 |
for i in range(len(x)): |
|
307 |
x[i] *= 0.5 |
|
308 |
Zx[i] = Za + (Zb - Za) * x[i] |
|
309 |
if path_type == 'line': |
|
310 |
y[i] = f.calgfunc(Zx[i], calcutype) |
|
311 |
elif path_type == 'half_circle': |
|
312 |
energy = origin + radius * np.exp((np.pi - Zx[i]) * 1.j) |
|
313 |
y[i] = f.calgfunc(energy, calcutype) |
|
314 |
#Four point Lobatto quadrature |
|
315 |
s1 = [1.0, 0.0, 5.0, 0.0, 5.0, 0.0, 1.0] |
|
316 |
s2 = [77.0, 432.0, 625.0, 672.0, 625.0, 432.0, 77.0] |
|
317 |
Wk = [0] * 7 |
|
318 |
Wp = [0] * 7 |
|
319 |
for i in range(7): |
|
320 |
Wk[i] = (Zh / 6.0) * s1[i] |
|
321 |
Wp[i] = (Zh / 1470.0) * s2[i] |
|
322 |
if path_type == 'line': |
|
323 |
Xp = [Za] + Zx + [Zb] |
|
324 |
elif path_type == 'half_circle': |
|
325 |
Xp = [Za] + Zx + [Zb] |
|
326 |
for i in range(7): |
|
327 |
factor = -1.j * radius * np.exp(1.j * (np.pi - Xp[i])) |
|
328 |
Wk[i] *= factor |
|
329 |
Wp[i] *= factor |
|
330 |
Xp[i] = origin + radius * np.exp((np.pi - Xp[i]) * 1.j) |
|
331 |
Qk = fa * Wk[0] + fb * Wk[6] |
|
332 |
Q = fa * Wp[0] + fb * Wp[6] |
|
333 |
for i in range(1, 6): |
|
334 |
Qk += y[i-1] * Wk[i] |
|
335 |
Q += y[i-1] * Wp[i] |
|
336 |
if np.isinf(np.max(np.abs(Q))): |
|
337 |
Q = Zh * (fa + fb) |
|
338 |
if path_type == 'line': |
|
339 |
Xp = [Za, Zb] |
|
340 |
elif path_type == 'half_circle': |
|
341 |
Xp = [origin + radius * np.exp((np.pi - Za) * 1.j), |
|
342 |
origin + radius * np.exp((np.pi - Zb) * 1.j)] |
|
343 |
Wp = [Zh, Zh] |
|
344 |
warn = 3 |
|
345 |
return Qk, Xp, Wp, fcnt, warn |
|
346 |
else: |
|
347 |
pass |
|
348 |
if trace: |
|
349 |
print fcnt, real(Za), imag(Za), abs(Zh) |
|
350 |
#Check accurancy of integral over this subinterval |
|
351 |
XXk = [Xp[0], Xp[2], Xp[4], Xp[6]] |
|
352 |
WWk = [Wk[0], Wk[2], Wk[4], Wk[6]] |
|
353 |
YYk = [fa, y[1], y[3], fb] |
|
354 |
if np.max(np.abs(Qk - Q)) <= tol: |
|
355 |
warn = 0 |
|
356 |
return Q, XXk, WWk, fcnt, warn |
|
357 |
#Subdivide into six subintevals |
|
358 |
else: |
|
359 |
Q, Xk, Wk, fcnt, warn = quadlstep(f, Za, Zx[1], fa, YYk[1], |
|
360 |
tol, trace, fcnt, hmin, |
|
361 |
calcutype, path_type, |
|
362 |
origin, radius) |
|
363 |
|
|
364 |
Qk, xkk, wkk, fcnt, warnk = quadlstep(f, Zx[1], |
|
365 |
Zx[3], YYk[1], YYk[2], tol, trace, fcnt, hmin, |
|
366 |
calcutype, path_type, |
|
367 |
origin, radius) |
|
368 |
Q += Qk |
|
369 |
Xk = Xk[:-1] + xkk |
|
370 |
Wk = Wk[:-1] + [Wk[-1] + wkk[0]] + wkk[1:] |
|
371 |
warn = max(warn, warnk) |
|
372 |
|
|
373 |
Qk, xkk, wkk, fcnt, warnk = quadlstep(f, Zx[3], Zb, YYk[2], fb, |
|
374 |
tol, trace, fcnt, hmin, |
|
375 |
calcutype, path_type, |
|
376 |
origin, radius) |
|
377 |
Q += Qk |
|
378 |
Xk = Xk[:-1] + xkk |
|
379 |
Wk = Wk[:-1] + [Wk[-1] + wkk[0]] + wkk[1:] |
|
380 |
warn = max(warn, warnk) |
|
381 |
return Q, Xk, Wk, fcnt, warn |
|
382 |
|
|
383 |
def mytextread0(filename): |
|
384 |
num = 0 |
|
385 |
df = file(filename) |
|
386 |
df.seek(0) |
|
387 |
for line in df: |
|
388 |
if num == 0: |
|
389 |
dim = line.strip().split(' ') |
|
390 |
row = int(dim[0]) |
|
391 |
col = int(dim[1]) |
|
392 |
mat = np.empty([row, col]) |
|
393 |
else: |
|
394 |
data = line.strip().split(' ') |
|
395 |
if len(data) == 0 or len(data)== 1: |
|
396 |
break |
|
397 |
else: |
|
398 |
for i in range(len(data)): |
|
399 |
mat[num - 1, i] = float(data[i]) |
|
400 |
num += 1 |
|
401 |
return mat |
|
402 |
|
|
403 |
def mytextread1(filename): |
|
404 |
num = 0 |
|
405 |
df = file(filename) |
|
406 |
df.seek(0) |
|
407 |
data = [] |
|
408 |
for line in df: |
|
409 |
tmp = line.strip() |
|
410 |
if len(tmp) != 0: |
|
411 |
data.append(float(tmp)) |
|
412 |
else: |
|
413 |
break |
|
414 |
dim = int(sqrt(len(data))) |
|
415 |
mat = np.empty([dim, dim]) |
|
416 |
for i in range(dim): |
|
417 |
for j in range(dim): |
|
418 |
mat[i, j] = data[num] |
|
419 |
num += 1 |
|
420 |
return mat |
|
421 |
|
|
422 |
def mytextwrite1(filename, mat): |
|
423 |
num = 0 |
|
424 |
df = open(filename,'w') |
|
425 |
df.seek(0) |
|
426 |
dim = mat.shape[0] |
|
427 |
if dim != mat.shape[1]: |
|
428 |
print 'matwirte, matrix is not square' |
|
429 |
for i in range(dim): |
|
430 |
for j in range(dim): |
|
431 |
df.write('%20.20e\n'% mat[i, j]) |
|
432 |
df.close() |
ase/transport/test_transport_calulator.py (revision 1) | ||
---|---|---|
1 |
from ase.transport.calculators import TransportCalculator |
|
2 |
import numpy as np |
|
3 |
|
|
4 |
#Aux. function to write data to a text file. |
|
5 |
def write(fname,xs,ys): |
|
6 |
fd = open(fname,'w') |
|
7 |
for x,y in zip(xs,ys): |
|
8 |
print >> fd, x, y |
|
9 |
fd.close() |
|
10 |
|
|
11 |
H_lead = np.zeros([4,4]) |
|
12 |
|
|
13 |
# On-site energies are zero |
|
14 |
for i in range(4): |
|
15 |
H_lead[i,i] = 0.0 |
|
16 |
|
|
17 |
# Nearest neighbor hopping is -1.0 |
|
18 |
for i in range(3): |
|
19 |
H_lead[i,i+1] = -1.0 |
|
20 |
H_lead[i+1,i] = -1.0 |
|
21 |
|
|
22 |
# Next-nearest neighbor hopping is 0.2 |
|
23 |
for i in range(2): |
|
24 |
H_lead[i,i+2] = 0.2 |
|
25 |
H_lead[i+2,i] = 0.2 |
|
26 |
|
|
27 |
H_scat = np.zeros([6,6]) |
|
28 |
# Principal layers on either side of S |
|
29 |
H_scat[:2,:2] = H_lead[:2,:2] |
|
30 |
H_scat[-2:,-2:] = H_lead[:2,:2] |
|
31 |
|
|
32 |
# Scattering region |
|
33 |
H_scat[2,2] = 0.0 |
|
34 |
H_scat[3,3] = 0.0 |
|
35 |
H_scat[2,3] = -0.8 |
|
36 |
H_scat[3,2] = -0.8 |
|
37 |
|
|
38 |
# External coupling |
|
39 |
H_scat[1,2] = 0.2 |
|
40 |
H_scat[2,1] = 0.2 |
|
41 |
H_scat[3,4] = 0.2 |
|
42 |
H_scat[4,3] = 0.2 |
|
43 |
|
|
44 |
energies = np.arange(-3,3,0.02) |
|
45 |
tcalc = TransportCalculator(h=H_scat, |
|
46 |
h1=H_lead, |
|
47 |
eta=0.02, |
|
48 |
energies=energies) |
|
49 |
|
|
50 |
T = tcalc.get_transmission() |
|
51 |
tcalc.set(pdos=[2, 3]) |
|
52 |
pdos = tcalc.get_pdos() |
|
53 |
|
|
54 |
tcalc.set(dos=True) |
|
55 |
dos = tcalc.get_dos() |
|
56 |
|
|
57 |
write('T.dat',tcalc.energies,T) |
|
58 |
write('pdos0.dat', tcalc.energies,pdos[0]) |
|
59 |
write('pdos1.dat', tcalc.energies,pdos[1]) |
|
60 |
|
|
61 |
#subdiagonalize |
|
62 |
h_rot, s_rot, eps, u = tcalc.subdiagonalize_bfs([2, 3], apply=True) |
|
63 |
T_rot = tcalc.get_transmission() |
|
64 |
dos_rot = tcalc.get_dos() |
|
65 |
pdos_rot = tcalc.get_pdos() |
|
66 |
|
|
67 |
write('T_rot.dat', tcalc.energies,T_rot) |
|
68 |
write('pdos0_rot.dat', tcalc.energies, pdos_rot[0]) |
|
69 |
write('pdos1_rot.dat', tcalc.energies, pdos_rot[1]) |
|
70 |
|
|
71 |
print 'Subspace eigenvalues:', eps |
|
72 |
assert sum(abs(eps-(-0.8, 0.8))) < 2.0e-15, 'Subdiagonalization. error' |
|
73 |
print 'Max deviation of T after the rotation:', np.abs(T-T_rot).max() |
|
74 |
assert max(abs(T-T_rot)) < 2.0e-15, 'Subdiagonalization. error' |
|
75 |
|
|
76 |
#remove coupling |
|
77 |
h_cut, s_cut = tcalc.cutcoupling_bfs([2], apply=True) |
|
78 |
T_cut = tcalc.get_transmission() |
|
79 |
dos_cut = tcalc.get_dos() |
|
80 |
pdos_cut = tcalc.get_pdos() |
|
81 |
|
|
82 |
write('T_cut.dat', tcalc.energies, T_cut) |
|
83 |
write('pdos0_cut.dat', tcalc.energies,pdos_cut[0]) |
|
84 |
write('pdos1_cut.dat', tcalc.energies,pdos_cut[1]) |
|
85 |
|
ase/transport/stm_test.py (revision 1) | ||
---|---|---|
1 |
import numpy as np |
|
2 |
import pylab |
|
3 |
import ase.transport.stm as stm |
|
4 |
|
|
5 |
|
|
6 |
# Parameters for a simple model. |
|
7 |
# |
|
8 |
# |
|
9 |
# * eps_a |
|
10 |
# v_ts / \ v_a2 |
|
11 |
# ... * * * * * * * * * ... |
|
12 |
# \/ \/ |
|
13 |
# t1 t2 |
|
14 |
# |
|
15 |
# Tip Surface |
|
16 |
# ----------------| |----------------------- |
|
17 |
t1 = -1.0 |
|
18 |
t2 = -2.0 |
|
19 |
eps_a = 0.4 |
|
20 |
|
|
21 |
v_ts = 0.05 |
|
22 |
v_a2 = 1.0 |
|
23 |
|
|
24 |
#Tip |
|
25 |
h1 = np.zeros([2, 2]) |
|
26 |
h1[0, 1] = t1 |
|
27 |
h1[1, 0] = t1 |
|
28 |
s1 = np.identity(2) |
|
29 |
|
|
30 |
h10 = np.zeros([2,2]) |
|
31 |
h10[0, 1] = t1 |
|
32 |
h10[1, 0] = t1 |
|
33 |
s10 = np.identity(2) |
|
34 |
|
|
35 |
|
|
36 |
#Surface with "molecule" a. |
|
37 |
h2 = np.zeros([2,2]) |
|
38 |
h2[0, 1] = v_a2 |
|
39 |
h2[1, 0] = v_a2 |
|
40 |
h1[0, 0] = eps_a |
|
41 |
s2 = np.identity(2) |
|
42 |
|
|
43 |
h20 = np.zeros([2,2]) |
|
44 |
h20[0, 1] = t2 |
|
45 |
h20[1, 0] = t2 |
|
46 |
s20 = np.identity(2) |
|
47 |
|
|
48 |
|
|
49 |
#Tip Surface coupling |
|
50 |
V_ts = np.zeros([2,2]) |
|
51 |
V_ts[1, 0] = v_ts |
|
52 |
|
|
53 |
eta1 = 0.0001 |
|
54 |
eta2 = 0.0001 |
|
55 |
|
|
56 |
stm = reload(stm) |
|
57 |
stm_calc = stm.STM(h1, s1, h2, s2, h10, s10, h20, s20, eta1, eta2) |
|
58 |
energies = np.arange(-3.0, 3.0, 0.01) |
|
59 |
stm_calc.initialize(energies) |
|
60 |
|
|
61 |
|
|
62 |
T_stm = stm_calc.get_transmission(V_ts) |
|
63 |
|
|
64 |
|
|
65 |
#Perform the full calculation and compare |
|
66 |
from ase.transport.calculators import TransportCalculator as TC |
|
67 |
|
|
68 |
h = np.zeros([4,4]) |
|
69 |
h[:2, :2] = h1 |
|
70 |
h[-2:, -2:] = h2 |
|
71 |
h[:2, -2:] = V_ts |
|
72 |
h[-2:, :2] = V_ts.T |
|
73 |
|
|
74 |
|
|
75 |
tc = TC(energies=energies, |
|
76 |
h=h, |
|
77 |
h1=h10, |
|
78 |
h2=h20, |
|
79 |
eta=eta1, eta1=eta1, eta2=eta2) |
|
80 |
|
|
81 |
T_full = tc.get_transmission() |
|
82 |
|
|
83 |
pylab.plot(stm_calc.energies, T_stm, 'b') |
|
84 |
pylab.plot(tc.energies, T_full, 'r--') |
|
85 |
pylab.show() |
|
86 |
|
|
87 |
|
|
88 |
#bias stuff |
|
89 |
biass = np.arange(-2.0, 2.0, 0.2) |
|
90 |
Is = [stm_calc.get_current(bias, V_ts) for bias in biass] |
|
91 |
pylab.plot(biass, Is, '+') |
|
92 |
pylab.show() |
|
93 |
|
|
94 |
|
ase/transport/selfenergy.py (revision 1) | ||
---|---|---|
1 |
import numpy as np |
|
2 |
|
|
3 |
|
|
4 |
class LeadSelfEnergy: |
|
5 |
conv = 1e-8 # Convergence criteria for surface Green function |
|
6 |
|
|
7 |
def __init__(self, hs_dii, hs_dij, hs_dim, eta=1e-4): |
|
8 |
self.h_ii, self.s_ii = hs_dii # onsite principal layer |
|
9 |
self.h_ij, self.s_ij = hs_dij # coupling between principal layers |
|
10 |
self.h_im, self.s_im = hs_dim # coupling to the central region |
|
11 |
self.nbf = self.h_im.shape[1] # nbf for the scattering region |
|
12 |
self.eta = eta |
|
13 |
self.energy = None |
|
14 |
self.bias = 0 |
|
15 |
self.sigma_mm = np.empty((self.nbf, self.nbf), complex) |
|
16 |
|
|
17 |
def retarded(self, energy): |
|
18 |
"""Return self-energy (sigma) evaluated at specified energy.""" |
|
19 |
if energy != self.energy: |
|
20 |
self.energy = energy |
|
21 |
z = energy - self.bias + self.eta * 1.j |
|
22 |
tau_im = z * self.s_im - self.h_im |
|
23 |
a_im = np.linalg.solve(self.get_sgfinv(energy), tau_im) |
|
24 |
tau_mi = z * self.s_im.T.conj() - self.h_im.T.conj() |
|
25 |
self.sigma_mm[:] = np.dot(tau_mi, a_im) |
|
26 |
|
|
27 |
return self.sigma_mm |
|
28 |
|
|
29 |
def set_bias(self, bias): |
|
30 |
self.bias = bias |
|
31 |
|
|
32 |
def get_lambda(self, energy): |
|
33 |
"""Return the lambda (aka Gamma) defined by i(S-S^d). |
|
34 |
|
|
35 |
Here S is the retarded selfenergy, and d denotes the hermitian |
|
36 |
conjugate. |
|
37 |
""" |
|
38 |
sigma_mm = self.retarded(energy) |
|
39 |
return 1.j * (sigma_mm - sigma_mm.T.conj()) |
|
40 |
|
|
41 |
def get_sgfinv(self, energy): |
|
42 |
"""The inverse of the retarded surface Green function""" |
|
43 |
z = energy - self.bias + self.eta * 1.j |
|
44 |
|
|
45 |
v_00 = z * self.s_ii.T.conj() - self.h_ii.T.conj() |
|
46 |
v_11 = v_00.copy() |
|
47 |
v_10 = z * self.s_ij - self.h_ij |
|
48 |
v_01 = z * self.s_ij.T.conj() - self.h_ij.T.conj() |
|
49 |
|
|
50 |
delta = self.conv + 1 |
|
51 |
while delta > self.conv: |
|
52 |
a = np.linalg.solve(v_11, v_01) |
|
53 |
b = np.linalg.solve(v_11, v_10) |
|
54 |
v_01_dot_b = np.dot(v_01, b) |
|
55 |
v_00 -= v_01_dot_b |
|
56 |
v_11 -= np.dot(v_10, a) |
|
57 |
v_11 -= v_01_dot_b |
|
58 |
v_01 = -np.dot(v_01, a) |
|
59 |
v_10 = -np.dot(v_10, b) |
|
60 |
delta = abs(v_01).max() |
|
61 |
|
|
62 |
return v_00 |
|
63 |
|
|
64 |
|
|
65 |
class BoxProbe: |
|
66 |
"""Box shaped Buttinger probe. |
|
67 |
|
|
68 |
Kramers-kroning: real = H(imag); imag = -H(real) |
|
69 |
""" |
|
70 |
def __init__(self, eta, a, b, energies, S, T=0.3): |
|
71 |
from Transport.Hilbert import hilbert |
|
72 |
se = np.empty(len(energies), complex) |
|
73 |
se.imag = .5 * (np.tanh(.5 * (energies - a) / T) - |
|
74 |
np.tanh(.5 * (energies - b) / T)) |
|
75 |
se.real = hilbert(se.imag) |
|
76 |
se.imag -= 1 |
|
77 |
self.selfenergy_e = eta * se |
|
78 |
self.energies = energies |
|
79 |
self.S = S |
|
80 |
|
|
81 |
def retarded(self, energy): |
|
82 |
return self.selfenergy_e[self.energies.searchsorted(energy)] * self.S |
|
83 |
|
ase/transport/stm.py (revision 1) | ||
---|---|---|
1 |
import numpy as np |
|
2 |
from ase.transport.tools import dagger |
|
3 |
from ase.transport.selfenergy import LeadSelfEnergy |
|
4 |
from ase.transport.greenfunction import GreenFunction |
|
5 |
import time |
|
6 |
from gpaw.mpi import world |
|
7 |
|
|
8 |
|
|
9 |
class STM: |
|
10 |
def __init__(self, h1, s1, h2, s2 ,h10, s10, h20, s20, eta1, eta2, w=0.5, pdos=[], logfile = None): |
|
11 |
"""XXX |
|
12 |
|
|
13 |
1. Tip |
|
14 |
2. Surface |
|
15 |
|
|
16 |
h1: ndarray |
|
17 |
Hamiltonian and overlap matrix for the isolated tip |
|
18 |
calculation. Note, h1 should contain (at least) one |
|
19 |
principal layer. |
|
20 |
|
|
21 |
h2: ndarray |
|
22 |
Same as h1 but for the surface. |
|
23 |
|
|
24 |
h10: ndarray |
|
25 |
periodic part of the tip. must include two and only |
|
26 |
two principal layers. |
|
27 |
|
|
28 |
h20: ndarray |
|
29 |
same as h10, but for the surface |
|
30 |
|
|
31 |
The s* are the corresponding overlap matrices. eta1, and eta |
|
32 |
2 are (finite) infinitesimals. """ |
|
33 |
|
|
34 |
self.pl1 = len(h10) // 2 #principal layer size for the tip |
|
35 |
self.pl2 = len(h20) // 2 #principal layer size for the surface |
|
36 |
self.h1 = h1 |
|
37 |
self.s1 = s1 |
|
38 |
self.h2 = h2 |
|
39 |
self.s2 = s2 |
|
40 |
self.h10 = h10 |
|
41 |
self.s10 = s10 |
|
42 |
self.h20 = h20 |
|
43 |
self.s20 = s20 |
|
44 |
self.eta1 = eta1 |
|
45 |
self.eta2 = eta2 |
|
46 |
self.w = w #asymmetry of the applied bias (0.5=>symmetric) |
|
47 |
self.pdos = [] |
|
48 |
self.log = logfile |
|
49 |
|
|
50 |
def initialize(self, energies, bias=0): |
|
51 |
""" |
|
52 |
energies: list of energies |
|
53 |
for which the transmission function should be evaluated. |
|
54 |
bias. |
|
55 |
Will precalculate the surface greenfunctions of the tip and |
|
56 |
surface. |
|
57 |
""" |
|
58 |
self.bias = bias |
|
59 |
self.energies = energies |
|
60 |
nenergies = len(energies) |
|
61 |
pl1, pl2 = self.pl1, self.pl2 |
|
62 |
nbf1, nbf2 = len(self.h1), len(self.h2) |
|
63 |
|
|
64 |
#periodic part of the tip |
|
65 |
hs1_dii = self.h10[:pl1, :pl1], self.s10[:pl1, :pl1] |
|
66 |
hs1_dij = self.h10[:pl1, pl1:2*pl1], self.s10[:pl1, pl1:2*pl1] |
|
67 |
#coupling betwen per. and non. per part of the tip |
|
68 |
h1_im = np.zeros((pl1, nbf1), complex) |
|
69 |
s1_im = np.zeros((pl1, nbf1), complex) |
|
70 |
h1_im[:pl1, :pl1], s1_im[:pl1, :pl1] = hs1_dij |
|
71 |
hs1_dim = [h1_im, s1_im] |
|
72 |
|
|
73 |
#periodic part the surface |
|
74 |
hs2_dii = self.h20[:pl2, :pl2], self.s20[:pl2, :pl2] |
|
75 |
hs2_dij = self.h20[pl2:2*pl2, :pl2], self.s20[pl2:2*pl2, :pl2] |
|
76 |
#coupling betwen per. and non. per part of the surface |
|
77 |
h2_im = np.zeros((pl2, nbf2), complex) |
|
78 |
s2_im = np.zeros((pl2, nbf2), complex) |
|
79 |
h2_im[-pl2:, -pl2:], s2_im[-pl2:, -pl2:] = hs2_dij |
|
80 |
hs2_dim = [h2_im, s2_im] |
|
81 |
|
|
82 |
#tip and surface greenfunction |
|
83 |
self.selfenergy1 = LeadSelfEnergy(hs1_dii, hs1_dij, hs1_dim, self.eta1) |
|
84 |
self.selfenergy2 = LeadSelfEnergy(hs2_dii, hs2_dij, hs2_dim, self.eta2) |
|
85 |
self.greenfunction1 = GreenFunction(self.h1-self.bias*self.w*self.s1, self.s1, |
|
86 |
[self.selfenergy1], self.eta1) |
|
87 |
self.greenfunction2 = GreenFunction(self.h2-self.bias*(self.w-1)*self.s2, self.s2, |
|
88 |
[self.selfenergy2], self.eta2) |
|
89 |
|
|
90 |
#Shift the bands due to the bias. |
|
91 |
bias_shift1 = -bias * self.w |
|
92 |
bias_shift2 = -bias * (self.w - 1) |
|
93 |
self.selfenergy1.set_bias(bias_shift1) |
|
94 |
self.selfenergy2.set_bias(bias_shift2) |
|
95 |
|
|
96 |
#tip and surface greenfunction matrices. |
|
97 |
nbf1_small = nbf1 #XXX Change this for efficiency in the future |
|
98 |
nbf2_small = nbf2 #XXX -||- |
|
99 |
coupling_list1 = range(nbf1_small)# XXX -||- |
|
100 |
coupling_list2 = range(nbf2_small)# XXX -||- |
|
101 |
self.gft1_emm = np.zeros((nenergies, nbf1_small, nbf1_small), complex) |
|
102 |
self.gft2_emm = np.zeros((nenergies, nbf2_small, nbf2_small), complex) |
|
103 |
|
|
104 |
for e, energy in enumerate(self.energies): |
|
105 |
if self.log != None: # and world.rank == 0: |
|
106 |
T = time.localtime() |
|
107 |
self.log.write(' %d:%02d:%02d, ' % (T[3], T[4], T[5]) + |
|
108 |
'%d, %d, %02f\n' % (world.rank, e, energy)) |
|
109 |
gft1_mm = self.greenfunction1.retarded(energy)[coupling_list1] |
|
110 |
gft1_mm = np.take(gft1_mm, coupling_list1, axis=1) |
|
111 |
|
|
112 |
gft2_mm = self.greenfunction2.retarded(energy)[coupling_list2] |
|
113 |
gft2_mm = np.take(gft2_mm, coupling_list2, axis=1) |
|
114 |
|
|
115 |
self.gft1_emm[e] = gft1_mm |
|
116 |
self.gft2_emm[e] = gft2_mm |
|
117 |
|
|
118 |
if self.log != None and world.rank == 0: |
|
119 |
self.log.flush() |
|
120 |
|
|
121 |
def get_transmission(self, v_12, v_11_2=None, v_22_1=None): |
|
122 |
"""XXX |
|
123 |
|
|
124 |
v_12: |
|
125 |
coupling between tip and surface |
|
126 |
v_11_2: |
|
127 |
correction to "on-site" tip elements due to the |
|
128 |
surface (eq.16). Is only included to first order. |
|
129 |
v_22_1: |
|
130 |
corretion to "on-site" surface elements due to he |
|
131 |
tip (eq.17). Is only included to first order. |
|
132 |
""" |
|
133 |
|
|
134 |
dim0 = v_12.shape[0] |
|
135 |
dim1 = v_12.shape[1] |
|
136 |
|
|
137 |
nenergies = len(self.energies) |
|
138 |
T_e = np.empty(nenergies,float) |
|
139 |
v_21 = dagger(v_12) |
|
140 |
for e, energy in enumerate(self.energies): |
|
141 |
gft1 = self.gft1_emm[e] |
|
142 |
if v_11_2!=None: |
|
143 |
gf1 = np.dot(v_11_2, np.dot(gft1, v_11_2)) |
|
144 |
gf1 += gft1 #eq. 16 |
|
145 |
else: |
|
146 |
gf1 = gft1 |
|
147 |
|
|
148 |
gft2 = self.gft2_emm[e] |
|
149 |
if v_22_1!=None: |
|
150 |
gf2 = np.dot(v_22_1,np.dot(gft2, v_22_1)) |
|
151 |
gf2 += gft2 #eq. 17 |
|
152 |
else: |
|
153 |
gf2 = gft2 |
|
154 |
|
|
155 |
a1 = (gf1 - dagger(gf1)) |
|
156 |
a2 = (gf2 - dagger(gf2)) |
|
157 |
self.v_12 = v_12 |
|
158 |
self.a2 = a2 |
|
159 |
self.v_21 = v_21 |
|
160 |
self.a1 = a1 |
|
161 |
v12_a2 = np.dot(v_12, a2[:dim1]) |
|
162 |
v21_a1 = np.dot(v_21, a1[-dim0:]) |
|
163 |
self.v12_a2 = v12_a2 |
|
164 |
self.v21_a1 = v21_a1 |
|
165 |
T = -np.trace(np.dot(v12_a2[:,:dim1], v21_a1[:,-dim0:])) #eq. 11 |
|
166 |
T_e[e] = T |
|
167 |
self.T_e = T_e |
|
168 |
return T_e |
|
169 |
|
|
170 |
|
|
171 |
def get_current(self, bias, v_12, v_11_2=None, v_22_1=None): |
|
172 |
"""Very simple function to calculate the current. |
|
173 |
|
|
174 |
Asummes zero temperature. |
|
175 |
|
|
176 |
bias: type? XXX |
|
177 |
bias voltage (V) |
|
178 |
|
|
179 |
v_12: XXX |
|
180 |
coupling between tip and surface. |
|
181 |
|
|
182 |
v_11_2: |
|
183 |
correction to onsite elements of the tip |
|
184 |
due to the potential of the surface. |
|
185 |
v_22_1: |
|
186 |
correction to onsite elements of the surface |
|
187 |
due to the potential of the tip. |
|
188 |
""" |
|
189 |
energies = self.energies |
|
190 |
T_e = self.get_transmission(v_12, v_11_2, v_22_1) |
|
191 |
bias_window = -np.array([bias * self.w, bias * (self.w - 1)]) |
|
192 |
bias_window.sort() |
|
193 |
self.bias_window = bias_window |
|
194 |
#print 'bias window', np.around(bias_window,3) |
|
195 |
#print 'Shift of tip lead do to the bias:', self.selfenergy1.bias |
|
196 |
#print 'Shift of surface lead do to the bias:', self.selfenergy2.bias |
|
197 |
i1 = sum(energies < bias_window[0]) |
|
198 |
i2 = sum(energies < bias_window[1]) |
|
199 |
step = 1 |
|
200 |
if i2 < i1: |
|
201 |
step = -1 |
|
202 |
|
|
203 |
return np.sign(bias)*np.trapz(x=energies[i1:i2:step], y=T_e[i1:i2:step]) |
|
204 |
|
|
205 |
|
|
206 |
|
|
207 |
|
|
208 |
|
|
209 |
|
ase/transport/greenfunction.py (revision 1) | ||
---|---|---|
1 |
import numpy as np |
|
2 |
|
|
3 |
class GreenFunction: |
|
4 |
"""Equilibrium retarded Green function.""" |
|
5 |
|
|
6 |
def __init__(self, H, S=None, selfenergies=[], eta=1e-4): |
|
7 |
self.H = H |
|
8 |
self.S = S |
|
9 |
self.selfenergies = selfenergies |
|
10 |
self.eta = eta |
|
11 |
self.energy = None |
|
12 |
self.Ginv = np.empty(H.shape, complex) |
|
13 |
|
|
14 |
def retarded(self, energy, inverse=False): |
|
15 |
"""Get retarded Green function at specified energy. |
|
16 |
|
|
17 |
If 'inverse' is True, the inverse Green function is returned (faster). |
|
18 |
""" |
|
19 |
if energy != self.energy: |
|
20 |
self.energy = energy |
|
21 |
z = energy + self.eta * 1.j |
|
22 |
|
|
23 |
if self.S is None: |
|
24 |
self.Ginv[:] = 0.0 |
|
25 |
self.Ginv.flat[:: len(self.S) + 1] = z |
|
26 |
else: |
|
27 |
self.Ginv[:] = z |
|
28 |
self.Ginv *= self.S |
|
29 |
self.Ginv -= self.H |
|
30 |
|
|
31 |
for selfenergy in self.selfenergies: |
|
32 |
self.Ginv -= selfenergy.retarded(energy) |
|
33 |
|
|
34 |
if inverse: |
|
35 |
return self.Ginv |
|
36 |
else: |
|
37 |
return np.linalg.inv(self.Ginv) |
|
38 |
|
|
39 |
def calculate(self, energy, sigma): |
|
40 |
"""XXX is this really needed""" |
|
41 |
ginv = energy * self.S - self.H - sigma |
|
42 |
return np.linalg.inv(ginv) |
|
43 |
|
|
44 |
def apply_retarded(self, energy, X): |
|
45 |
"""Apply retarded Green function to X. |
|
46 |
|
|
47 |
Returns the matrix product G^r(e) . X |
|
48 |
""" |
|
49 |
return np.linalg.solve(self.retarded(energy, inverse=True), X) |
|
50 |
|
|
51 |
def dos(self, energy): |
|
52 |
"""Total density of states -1/pi Im(Tr(GS))""" |
|
53 |
if self.S is None: |
|
54 |
return -self(energy).imag.trace() / np.pi |
|
55 |
else: |
|
56 |
GS = self.apply_retarded(energy, self.S) |
|
57 |
return -GS.imag.trace() / np.pi |
|
58 |
|
|
59 |
def pdos(self, energy): |
|
60 |
"""Projected density of states -1/pi Im(SGS/S)""" |
|
61 |
if self.S is None: |
|
62 |
return -self.retarded(energy).imag.diagonal() / np.pi |
|
63 |
else: |
|
64 |
S = self.S |
|
65 |
SGS = np.dot(S, self.apply_retarded(energy, S)) |
|
66 |
return -(SGS.diagonal() / S.diagonal()).imag / np.pi |
ase/transport/calculators.py (revision 1) | ||
---|---|---|
1 |
import numpy as np |
|
2 |
|
|
3 |
from numpy import linalg |
|
4 |
from ase.transport.selfenergy import LeadSelfEnergy, BoxProbe |
Formats disponibles : Unified diff