Skip to content

Commit

Permalink
added new/to for the Lattice
Browse files Browse the repository at this point in the history
This makes Lattice work exactly the same as Geometry.
Also adde Lattice.to["cuboid"]

Signed-off-by: Nick Papior <nickpapior@gmail.com>
  • Loading branch information
zerothi committed Oct 11, 2023
1 parent 240f197 commit eebe607
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 18 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ we hit release version 1.0.0.

## [0.14.3] - YYYY-MM-DD

### Added
- added `Lattice.to` and `Lattice.new` to function the same
as `Geometry`, added Lattice.to["cuboid"]


## [0.14.2] - 2023-10-04
Expand Down
3 changes: 3 additions & 0 deletions src/sisl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@
# Since __getitem__ always instantiate the class, we have to use the
# contained lookup table.
Geometry.new.register(BaseSile, Geometry.new._dispatchs[str])
Geometry.to.register(BaseSile, Geometry.to._dispatchs[str])
Lattice.new.register(BaseSile, Lattice.new._dispatchs[str])
Lattice.to.register(BaseSile, Lattice.to._dispatchs[str])

# Import the default geom structure
# This enables:
Expand Down
126 changes: 110 additions & 16 deletions src/sisl/lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
import warnings
from numbers import Integral
from typing import TYPE_CHECKING, Tuple, Union
from pathlib import Path

import numpy as np
from numpy import dot, ndarray

from . import _array as _a
from . import _plot as plt
from ._dispatcher import AbstractDispatch, ClassDispatcher, TypeDispatcher
from ._dispatch_class import _ToNew
from ._internal import set_module
from ._lattice import cell_invert, cell_reciprocal
from ._math_small import cross3, dot3
Expand All @@ -34,7 +37,12 @@


@set_module("sisl")
class Lattice:
class Lattice(_ToNew,
new=ClassDispatcher("new",
instance_dispatcher=TypeDispatcher),
to=ClassDispatcher("to",
type_dispatcher=None)
):
r""" A cell class to retain lattice vectors and a supercell structure
The supercell structure is comprising the *primary* unit-cell and neighbouring
Expand Down Expand Up @@ -102,6 +110,7 @@ def origin(self, origin):
""" Set origin for the cell """
self._origin[:] = origin

@deprecation("toCuboid is deprecated, please use lattice.to['cuboid'](...) instead.")
def toCuboid(self, orthogonal=False):
""" A cuboid with vectors as this unit-cell and center with respect to its origin
Expand All @@ -110,20 +119,7 @@ def toCuboid(self, orthogonal=False):
orthogonal : bool, optional
if true the cuboid has orthogonal sides such that the entire cell is contained
"""
if not orthogonal:
return Cuboid(self.cell.copy(), self.center() + self.origin)
def find_min_max(cmin, cmax, new):
for i in range(3):
cmin[i] = min(cmin[i], new[i])
cmax[i] = max(cmax[i], new[i])
cell = self.cell
cmin = cell.min(0)
cmax = cell.max(0)
find_min_max(cmin, cmax, cell[[0, 1], :].sum(0))
find_min_max(cmin, cmax, cell[[0, 2], :].sum(0))
find_min_max(cmin, cmax, cell[[1, 2], :].sum(0))
find_min_max(cmin, cmax, cell.sum(0))
return Cuboid(cmax - cmin, self.center() + self.origin)
return self.to[Cuboid](orthogonal=orthogonal)

def parameters(self, rad=False) -> Tuple[float, float, float, float, float, float]:
r""" Cell parameters of this cell in 3 lengths and 3 angles
Expand Down Expand Up @@ -1185,8 +1181,106 @@ def __plot__(self, axis=None, axes=False, *args, **kwargs):

return axes

new_dispatch = Lattice.new
to_dispatch = Lattice.to

# Define base-class for this
class LatticeNewDispatcher(AbstractDispatch):
""" Base dispatcher from class passing arguments to Geometry class
This forwards all `__call__` calls to `dispatch`
"""

def __call__(self, *args, **kwargs):
return self.dispatch(*args, **kwargs)

class LatticeNewLatticeDispatcher(LatticeNewDispatcher):
def dispatch(self, lattice):
return lattice
new_dispatch.register(Lattice, LatticeNewLatticeDispatcher)

