import numpy as np
from ase.units import Bohr
from ase.cell import Cell
from ...potential_definitions import PotSectionDefinition, PotValueDefinition
from ...potential_sections import PotentialSection
from ....ase.pbc import check_symmetry
from ....common.grammar_types import DefKeyword, Sequence, Table, Integer, Array
from ....physics.lattice_data import Pearson
from ....sprkkr.atoms_region import AtomsRegion
[docs]
def _sections_lattice_left_symmetry_error(_):
return "Left region of a 2D calculations have to be symmetric in all dimensions"
[docs]
def _sections_lattice_right_symmetry_error(_):
return "Right region of a 2D calculations have to be symmetric in all dimensions"
[docs]
def _sections_lattice_central_symmetry_error(_):
return "Central region of a 2D calculations have to be symmetric just in X and Y axis."
[docs]
def _sections_lattice_atoms_pbc_error(_):
return "For a 2D calculation, atoms have to be periodic in X and Y axes."
[docs]
def _sections_lattice_value_is_not_none(value):
return value is not None
[docs]
def _sections_lattice_value_is_none(value):
return value is None
[docs]
class LatticeSection(PotentialSection):
"""This section retrieves the lattice geometry and
it creates (during reading) the ASE Cell object"""
[docs]
def _set_from_atoms(self, atoms, write_io_data):
pbc = atoms.pbc.sum()
if pbc == 3:
self.SYSDIM = "3D"
bcell = atoms.cell
elif pbc == 2:
if not ("left" in atoms.regions and "right" in atoms.regions and "central" in atoms.regions):
raise ValueError(
"To run a 2D calculation, an atoms object have to have defined "
"'left', 'right' and 'central' region."
)
check_symmetry(atoms.regions["left"].pbc, True, _sections_lattice_left_symmetry_error)
check_symmetry(atoms.regions["right"].pbc, True, _sections_lattice_right_symmetry_error)
check_symmetry(atoms.regions["central"].pbc, [True, True, False], _sections_lattice_central_symmetry_error)
check_symmetry(atoms.pbc, [True, True, False], _sections_lattice_atoms_pbc_error)
self.SYSDIM = "2D"
bcell = atoms.regions["left"].cell
else:
raise ValueError(f"I don't know which calculation I should run with the periodicity of type: {atoms.pbc}.")
bravais_lattice = bcell.get_bravais_lattice()
alat = bravais_lattice.a
pearson_symbol = bravais_lattice.pearson_symbol
self["BRAVAIS"].set(Pearson.from_symbol(pearson_symbol).xband_data())
if self.SYSDIM() == "3D":
self.SYSTYPE = "BULK"
write_io_data["sites_order"] = slice(None)
self.A_L3.clear()
self.A_R3.clear()
self.NQ_L.clear()
self.NQ_R.clear()
cell = atoms.cell
elif self.SYSDIM() == "2D":
for i in "left", "right", "central":
if i not in atoms.regions:
raise ValueError("For a 2D problem, the 'left', 'right' and 'central' regions have to be defined")
self.SYSTYPE = "LIV" if atoms.regions["right"].only_vacuum_atoms() else "LIR"
cell = atoms.cell
if (cell[2] == [0, 0, 0]).all():
if (atoms.regions["central"] == [0, 0, 0]).all():
raise ValueError(
"For a 2D problem, the third (the z-) cell vector has to be specified either for "
" atoms object, or for its central region"
)
cell[2] = (
atoms.regions["left"].cell[2] + atoms.regions["right"].cell[2] + atoms.regions["central"].cell[2]
)
self.A_L3 = atoms.regions["left"].cell[2] / alat
self.A_R3 = atoms.regions["right"].cell[2] / alat
self.NQ_L = len(atoms.regions["left"])
self.NQ_R = len(atoms.regions["right"])
for i, j in [("left", "right"), ("left", "central"), ("right", "central")]:
if atoms.regions[i].shared_ids_with(atoms.regions[j]):
raise ValueError(f"{i.capitalize()} and {j} region can not share the same site")
la = len(atoms)
if self.NQ_L() + self.NQ_R() + len(atoms.regions["central"]) != la:
raise ValueError("The left, central and right do not cover all the atoms in the sample")
def order(region):
region = atoms.regions[region]
z = region.positions[:, 2]
return region.ids[np.argsort(z)]
write_io_data["sites_order"] = np.concatenate(list(map(order, ["left", "central", "right"])))
for i, j in zip(range(len(atoms)), write_io_data["sites_order"]):
if i != j:
break
else:
# optimalization - do not reorder
write_io_data["sites_order"] = slice(None)
else:
raise ValueError(f"{self.SISDIM()} problem periodicity type not implemented")
write_io_data["lattice.alat"] = alat
self["ALAT"].set(alat / Bohr)
self["SCALED_PRIMITIVE_CELL"].set(cell / alat)
[docs]
def _update_atoms(self, atoms, read_io_data):
alat = self["ALAT"]() * Bohr
cell = Cell(self["SCALED_PRIMITIVE_CELL"]() * alat)
read_io_data["lattice.alat"] = alat
if self.SYSDIM() == "3D":
regions = []
pbc = [True, True, True]
elif self.SYSDIM() == "2D":
lc = cell.copy()
lc[2] = self.A_L3() * alat
rc = cell.copy()
rc[2] = self.A_R3() * alat
cc = cell.copy()
cc[2] -= lc[2] + rc[2]
regions = [
AtomsRegion("left", slice(None, self.NQ_L()), lc, [True, True, True]), # NOQA E241
AtomsRegion("central", slice(self.NQ_L(), -self.NQ_R()), cc, [True, True, False]), # NOQA E241
AtomsRegion("right", slice(-self.NQ_R(), None), rc, [True, True, True]), # NOQA E241
]
pbc = [True, True, False]
else:
raise ValueError(f"Unknown problem periodicity type {self.SYSDIM()}")
def update(atoms):
atoms.cell = cell
atoms.pbc = pbc
atoms.set_regions(regions)
read_io_data.apply_on_atoms(update, atoms)
[docs]
class LatticeSectionDefinition(PotSectionDefinition):
[docs]
def __init__(self, name="LATTICE", **kwargs):
V = PotValueDefinition
members = [
V("SYSDIM", DefKeyword("3D", "2D")),
V("SYSTYPE", DefKeyword("BULK", "LIV", "LIR")),
V(
"BRAVAIS",
Sequence(int, str, str, str, str, allowed_values=(i.xband_data() for i in Pearson.pearsons.values())),
),
V("ALAT", float),
# Keywords and thus the numbering has just (or at least) 10 char long
V(
"SCALED_PRIMITIVE_CELL",
Table(
[float] * 3,
numbering=Integer(prefix="A(", postfix=")", after_format="<10"),
length=3,
format=">22.14f",
),
),
V("NQ_L", int, is_optional=True),
V("A_L(3)", Array(float, length=3, format="<10"), is_optional=True),
V("NQ_R", int, is_optional=True),
V("A_R(3)", Array(float, length=3, format="<10"), is_optional=True),
]
super().__init__(name, members, has_hidden_members=True)
[docs]
def validate(self, data, why: str = "set"):
d3 = data["SYSDIM"] == "3D"
if d3 != (data["SYSTYPE"] == "BULK"):
raise ValueError("LATTICE.SYSDIM have to be 2D to create LIV or LIR structures")
op = _sections_lattice_value_is_not_none if d3 else _sections_lattice_value_is_none
if op(data["NQ_L"]):
raise ValueError("LATTICE.NQ_L can be specified only for 2D structures")
if op(data["A_L(3)"]):
raise ValueError("LATTICE.A_L(3) can be specified only for 2D structures")
if op(data["NQ_R"]):
raise ValueError("LATTICE.NQ_R can be specified only for 2D structures")
if op(data["A_R(3)"]):
raise ValueError("LATTICE.A_R(3) can be specified only for 2D structures")
return True
result_class = LatticeSection
section = LatticeSectionDefinition