0
# Crystal Structure Handling
1
2
Handle crystalline structure representations, transformations, and symmetry operations for phonon calculations. Phonopy provides comprehensive tools for managing crystal structures including unit cells, supercells, primitive cells, and symmetry operations.
3
4
## Capabilities
5
6
### PhonopyAtoms Class
7
8
Core container for atomic structure information with comprehensive manipulation capabilities.
9
10
```python { .api }
11
class PhonopyAtoms:
12
def __init__(
13
self,
14
symbols: Optional[Sequence] = None,
15
numbers: Optional[Union[Sequence, np.ndarray]] = None,
16
masses: Optional[Union[Sequence, np.ndarray]] = None,
17
magnetic_moments: Optional[Union[Sequence, np.ndarray]] = None,
18
scaled_positions: Optional[Union[Sequence, np.ndarray]] = None,
19
positions: Optional[Union[Sequence, np.ndarray]] = None,
20
cell: Optional[Union[Sequence, np.ndarray]] = None,
21
atoms: Optional["PhonopyAtoms"] = None,
22
magmoms: Optional[Union[Sequence, np.ndarray]] = None,
23
pbc: Optional[bool] = None
24
):
25
"""
26
Initialize atomic structure container.
27
28
Parameters:
29
- symbols: Chemical symbols as list of strings
30
- numbers: Atomic numbers as list/array of integers
31
- masses: Atomic masses in atomic mass units (amu)
32
- magnetic_moments: Magnetic moments for each atom
33
- scaled_positions: Atomic positions in fractional coordinates
34
- positions: Atomic positions in Cartesian coordinates (Å)
35
- cell: Unit cell lattice vectors as 3x3 array (Å)
36
- atoms: Copy from existing PhonopyAtoms object (deprecated)
37
- magmoms: Magnetic moments for each atom (deprecated, use magnetic_moments)
38
- pbc: Periodic boundary conditions (deprecated, unused)
39
"""
40
41
def copy(self) -> PhonopyAtoms:
42
"""Create deep copy of PhonopyAtoms object."""
43
44
def __len__(self) -> int:
45
"""Get number of atoms in structure."""
46
47
def __getitem__(self, index) -> PhonopyAtoms:
48
"""Get subset of atoms by index or slice."""
49
```
50
51
**Example:**
52
53
```python
54
from phonopy.structure.atoms import PhonopyAtoms
55
import numpy as np
56
57
# Create structure from scratch
58
lattice = [[4.0, 0.0, 0.0],
59
[0.0, 4.0, 0.0],
60
[0.0, 0.0, 4.0]]
61
62
positions = [[0.0, 0.0, 0.0],
63
[0.5, 0.5, 0.5]]
64
65
symbols = ['Si', 'Si']
66
67
atoms = PhonopyAtoms(symbols=symbols,
68
scaled_positions=positions,
69
cell=lattice)
70
71
# Alternative using atomic numbers
72
numbers = [14, 14] # Silicon
73
atoms = PhonopyAtoms(numbers=numbers,
74
scaled_positions=positions,
75
cell=lattice)
76
77
# Create copy and modify
78
atoms_copy = atoms.copy()
79
print(f"Number of atoms: {len(atoms)}")
80
```
81
82
### Structure Properties
83
84
Access and manipulate structural properties.
85
86
```python { .api }
87
def get_cell(self) -> ndarray:
88
"""
89
Get unit cell lattice vectors.
90
91
Returns:
92
3x3 array of lattice vectors in Å
93
"""
94
95
def get_positions(self) -> ndarray:
96
"""
97
Get atomic positions in Cartesian coordinates.
98
99
Returns:
100
Nx3 array of positions in Å
101
"""
102
103
def get_scaled_positions(self) -> ndarray:
104
"""
105
Get atomic positions in fractional coordinates.
106
107
Returns:
108
Nx3 array of fractional positions [0,1)
109
"""
110
111
def get_masses(self) -> ndarray:
112
"""
113
Get atomic masses.
114
115
Returns:
116
Array of masses in atomic mass units (amu)
117
"""
118
119
def get_chemical_symbols(self) -> list:
120
"""
121
Get chemical symbols.
122
123
Returns:
124
List of element symbols as strings
125
"""
126
127
def get_atomic_numbers(self) -> ndarray:
128
"""
129
Get atomic numbers.
130
131
Returns:
132
Array of atomic numbers (integers)
133
"""
134
135
def get_magnetic_moments(self) -> ndarray:
136
"""Get magnetic moments for each atom."""
137
138
def get_number_of_atoms(self) -> int:
139
"""Get total number of atoms."""
140
141
@property
142
def volume(self) -> float:
143
"""Get unit cell volume in ų."""
144
```
145
146
**Example:**
147
148
```python
149
# Access structure properties
150
cell = atoms.get_cell()
151
positions = atoms.get_positions() # Cartesian coordinates
152
scaled_pos = atoms.get_scaled_positions() # Fractional coordinates
153
symbols = atoms.get_chemical_symbols()
154
masses = atoms.get_masses()
155
156
print(f"Cell volume: {atoms.volume:.2f} ų")
157
print(f"Lattice parameters: {np.linalg.norm(cell, axis=1)}")
158
print(f"Chemical formula: {symbols}")
159
```
160
161
### Structure Modifications
162
163
Modify atomic structures for phonon calculations.
164
165
```python { .api }
166
def set_cell(self, cell: ArrayLike):
167
"""
168
Set unit cell lattice vectors.
169
170
Parameters:
171
- cell: 3x3 array of lattice vectors in Å
172
"""
173
174
def set_positions(self, positions: ArrayLike):
175
"""
176
Set atomic positions in Cartesian coordinates.
177
178
Parameters:
179
- positions: Nx3 array of positions in Å
180
"""
181
182
def set_scaled_positions(self, scaled_positions: ArrayLike):
183
"""
184
Set atomic positions in fractional coordinates.
185
186
Parameters:
187
- scaled_positions: Nx3 array of fractional positions
188
"""
189
190
def set_masses(self, masses: ArrayLike):
191
"""
192
Set atomic masses.
193
194
Parameters:
195
- masses: Array of masses in amu
196
"""
197
198
def set_chemical_symbols(self, symbols: list):
199
"""
200
Set chemical symbols.
201
202
Parameters:
203
- symbols: List of element symbols
204
"""
205
206
def set_magnetic_moments(self, magmoms: ArrayLike):
207
"""
208
Set magnetic moments.
209
210
Parameters:
211
- magmoms: Array of magnetic moments
212
"""
213
```
214
215
**Example:**
216
217
```python
218
# Scale unit cell (e.g., for thermal expansion)
219
new_cell = atoms.get_cell() * 1.02 # 2% expansion
220
atoms.set_cell(new_cell)
221
222
# Modify atomic positions
223
positions = atoms.get_positions()
224
positions[0] += [0.1, 0.0, 0.0] # Displace first atom
225
atoms.set_positions(positions)
226
227
# Set custom masses (e.g., for isotope effects)
228
masses = atoms.get_masses()
229
masses[0] = 28.0 # Set first atom to Si-28
230
atoms.set_masses(masses)
231
```
232
233
### Supercell Generation
234
235
Create supercells for phonon calculations.
236
237
```python { .api }
238
def get_supercell(
239
unitcell: PhonopyAtoms,
240
supercell_matrix: ArrayLike,
241
symprec: float = 1e-5
242
) -> Supercell:
243
"""
244
Create supercell from unit cell.
245
246
Parameters:
247
- unitcell: Unit cell PhonopyAtoms object
248
- supercell_matrix: 3x3 transformation matrix or [nx, ny, nz]
249
- symprec: Symmetry precision for atom mapping
250
251
Returns:
252
Supercell object (extends PhonopyAtoms)
253
"""
254
255
class Supercell(PhonopyAtoms):
256
"""Supercell structure class with additional supercell-specific methods."""
257
258
def get_supercell_to_unitcell_map(self) -> ndarray:
259
"""
260
Get mapping from supercell atoms to unit cell atoms.
261
262
Returns:
263
Array mapping each supercell atom to its unit cell origin
264
"""
265
266
def get_unitcell_to_supercell_map(self) -> list:
267
"""
268
Get mapping from unit cell atoms to supercell atoms.
269
270
Returns:
271
List of arrays containing supercell indices for each unit cell atom
272
"""
273
```
274
275
**Example:**
276
277
```python
278
from phonopy.structure.cells import get_supercell
279
280
# Create 2x2x2 supercell
281
supercell_matrix = [[2, 0, 0], [0, 2, 0], [0, 0, 2]]
282
supercell = get_supercell(atoms, supercell_matrix)
283
284
print(f"Unit cell atoms: {len(atoms)}")
285
print(f"Supercell atoms: {len(supercell)}")
286
print(f"Supercell volume: {supercell.volume:.2f} ų")
287
288
# Get atom mappings
289
sc_to_uc_map = supercell.get_supercell_to_unitcell_map()
290
uc_to_sc_map = supercell.get_unitcell_to_supercell_map()
291
292
print(f"Supercell atom 0 maps to unit cell atom: {sc_to_uc_map[0]}")
293
print(f"Unit cell atom 0 maps to supercell atoms: {uc_to_sc_map[0]}")
294
```
295
296
### Primitive Cell Operations
297
298
Handle primitive cell transformations and standardization.
299
300
```python { .api }
301
def get_primitive(
302
unitcell: PhonopyAtoms,
303
primitive_matrix: ArrayLike | str,
304
symprec: float = 1e-5
305
) -> Primitive:
306
"""
307
Create primitive cell from unit cell.
308
309
Parameters:
310
- unitcell: Unit cell PhonopyAtoms object
311
- primitive_matrix: 3x3 transformation matrix or string ('auto', 'F', 'I', etc.)
312
- symprec: Symmetry precision
313
314
Returns:
315
Primitive cell object (extends PhonopyAtoms)
316
"""
317
318
def get_primitive_matrix(
319
spacegroup_type: str,
320
lattice: ArrayLike = None
321
) -> ndarray:
322
"""
323
Get primitive transformation matrix for given space group.
324
325
Parameters:
326
- spacegroup_type: Bravais lattice type ('P', 'F', 'I', 'A', 'C', 'R')
327
- lattice: Unit cell lattice vectors for rhombohedral case
328
329
Returns:
330
3x3 primitive transformation matrix
331
"""
332
333
def guess_primitive_matrix(
334
unitcell: PhonopyAtoms,
335
symprec: float = 1e-5
336
) -> ndarray:
337
"""
338
Automatically determine primitive matrix from unit cell symmetry.
339
340
Parameters:
341
- unitcell: Unit cell structure
342
- symprec: Symmetry precision
343
344
Returns:
345
3x3 primitive transformation matrix
346
"""
347
348
class Primitive(PhonopyAtoms):
349
"""Primitive cell structure class."""
350
351
def get_primitive_to_unitcell_map(self) -> ndarray:
352
"""Get mapping from primitive to unit cell atoms."""
353
354
def get_unitcell_to_primitive_map(self) -> list:
355
"""Get mapping from unit cell to primitive atoms."""
356
```
357
358
**Example:**
359
360
```python
361
from phonopy.structure.cells import (get_primitive, get_primitive_matrix,
362
guess_primitive_matrix)
363
364
# Get primitive cell automatically
365
primitive = get_primitive(atoms, primitive_matrix='auto')
366
print(f"Unit cell atoms: {len(atoms)}")
367
print(f"Primitive cell atoms: {len(primitive)}")
368
369
# Get primitive matrix for face-centered cubic
370
fcc_matrix = get_primitive_matrix('F')
371
print("FCC primitive matrix:")
372
print(fcc_matrix)
373
374
# Guess primitive matrix from symmetry
375
guessed_matrix = guess_primitive_matrix(atoms, symprec=1e-5)
376
print("Guessed primitive matrix:")
377
print(guessed_matrix)
378
379
# Apply specific primitive matrix
380
primitive_custom = get_primitive(atoms, fcc_matrix)
381
```
382
383
### Crystal Symmetry
384
385
Analyze and apply crystal symmetry operations.
386
387
```python { .api }
388
class Symmetry:
389
"""Crystal symmetry operations and analysis."""
390
391
def __init__(
392
self,
393
cell: PhonopyAtoms,
394
symprec: float = 1e-5,
395
angle_tolerance: float = -1.0,
396
is_symmetry: bool = True
397
):
398
"""
399
Initialize symmetry analysis.
400
401
Parameters:
402
- cell: Crystal structure
403
- symprec: Symmetry precision
404
- angle_tolerance: Angle tolerance for symmetry detection
405
- is_symmetry: Whether to use symmetry
406
"""
407
408
def get_symmetry_operations(self) -> dict:
409
"""
410
Get symmetry operations.
411
412
Returns:
413
Dictionary with 'rotations' and 'translations' arrays
414
"""
415
416
def get_reciprocal_operations(self) -> ndarray:
417
"""Get symmetry operations in reciprocal space."""
418
419
def get_pointgroup_operations(self) -> ndarray:
420
"""Get point group operations (rotations only)."""
421
422
def get_international_table(self) -> str:
423
"""Get international space group symbol."""
424
425
def get_wyckoff_positions(self) -> list:
426
"""Get Wyckoff positions for atoms."""
427
428
@property
429
def dataset(self) -> dict:
430
"""Symmetry dataset from spglib."""
431
```
432
433
**Example:**
434
435
```python
436
from phonopy.structure.symmetry import Symmetry
437
438
# Analyze crystal symmetry
439
symmetry = Symmetry(atoms, symprec=1e-5)
440
441
# Get symmetry operations
442
sym_ops = symmetry.get_symmetry_operations()
443
rotations = sym_ops['rotations']
444
translations = sym_ops['translations']
445
446
print(f"Number of symmetry operations: {len(rotations)}")
447
print(f"Space group: {symmetry.get_international_table()}")
448
449
# Get point group operations
450
point_group = symmetry.get_pointgroup_operations()
451
print(f"Point group order: {len(point_group)}")
452
453
# Wyckoff positions
454
wyckoff = symmetry.get_wyckoff_positions()
455
print(f"Wyckoff positions: {wyckoff}")
456
```
457
458
### Brillouin Zone Operations
459
460
Handle Brillouin zone operations and k-point generation.
461
462
```python { .api }
463
class BrillouinZone:
464
"""Brillouin zone operations and k-point handling."""
465
466
def __init__(
467
self,
468
primitive: PhonopyAtoms,
469
mesh: ArrayLike = None,
470
is_shift: ArrayLike = None,
471
is_time_reversal: bool = True,
472
is_gamma_center: bool = False,
473
rotations: ArrayLike = None,
474
qpoints: ArrayLike = None
475
):
476
"""
477
Initialize Brillouin zone operations.
478
479
Parameters:
480
- primitive: Primitive cell structure
481
- mesh: k-point mesh dimensions [nx, ny, nz]
482
- is_shift: Mesh shift [sx, sy, sz]
483
- is_time_reversal: Apply time-reversal symmetry
484
- is_gamma_center: Center mesh on gamma point
485
- rotations: Symmetry rotation matrices
486
- qpoints: Specific q-points to use
487
"""
488
489
def get_qpoints(self) -> ndarray:
490
"""Get q-points in reciprocal space."""
491
492
def get_weights(self) -> ndarray:
493
"""Get integration weights for q-points."""
494
495
def get_grid_points(self) -> ndarray:
496
"""Get grid point indices."""
497
498
def get_ir_reciprocal_mesh(self) -> tuple:
499
"""
500
Get irreducible reciprocal mesh.
501
502
Returns:
503
Tuple of (grid_points, weights, grid_mapping)
504
"""
505
```
506
507
### Distance Calculations
508
509
Calculate atomic distances and coordination environments.
510
511
```python { .api }
512
class ShortestPairs:
513
"""Calculate shortest interatomic distances and coordination."""
514
515
def __init__(
516
self,
517
cell: PhonopyAtoms,
518
cutoff_radius: float = None
519
):
520
"""
521
Initialize distance calculations.
522
523
Parameters:
524
- cell: Crystal structure
525
- cutoff_radius: Maximum distance to consider (Å)
526
"""
527
528
def get_shortest_distances(self) -> ndarray:
529
"""Get shortest distances between atoms."""
530
531
def get_nearest_neighbors(self, atom_index: int) -> dict:
532
"""
533
Get nearest neighbors for specific atom.
534
535
Parameters:
536
- atom_index: Index of target atom
537
538
Returns:
539
Dictionary with neighbor indices, distances, and vectors
540
"""
541
```
542
543
## Structure Creation Examples
544
545
### From Crystallographic Data
546
547
```python
548
# Silicon diamond structure
549
lattice_parameter = 5.43 # Å
550
lattice = [[lattice_parameter, 0, 0],
551
[0, lattice_parameter, 0],
552
[0, 0, lattice_parameter]]
553
554
positions = [[0.0, 0.0, 0.0],
555
[0.25, 0.25, 0.25]]
556
557
si_structure = PhonopyAtoms(
558
symbols=['Si', 'Si'],
559
scaled_positions=positions,
560
cell=lattice
561
)
562
563
# Face-centered cubic (FCC) structure
564
fcc_lattice = [[0.0, 2.0, 2.0],
565
[2.0, 0.0, 2.0],
566
[2.0, 2.0, 0.0]]
567
568
fcc_structure = PhonopyAtoms(
569
symbols=['Al'],
570
scaled_positions=[[0.0, 0.0, 0.0]],
571
cell=fcc_lattice
572
)
573
```
574
575
### From File Formats
576
577
```python
578
from phonopy.interface.vasp import read_vasp
579
from phonopy.interface.phonopy_yaml import PhonopyYaml
580
581
# Read from VASP POSCAR
582
atoms_vasp = read_vasp("POSCAR")
583
584
# Read from phonopy.yaml
585
ph_yaml = PhonopyYaml("phonopy.yaml")
586
atoms_yaml = ph_yaml.unitcell
587
```
588
589
### Structure Transformations
590
591
```python
592
# Apply strain to unit cell
593
strain_matrix = [[1.02, 0, 0], # 2% expansion along a
594
[0, 1.0, 0], # No change along b
595
[0, 0, 0.98]] # 2% compression along c
596
597
strained_cell = np.dot(atoms.get_cell(), strain_matrix)
598
atoms_strained = atoms.copy()
599
atoms_strained.set_cell(strained_cell)
600
601
# Rotate structure
602
from scipy.spatial.transform import Rotation
603
rotation = Rotation.from_euler('z', 45, degrees=True) # 45° around z
604
rotation_matrix = rotation.as_matrix()
605
606
rotated_cell = np.dot(atoms.get_cell(), rotation_matrix.T)
607
rotated_positions = np.dot(atoms.get_positions(), rotation_matrix.T)
608
609
atoms_rotated = atoms.copy()
610
atoms_rotated.set_cell(rotated_cell)
611
atoms_rotated.set_positions(rotated_positions)
612
```
613
614
### Complete Structure Workflow
615
616
```python
617
from phonopy import Phonopy
618
from phonopy.structure.atoms import PhonopyAtoms
619
from phonopy.structure.cells import get_supercell, get_primitive
620
from phonopy.structure.symmetry import Symmetry
621
622
# 1. Create or load unit cell structure
623
unitcell = PhonopyAtoms(
624
symbols=['Mg', 'O'],
625
scaled_positions=[[0.0, 0.0, 0.0], [0.5, 0.5, 0.5]],
626
cell=[[4.2, 0, 0], [0, 4.2, 0], [0, 0, 4.2]]
627
)
628
629
# 2. Analyze symmetry
630
symmetry = Symmetry(unitcell, symprec=1e-5)
631
print(f"Space group: {symmetry.get_international_table()}")
632
633
# 3. Get primitive cell
634
primitive = get_primitive(unitcell, primitive_matrix='auto')
635
print(f"Primitive cell volume: {primitive.volume:.2f} ų")
636
637
# 4. Create supercell for phonon calculations
638
supercell_matrix = [[2, 0, 0], [0, 2, 0], [0, 0, 2]]
639
supercell = get_supercell(unitcell, supercell_matrix)
640
641
# 5. Initialize Phonopy with structures
642
ph = Phonopy(unitcell, supercell_matrix, primitive_matrix='auto')
643
644
print(f"Unit cell: {len(ph.unitcell)} atoms")
645
print(f"Primitive: {len(ph.primitive)} atoms")
646
print(f"Supercell: {len(ph.supercell)} atoms")
647
```