class LatticeNewAseDispatcher(LatticeNewDispatcher):
def dispatch(self, aseg, **kwargs):
cell = aseg.get_cell()
nsc = [3 if pbc else 1 for pbc in aseg.pbc]
return Lattice(cell, nsc=nsc)
new_dispatch.register("ase", LatticeNewAseDispatcher)

# currently we can't ensure the ase Atoms type
# to get it by type(). That requires ase to be importable.
try:
from ase import Cell as ase_Cell
new_dispatch.register(ase_Cell, LatticeNewAseDispatcher)
# ensure we don't pollute name-space
del ase_Cell
except Exception:
pass

class LatticeNewFileDispatcher(LatticeNewDispatcher):
def dispatch(self, *args, **kwargs):
""" Defer the `Lattice.read` method by passing down arguments """
return self._obj.read(*args, **kwargs)
new_dispatch.register(str, LatticeNewFileDispatcher)
new_dispatch.register(Path, LatticeNewFileDispatcher)
# see sisl/__init__.py for new_dispatch.register(BaseSile, ...)


class LatticeToDispatcher(AbstractDispatch):
""" Base dispatcher from class passing from Lattice class """
@staticmethod
def _ensure_object(obj):
if isinstance(obj, type):
raise TypeError(f"Dispatcher on {obj} must not be called on the class.")

class LatticeToAseDispatcher(LatticeToDispatcher):
def dispatch(self, **kwargs):
from ase import Cell as ase_Cell
lattice = self._obj
self._ensure_object(lattice)
return ase_Cell(lattice.cell.copy())

to_dispatch.register("ase", LatticeToAseDispatcher)

class LatticeToSileDispatcher(LatticeToDispatcher):
def dispatch(self, *args, **kwargs):
lattice = self._obj
self._ensure_object(lattice)
return lattice.write(*args, **kwargs)
to_dispatch.register("str", LatticeToSileDispatcher)
to_dispatch.register("path", LatticeToSileDispatcher)
# to do geom.to[Path](path)
to_dispatch.register(str, LatticeToSileDispatcher)
to_dispatch.register(Path, LatticeToSileDispatcher)

class LatticeToCuboidDispatcher(LatticeToDispatcher):
def dispatch(self, *args, orthogonal=False, **kwargs):
lattice = self._obj
self._ensure_object(lattice)
cell = lattice.cell.copy()
offset = lattice.center() + self.origin
if not orthogonal:
return Cuboid(cell, offset)

def find_min_max(cmin, cmax, new):
for i in range(3):
cmin[i] = min(cmin[i], new[i])
cmax[i] = max(cmax[i], new[i])
cmin = cell.min(0)
cmax = cell.max(0)
find_min_max(cmin, cmax, cell[[0, 1], :].sum(0))
find_min_max(cmin, cmax, cell[[0, 2], :].sum(0))
find_min_max(cmin, cmax, cell[[1, 2], :].sum(0))
find_min_max(cmin, cmax, cell.sum(0))
return Cuboid(cmax - cmin, offset)

to_dispatch.register("cuboid", LatticeToCuboidDispatcher)
to_dispatch.register(Cuboid, LatticeToCuboidDispatcher)


# Remove references
del new_dispatch, to_dispatch


# same reference
class SuperCell(Lattice):
""" Deprecated class, please use `Lattice` instead """
def __init__(self, *args, **kwargs):
Expand Down
10 changes: 8 additions & 2 deletions src/sisl/tests/test_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,15 @@ def test_plane2():

def test_tocuboid_simple():
lattice = Lattice([1, 1, 1, 90, 90, 90])
c1 = lattice.toCuboid()
with pytest.warns(sisl.SislDeprecation):
c1 = lattice.toCuboid()
assert np.allclose(lattice.cell, c1._v)
c2 = lattice.toCuboid(True)
c1 = lattice.to["cuboid"]()
assert np.allclose(lattice.cell, c1._v)
with pytest.warns(sisl.SislDeprecation):
c2 = lattice.toCuboid(True)
assert np.allclose(c1._v, c2._v)
c2 = lattice.to["cuboid"](True)
assert np.allclose(c1._v, c2._v)


Expand Down

0 comments on commit eebe607

Please sign in to comment.