atorvi 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
atorvi/__init__.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ atorvi - ATomic ORbitals VIsualization
3
+ a library for visualizing individual atomic orbitals and their various linear combinations.
4
+ The result is a file in .xsf format, which can be opened and visualized using software like XCrysDen or VESTA.
5
+ """
6
+
7
+ __author__ = "Dmitry Korotin"
8
+ __author_email__ = "dmitry@korotin.name"
9
+ __version__ = "0.1.1"
10
+ __license__ = "MIT"
11
+
12
+ from .atomic_orbitals import (
13
+ radial_part,
14
+ get_orbital,
15
+ supported_orbitals,
16
+ p_orbitals,
17
+ d_orbitals,
18
+ f_orbitals,
19
+ get_atomic_number
20
+ )
21
+
22
+ from .atorvi import (
23
+ OrbitalFile,
24
+ main
25
+ )
26
+
27
+ __all__ = [
28
+ 'OrbitalFile',
29
+ 'radial_part',
30
+ 'get_orbital',
31
+ 'supported_orbitals',
32
+ 'p_orbitals',
33
+ 'd_orbitals',
34
+ 'f_orbitals',
35
+ 'get_atomic_number'
36
+ ]
@@ -0,0 +1,624 @@
1
+ __author__ = "Dmitry Korotin"
2
+ __author_email__ = "dmitry@korotin.name"
3
+
4
+ import numpy as np
5
+ from scipy.special import sph_harm, genlaguerre, factorial
6
+
7
+ p_orbitals = ["p_z", "p_x", "p_y"]
8
+ d_orbitals = ["d_{3z^2-r^2}", "d_{xz}", "d_{yz}", "d_{xy}", "d_{x^2-y^2}"]
9
+ f_orbitals = [
10
+ "f_{z^3}",
11
+ "f_{xz^2}",
12
+ "f_{yz^2}",
13
+ "f_{xyz}",
14
+ "f_{z(x^2-y^2)}",
15
+ "f_{x(x^2-3y^2)}",
16
+ "f_{y(3x^2-y^2)}"
17
+ ]
18
+
19
+ supported_orbitals = ["s"] + p_orbitals + d_orbitals + f_orbitals
20
+
21
+ def get_atomic_number(element_name):
22
+ """
23
+ Returns the atomic number of the given element.
24
+
25
+ This function takes the symbol of a chemical element and returns its
26
+ corresponding atomic number from the periodic table. If the element is
27
+ not found, the function returns None.
28
+
29
+ Parameters:
30
+ ----------
31
+ element_name : str
32
+ The symbol of the chemical element (e.g., 'H' for hydrogen, 'O' for oxygen).
33
+
34
+ Returns:
35
+ -------
36
+ int or None
37
+ The atomic number of the element if it is found, otherwise None.
38
+ """
39
+ # fmt: off
40
+ periodic_table = {
41
+ 'H': 1, 'He': 2, 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10,
42
+ 'Na': 11, 'Mg': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18,
43
+ 'K': 19, 'Ca': 20, 'Sc': 21, 'Ti': 22, 'V': 23, 'Cr': 24, 'Mn': 25, 'Fe': 26,
44
+ 'Co': 27, 'Ni': 28, 'Cu': 29, 'Zn': 30, 'Ga': 31, 'Ge': 32, 'As': 33, 'Se': 34,
45
+ 'Br': 35, 'Kr': 36, 'Rb': 37, 'Sr': 38, 'Y': 39, 'Zr': 40, 'Nb': 41, 'Mo': 42,
46
+ 'Tc': 43, 'Ru': 44, 'Rh': 45, 'Pd': 46, 'Ag': 47, 'Cd': 48, 'In': 49, 'Sn': 50,
47
+ 'Sb': 51, 'Te': 52, 'I': 53, 'Xe': 54, 'Cs': 55, 'Ba': 56, 'La': 57, 'Ce': 58,
48
+ 'Pr': 59, 'Nd': 60, 'Pm': 61, 'Sm': 62, 'Eu': 63, 'Gd': 64, 'Tb': 65, 'Dy': 66,
49
+ 'Ho': 67, 'Er': 68, 'Tm': 69, 'Yb': 70, 'Lu': 71, 'Hf': 72, 'Ta': 73, 'W': 74,
50
+ 'Re': 75, 'Os': 76, 'Ir': 77, 'Pt': 78, 'Au': 79, 'Hg': 80, 'Tl': 81, 'Pb': 82,
51
+ 'Bi': 83, 'Th': 90, 'Pa': 91, 'U': 92, 'Np': 93, 'Pu': 94, 'Am': 95, 'Cm': 96,
52
+ 'Bk': 97, 'Cf': 98, 'Es': 99, 'Fm': 100, 'Md': 101, 'No': 102, 'Lr': 103,
53
+ 'Rf': 104, 'Db': 105, 'Sg': 106, 'Bh': 107, 'Hs': 108, 'Mt': 109, 'Ds': 110,
54
+ 'Rg': 111, 'Cn': 112, 'Nh': 113, 'Fl': 114, 'Mc': 115, 'Lv': 116, 'Ts': 117,
55
+ 'Og': 118
56
+ }
57
+ # fmt: on
58
+ return periodic_table.get(element_name)
59
+
60
+
61
+ def cart2sph(x, y, z):
62
+ """
63
+ Convert Cartesian coordinates to spherical coordinates.
64
+
65
+ This function converts Cartesian coordinates (x, y, z) into spherical
66
+ coordinates (r, theta, phi). The spherical coordinates are defined as follows:
67
+ - r: the radial distance from the origin,
68
+ - theta: the polar angle (angle from the z-axis),
69
+ - phi: the azimuthal angle (angle from the x-axis in the xy-plane).
70
+
71
+ Parameters:
72
+ ----------
73
+ x : float
74
+ The x-coordinate in Cartesian coordinates.
75
+ y : float
76
+ The y-coordinate in Cartesian coordinates.
77
+ z : float
78
+ The z-coordinate in Cartesian coordinates.
79
+
80
+ Returns:
81
+ -------
82
+ np.ndarray
83
+ A numpy array containing the spherical coordinates [r, theta, phi].
84
+ """
85
+ xy_sq = x**2 + y**2
86
+ r = np.sqrt(xy_sq + z**2)
87
+ theta = np.arctan2(np.sqrt(xy_sq), z)
88
+ phi = np.arctan2(y, x)
89
+
90
+ return np.array([r, theta, phi])
91
+
92
+ def radial_part(r, l, z):
93
+ """
94
+ Compute the radial part of the hydrogen-like atomic wavefunction.
95
+
96
+ Parameters:
97
+ r (float or ndarray): Radial distance from the nucleus in Bohr radii
98
+ l (int): Orbital angular momentum quantum number (0 <= l < n)
99
+ z (int): Atomic number (for hydrogen, Z=1)
100
+
101
+ Returns:
102
+ R_nl (float or ndarray): Radial wavefunction value(s) at r
103
+ """
104
+ # Bohr radius
105
+ a_0 = 0.52917720859
106
+
107
+ # define principal quantum number from z
108
+ if z < 3:
109
+ n = 1
110
+ elif z < 11:
111
+ n = 2
112
+ elif z < 19 or z in range(21, 31):
113
+ n = 3
114
+ elif z < 37 or z in range(39, 49) or z in range(57, 72):
115
+ n = 4
116
+ elif z < 55 or z in range(72, 81) or z in range(89, 104):
117
+ n = 5
118
+
119
+ # Use tabulated screened nuclear charge (Clementi's by default and Slater's if not available)
120
+ zeff = Z_EFF[z][l]
121
+
122
+ # Prefactor
123
+ rho = 2 * zeff * r / (n * a_0)
124
+ norm_factor = np.sqrt((2 * zeff / (n * a_0))**3 * factorial(n - l - 1) / (2 * n * factorial(n + l)))
125
+
126
+ # Laguerre polynomial
127
+ L = genlaguerre(n - l - 1, 2 * l + 1)
128
+
129
+ # Radial wavefunction
130
+ R_nl = norm_factor * np.exp(-rho/2.0) * rho**l * L(rho)
131
+
132
+ return R_nl
133
+
134
+
135
+ def get_orbital(orb, sph_coords, z):
136
+ """
137
+ Calculate the value of a specific atomic orbital at given spherical coordinates.
138
+
139
+ This function computes the value of a specified atomic orbital at given
140
+ spherical coordinates for an atom with the given atomic number.
141
+
142
+ Parameters:
143
+ ----------
144
+ orb : str
145
+ The type of orbital (e.g., "s", "p_x", "d_{xy}", etc.).
146
+ sph_coords : numpy.ndarray
147
+ An array of spherical coordinates [r, theta, phi].
148
+ z : int
149
+ The atomic number of the element.
150
+
151
+ Returns:
152
+ -------
153
+ float
154
+ The value of the atomic orbital at the given coordinates.
155
+ """
156
+
157
+ r = sph_coords[0]
158
+ theta = sph_coords[1]
159
+ phi = sph_coords[2]
160
+ # fmt: off
161
+ if orb == "s":
162
+ orbital = np.real( radial_part(r,0,z)*sph_harm(0, 0, phi,theta) )
163
+ elif orb == "p_z":
164
+ orbital = np.real( radial_part(r,1,z)*(sph_harm(0, 1, phi,theta)) )
165
+ elif orb == "p_x":
166
+ orbital = np.real( radial_part(r,1,z)*(1/np.sqrt(2.0))*(sph_harm(-1, 1, phi,theta)-sph_harm(1, 1, phi,theta)) )
167
+ elif orb == "p_y":
168
+ orbital = np.real( radial_part(r,1,z)*(1j/np.sqrt(2.0))*(sph_harm(-1, 1, phi,theta)+sph_harm(1, 1, phi,theta)) )
169
+ elif orb == "d_{3z^2-r^2}":
170
+ orbital = np.real( radial_part(r,2,z)*sph_harm(0, 2, phi,theta) )
171
+ elif orb == "d_{xz}":
172
+ orbital = np.real( radial_part(r,2,z)*(1/np.sqrt(2.0))*(sph_harm(-1, 2, phi,theta) - sph_harm(1, 2, phi,theta)) )
173
+ elif orb == "d_{yz}":
174
+ orbital = np.real( radial_part(r,2,z)*(1j/np.sqrt(2.0))*(sph_harm(-1, 2, phi,theta) + sph_harm(1, 2, phi,theta)) )
175
+ elif orb == "d_{xy}":
176
+ orbital = np.real( radial_part(r,2,z)*(1j/np.sqrt(2.0))*(sph_harm(-2, 2, phi,theta) - sph_harm(2, 2, phi,theta)) )
177
+ elif orb == "d_{x^2-y^2}":
178
+ orbital = np.real( radial_part(r,2,z)*(1/np.sqrt(2.0))*(sph_harm(-2, 2, phi,theta) + sph_harm(2, 2, phi,theta)))
179
+ elif orb == "f_{z^3}":
180
+ orbital = np.real( radial_part(r,3,z)*sph_harm(0, 3, phi,theta) )
181
+ elif orb == "f_{xz^2}":
182
+ orbital = np.real( radial_part(r,3,z)*(1/np.sqrt(2.0))*(sph_harm(-1, 3, phi,theta) - sph_harm(1, 3, phi,theta)) )
183
+ elif orb == "f_{yz^2}":
184
+ orbital = np.real( radial_part(r,3,z)*(1j/np.sqrt(2.0))*(sph_harm(-1, 3, phi,theta) + sph_harm(1, 3, phi,theta)) )
185
+ elif orb == "f_{xyz}":
186
+ orbital = np.real( radial_part(r,3,z)*(1j/np.sqrt(2.0))*(sph_harm(-2, 3, phi,theta) - sph_harm(2, 3, phi,theta)) )
187
+ elif orb == "f_{z(x^2-y^2)}":
188
+ orbital = np.real( radial_part(r,3,z)*(1/np.sqrt(2.0))*(sph_harm(-2, 3, phi,theta) + sph_harm(2, 3, phi,theta)) )
189
+ elif orb == "f_{x(x^2-3y^2)}":
190
+ orbital = np.real( radial_part(r,3,z)*(1/np.sqrt(2.0))*(sph_harm(-3, 3, phi,theta) - sph_harm(3, 3, phi,theta)) )
191
+ elif orb == "f_{y(3x^2-y^2)}":
192
+ orbital = np.real( radial_part(r,3,z)*(1j/np.sqrt(2.0))*(sph_harm(-3, 3, phi,theta) + sph_harm(3, 3, phi,theta)) )
193
+ else:
194
+ raise ValueError(f"Unknown orbital: {orb}")
195
+ # fmt: on
196
+
197
+ return orbital
198
+
199
+ # Effective nuclear charge using Clementi or Slater method (when Clementi is not available).
200
+ Z_EFF = {
201
+ 1: {0: 1.0, 1: 1.0, 2: 0.30000000000000004, 3: 0.30000000000000004},
202
+ 2: {0: 1.6875, 1: 1.7, 2: 0.30000000000000004, 3: 0.30000000000000004},
203
+ 3: {0: 1.2792, 1: 1.3, 2: 0.3500000000000001, 3: 0.3500000000000001},
204
+ 4: {
205
+ 0: 1.912,
206
+ 1: 1.9500000000000002,
207
+ 2: 0.3500000000000001,
208
+ 3: 0.3500000000000001,
209
+ },
210
+ 5: {0: 2.5762, 1: 2.4214, 2: 0.34999999999999964, 3: 0.34999999999999964},
211
+ 6: {0: 3.2166, 1: 3.1358, 2: 0.34999999999999964, 3: 0.34999999999999964},
212
+ 7: {0: 3.8474, 1: 3.834, 2: 0.34999999999999964, 3: 0.34999999999999964},
213
+ 8: {0: 4.4916, 1: 4.4532, 2: 0.34999999999999964, 3: 0.34999999999999964},
214
+ 9: {0: 5.1276, 1: 5.1, 2: 0.34999999999999964, 3: 0.34999999999999964},
215
+ 10: {0: 5.7584, 1: 5.7584, 2: 0.34999999999999964, 3: 0.34999999999999964},
216
+ 11: {
217
+ 0: 2.5074000000000005,
218
+ 1: 2.1999999999999993,
219
+ 2: 0.34999999999999964,
220
+ 3: 0.34999999999999964,
221
+ },
222
+ 12: {
223
+ 0: 3.307500000000001,
224
+ 1: 2.8499999999999996,
225
+ 2: 0.34999999999999964,
226
+ 3: 0.34999999999999964,
227
+ },
228
+ 13: {0: 4.1172, 1: 4.0656, 2: 0.34999999999999964, 3: 0.34999999999999964},
229
+ 14: {0: 4.9032, 1: 4.2852, 2: 0.34999999999999964, 3: 0.34999999999999964},
230
+ 15: {0: 5.6418, 1: 4.8864, 2: 0.34999999999999964, 3: 0.34999999999999964},
231
+ 16: {
232
+ 0: 6.366900000000001,
233
+ 1: 5.4818999999999996,
234
+ 2: 0.34999999999999964,
235
+ 3: 0.34999999999999964,
236
+ },
237
+ 17: {
238
+ 0: 7.068300000000001,
239
+ 1: 6.116099999999999,
240
+ 2: 0.3500000000000014,
241
+ 3: 0.3500000000000014,
242
+ },
243
+ 18: {
244
+ 0: 7.7568,
245
+ 1: 6.764100000000001,
246
+ 2: 0.3500000000000014,
247
+ 3: 0.3500000000000014,
248
+ },
249
+ 19: {
250
+ 0: 3.4952000000000005,
251
+ 1: 2.1999999999999993,
252
+ 2: 0.3500000000000014,
253
+ 3: 0.3500000000000014,
254
+ },
255
+ 20: {
256
+ 0: 4.398,
257
+ 1: 2.849999999999998,
258
+ 2: 0.3500000000000014,
259
+ 3: 0.3500000000000014,
260
+ },
261
+ 21: {0: 10.3398, 1: 9.4062, 2: 7.1198999999999995, 3: 2.3500000000000014},
262
+ 22: {0: 11.033100000000001, 1: 10.1037, 2: 8.1414, 3: 2.3500000000000014},
263
+ 23: {0: 11.709299999999999, 1: 10.785, 2: 8.9829, 3: 2.3500000000000014},
264
+ 24: {0: 12.3678, 1: 11.466000000000001, 2: 9.7566, 3: 1.3500000000000014},
265
+ 25: {0: 13.0179, 1: 12.109200000000001, 2: 10.5282, 3: 2.3500000000000014},
266
+ 26: {0: 13.6761, 1: 12.777899999999999, 2: 11.1798, 3: 2.3500000000000014},
267
+ 27: {0: 14.322299999999998, 1: 13.4346, 2: 11.8554, 3: 2.3500000000000014},
268
+ 28: {0: 14.961, 1: 14.085, 2: 12.529499999999999, 3: 2.3500000000000014},
269
+ 29: {0: 15.5943, 1: 14.730599999999999, 2: 13.2006, 3: 1.3500000000000014},
270
+ 30: {0: 16.2192, 1: 15.369299999999999, 2: 13.8783, 3: 2.3500000000000014},
271
+ 31: {
272
+ 0: 7.066800000000001,
273
+ 1: 6.221599999999999,
274
+ 2: 0.3500000000000014,
275
+ 3: 0.3500000000000014,
276
+ },
277
+ 32: {
278
+ 0: 8.043599999999998,
279
+ 1: 6.7804,
280
+ 2: 0.3500000000000014,
281
+ 3: 0.3500000000000014,
282
+ },
283
+ 33: {
284
+ 0: 8.944000000000003,
285
+ 1: 7.449200000000001,
286
+ 2: 0.3500000000000014,
287
+ 3: 0.3500000000000014,
288
+ },
289
+ 34: {
290
+ 0: 9.7576,
291
+ 1: 8.287199999999999,
292
+ 2: 0.3500000000000014,
293
+ 3: 0.3500000000000014,
294
+ },
295
+ 35: {
296
+ 0: 10.552799999999998,
297
+ 1: 9.027999999999999,
298
+ 2: 0.3500000000000014,
299
+ 3: 0.3500000000000014,
300
+ },
301
+ 36: {
302
+ 0: 11.3156,
303
+ 1: 9.769199999999998,
304
+ 2: 0.3500000000000014,
305
+ 3: 0.3500000000000014,
306
+ },
307
+ 37: {
308
+ 0: 4.984499999999997,
309
+ 1: 2.200000000000003,
310
+ 2: 0.3500000000000014,
311
+ 3: 0.3500000000000014,
312
+ },
313
+ 38: {
314
+ 0: 6.070499999999999,
315
+ 1: 2.8500000000000014,
316
+ 2: 0.3500000000000014,
317
+ 3: 0.3500000000000014,
318
+ },
319
+ 39: {0: 14.2636, 1: 12.7456, 2: 15.958399999999997, 3: 2.3500000000000014},
320
+ 40: {0: 14.901600000000002, 1: 13.46, 2: 13.0716, 3: 2.3500000000000014},
321
+ 41: {
322
+ 0: 15.282800000000002,
323
+ 1: 14.084400000000002,
324
+ 2: 11.2376,
325
+ 3: 1.3500000000000014,
326
+ },
327
+ 42: {0: 16.0964, 1: 14.9768, 2: 11.392400000000002, 3: 1.3500000000000014},
328
+ 43: {0: 17.1984, 1: 15.8112, 2: 12.881999999999998, 3: 2.3500000000000014},
329
+ 44: {0: 17.656, 1: 16.4348, 2: 12.8128, 3: 1.3500000000000014},
330
+ 45: {0: 18.5816, 1: 17.1396, 2: 13.4424, 3: 1.3500000000000014},
331
+ 46: {0: 18.986, 1: 17.7232, 2: 13.617599999999996, 3: 0.3500000000000014},
332
+ 47: {0: 19.8648, 1: 18.5624, 2: 14.762799999999999, 3: 1.3500000000000014},
333
+ 48: {0: 20.8692, 1: 19.4112, 2: 15.876800000000003, 3: 2.3500000000000014},
334
+ 49: {
335
+ 0: 9.511499999999998,
336
+ 1: 8.469999999999999,
337
+ 2: 0.3500000000000014,
338
+ 3: 0.3500000000000014,
339
+ },
340
+ 50: {
341
+ 0: 10.628500000000003,
342
+ 1: 9.102000000000004,
343
+ 2: 0.3500000000000014,
344
+ 3: 0.3500000000000014,
345
+ },
346
+ 51: {
347
+ 0: 11.611000000000004,
348
+ 1: 9.994500000000002,
349
+ 2: 0.3500000000000014,
350
+ 3: 0.3500000000000014,
351
+ },
352
+ 52: {
353
+ 0: 12.537999999999997,
354
+ 1: 10.808500000000002,
355
+ 2: 0.3500000000000014,
356
+ 3: 0.3500000000000014,
357
+ },
358
+ 53: {
359
+ 0: 13.403500000000001,
360
+ 1: 11.6115,
361
+ 2: 0.3500000000000014,
362
+ 3: 0.3500000000000014,
363
+ },
364
+ 54: {
365
+ 0: 14.218000000000004,
366
+ 1: 12.424500000000002,
367
+ 2: 0.3500000000000014,
368
+ 3: 0.3500000000000014,
369
+ },
370
+ 55: {
371
+ 0: 15.444500000000005,
372
+ 1: 13.650999999999996,
373
+ 2: 1.3500000000000014,
374
+ 3: 1.3500000000000014,
375
+ },
376
+ 56: {
377
+ 0: 16.619500000000002,
378
+ 1: 14.8005,
379
+ 2: 2.3500000000000014,
380
+ 3: 2.3500000000000014,
381
+ },
382
+ 57: {0: 28.7964, 1: 27.7064, 2: 24.7252, 3: 1.3599999999999994},
383
+ 58: {0: 29.68, 1: 28.6064, 2: 25.660799999999995, 3: 1.676000000000002},
384
+ 59: {0: 30.3332, 1: 29.0568, 2: 26.297200000000004, 3: 21.1008},
385
+ 60: {0: 30.9864, 1: 30.014, 2: 26.809200000000004, 3: 22.266},
386
+ 61: {0: 31.6396, 1: 30.6232, 2: 27.739999999999995, 3: 23.134},
387
+ 62: {0: 32.2924, 1: 31.088, 2: 28.239599999999996, 3: 23.531599999999997},
388
+ 63: {0: 32.868, 1: 31.8748, 2: 28.940799999999996, 3: 24.32},
389
+ 64: {0: 33.444, 1: 32.6468, 2: 29.6336, 3: 25.013599999999997},
390
+ 65: {0: 34.02, 1: 33.3988, 2: 30.310000000000002, 3: 25.864800000000002},
391
+ 66: {0: 34.592, 1: 33.826, 2: 31.018, 3: 26.536},
392
+ 67: {0: 35.312, 1: 34.5628, 2: 31.671599999999998, 3: 27.4696},
393
+ 68: {0: 36.232, 1: 35.1092, 2: 32.2712, 3: 27.9784},
394
+ 69: {0: 37.1376, 1: 35.988, 2: 32.944, 3: 28.634},
395
+ 70: {0: 37.5176, 1: 36.402, 2: 33.5896, 3: 29.432000000000002},
396
+ 71: {0: 38.2692, 1: 37.1904, 2: 35.2892, 3: 30.931200000000004},
397
+ 72: {0: 21.833, 1: 19.585, 2: 16.619500000000002, 3: 2.3499999999999943},
398
+ 73: {0: 22.6935, 1: 20.4735, 2: 16.368000000000002, 3: 2.3499999999999943},
399
+ 74: {
400
+ 0: 23.5415,
401
+ 1: 21.325500000000005,
402
+ 2: 16.741999999999997,
403
+ 3: 2.3499999999999943,
404
+ },
405
+ 75: {0: 24.357, 1: 22.144, 2: 17.382999999999996, 3: 2.3499999999999943},
406
+ 76: {0: 25.095, 1: 22.909999999999997, 2: 17.997, 3: 2.3499999999999943},
407
+ 77: {0: 25.8455, 1: 23.661, 2: 18.695999999999998, 3: 2.3499999999999943},
408
+ 78: {0: 26.587999999999994, 1: 24.4195, 2: 19.4075, 3: 1.3499999999999943},
409
+ 79: {0: 27.3275, 1: 25.17, 2: 20.1265, 3: 1.3499999999999943},
410
+ 80: {
411
+ 0: 28.111000000000004,
412
+ 1: 25.967,
413
+ 2: 20.855999999999995,
414
+ 3: 2.3499999999999943,
415
+ },
416
+ 81: {
417
+ 0: 29.122,
418
+ 1: 27.088499999999996,
419
+ 2: 22.025000000000006,
420
+ 3: 3.3499999999999943,
421
+ },
422
+ 82: {0: 30.131500000000003, 1: 28.03, 2: 23.152, 3: 4.349999999999994},
423
+ 83: {0: 31.028999999999996, 1: 29.021, 2: 24.244, 3: 5.349999999999994},
424
+ 84: {
425
+ 0: 32.023,
426
+ 1: 30.024500000000003,
427
+ 2: 25.304000000000002,
428
+ 3: 6.349999999999994,
429
+ },
430
+ 85: {0: 32.9335, 1: 31.04, 2: 26.339, 3: 7.349999999999994},
431
+ 86: {0: 33.893, 1: 31.970999999999997, 2: 27.353, 3: 8.349999999999994},
432
+ 87: {
433
+ 0: 29.349999999999994,
434
+ 1: 29.349999999999994,
435
+ 2: 15.849999999999994,
436
+ 3: 9.349999999999994,
437
+ },
438
+ 88: {
439
+ 0: 30.349999999999994,
440
+ 1: 30.349999999999994,
441
+ 2: 16.849999999999994,
442
+ 3: 10.349999999999994,
443
+ },
444
+ 89: {
445
+ 0: 31.349999999999994,
446
+ 1: 31.349999999999994,
447
+ 2: 17.849999999999994,
448
+ 3: 11.349999999999994,
449
+ },
450
+ 90: {
451
+ 0: 32.349999999999994,
452
+ 1: 32.349999999999994,
453
+ 2: 18.849999999999994,
454
+ 3: 12.349999999999994,
455
+ },
456
+ 91: {
457
+ 0: 33.349999999999994,
458
+ 1: 33.349999999999994,
459
+ 2: 17.849999999999994,
460
+ 3: 12.650000000000006,
461
+ },
462
+ 92: {
463
+ 0: 34.349999999999994,
464
+ 1: 34.349999999999994,
465
+ 2: 17.849999999999994,
466
+ 3: 13.299999999999997,
467
+ },
468
+ 93: {
469
+ 0: 35.349999999999994,
470
+ 1: 35.349999999999994,
471
+ 2: 17.849999999999994,
472
+ 3: 13.950000000000003,
473
+ },
474
+ 94: {
475
+ 0: 36.349999999999994,
476
+ 1: 36.349999999999994,
477
+ 2: 16.849999999999994,
478
+ 3: 14.25,
479
+ },
480
+ 95: {
481
+ 0: 37.349999999999994,
482
+ 1: 37.349999999999994,
483
+ 2: 16.849999999999994,
484
+ 3: 14.900000000000006,
485
+ },
486
+ 96: {
487
+ 0: 38.349999999999994,
488
+ 1: 38.349999999999994,
489
+ 2: 17.849999999999994,
490
+ 3: 15.900000000000006,
491
+ },
492
+ 97: {
493
+ 0: 39.349999999999994,
494
+ 1: 39.349999999999994,
495
+ 2: 16.849999999999994,
496
+ 3: 16.200000000000003,
497
+ },
498
+ 98: {
499
+ 0: 40.349999999999994,
500
+ 1: 40.349999999999994,
501
+ 2: 16.849999999999994,
502
+ 3: 16.849999999999994,
503
+ },
504
+ 99: {
505
+ 0: 41.349999999999994,
506
+ 1: 41.349999999999994,
507
+ 2: 16.849999999999994,
508
+ 3: 17.5,
509
+ },
510
+ 100: {
511
+ 0: 42.349999999999994,
512
+ 1: 42.349999999999994,
513
+ 2: 16.849999999999994,
514
+ 3: 18.150000000000006,
515
+ },
516
+ 101: {
517
+ 0: 43.349999999999994,
518
+ 1: 43.349999999999994,
519
+ 2: 16.849999999999994,
520
+ 3: 18.799999999999997,
521
+ },
522
+ 102: {
523
+ 0: 44.349999999999994,
524
+ 1: 44.349999999999994,
525
+ 2: 16.849999999999994,
526
+ 3: 19.450000000000003,
527
+ },
528
+ 103: {
529
+ 0: 45.349999999999994,
530
+ 1: 45.349999999999994,
531
+ 2: 17.849999999999994,
532
+ 3: 20.450000000000003,
533
+ },
534
+ 104: {
535
+ 0: 46.349999999999994,
536
+ 1: 46.349999999999994,
537
+ 2: 18.849999999999994,
538
+ 3: 21.450000000000003,
539
+ },
540
+ 105: {
541
+ 0: 47.349999999999994,
542
+ 1: 47.349999999999994,
543
+ 2: 19.849999999999994,
544
+ 3: 22.450000000000003,
545
+ },
546
+ 106: {
547
+ 0: 48.349999999999994,
548
+ 1: 48.349999999999994,
549
+ 2: 20.849999999999994,
550
+ 3: 23.450000000000003,
551
+ },
552
+ 107: {
553
+ 0: 49.349999999999994,
554
+ 1: 49.349999999999994,
555
+ 2: 21.849999999999994,
556
+ 3: 24.450000000000003,
557
+ },
558
+ 108: {
559
+ 0: 50.349999999999994,
560
+ 1: 50.349999999999994,
561
+ 2: 22.849999999999994,
562
+ 3: 25.450000000000003,
563
+ },
564
+ 109: {
565
+ 0: 51.349999999999994,
566
+ 1: 51.349999999999994,
567
+ 2: 23.849999999999994,
568
+ 3: 26.450000000000003,
569
+ },
570
+ 110: {
571
+ 0: 52.349999999999994,
572
+ 1: 52.349999999999994,
573
+ 2: 24.849999999999994,
574
+ 3: 27.450000000000003,
575
+ },
576
+ 111: {
577
+ 0: 53.349999999999994,
578
+ 1: 53.349999999999994,
579
+ 2: 25.849999999999994,
580
+ 3: 28.450000000000003,
581
+ },
582
+ 112: {
583
+ 0: 54.349999999999994,
584
+ 1: 54.349999999999994,
585
+ 2: 26.849999999999994,
586
+ 3: 29.450000000000003,
587
+ },
588
+ 113: {
589
+ 0: 55.349999999999994,
590
+ 1: 55.349999999999994,
591
+ 2: 27.849999999999994,
592
+ 3: 30.450000000000003,
593
+ },
594
+ 114: {
595
+ 0: 56.349999999999994,
596
+ 1: 56.349999999999994,
597
+ 2: 28.849999999999994,
598
+ 3: 31.450000000000003,
599
+ },
600
+ 115: {
601
+ 0: 57.349999999999994,
602
+ 1: 57.349999999999994,
603
+ 2: 29.849999999999994,
604
+ 3: 32.45,
605
+ },
606
+ 116: {
607
+ 0: 58.349999999999994,
608
+ 1: 58.349999999999994,
609
+ 2: 30.849999999999994,
610
+ 3: 33.45,
611
+ },
612
+ 117: {
613
+ 0: 59.349999999999994,
614
+ 1: 59.349999999999994,
615
+ 2: 31.849999999999994,
616
+ 3: 34.45,
617
+ },
618
+ 118: {
619
+ 0: 60.349999999999994,
620
+ 1: 60.349999999999994,
621
+ 2: 32.849999999999994,
622
+ 3: 35.45,
623
+ },
624
+ }
atorvi/atorvi.py ADDED
@@ -0,0 +1,392 @@
1
+ __author__ = "Dmitry Korotin"
2
+ __author_email__ = "dmitry@korotin.name"
3
+
4
+ import numpy as np
5
+ from .atomic_orbitals import *
6
+
7
+ class OrbitalFile:
8
+ """
9
+ A class for creating and managing files containing atomic orbital data.
10
+
11
+ This class provides methods for adding atoms and orbitals, setting up the
12
+ computational box, and writing the orbital data to a file.
13
+
14
+ """
15
+
16
+ def __init__(self, filename, grid_step=0.05) -> None:
17
+ self.file = open(filename, "w")
18
+ self.box_origin = [-2, -2, -2]
19
+ self.box_size = [4, 4, 4]
20
+ self.orbitals = []
21
+ self.grid_step = grid_step
22
+ self.atoms = []
23
+ self.is_crystal = False
24
+
25
+ def __del__(self):
26
+ if not self.file.closed:
27
+ self.file.close()
28
+
29
+ def _calculate_orbital_grid(self, orbital, position=[0.0, 0.0, 0.0], znumber=8):
30
+
31
+ origin = self.box_origin - position
32
+ x = np.linspace(origin[0], origin[0] + self.box_size[0], self.grid[0])
33
+ y = np.linspace(origin[1], origin[1] + self.box_size[1], self.grid[1])
34
+ z = np.linspace(origin[2], origin[2] + self.box_size[2], self.grid[2])
35
+
36
+ xx, yy, zz = np.meshgrid(x, y, z, indexing="ij")
37
+
38
+ sph_coords = cart2sph(xx, yy, zz)
39
+
40
+ orbital_grid = get_orbital(orbital, sph_coords, znumber)
41
+
42
+ return orbital_grid
43
+
44
+ def _write_orbitals(self, squared):
45
+
46
+ self._generate_box()
47
+
48
+ for orb in self.orbitals:
49
+ self.datagrid += orb[3] * self._calculate_orbital_grid(
50
+ orb[0], orb[1], orb[2]
51
+ )
52
+
53
+ if squared:
54
+ self.datagrid = self.datagrid**2
55
+
56
+ self.file.write("\nBEGIN_BLOCK_DATAGRID_3D\nmy_datagrid\n")
57
+ self.file.write("BEGIN_DATAGRID_3D\n")
58
+ self.file.write(f"{self.grid[0]} {self.grid[1]} {self.grid[2]}\n")
59
+ self.file.write(
60
+ f"{self.box_origin[0]} {self.box_origin[1]} {self.box_origin[2]}\n"
61
+ )
62
+ self.file.write(f"{self.box_size[0]} 0 0\n")
63
+ self.file.write(f"0 {self.box_size[1]} 0\n")
64
+ self.file.write(f"0 0 {self.box_size[2]}\n")
65
+
66
+ outdata = self.datagrid.T
67
+ for vec in outdata:
68
+ np.savetxt(self.file, vec, fmt="%12.9f", footer=" ", comments="")
69
+
70
+ self.file.write("END_DATAGRID_3D\n")
71
+ self.file.write("END_BLOCK_DATAGRID_3D\n")
72
+
73
+ def _write_atoms(self):
74
+
75
+ if self.is_crystal:
76
+ self.file.write("CRYSTAL\nPRIMVEC\n")
77
+ self.file.writelines(
78
+ f"{row[0]:9.6f} {row[1]:9.6f} {row[2]:9.6f}\n"
79
+ for row in self.lattice.matrix
80
+ )
81
+
82
+ self.file.write("CONVVEC\n")
83
+ self.file.writelines(
84
+ f"{row[0]:9.6f} {row[1]:9.6f} {row[2]:9.6f}\n"
85
+ for row in self.lattice.matrix
86
+ )
87
+
88
+ self.file.write("PRIMCOORD\n")
89
+ self.file.write(f"{len(self.atoms)} 1\n")
90
+ else:
91
+
92
+ self.file.write("ATOMS\n")
93
+
94
+ for at in self.atoms:
95
+ atomic_number = get_atomic_number(at[0])
96
+ x, y, z = at[1]
97
+ self.file.write(f"{atomic_number:3n} {x:9.6f} {y:9.6f} {z:9.6f}\n")
98
+
99
+ def add_atoms(self, atoms):
100
+ """
101
+ Add atoms to the system.
102
+
103
+ This method adds atoms to the system if it's not a periodic system.
104
+
105
+ Parameters:
106
+ ----------
107
+ atoms : list
108
+ A list of atoms to add, where each atom is represented by a tuple
109
+ containing the element symbol and its coordinates.
110
+
111
+ Raises:
112
+ ------
113
+ ValueError
114
+ If the system is periodic (crystal structure).
115
+ """
116
+
117
+ if not self.is_crystal:
118
+ for a in atoms:
119
+ self.atoms.append(a)
120
+ else:
121
+ raise ValueError("It is a periodic system. Do not add atoms manually")
122
+
123
+ def write_data(self, squared=False):
124
+ """
125
+ Write the orbital and atom data to the file.
126
+
127
+ This method writes the atom positions and orbital data to the file
128
+ and closes it.
129
+ """
130
+
131
+ if len(self.atoms) > 0:
132
+ self._write_atoms()
133
+
134
+ if len(self.orbitals) > 0:
135
+ self._write_orbitals( squared )
136
+
137
+ print(f"\nFile {self.file.name} successfully written")
138
+
139
+ self.file.close()
140
+
141
+ def _generate_box(self):
142
+ min_orbital_size = 5.0
143
+
144
+ orbitals_positions = [orb[1] for orb in self.orbitals]
145
+
146
+ box_center, box_size = get_bbox_center_and_size(orbitals_positions)
147
+
148
+ box_size += [min_orbital_size, min_orbital_size, min_orbital_size]
149
+ self.box_size = np.array(box_size)
150
+
151
+ self.box_origin = box_center - self.box_size / 2.0
152
+
153
+ self.grid = np.array(self.box_size // self.grid_step, dtype=int)
154
+ self.datagrid = np.zeros(self.grid)
155
+
156
+ def add_orbital(self, orbital, position=[0.0, 0.0, 0.0], znumber=8, coeff=1.0):
157
+ """
158
+ Add an orbital to the system.
159
+
160
+ This method adds an orbital with specified parameters to the system.
161
+
162
+ Parameters:
163
+ ----------
164
+ orbital : str
165
+ The type of orbital (e.g., "s", "p_x", "d_{xy}", etc.).
166
+ position : list, optional
167
+ The position of the orbital (default is [0.0, 0.0, 0]).
168
+ znumber : int, optional
169
+ The atomic number of the element (default is 8).
170
+ coeff : float, optional
171
+ The coefficient of the orbital (default is 1.0).
172
+ """
173
+ self.orbitals.append([orbital, position, znumber, coeff])
174
+
175
+ def add_orbital_at_atom(self, orbital, atom_index, coeff=1.0):
176
+ """
177
+ Add an orbital at a specific atom's position.
178
+
179
+ This method adds an orbital at the position of a specified atom.
180
+
181
+ Parameters:
182
+ ----------
183
+ orbital : str
184
+ The type of orbital (e.g., "s", "p_x", "d_{xy}", etc.).
185
+ atom_index : int
186
+ The index of the atom in the system.
187
+ coeff : float, optional
188
+ The coefficient of the orbital (default is 1.0).
189
+
190
+ Raises:
191
+ ------
192
+ KeyError
193
+ If the atom_index is out of range.
194
+ """
195
+ if atom_index in range(0, len(self.atoms)):
196
+ position = self.atoms[atom_index][1]
197
+ znumber = get_atomic_number(self.atoms[atom_index][0])
198
+ self.orbitals.append([orbital, position, znumber, coeff])
199
+ else:
200
+ raise KeyError("Wrong atom_index")
201
+
202
+ def crystal_from_file(self, filename):
203
+ """
204
+ Create a crystal structure from a file.
205
+
206
+ This method reads a crystal structure from a file and sets up the system.
207
+
208
+ Parameters:
209
+ ----------
210
+ filename : str
211
+ The path to the file containing the crystal structure.
212
+
213
+ Returns:
214
+ -------
215
+ pymatgen.core.structure.IStructure
216
+ The created crystal structure.
217
+ """
218
+ try:
219
+ from pymatgen.core.structure import IStructure
220
+ except ImportError:
221
+ raise ImportError(
222
+ "\nOptional dependency 'pymatgen' required for crystal_from_file is not installed. \n\
223
+ You can install it by running: pip install pymatgen"
224
+ )
225
+
226
+ structure = IStructure.from_file(filename)
227
+ self.crystal_from_pymatgen(structure)
228
+
229
+ return structure
230
+
231
+ def crystal_from_pymatgen(self, structure):
232
+ """
233
+ Set up the crystal structure from a pymatgen Structure object.
234
+
235
+ This method sets up the crystal structure using a pymatgen Structure object.
236
+
237
+ Parameters:
238
+ ----------
239
+ structure : pymatgen.core.structure.IStructure
240
+ The pymatgen Structure object representing the crystal.
241
+ """
242
+
243
+ crystal = structure
244
+ self.is_crystal = True
245
+
246
+ self.lattice = crystal.lattice
247
+
248
+ for atom in crystal.sites:
249
+ self.atoms.append((atom.specie.symbol, atom.coords))
250
+
251
+ def add_orbital_at_element(self, orbital, element, coeff=1.0):
252
+ """
253
+ Add an orbital to all atoms of a specific element.
254
+
255
+ This method adds the specified orbital to all atoms of the given element.
256
+
257
+ Parameters:
258
+ ----------
259
+ orbital : str
260
+ The type of orbital (e.g., "s", "p_x", "d_{xy}", etc.).
261
+ element : str
262
+ The symbol of the element.
263
+ coeff : float, optional
264
+ The coefficient of the orbital (default is 1.0).
265
+ """
266
+ for iatom, atom in enumerate(self.atoms):
267
+ if element == atom[0]:
268
+ self.add_orbital_at_atom(orbital, iatom, coeff)
269
+
270
+ def get_bbox_center_and_size(points):
271
+ """
272
+ Calculate the center and size of a bounding box for a set of points.
273
+
274
+ This function takes a list of points and calculates the center and size of
275
+ the smallest bounding box that contains all the points.
276
+
277
+ Parameters:
278
+ ----------
279
+ points : list of tuples or numpy.ndarray
280
+ A list of points, where each point is represented by a tuple or array
281
+ of coordinates.
282
+
283
+ Returns:
284
+ -------
285
+ tuple
286
+ A tuple containing two numpy arrays:
287
+ - center: The coordinates of the center of the bounding box.
288
+ - bounding_box_size: The size of the bounding box in each dimension.
289
+ """
290
+ points_array = np.array(points)
291
+
292
+ center = np.mean(points_array, axis=0)
293
+ distances = points_array - center
294
+ max_distances = np.max(np.abs(distances), axis=0)
295
+ bounding_box_size = 2 * max_distances
296
+
297
+ return center, bounding_box_size
298
+
299
+ def main():
300
+ """
301
+ Interactive mode for atorvi orbital visualization.
302
+ Allows users to specify orbitals and their positions interactively.
303
+ """
304
+ from importlib.metadata import version
305
+
306
+ print("\nATORVI - ATomic ORbital VIsualization")
307
+ print(f"Version: {version('atorvi')}")
308
+ print(f"Author: {__author__}")
309
+ print(f"Contact: {__author_email__}\n")
310
+
311
+ # Get output filename
312
+ filename = input("Enter output filename (e.g. 'orbitals.xsf'): ").strip()
313
+ if not filename:
314
+ filename = "orbitals.xsf"
315
+
316
+ # Initialize orbital file
317
+ orbital_file = OrbitalFile(filename)
318
+
319
+ print("\nSupported orbitals are:")
320
+ print("s, p_z, p_x, p_y")
321
+ print(f"{' '.join(map(str, d_orbitals))}")
322
+ print(f"{' '.join(map(str, f_orbitals))}")
323
+
324
+ print("\nEnter orbital data in format: Element orbital [x y z]")
325
+ print("\nExample: 'O p_z 0 0 1' or 'Fe d_{xy}'")
326
+ print("Type 'done' to finish and generate visualization")
327
+
328
+ while True:
329
+ try:
330
+ # Get user input
331
+ user_input = input("\nEnter orbital data: ").strip()
332
+
333
+ # Check for exit condition
334
+ if user_input.lower() == 'done':
335
+ break
336
+
337
+ # Parse input
338
+ parts = user_input.split()
339
+ if len(parts) < 2:
340
+ print("Error: Please provide at least element and orbital type")
341
+ continue
342
+
343
+ element = parts[0]
344
+ orbital_type = parts[1]
345
+
346
+ if orbital_type not in supported_orbitals:
347
+ print(f"Error: Unsupported orbital type '{orbital_type}'")
348
+ continue
349
+
350
+ # Parse position if provided
351
+ if len(parts) == 5:
352
+ try:
353
+ position = [float(parts[2]), float(parts[3]), float(parts[4])]
354
+ except ValueError:
355
+ print("Error: Position coordinates must be numbers")
356
+ continue
357
+ else:
358
+ position = [0.0, 0.0, 0.0]
359
+
360
+ # Get atomic number
361
+ try:
362
+ z_number = get_atomic_number(element)
363
+ except KeyError:
364
+ print(f"Error: Unknown element '{element}'")
365
+ continue
366
+
367
+ # Add orbital
368
+ orbital_file.add_orbital(
369
+ orbital=orbital_type,
370
+ position=position,
371
+ znumber=z_number,
372
+ coeff=1.0
373
+ )
374
+ print(f"Added {orbital_type} orbital for {element} at position {position}")
375
+
376
+ except KeyboardInterrupt:
377
+ print("\nOperation cancelled by user")
378
+ return
379
+ except Exception as e:
380
+ print(f"Error: {str(e)}")
381
+ continue
382
+
383
+ try:
384
+ print("\nGenerating visualization...")
385
+ orbital_file.write_data(squared=False)
386
+ print("\nDone! You can now visualize the orbitals using XCrySDen, VESTA or similar software.")
387
+ except Exception as e:
388
+ print(f"Error while generating visualization: {str(e)}")
389
+
390
+
391
+ if __name__ == "__main__":
392
+ main()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Dmitry Korotin dmitry@korotin.name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.1
2
+ Name: atorvi
3
+ Version: 0.1.1
4
+ Summary: Package for visualizing atomic orbitals
5
+ Author-email: Dmitry Korotin <dmitry@korotin.name>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/dkorotin/atorvi
8
+ Keywords: atomic orbitals,visualization
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.6
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy
16
+ Requires-Dist: scipy
17
+ Provides-Extra: pymatgen
18
+ Requires-Dist: pymatgen; extra == "pymatgen"
19
+
20
+ # atorvi - ATomic ORbitals VIsualization
21
+
22
+ ![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)
23
+
24
+ **atorvi** is a Python package for visualizing individual atomic orbitals and their various linear combinations. The library generates requested atomic orbitals on a 3D mesh and exports them, along with the inputted crystal structure (molecule or periodic crystal), into a file in [XCrysDen .xsf format](http://www.xcrysden.org/doc/XSF.html).
25
+
26
+ The resulting `.xsf` file can be opened and visualized using your favorite visualization software, such as [XCrysDen](http://www.xcrysden.org/), [VESTA](https://jp-minerals.org/vesta/en/) or [VMD](https://www.ks.uiuc.edu/Research/vmd/).
27
+
28
+ ## Installation
29
+
30
+ To install **atorvi**, you can use `pip`:
31
+
32
+ ```bash
33
+ pip install atorvi
34
+ ```
35
+
36
+ ## Features
37
+
38
+ - **Atomic Orbitals Generation**: Create atomic orbitals at a specific position in space or at a designated atom/element within a crystal structure.
39
+
40
+ - **Flexible Structure Input**: Input crystal structures either manually, atom-by-atom, or directly from standard file formats such as POSCAR, XSF, CIF, and more. The integration with the `pymatgen` package simplifies structure handling.
41
+
42
+ - **Orbital Hybridization**: Implement orbital hybridization by mixing different orbitals with custom coefficients, allowing for the exploration of complex bonding interactions.
43
+
44
+ - **Orbital Squared Moduli**: Generate squared moduli of orbitals, providing insight into their spatial distribution and probability densities.
45
+
46
+ Examples of orbitals generated with **atorvi**: could be found in [examples](./examples/) folder.
47
+
48
+ ## Quick start
49
+
50
+ The full user manual is available in [docs/atorvi_manual.md](./docs/atorvi_manual.md).
51
+
52
+ **atorvi** can be used in two modes: package mode (for scripting and notebooks) and CLI interactive mode (only basic functionality is available).
53
+
54
+ ### 1.Package Mode
55
+ In your Python script or Jupyter notebook, you can generate and visualize atomic orbitals with the following example:
56
+
57
+ ```python
58
+ import atorvi
59
+
60
+ # Example: visualize a d_{3z^2-r^2} orbital for an Ni atom (Z = 28)
61
+ outfile = atorvi.OrbitalFile("Ni_orbital.xsf")
62
+
63
+ outfile.add_orbital("d_{3z^2-r^2}", position=[0, 0, 0], znumber=28)
64
+
65
+ outfile.write_data()
66
+ ```
67
+
68
+ Then just open the `Ni_orbital.xsf` file in your favorite visualization software.
69
+
70
+ The orbitals available for generation are:
71
+ $$s$$
72
+ $$p_z, p_x, p_y$$
73
+ $$d_{3z^2-r^2}, d_{xz}, d_{yz}, d_{xy}, d_{x^2-y^2} $$
74
+ $$f_{z^3}, f_{xz^2}, f_{yz^2}, f_{xyz}, f_{z(x^2-y^2)}, f_{x(x^2-3y^2)}, f_{y(3x^2-y^2)}$$
75
+
76
+ ```python
77
+ print(atorvi.supported_orbitals)
78
+
79
+ ['s',
80
+ 'p_z', 'p_x', 'p_y',
81
+ 'd_{3z^2-r^2}', 'd_{xz}', 'd_{yz}', 'd_{xy}', 'd_{x^2-y^2}',
82
+ 'f_{z^3}', 'f_{xz^2}', 'f_{yz^2}', 'f_{xyz}', 'f_{z(x^2-y^2)}', 'f_{x(x^2-3y^2)}', 'f_{y(3x^2-y^2)}']
83
+ ```
84
+
85
+ ### 2. CLI Interactive Mode
86
+ atorvi also offers an interactive mode via the command line interface (CLI). You can simply run the script directly in a terminal:
87
+
88
+ ```bash
89
+ atorvi_cli
90
+ ```
91
+
92
+ Follow the prompts to generate orbitals and export them to XCrysDen-compatible formats.
93
+
94
+ Once the file is generated, you can open it using visualization tools like XCrysDen or VESTA.
95
+
96
+ ## Author
97
+
98
+ `atorvi` is developed and maintained by [Dmitry Korotin](https://www.researchgate.net/profile/Dmitry-Korotin). Contributions, suggestions, and feedback are welcome to help improve the project.
99
+
100
+ ## License
101
+
102
+ `atorvi` is released under the MIT License. You are free to use, modify, and distribute the software, provided that the original copyright and permission notice are included in all copies or substantial portions of the software.
103
+
104
+ The author kindly asks that you cite this GitHub repository [github.com/dkorotin/atorvi](https://github.com/dkorotin/atorvi) and the related paper (link will be available soon) in any publications that use images or data generated with the `atorvi` package.
105
+
106
+ For more details, refer to the full [MIT License](./LICENSE).
@@ -0,0 +1,9 @@
1
+ atorvi/__init__.py,sha256=MfCiHbcrEQ4aaVvCvW2z132FSvJP0x8axN99rhan-ok,754
2
+ atorvi/atomic_orbitals.py,sha256=aUWciftqq9XxGGJ8Z6c5MJTvLskZnRFN0PpfVhqn5gI,20291
3
+ atorvi/atorvi.py,sha256=1KHzWxxk-hZ-UxjYBksnT0LN-8_9ojdTNMSj7pi9HwI,12594
4
+ atorvi-0.1.1.dist-info/LICENSE,sha256=II7eEoRanRl9pcHPqcHDvaC6_jhL5OnFzpQQCYAwMw8,1090
5
+ atorvi-0.1.1.dist-info/METADATA,sha256=sq1SZnssHA4kBpPET4bchZK1UvxggdC_FxEgmEenNfs,4517
6
+ atorvi-0.1.1.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
7
+ atorvi-0.1.1.dist-info/entry_points.txt,sha256=rnPj_hrqVhtwpJEP_28sStjyYbLKOIdzykH2y9LCL5Q,43
8
+ atorvi-0.1.1.dist-info/top_level.txt,sha256=u6MN9V2sONM4ZqwOiSrsE6DKvDO1JJf--RzWWgEWe2k,7
9
+ atorvi-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.2.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ atorvi_cli = atorvi:main
@@ -0,0 +1 @@
1
+ atorvi