From a6df54944a24f65e9543fd6991ce0b1c51207c81 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Sat, 4 Nov 2023 13:12:26 +0100 Subject: [PATCH 01/15] enh: creation of chiral GNRs and [n]-triangulene --- CHANGELOG.md | 2 + src/sisl/geom/flat.py | 36 +++++++++++++-- src/sisl/geom/nanoribbon.py | 87 +++++++++++++++++++++++++++++++------ 3 files changed, 109 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ce2126f15..58e7d3bd4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ we hit release version 1.0.0. ## [0.14.3] - 2023-11-07 ### Added +- Creation of chiral GNRs (`kind=chiral` in `sisl.geom.nanoribbon`/`sisl.geom.graphene_nanoribbon` as well as `sisl.geom.cgnr`) +- Creation of [n]-triangulenes (`sisl.geom.triangulene`) - Creation of honeycomb flakes (`sisl.geom.honeycomb_flake`, `sisl.geom.graphene_flake`). #636 - added `Geometry.as_supercell` to create the supercell structure, diff --git a/src/sisl/geom/flat.py b/src/sisl/geom/flat.py index 2610125048..05ef8e6950 100644 --- a/src/sisl/geom/flat.py +++ b/src/sisl/geom/flat.py @@ -8,11 +8,11 @@ from ._common import geometry_define_nsc -__all__ = ["honeycomb", "graphene", "honeycomb_flake", "graphene_flake"] +__all__ = ["honeycomb", "graphene", "honeycomb_flake", "graphene_flake", "triangulene"] @set_module("sisl.geom") -def honeycomb(bond: float, atoms, orthogonal: bool = False): +def honeycomb(bond: float, atoms, orthogonal: bool = False) -> Geometry: """Honeycomb lattice with 2 or 4 atoms per unit-cell, latter orthogonal cell This enables creating BN lattices with ease, or graphene lattices. @@ -65,7 +65,7 @@ def honeycomb(bond: float, atoms, orthogonal: bool = False): @set_module("sisl.geom") -def graphene(bond: float = 1.42, atoms=None, orthogonal: bool = False): +def graphene(bond: float = 1.42, atoms=None, orthogonal: bool = False) -> Geometry: """Graphene lattice with 2 or 4 atoms per unit-cell, latter orthogonal cell Parameters @@ -204,3 +204,33 @@ def graphene_flake( if atoms is None: atoms = Atom(Z=6, R=bond * 1.01) return honeycomb_flake(shells, bond, atoms, vacuum) + + +@set_module("sisl.geom") +def triangulene(n: int, bond: float = 1.42, atoms=None) -> Geometry: + """Construction of an [n]-triangulene geometry + + Parameters + ---------- + n : + size of the triangulene + bond : + bond length between atoms (*not* lattice constant) + atoms : + the atom (or atoms) that the honeycomb lattice consists of. + Default to Carbon atom. + """ + if atoms is None: + atoms = Atom(Z=6, R=bond * 1.01) + geom = graphene(bond=bond, atoms=atoms) * (n + 1, n + 1, 1) + idx = np.where(geom.xyz[:, 0] <= geom.cell[0, 0] + 0.01)[0] + geom = geom.sub(idx[1:]) + geom.cell[:2] *= (n + 4) / (n + 1) + + # Center the molecule in cell + geom = geom.move([geom.cell[0, 0] - geom.xyz[-1, 0], 0, geom.cell[2, 2] / 2]) + + # Set boundary conditions + geometry_define_nsc(geom, [False, False, False]) + + return geom diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index 1543a386e9..0de0cec9e4 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -6,7 +6,7 @@ import numpy as np -from sisl import Atom, geom +from sisl import Atom, Geometry, geom from sisl._internal import set_module from ._common import geometry_define_nsc @@ -17,14 +17,17 @@ "graphene_nanoribbon", "agnr", "zgnr", + "cgnr", "heteroribbon", "graphene_heteroribbon", ] @set_module("sisl.geom") -def nanoribbon(width: int, bond: float, atoms, kind: str = "armchair"): - r"""Construction of a nanoribbon unit cell of type armchair or zigzag. +def nanoribbon( + width: int, bond: float, atoms, kind: str = "armchair", index: (int, int) = (3, 1) +) -> Geometry: + r"""Construction of a nanoribbon unit cell of type armchair, zigzag or (n,m)-chiral. The geometry is oriented along the :math:`x` axis. @@ -36,8 +39,10 @@ def nanoribbon(width: int, bond: float, atoms, kind: str = "armchair"): bond length between atoms in the honeycomb lattice atoms : Atom atom (or atoms) in the honeycomb lattice - kind : {'armchair', 'zigzag'} + kind : {'armchair', 'zigzag', 'chiral'} type of ribbon + index : + chiral index (n, m), only processed for chiral types See Also -------- @@ -45,6 +50,7 @@ def nanoribbon(width: int, bond: float, atoms, kind: str = "armchair"): graphene : graphene geometry graphene_nanoribbon : graphene nanoribbon agnr : armchair graphene nanoribbon + cgnr : chiral graphene nanoribbon zgnr : zigzag graphene nanoribbon """ if not isinstance(width, Integral): @@ -65,7 +71,7 @@ def nanoribbon(width: int, bond: float, atoms, kind: str = "armchair"): else: ribbon = ribbon.repeat(n, 1) - elif kind == "zigzag": + elif kind in ["zigzag", "chiral"]: # Construct zigzag GNR ribbon = ribbon.rotate(90, [0, 0, -1], what="abc+xyz") if m == 1: @@ -82,6 +88,23 @@ def nanoribbon(width: int, bond: float, atoms, kind: str = "armchair"): # Sort along x, then y ribbon = ribbon.sort(axis=(0, 1)) + if kind == "chiral": + # continue with the zigzag ribbon as building block + n, m = index + ribbon = ribbon.tile(n + 1, 0) + r = ribbon.xyz[1] - ribbon.xyz[width] + ribbon.cell[0] += r + (m - 1) * (ribbon.xyz[2] - ribbon.xyz[0]) + ribbon = ribbon.remove(range(width)) + # determine rotation angle + x = ribbon.cell[0].copy() + x /= x.dot(x) ** 0.5 + angle = np.arccos(x.dot([1, 0, 0])) + ribbon = ribbon.rotate( + angle, [0, 0, -1], origin=ribbon.xyz[0], rad=True, what="abc+xyz" + ) + # set lattice vectors orthogonal + ribbon.cell[0, 1] = ribbon.cell[1, 0] = 0 + else: raise ValueError(f"nanoribbon: kind must be armchair or zigzag ({kind})") @@ -98,8 +121,12 @@ def nanoribbon(width: int, bond: float, atoms, kind: str = "armchair"): @set_module("sisl.geom") def graphene_nanoribbon( - width: int, bond: float = 1.42, atoms=None, kind: str = "armchair" -): + width: int, + bond: float = 1.42, + atoms=None, + kind: str = "armchair", + index: (int, int) = (3, 1), +) -> Geometry: r"""Construction of a graphene nanoribbon Parameters @@ -110,8 +137,10 @@ def graphene_nanoribbon( C-C bond length atoms : Atom, optional atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` - kind : {'armchair', 'zigzag'} + kind : {'armchair', 'zigzag', 'chiral} type of ribbon + index : + chiral index (n, m), only processed for chiral types See Also -------- @@ -120,14 +149,15 @@ def graphene_nanoribbon( nanoribbon : honeycomb nanoribbon (used for this method) agnr : armchair graphene nanoribbon zgnr : zigzag graphene nanoribbon + cgnr : chiral graphene nanoribbon """ if atoms is None: atoms = Atom(Z=6, R=bond * 1.01) - return nanoribbon(width, bond, atoms, kind=kind) + return nanoribbon(width, bond, atoms, kind=kind, index=index) @set_module("sisl.geom") -def agnr(width: int, bond: float = 1.42, atoms=None): +def agnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: r"""Construction of an armchair graphene nanoribbon Parameters @@ -146,12 +176,13 @@ def agnr(width: int, bond: float = 1.42, atoms=None): nanoribbon : honeycomb nanoribbon graphene_nanoribbon : graphene nanoribbon zgnr : zigzag graphene nanoribbon + cgnr : chiral graphene nanoribbon """ return graphene_nanoribbon(width, bond, atoms, kind="armchair") @set_module("sisl.geom") -def zgnr(width: int, bond: float = 1.42, atoms=None): +def zgnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: r"""Construction of a zigzag graphene nanoribbon Parameters @@ -170,10 +201,40 @@ def zgnr(width: int, bond: float = 1.42, atoms=None): nanoribbon : honeycomb nanoribbon graphene_nanoribbon : graphene nanoribbon agnr : armchair graphene nanoribbon + cgnr : chiral graphene nanoribbon """ return graphene_nanoribbon(width, bond, atoms, kind="zigzag") +@set_module("sisl.geom") +def cgnr(n: int, m: int, width: int, bond: float = 1.42, atoms=None) -> Geometry: + r"""Construction of an (n, m, w)-chiral graphene nanoribbon + + Parameters + ---------- + n : + first chiral index + m : + second chiral index + width : + number of atoms in the transverse direction + bond : + C-C bond length + atoms : Atom, optional + atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` + + See Also + -------- + honeycomb : honeycomb lattices + graphene : graphene geometry + nanoribbon : honeycomb nanoribbon + graphene_nanoribbon : graphene nanoribbon + agnr : armchair graphene nanoribbon + zgnr : zigzag graphene nanoribbon + """ + return graphene_nanoribbon(width, bond, atoms, kind="chiral", index=(n, m)) + + @set_module("sisl.geom") @dataclass class _heteroribbon_section(CompositeGeometrySection): @@ -638,7 +699,7 @@ def _parse_shift(self, shift, prev, align): @set_module("sisl.geom") -def heteroribbon(sections, section_cls=_heteroribbon_section, **kwargs): +def heteroribbon(sections, section_cls=_heteroribbon_section, **kwargs) -> Geometry: """Build a nanoribbon consisting of several nanoribbons of different widths. This function uses `composite_geometry`, but defaulting to the usage @@ -688,7 +749,7 @@ def graphene_heteroribbon( bond: float = 1.42, atoms=None, **kwargs, -): +) -> Geometry: """Build a graphene nanoribbon consisting of several nanoribbons of different widths Please see `heteroribbon` for arguments, the only difference is that the `bond` and `atoms` From 71ae22158cb55969d69ede113e6ce0f496e1b306 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Sat, 4 Nov 2023 13:44:18 +0100 Subject: [PATCH 02/15] added tests for new geometries --- src/sisl/geom/tests/test_geom.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sisl/geom/tests/test_geom.py b/src/sisl/geom/tests/test_geom.py index 3112b55450..70cdab1637 100644 --- a/src/sisl/geom/tests/test_geom.py +++ b/src/sisl/geom/tests/test_geom.py @@ -74,6 +74,14 @@ def test_flat_flakes(): ) +def test_triangulene(): + g = triangulene(3) + assert g.na == 22 + g = triangulene(3, atoms=["B", "N"]) + assert g.atoms.nspecie == 2 + g = triangulene(3, bond=1.6) + + def test_nanotube(): a = nanotube(1.42) assert is_right_handed(a) @@ -116,6 +124,8 @@ def test_nanoribbon(): for w in range(0, 5): nanoribbon(w, 1.42, Atom(6), kind="armchair") nanoribbon(w, 1.42, Atom(6), kind="zigzag") + nanoribbon(w, 1.42, Atom(6), kind="chiral") + nanoribbon(w, 1.42, Atom(6), kind="chiral", index=(2, 2)) nanoribbon(w, 1.42, (Atom(5), Atom(7)), kind="armchair") a = nanoribbon(w, 1.42, (Atom(5), Atom(7)), kind="zigzag") assert is_right_handed(a) @@ -128,6 +138,9 @@ def test_nanoribbon(): def test_graphene_nanoribbon(): + graphene_nanoribbon(6, kind="armchair") + graphene_nanoribbon(6, kind="zigzag") + graphene_nanoribbon(6, kind="chiral") a = graphene_nanoribbon(5) assert is_right_handed(a) @@ -142,6 +155,13 @@ def test_zgnr(): assert is_right_handed(a) +def test_cgnr(): + a = cgnr(3, 1, 6) + a = cgnr(3, 1, 6, bond=1.6) + a = cgnr(3, 1, 6, atoms=["B", "N"]) + assert is_right_handed(a) + + @pytest.mark.parametrize( "W, invert_first", itertools.product(range(3, 20), [True, False]), From b3bf5724dc077478f01fa7eacae610dc467d80d0 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Sat, 4 Nov 2023 13:48:05 +0100 Subject: [PATCH 03/15] doc: introduced 0D section and new geometries --- docs/api/default_geom.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/api/default_geom.rst b/docs/api/default_geom.rst index c2436278e0..abd7c5427a 100644 --- a/docs/api/default_geom.rst +++ b/docs/api/default_geom.rst @@ -42,6 +42,15 @@ Surfaces (slabs) fcc_slab rocksalt_slab +0D materials +============ + +.. autosummary:: + :toctree: generated/ + + honeycomb_flake + graphene_flake + triangulene 1D materials ============ @@ -52,6 +61,7 @@ Surfaces (slabs) nanoribbon agnr zgnr + cgnr graphene_nanoribbon nanotube heteroribbon @@ -67,8 +77,6 @@ Surfaces (slabs) honeycomb bilayer graphene - honeycomb_flake - graphene_flake Helpers From fc3d8e798f88016d29943f7bbd7cce60e479fe05 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Sat, 4 Nov 2023 22:48:22 +0100 Subject: [PATCH 04/15] simple fixes --- src/sisl/geom/nanoribbon.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index 0de0cec9e4..964053bf8b 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -42,7 +42,7 @@ def nanoribbon( kind : {'armchair', 'zigzag', 'chiral'} type of ribbon index : - chiral index (n, m), only processed for chiral types + chiral index (n, m), only used if `kind=chiral` See Also -------- @@ -71,7 +71,7 @@ def nanoribbon( else: ribbon = ribbon.repeat(n, 1) - elif kind in ["zigzag", "chiral"]: + elif kind in ("zigzag", "chiral"): # Construct zigzag GNR ribbon = ribbon.rotate(90, [0, 0, -1], what="abc+xyz") if m == 1: @@ -137,10 +137,10 @@ def graphene_nanoribbon( C-C bond length atoms : Atom, optional atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` - kind : {'armchair', 'zigzag', 'chiral} + kind : {'armchair', 'zigzag', 'chiral'} type of ribbon index : - chiral index (n, m), only processed for chiral types + chiral index (n, m), only used if `kind=chiral` See Also -------- @@ -213,9 +213,9 @@ def cgnr(n: int, m: int, width: int, bond: float = 1.42, atoms=None) -> Geometry Parameters ---------- n : - first chiral index + first chirality index (zigzag segments) m : - second chiral index + second chirality index (armchair segments) width : number of atoms in the transverse direction bond : From 8bcbe5566317fdb0a6c6c1fe66a358faf5ad26cc Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Sat, 4 Nov 2023 23:07:50 +0100 Subject: [PATCH 05/15] adopted chirality for the (n, m)-index --- src/sisl/geom/nanoribbon.py | 23 ++++++++++++++--------- src/sisl/geom/tests/test_geom.py | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index 964053bf8b..199057cb7d 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -3,6 +3,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. from dataclasses import dataclass, field from numbers import Integral +from typing import Tuple import numpy as np @@ -25,7 +26,11 @@ @set_module("sisl.geom") def nanoribbon( - width: int, bond: float, atoms, kind: str = "armchair", index: (int, int) = (3, 1) + width: int, + bond: float, + atoms, + kind: str = "armchair", + chirality: Tuple[int, int] = (3, 1), ) -> Geometry: r"""Construction of a nanoribbon unit cell of type armchair, zigzag or (n,m)-chiral. @@ -41,8 +46,8 @@ def nanoribbon( atom (or atoms) in the honeycomb lattice kind : {'armchair', 'zigzag', 'chiral'} type of ribbon - index : - chiral index (n, m), only used if `kind=chiral` + chirality : + index (n, m), only used if `kind=chiral` See Also -------- @@ -90,7 +95,7 @@ def nanoribbon( if kind == "chiral": # continue with the zigzag ribbon as building block - n, m = index + n, m = chirality ribbon = ribbon.tile(n + 1, 0) r = ribbon.xyz[1] - ribbon.xyz[width] ribbon.cell[0] += r + (m - 1) * (ribbon.xyz[2] - ribbon.xyz[0]) @@ -125,7 +130,7 @@ def graphene_nanoribbon( bond: float = 1.42, atoms=None, kind: str = "armchair", - index: (int, int) = (3, 1), + chirality: Tuple[int, int] = (3, 1), ) -> Geometry: r"""Construction of a graphene nanoribbon @@ -139,8 +144,8 @@ def graphene_nanoribbon( atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` kind : {'armchair', 'zigzag', 'chiral'} type of ribbon - index : - chiral index (n, m), only used if `kind=chiral` + chirality : + index (n, m), only used if `kind=chiral` See Also -------- @@ -153,7 +158,7 @@ def graphene_nanoribbon( """ if atoms is None: atoms = Atom(Z=6, R=bond * 1.01) - return nanoribbon(width, bond, atoms, kind=kind, index=index) + return nanoribbon(width, bond, atoms, kind=kind, chirality=chirality) @set_module("sisl.geom") @@ -232,7 +237,7 @@ def cgnr(n: int, m: int, width: int, bond: float = 1.42, atoms=None) -> Geometry agnr : armchair graphene nanoribbon zgnr : zigzag graphene nanoribbon """ - return graphene_nanoribbon(width, bond, atoms, kind="chiral", index=(n, m)) + return graphene_nanoribbon(width, bond, atoms, kind="chiral", chirality=(n, m)) @set_module("sisl.geom") diff --git a/src/sisl/geom/tests/test_geom.py b/src/sisl/geom/tests/test_geom.py index 70cdab1637..84e7d84f3b 100644 --- a/src/sisl/geom/tests/test_geom.py +++ b/src/sisl/geom/tests/test_geom.py @@ -125,7 +125,7 @@ def test_nanoribbon(): nanoribbon(w, 1.42, Atom(6), kind="armchair") nanoribbon(w, 1.42, Atom(6), kind="zigzag") nanoribbon(w, 1.42, Atom(6), kind="chiral") - nanoribbon(w, 1.42, Atom(6), kind="chiral", index=(2, 2)) + nanoribbon(w, 1.42, Atom(6), kind="chiral", chirality=(2, 2)) nanoribbon(w, 1.42, (Atom(5), Atom(7)), kind="armchair") a = nanoribbon(w, 1.42, (Atom(5), Atom(7)), kind="zigzag") assert is_right_handed(a) From aa21049daffca4d5809d6f7c83ce15711ce140ef Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Mon, 6 Nov 2023 22:02:27 +0100 Subject: [PATCH 06/15] redefined ribbon cell vectors and positioning in cells --- src/sisl/geom/nanoribbon.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index 199057cb7d..c9226701db 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -30,6 +30,7 @@ def nanoribbon( bond: float, atoms, kind: str = "armchair", + vacuum: float = 20.0, chirality: Tuple[int, int] = (3, 1), ) -> Geometry: r"""Construction of a nanoribbon unit cell of type armchair, zigzag or (n,m)-chiral. @@ -46,6 +47,8 @@ def nanoribbon( atom (or atoms) in the honeycomb lattice kind : {'armchair', 'zigzag', 'chiral'} type of ribbon + vacuum : + separation in transverse direction chirality : index (n, m), only used if `kind=chiral` @@ -75,6 +78,7 @@ def nanoribbon( ribbon = ribbon.remove(3 * (n + 1)).remove(0) else: ribbon = ribbon.repeat(n, 1) + ribbon.cell[1, 1] += vacuum elif kind in ("zigzag", "chiral"): # Construct zigzag GNR @@ -86,9 +90,10 @@ def nanoribbon( ribbon = ribbon.tile(n, 0) # Invert y-coordinates ribbon.xyz[:, 1] *= -1 + # Set lattice vectors strictly orthogonal ribbon.cell[:, :] = np.diag( - [ribbon.cell[1, 0], -ribbon.cell[0, 1], ribbon.cell[2, 2]] + [ribbon.cell[1, 0], -ribbon.cell[0, 1] + vacuum, ribbon.cell[2, 2]] ) # Sort along x, then y ribbon = ribbon.sort(axis=(0, 1)) @@ -101,27 +106,22 @@ def nanoribbon( ribbon.cell[0] += r + (m - 1) * (ribbon.xyz[2] - ribbon.xyz[0]) ribbon = ribbon.remove(range(width)) # determine rotation angle - x = ribbon.cell[0].copy() - x /= x.dot(x) ** 0.5 - angle = np.arccos(x.dot([1, 0, 0])) + x = ribbon.cell[0] + angle = np.arccos(x.dot([1, 0, 0]) / x.dot(x) ** 0.5) ribbon = ribbon.rotate( angle, [0, 0, -1], origin=ribbon.xyz[0], rad=True, what="abc+xyz" ) - # set lattice vectors orthogonal - ribbon.cell[0, 1] = ribbon.cell[1, 0] = 0 + # first lattice vector strictly along x + ribbon.cell[0, 1] = 0 else: raise ValueError(f"nanoribbon: kind must be armchair or zigzag ({kind})") - # Separate ribbons along y-axis - ribbon.cell[1, 1] += 20.0 - - # Move inside unit cell - xyz = ribbon.xyz.min(axis=0) * [1, 1, 0] - geometry_define_nsc(ribbon, [True, False, False]) - return ribbon.move(-xyz + [0, 10, 0]) + ribbon = ribbon.move(ribbon.center(what="cell") - ribbon.center()) + + return ribbon @set_module("sisl.geom") @@ -130,6 +130,7 @@ def graphene_nanoribbon( bond: float = 1.42, atoms=None, kind: str = "armchair", + vacuum: float = 20.0, chirality: Tuple[int, int] = (3, 1), ) -> Geometry: r"""Construction of a graphene nanoribbon @@ -144,6 +145,8 @@ def graphene_nanoribbon( atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` kind : {'armchair', 'zigzag', 'chiral'} type of ribbon + vacuum : + separation in transverse direction chirality : index (n, m), only used if `kind=chiral` From 7746cf0f81ef440bdf8ff0f69c2e611bf2bd91d6 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Mon, 6 Nov 2023 22:20:17 +0100 Subject: [PATCH 07/15] chirality index unified and vacuum argument introduced for all ribbons --- src/sisl/geom/nanoribbon.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index c9226701db..6c2b5e4b16 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -161,11 +161,11 @@ def graphene_nanoribbon( """ if atoms is None: atoms = Atom(Z=6, R=bond * 1.01) - return nanoribbon(width, bond, atoms, kind=kind, chirality=chirality) + return nanoribbon(width, bond, atoms, kind=kind, vacuum=vacuum, chirality=chirality) @set_module("sisl.geom") -def agnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: +def agnr(width: int, bond: float = 1.42, atoms=None, vacuum: float = 20.0) -> Geometry: r"""Construction of an armchair graphene nanoribbon Parameters @@ -176,6 +176,8 @@ def agnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: C-C bond length atoms : Atom, optional atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` + vacuum : + separation in transverse direction See Also -------- @@ -186,11 +188,11 @@ def agnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: zgnr : zigzag graphene nanoribbon cgnr : chiral graphene nanoribbon """ - return graphene_nanoribbon(width, bond, atoms, kind="armchair") + return graphene_nanoribbon(width, bond, atoms, kind="armchair", vacuum=vacuum) @set_module("sisl.geom") -def zgnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: +def zgnr(width: int, bond: float = 1.42, atoms=None, vacuum: float = 20.0) -> Geometry: r"""Construction of a zigzag graphene nanoribbon Parameters @@ -201,6 +203,9 @@ def zgnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: C-C bond length atoms : Atom, optional atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` + vacuum : + separation in transverse direction + See Also -------- @@ -211,25 +216,31 @@ def zgnr(width: int, bond: float = 1.42, atoms=None) -> Geometry: agnr : armchair graphene nanoribbon cgnr : chiral graphene nanoribbon """ - return graphene_nanoribbon(width, bond, atoms, kind="zigzag") + return graphene_nanoribbon(width, bond, atoms, kind="zigzag", vacuum=vacuum) @set_module("sisl.geom") -def cgnr(n: int, m: int, width: int, bond: float = 1.42, atoms=None) -> Geometry: +def cgnr( + width: int, + chirality: Tuple[int, int], + bond: float = 1.42, + atoms=None, + vacuum: float = 20.0, +) -> Geometry: r"""Construction of an (n, m, w)-chiral graphene nanoribbon Parameters ---------- - n : - first chirality index (zigzag segments) - m : - second chirality index (armchair segments) width : number of atoms in the transverse direction + chirality : + index (n, m) bond : C-C bond length atoms : Atom, optional atom (or atoms) in the honeycomb lattice. Defaults to ``Atom(6)`` + vacuum : + separation in transverse direction See Also -------- @@ -240,7 +251,9 @@ def cgnr(n: int, m: int, width: int, bond: float = 1.42, atoms=None) -> Geometry agnr : armchair graphene nanoribbon zgnr : zigzag graphene nanoribbon """ - return graphene_nanoribbon(width, bond, atoms, kind="chiral", chirality=(n, m)) + return graphene_nanoribbon( + width, bond, atoms, kind="chiral", vacuum=vacuum, chirality=chirality + ) @set_module("sisl.geom") From 1b47a5663916cf0a10dfb8aa3a12e85381b6f85e Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Mon, 6 Nov 2023 22:30:12 +0100 Subject: [PATCH 08/15] orthogonal cell for triangulene --- src/sisl/geom/flat.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sisl/geom/flat.py b/src/sisl/geom/flat.py index 05ef8e6950..89e48f4f84 100644 --- a/src/sisl/geom/flat.py +++ b/src/sisl/geom/flat.py @@ -207,7 +207,7 @@ def graphene_flake( @set_module("sisl.geom") -def triangulene(n: int, bond: float = 1.42, atoms=None) -> Geometry: +def triangulene(n: int, bond: float = 1.42, atoms=None, vacuum: float = 20.0) -> Geometry: """Construction of an [n]-triangulene geometry Parameters @@ -219,16 +219,21 @@ def triangulene(n: int, bond: float = 1.42, atoms=None) -> Geometry: atoms : the atom (or atoms) that the honeycomb lattice consists of. Default to Carbon atom. + vacuum: + Amount of vacuum to add to the cell on all directions """ if atoms is None: atoms = Atom(Z=6, R=bond * 1.01) geom = graphene(bond=bond, atoms=atoms) * (n + 1, n + 1, 1) idx = np.where(geom.xyz[:, 0] <= geom.cell[0, 0] + 0.01)[0] geom = geom.sub(idx[1:]) - geom.cell[:2] *= (n + 4) / (n + 1) + + # Set the cell according to the requested vacuum + size = np.max(geom.xyz[:], axis=0) - np.min(geom.xyz[:], axis=0) + geom.cell[:] = np.diag(size + vacuum) # Center the molecule in cell - geom = geom.move([geom.cell[0, 0] - geom.xyz[-1, 0], 0, geom.cell[2, 2] / 2]) + geom = geom.move(geom.center(what="cell") - geom.center()) # Set boundary conditions geometry_define_nsc(geom, [False, False, False]) From ca3275f5c9bfade8e8e129030683af7b8a1c3166 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Mon, 6 Nov 2023 22:51:44 +0100 Subject: [PATCH 09/15] fix test_cgnr --- src/sisl/geom/tests/test_geom.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sisl/geom/tests/test_geom.py b/src/sisl/geom/tests/test_geom.py index 84e7d84f3b..01b5a4e75e 100644 --- a/src/sisl/geom/tests/test_geom.py +++ b/src/sisl/geom/tests/test_geom.py @@ -156,9 +156,9 @@ def test_zgnr(): def test_cgnr(): - a = cgnr(3, 1, 6) - a = cgnr(3, 1, 6, bond=1.6) - a = cgnr(3, 1, 6, atoms=["B", "N"]) + cgnr(6, (3, 1), vacuum=0) + cgnr(6, (3, 1), bond=1.6) + a = cgnr(6, (3, 1), atoms=["B", "N"]) assert is_right_handed(a) From 65bffa69e6f036ead7adc63215b108e68aa8339d Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Mon, 6 Nov 2023 23:06:51 +0100 Subject: [PATCH 10/15] black fix --- src/sisl/geom/flat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sisl/geom/flat.py b/src/sisl/geom/flat.py index 89e48f4f84..f9fafc4382 100644 --- a/src/sisl/geom/flat.py +++ b/src/sisl/geom/flat.py @@ -207,7 +207,9 @@ def graphene_flake( @set_module("sisl.geom") -def triangulene(n: int, bond: float = 1.42, atoms=None, vacuum: float = 20.0) -> Geometry: +def triangulene( + n: int, bond: float = 1.42, atoms=None, vacuum: float = 20.0 +) -> Geometry: """Construction of an [n]-triangulene geometry Parameters From 493e20a5d095919d8e356f16590a445c6873621b Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Tue, 7 Nov 2023 11:41:31 +0100 Subject: [PATCH 11/15] minor fixes --- src/sisl/geom/flat.py | 2 +- src/sisl/geom/nanoribbon.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sisl/geom/flat.py b/src/sisl/geom/flat.py index f9fafc4382..356b8e38e7 100644 --- a/src/sisl/geom/flat.py +++ b/src/sisl/geom/flat.py @@ -231,7 +231,7 @@ def triangulene( geom = geom.sub(idx[1:]) # Set the cell according to the requested vacuum - size = np.max(geom.xyz[:], axis=0) - np.min(geom.xyz[:], axis=0) + size = geom.xyz.max(axis=0) - geom.xyz.min(axis=0) geom.cell[:] = np.diag(size + vacuum) # Center the molecule in cell diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index 6c2b5e4b16..d2cb0beb38 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -107,7 +107,9 @@ def nanoribbon( ribbon = ribbon.remove(range(width)) # determine rotation angle x = ribbon.cell[0] - angle = np.arccos(x.dot([1, 0, 0]) / x.dot(x) ** 0.5) + angle = np.arccos( + x.dot([1, 0, 0]) / x.dot(x) ** 0.5 + ) # angle of vectors, x and b=[1, 0, 0] ribbon = ribbon.rotate( angle, [0, 0, -1], origin=ribbon.xyz[0], rad=True, what="abc+xyz" ) @@ -234,7 +236,7 @@ def cgnr( width : number of atoms in the transverse direction chirality : - index (n, m) + index (n, m) corresponding to an edge with n zigzag segments followed by m armchair segments bond : C-C bond length atoms : Atom, optional From 8c16eaa0fd80dc1daf67ceb2212f052f89888265 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Wed, 8 Nov 2023 11:44:58 +0100 Subject: [PATCH 12/15] nanoribbon atoms begin at zero along the first lattice vector (backwards compatibility) --- src/sisl/geom/nanoribbon.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index d2cb0beb38..eead8e4a2f 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -69,6 +69,7 @@ def nanoribbon( n, m = width // 2, width % 2 ribbon = geom.honeycomb(bond, atoms, orthogonal=True) + angle = 0 kind = kind.lower() if kind == "armchair": @@ -121,8 +122,13 @@ def nanoribbon( geometry_define_nsc(ribbon, [True, False, False]) + # move geometry into middle of the cell ribbon = ribbon.move(ribbon.center(what="cell") - ribbon.center()) + # first atom to zero along the first lattice vector + x = ribbon.xyz[0] + ribbon = ribbon.move([x[1] * np.tan(angle) - x[0], 0, 0]) + return ribbon From 77dae38dcc871be17a7f3d593b64b33398e11f75 Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Wed, 8 Nov 2023 14:36:26 +0100 Subject: [PATCH 13/15] vacuum as new parameter for heteroribbon --- src/sisl/geom/nanoribbon.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index eead8e4a2f..e3a8fa7ac9 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -322,6 +322,7 @@ class _heteroribbon_section(CompositeGeometrySection): atoms: Atom = None bond: float = None kind: str = "armchair" + vacuum: float = 20.0 shift_quantum: bool = False on_lone_atom: str = field(default="ignore", repr=False) invert_first: bool = field(default=False, repr=False) @@ -422,7 +423,7 @@ def _offset_from_center(self, align, prev): def build_section(self, prev): new_section = nanoribbon( - bond=self.bond, atoms=self.atoms, width=self.W, kind=self.kind + bond=self.bond, atoms=self.atoms, width=self.W, kind=self.kind, vacuum=self.vacuum, ) align, offset = self._align_offset(prev, new_section) @@ -511,7 +512,7 @@ def add_section(self, geom, new_section): new_min = new_section[:, self.trans_ax].min() new_max = new_section[:, self.trans_ax].max() if new_min < 0: - cell_offset = -new_min + 14 + cell_offset = -new_min + self.vacuum geom = geom.add_vacuum(cell_offset, self.trans_ax) move = np.zeros(3) move[self.trans_ax] = cell_offset @@ -519,7 +520,7 @@ def add_section(self, geom, new_section): new_section = new_section.move(move) if new_max > geom.cell[1, 1]: geom = geom.add_vacuum( - new_max - geom.cell[self.trans_ax, self.trans_ax] + 14, self.trans_ax + new_max - geom.cell[self.trans_ax, self.trans_ax] + self.vacuum, self.trans_ax ) self.xyz = new_section.xyz From e3482c5ffa610ada08643f356837c6286a84033e Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Wed, 8 Nov 2023 15:08:21 +0100 Subject: [PATCH 14/15] final polish after rebase --- CHANGELOG.md | 9 +++++++-- src/sisl/geom/nanoribbon.py | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e7d3bd4d..39fe2cda46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,18 @@ we hit release version 1.0.0. ## [0.14.4] - YYYY-MM-DD +### Added +- Creation of chiral GNRs (`kind=chiral` in `sisl.geom.nanoribbon`/`sisl.geom.graphene_nanoribbon` as well as `sisl.geom.cgnr`) +- Creation of [n]-triangulenes (`sisl.geom.triangulene`) + + +### Changed +- `vacuum` is now an optional parameter for all ribbon structures ## [0.14.3] - 2023-11-07 ### Added -- Creation of chiral GNRs (`kind=chiral` in `sisl.geom.nanoribbon`/`sisl.geom.graphene_nanoribbon` as well as `sisl.geom.cgnr`) -- Creation of [n]-triangulenes (`sisl.geom.triangulene`) - Creation of honeycomb flakes (`sisl.geom.honeycomb_flake`, `sisl.geom.graphene_flake`). #636 - added `Geometry.as_supercell` to create the supercell structure, diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index e3a8fa7ac9..1bb95f2525 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -423,7 +423,11 @@ def _offset_from_center(self, align, prev): def build_section(self, prev): new_section = nanoribbon( - bond=self.bond, atoms=self.atoms, width=self.W, kind=self.kind, vacuum=self.vacuum, + bond=self.bond, + atoms=self.atoms, + width=self.W, + kind=self.kind, + vacuum=self.vacuum, ) align, offset = self._align_offset(prev, new_section) @@ -520,7 +524,8 @@ def add_section(self, geom, new_section): new_section = new_section.move(move) if new_max > geom.cell[1, 1]: geom = geom.add_vacuum( - new_max - geom.cell[self.trans_ax, self.trans_ax] + self.vacuum, self.trans_ax + new_max - geom.cell[self.trans_ax, self.trans_ax] + self.vacuum, + self.trans_ax, ) self.xyz = new_section.xyz From b741ac23be50b88a27fe1e38f75632586344915f Mon Sep 17 00:00:00 2001 From: Thomas Frederiksen Date: Wed, 8 Nov 2023 15:32:56 +0100 Subject: [PATCH 15/15] added vacuum to docstring --- src/sisl/geom/nanoribbon.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sisl/geom/nanoribbon.py b/src/sisl/geom/nanoribbon.py index 1bb95f2525..2d6db6e2d4 100644 --- a/src/sisl/geom/nanoribbon.py +++ b/src/sisl/geom/nanoribbon.py @@ -294,6 +294,8 @@ class _heteroribbon_section(CompositeGeometrySection): The bond length of the ribbon. kind: {'armchair', 'zigzag'} The kind of ribbon that this section should be. + vacuum : + minimum separation in transverse direction shift_quantum: bool, optional Whether the implementation will assist avoiding lone atoms (< 2 neighbours).