root / ase / io / eps.py @ 14
Historique | Voir | Annoter | Télécharger (6,04 ko)
| 1 |
import time |
|---|---|
| 2 |
from math import sqrt |
| 3 |
|
| 4 |
import numpy as np |
| 5 |
|
| 6 |
from ase.utils import rotate |
| 7 |
from ase.data import covalent_radii |
| 8 |
from ase.data.colors import jmol_colors |
| 9 |
|
| 10 |
|
| 11 |
class EPS: |
| 12 |
def __init__(self, atoms, |
| 13 |
rotation='', show_unit_cell=False, radii=None, |
| 14 |
bbox=None, colors=None, scale=20): |
| 15 |
self.numbers = atoms.get_atomic_numbers()
|
| 16 |
self.colors = colors
|
| 17 |
if colors is None: |
| 18 |
self.colors = jmol_colors[self.numbers] |
| 19 |
|
| 20 |
if radii is None: |
| 21 |
radii = covalent_radii[self.numbers]
|
| 22 |
elif type(radii) is float: |
| 23 |
radii = covalent_radii[self.numbers] * radii
|
| 24 |
|
| 25 |
natoms = len(atoms)
|
| 26 |
|
| 27 |
if isinstance(rotation, str): |
| 28 |
rotation = rotate(rotation) |
| 29 |
|
| 30 |
A = atoms.get_cell() |
| 31 |
if show_unit_cell > 0: |
| 32 |
L, T, D = self.cell_to_lines(A)
|
| 33 |
C = np.empty((2, 2, 2, 3)) |
| 34 |
for c1 in range(2): |
| 35 |
for c2 in range(2): |
| 36 |
for c3 in range(2): |
| 37 |
C[c1, c2, c3] = np.dot([c1, c2, c3], A) |
| 38 |
C.shape = (8, 3) |
| 39 |
C = np.dot(C, rotation) # Unit cell vertices
|
| 40 |
else:
|
| 41 |
L = np.empty((0, 3)) |
| 42 |
T = None
|
| 43 |
D = None
|
| 44 |
C = None
|
| 45 |
|
| 46 |
nlines = len(L)
|
| 47 |
|
| 48 |
X = np.empty((natoms + nlines, 3))
|
| 49 |
R = atoms.get_positions() |
| 50 |
X[:natoms] = R |
| 51 |
X[natoms:] = L |
| 52 |
|
| 53 |
r2 = radii**2
|
| 54 |
for n in range(nlines): |
| 55 |
d = D[T[n]] |
| 56 |
if ((((R - L[n] - d)**2).sum(1) < r2) & |
| 57 |
(((R - L[n] + d)**2).sum(1) < r2)).any(): |
| 58 |
T[n] = -1
|
| 59 |
|
| 60 |
X = np.dot(X, rotation) |
| 61 |
R = X[:natoms] |
| 62 |
|
| 63 |
if bbox is None: |
| 64 |
X1 = (R - radii[:, None]).min(0) |
| 65 |
X2 = (R + radii[:, None]).max(0) |
| 66 |
if show_unit_cell == 2: |
| 67 |
X1 = np.minimum(X1, C.min(0))
|
| 68 |
X2 = np.maximum(X2, C.max(0))
|
| 69 |
M = (X1 + X2) / 2
|
| 70 |
S = 1.05 * (X2 - X1)
|
| 71 |
w = scale * S[0]
|
| 72 |
if w > 500: |
| 73 |
w = 500
|
| 74 |
scale = w / S[0]
|
| 75 |
h = scale * S[1]
|
| 76 |
offset = np.array([scale * M[0] - w / 2, scale * M[1] - h / 2, 0]) |
| 77 |
else:
|
| 78 |
w = (bbox[2] - bbox[0]) * scale |
| 79 |
h = (bbox[3] - bbox[1]) * scale |
| 80 |
offset = np.array([bbox[0], bbox[1], 0]) * scale |
| 81 |
|
| 82 |
self.w = w
|
| 83 |
self.h = h
|
| 84 |
|
| 85 |
X *= scale |
| 86 |
X -= offset |
| 87 |
|
| 88 |
if nlines > 0: |
| 89 |
D = np.dot(D, rotation)[:, :2] * scale
|
| 90 |
|
| 91 |
if C is not None: |
| 92 |
C *= scale |
| 93 |
C -= offset |
| 94 |
|
| 95 |
A = np.dot(A, rotation) |
| 96 |
A *= scale |
| 97 |
|
| 98 |
self.A = A
|
| 99 |
self.X = X
|
| 100 |
self.D = D
|
| 101 |
self.T = T
|
| 102 |
self.C = C
|
| 103 |
self.natoms = natoms
|
| 104 |
self.d = 2 * scale * radii |
| 105 |
|
| 106 |
def cell_to_lines(self, A): |
| 107 |
nlines = 0
|
| 108 |
nn = [] |
| 109 |
for c in range(3): |
| 110 |
d = sqrt((A[c]**2).sum())
|
| 111 |
n = max(2, int(d / 0.3)) |
| 112 |
nn.append(n) |
| 113 |
nlines += 4 * n
|
| 114 |
|
| 115 |
X = np.empty((nlines, 3))
|
| 116 |
T = np.empty(nlines, int)
|
| 117 |
D = np.zeros((3, 3)) |
| 118 |
|
| 119 |
n1 = 0
|
| 120 |
for c in range(3): |
| 121 |
n = nn[c] |
| 122 |
dd = A[c] / (4 * n - 2) |
| 123 |
D[c] = dd |
| 124 |
P = np.arange(1, 4 * n + 1, 4)[:, None] * dd |
| 125 |
T[n1:] = c |
| 126 |
for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]: |
| 127 |
n2 = n1 + n |
| 128 |
X[n1:n2] = P + i * A[(c + 1) % 3] + j * A[(c + 2) % 3] |
| 129 |
n1 = n2 |
| 130 |
|
| 131 |
return X, T, D
|
| 132 |
|
| 133 |
def write(self, filename): |
| 134 |
self.filename = filename
|
| 135 |
self.write_header()
|
| 136 |
self.write_body()
|
| 137 |
self.write_trailer()
|
| 138 |
|
| 139 |
def write_header(self): |
| 140 |
import matplotlib |
| 141 |
if matplotlib.__version__ <= '0.8': |
| 142 |
raise RuntimeError('Your version of matplotlib (%s) is too old' % |
| 143 |
matplotlib.__version__) |
| 144 |
|
| 145 |
from matplotlib.backends.backend_ps import RendererPS, \ |
| 146 |
GraphicsContextPS, psDefs |
| 147 |
|
| 148 |
self.fd = open(self.filename, 'w') |
| 149 |
self.fd.write('%!PS-Adobe-3.0 EPSF-3.0\n') |
| 150 |
self.fd.write('%%Creator: G2\n') |
| 151 |
self.fd.write('%%CreationDate: %s\n' % time.ctime(time.time())) |
| 152 |
self.fd.write('%%Orientation: portrait\n') |
| 153 |
bbox = (0, 0, self.w, self.h) |
| 154 |
self.fd.write('%%%%BoundingBox: %d %d %d %d\n' % bbox) |
| 155 |
self.fd.write('%%EndComments\n') |
| 156 |
|
| 157 |
Ndict = len(psDefs)
|
| 158 |
self.fd.write('%%BeginProlog\n') |
| 159 |
self.fd.write('/mpldict %d dict def\n' % Ndict) |
| 160 |
self.fd.write('mpldict begin\n') |
| 161 |
for d in psDefs: |
| 162 |
d = d.strip() |
| 163 |
for l in d.split('\n'): |
| 164 |
self.fd.write(l.strip() + '\n') |
| 165 |
self.fd.write('%%EndProlog\n') |
| 166 |
|
| 167 |
self.fd.write('mpldict begin\n') |
| 168 |
self.fd.write('%d %d 0 0 clipbox\n' % (self.w, self.h)) |
| 169 |
|
| 170 |
self.renderer = RendererPS(self.w, self.h, self.fd) |
| 171 |
|
| 172 |
def write_body(self): |
| 173 |
try:
|
| 174 |
from matplotlib.path import Path |
| 175 |
except ImportError: |
| 176 |
Path = None
|
| 177 |
from matplotlib.patches import Circle, Polygon |
| 178 |
else:
|
| 179 |
from matplotlib.patches import Circle, PathPatch |
| 180 |
|
| 181 |
indices = self.X[:, 2].argsort() |
| 182 |
for a in indices: |
| 183 |
xy = self.X[a, :2] |
| 184 |
if a < self.natoms: |
| 185 |
circle = Circle(xy, self.d[a] / 2, facecolor=self.colors[a]) |
| 186 |
circle.draw(self.renderer)
|
| 187 |
else:
|
| 188 |
a -= self.natoms
|
| 189 |
c = self.T[a]
|
| 190 |
if c != -1: |
| 191 |
hxy = self.D[c]
|
| 192 |
if Path is None: |
| 193 |
line = Polygon((xy + hxy, xy - hxy)) |
| 194 |
else:
|
| 195 |
line = PathPatch(Path((xy + hxy, xy - hxy))) |
| 196 |
line.draw(self.renderer)
|
| 197 |
|
| 198 |
def write_trailer(self): |
| 199 |
self.fd.write('end\n') |
| 200 |
self.fd.write('showpage\n') |
| 201 |
self.fd.close()
|
| 202 |
|
| 203 |
|
| 204 |
def write_eps(filename, atoms, **parameters): |
| 205 |
if isinstance(atoms, list): |
| 206 |
assert len(atoms) == 1 |
| 207 |
atoms = atoms[0]
|
| 208 |
EPS(atoms, **parameters).write(filename) |