-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
256 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -267,3 +267,4 @@ $RECYCLE.BIN/ | |
session | ||
test/Testing.ipynb | ||
test/data/temp | ||
test/data/temp.vasp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
"""Routines for generating and writing displaced structure.""" | ||
|
||
# Design note: | ||
# These routines are not implemented in ReferenceStructure to give | ||
# greater modularity. For example, when different displacement methods are added (such | ||
# as Monte Carlo rattling or random displacements), we'd rather not add to code to | ||
# Reference Structure. These functions stand alone, just like the IO functions. | ||
|
||
from pathlib import Path | ||
|
||
import numpy as np | ||
from numpy.typing import NDArray | ||
|
||
from ramannoodle.structure.reference import ReferenceStructure | ||
from ramannoodle.structure.structure_utils import displace_positions | ||
from ramannoodle.exceptions import ( | ||
get_type_error, | ||
get_shape_error, | ||
verify_ndarray_shape, | ||
verify_list_len, | ||
) | ||
from ramannoodle.io.io_utils import pathify_as_list | ||
import ramannoodle.io.generic as rn_io | ||
|
||
|
||
def get_displaced_positions( | ||
ref_structure: ReferenceStructure, | ||
cart_displacement: NDArray[np.float64], | ||
amplitudes: NDArray[np.float64], | ||
) -> list[NDArray[np.float64]]: | ||
"""Return list of displaced positions given a displacement and amplitudes. | ||
Parameters | ||
---------- | ||
ref_structure | ||
reference structure of N atoms | ||
cart_displacement | ||
2D array with shape (N,3) | ||
amplitudes | ||
1D array with shape (M,) | ||
Returns | ||
------- | ||
: | ||
1D list of length M | ||
""" | ||
try: | ||
cart_displacement = cart_displacement / float(np.linalg.norm(cart_displacement)) | ||
except TypeError as err: | ||
raise get_type_error("cart_displacement", cart_displacement, "ndarray") from err | ||
verify_ndarray_shape("amplitudes", amplitudes, (None,)) | ||
|
||
positions = [] | ||
for amplitude in amplitudes: | ||
try: | ||
displacement = ref_structure.get_frac_displacement( | ||
cart_displacement * amplitude | ||
) | ||
except ValueError as err: | ||
raise get_shape_error( | ||
"cart_displacement", | ||
cart_displacement, | ||
f"({len(ref_structure.positions)}, 3)", | ||
) from err | ||
positions.append(displace_positions(ref_structure.positions, displacement)) | ||
|
||
return positions | ||
|
||
|
||
def write_displaced_structures( # pylint: disable=too-many-arguments | ||
ref_structure: ReferenceStructure, | ||
cart_displacement: NDArray[np.float64], | ||
amplitudes: NDArray[np.float64], | ||
file_paths: str | Path | list[str] | list[Path], | ||
file_format: str, | ||
overwrite: bool = False, | ||
) -> None: | ||
"""Write displaced structures to files. | ||
Parameters | ||
---------- | ||
ref_structure | ||
reference structure of N atoms | ||
cart_displacement | ||
2D array with shape (N,3) | ||
amplitudes | ||
1D array with shape (M,) | ||
file_paths | ||
file_format | ||
supports: "poscar" | ||
overwrite | ||
""" | ||
file_paths = pathify_as_list(file_paths) | ||
position_list = get_displaced_positions( | ||
ref_structure, cart_displacement, amplitudes | ||
) | ||
verify_list_len("file_paths", file_paths, len(position_list)) | ||
|
||
for position, filepath in zip(position_list, file_paths): | ||
rn_io.write_structure( | ||
ref_structure.lattice, | ||
ref_structure.atomic_numbers, | ||
position, | ||
filepath, | ||
file_format, | ||
overwrite, | ||
) | ||
|
||
|
||
def get_ast_displaced_positions( | ||
ref_structure: ReferenceStructure, | ||
atom_index: int, | ||
cart_direction: NDArray[np.float64], | ||
amplitudes: NDArray[np.float64], | ||
) -> list[NDArray[np.float64]]: | ||
"""Return list of displaced positions with an atom displaced along a direction. | ||
Parameters | ||
---------- | ||
ref_structure | ||
reference structure of N atoms | ||
atom_index | ||
cart_direction | ||
1D array with shape (3,) | ||
amplitudes | ||
1D array with shape (M,) | ||
Returns | ||
------- | ||
: | ||
1D list of length M | ||
""" | ||
try: | ||
cart_direction = cart_direction / float(np.linalg.norm(cart_direction)) | ||
except TypeError as err: | ||
raise get_type_error("cart_direction", cart_direction, "ndarray") from err | ||
positions = [] | ||
for amplitude in amplitudes: | ||
cart_displacement = ref_structure.positions * 0 | ||
try: | ||
cart_displacement[atom_index] = cart_direction * amplitude | ||
except IndexError as err: | ||
raise IndexError(f"invalid atom_index: {atom_index}") from err | ||
displacement = ref_structure.get_frac_displacement(cart_displacement) | ||
positions.append(displace_positions(ref_structure.positions, displacement)) | ||
|
||
return positions | ||
|
||
|
||
def write_ast_displaced_structures( # pylint: disable=too-many-arguments | ||
ref_structure: ReferenceStructure, | ||
atom_index: int, | ||
cart_direction: NDArray[np.float64], | ||
amplitudes: NDArray[np.float64], | ||
file_paths: str | Path | list[str] | list[Path], | ||
file_format: str, | ||
overwrite: bool = False, | ||
) -> None: | ||
"""Return displaced structures with an atom displaced along a direction. | ||
Parameters | ||
---------- | ||
ref_structure | ||
reference structure of N atoms | ||
atom_index | ||
cart_direction | ||
1D array with shape (3,) | ||
amplitudes | ||
1D array with shape (M,) | ||
file_paths | ||
file_format | ||
supports: "poscar" | ||
overwrite | ||
Returns | ||
------- | ||
: | ||
1D list of length M | ||
""" | ||
file_paths = pathify_as_list(file_paths) | ||
position_list = get_ast_displaced_positions( | ||
ref_structure, atom_index, cart_direction, amplitudes | ||
) | ||
verify_list_len("file_paths", file_paths, len(position_list)) | ||
|
||
for position, filepath in zip(position_list, file_paths): | ||
rn_io.write_structure( | ||
ref_structure.lattice, | ||
ref_structure.atomic_numbers, | ||
position, | ||
filepath, | ||
file_format, | ||
overwrite, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
"""Tests for structure displacement routines.""" | ||
|
||
import numpy as np | ||
from numpy.typing import NDArray | ||
|
||
import pytest | ||
|
||
import ramannoodle.io.vasp as vasp_io | ||
from ramannoodle.structure.reference import ReferenceStructure | ||
from ramannoodle.structure.displace import write_ast_displaced_structures | ||
|
||
# pylint: disable=protected-access | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"outcar_ref_structure_fixture,atom_index,cart_direction,amplitude,outcar_known", | ||
[ | ||
( | ||
"test/data/TiO2/phonons_OUTCAR", | ||
42, | ||
np.array([0.0, 0, 1]), | ||
0.1, | ||
"test/data/TiO2/O43_0.1z_eps_OUTCAR", | ||
), | ||
( | ||
"test/data/TiO2/phonons_OUTCAR", | ||
4, | ||
np.array([10, 0, 0]), | ||
-0.2, | ||
"test/data/TiO2/Ti5_m0.2x_eps_OUTCAR", | ||
), | ||
], | ||
indirect=["outcar_ref_structure_fixture"], | ||
) | ||
def test_write_ast_displaced_structures( | ||
outcar_ref_structure_fixture: ReferenceStructure, | ||
atom_index: int, | ||
cart_direction: NDArray[np.float64], | ||
amplitude: float, | ||
outcar_known: str, | ||
) -> None: | ||
"""Test write_displaced_structures.""" | ||
amplitudes = np.array([amplitude]) | ||
write_ast_displaced_structures( | ||
outcar_ref_structure_fixture, | ||
atom_index, | ||
cart_direction, | ||
amplitudes, | ||
["test/data/temp.vasp"], | ||
"poscar", | ||
overwrite=True, | ||
) | ||
|
||
known_positions = vasp_io.outcar.read_positions(outcar_known) | ||
assert np.isclose( | ||
vasp_io.poscar.read_positions("test/data/temp.vasp"), known_positions | ||
).all() |