Пример #1
0
    def dispatch(self,
                 t=(-2.414, -0.168),
                 beta=(-1.847, -3.077),
                 a=1.42,
                 orthogonal=False):
        distance = self._obj.distance
        da = 0.0005

        R = (distance(0, a) + da, distance(1, a) + da, distance(2, a) + da)

        def construct(H, ia, atoms, atoms_xyz=None):
            idx_t012, rij_t012 = H.geometry.close(ia,
                                                  R=R,
                                                  atoms=atoms,
                                                  atoms_xyz=atoms_xyz,
                                                  ret_rij=True)
            H[ia, idx_t012[0]] = 0.
            H[ia, idx_t012[1]] = t[0] * np.exp(beta[0] * (rij_t012[1] - R[1]))
            H[ia, idx_t012[2]] = t[1] * np.exp(beta[1] * (rij_t012[2] - R[2]))

        # Define the graphene lattice
        C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1]))
        graphene = si.geom.graphene(a, C, orthogonal=orthogonal)
        # Define the Hamiltonian
        H = si.Hamiltonian(graphene)
        H.construct(construct)
        return H
Пример #2
0
    def from_yaml(cls, file, nodes=()):
        """ Parse the yaml file """
        from sisl_toolbox.siesta.minimizer._yaml_reader import read_yaml, parse_variable
        dic = read_yaml(file, nodes)

        element = dic["element"]
        tag = dic.get("tag")
        mass = dic.get("mass", None)

        # get default options for pseudo
        opts = NotNonePropertyDict()
        pseudo = dic["pseudo"]
        opts["logr"] = parse_variable(pseudo.get("log-radii"), unit="Ang").value
        opts["rcore"] = parse_variable(pseudo.get("core-correction"), unit="Ang").value
        opts["xc"] = pseudo.get("xc")
        opts["equation"] = pseudo.get("equation")
        opts["flavor"] = pseudo.get("flavor")
        define = pseudo.get("define", ('NEW_CC', 'FREE_FORMAT_RC_INPUT', 'NO_PS_CUTOFFS'))

        # Now on to parsing the valence shells
        orbs = []
        for key in dic:
            if key not in _shell_order:
                continue

            # Now we know the occupation is a shell
            pseudo = dic[key].get("pseudo", {})
            cutoff = parse_variable(pseudo.get("cutoff"), 2.1, "Ang").value
            charge = parse_variable(pseudo.get("charge"), 0.).value
            orbs.append(si.AtomicOrbital(key, m=0, R=cutoff, q0=charge))

        atom = si.Atom(element, orbs, mass=mass, tag=tag)
        return cls(atom, define, **opts)
Пример #3
0
 def dispatch(self, t=-2.7, a=1.42, orthogonal=False):
     # Define the graphene lattice
     da = 0.0005
     C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=a + da))
     graphene = si.geom.graphene(a, C, orthogonal=orthogonal)
     # Define the Hamiltonian
     H = si.Hamiltonian(graphene)
     H.construct([(da, a + da), (0, t)])
     return H
Пример #4
0
    def dispatch(self, a=1.42, orthogonal=False):
        distance = self._obj.distance
        da = 0.0005

        R = (distance(0, a) + da, distance(1, a) + da, distance(2, a) + da,
             distance(3, a) + da)
        # Define the graphene lattice
        C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1]))
        graphene = si.geom.graphene(a, C, orthogonal=orthogonal)
        # Define the Hamiltonian
        H = si.Hamiltonian(graphene, orthogonal=False)
        t = [(-0.45, 1), (-2.78, 0.117), (-0.15, 0.004), (-0.095, 0.002)]
        H.construct([R, t])
        return H
Пример #5
0
    def dispatch(self, set='A', a=1.42, orthogonal=False):
        distance = self._obj.distance
        da = 0.0005
        H_orthogonal = True
        #U = 2.0

        R = tuple(distance(i, a) + da for i in range(4))
        if set == 'A':
            # same as simple
            t = (0, -2.7)
            #U = 0.
        elif set == 'B':
            # same as simple
            t = (0, -2.7)
        elif set == 'C':
            t = (0, -2.7, -0.2)
        elif set == 'D':
            t = (0, -2.7, -0.2, -0.18)
        elif set == 'E':
            # same as D, but specific for GNR
            t = (0, -2.7, -0.2, -0.18)
        elif set == 'F':
            # same as D, but specific for GNR
            t = [(0, 1), (-2.7, 0.11), (-0.09, 0.045), (-0.27, 0.065)]
            H_orthogonal = False
        elif set == 'G':
            # same as D, but specific for GNR
            t = [(0, 1), (-2.97, 0.073), (-0.073, 0.018), (-0.33, 0.026)]
            #U = 0.
            H_orthogonal = False
        else:
            raise ValueError(
                f"Set specification for {self.doi} does not exist, should be one of [A-G]"
            )

        # Reduce size of R
        R = R[:len(t)]

        # Currently we do not carry over U, since it is not specified for the
        # sisl objects....

        # Define the graphene lattice
        C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1]))
        graphene = si.geom.graphene(a, C, orthogonal=orthogonal)
        graphene.optimize_nsc([0, 1])
        # Define the Hamiltonian
        H = si.Hamiltonian(graphene, orthogonal=H_orthogonal)
        H.construct([R, t])
        return H
Пример #6
0
    def dispatch(self, t=-2.7, a=1.42, orthogonal=False):
        distance = self._obj.distance
        da = 0.0005

        R = (distance(0, a) + da, distance(1, a) + da)

        def construct(H, ia, atoms, atoms_xyz=None):
            idx_t01, rij_t01 = H.geometry.close(ia,
                                                R=R,
                                                atoms=atoms,
                                                atoms_xyz=atoms_xyz,
                                                ret_rij=True)
            H[ia, idx_t01[0]] = 0.
            H[ia, idx_t01[1]] = t * (a / rij_t01[1])**2

        # Define the graphene lattice
        C = si.Atom(6, si.AtomicOrbital(n=2, l=1, m=0, R=R[-1]))
        graphene = si.geom.graphene(a, C, orthogonal=orthogonal)
        # Define the Hamiltonian
        H = si.Hamiltonian(graphene)
        H.construct(construct)
        return H
Пример #7
0
    def from_dict(cls, dic):
        """ Return an `AtomBasis` from a dictionary

        Parameters
        ----------
        dic : dict
        """
        from sisl_toolbox.siesta.atom._atom import _shell_order
        element = dic["element"]
        tag = dic.get("tag")
        mass = dic.get("mass", None)

        # get default options for pseudo
        opts = NotNonePropertyDict()

        basis = dic.get("basis", {})
        opts["ion_charge"] = parse_variable(basis.get("ion-charge")).value
        opts["type"] = basis.get("type")

        def get_radius(orbs, zeta):
            for orb in orbs:
                if orb.zeta == zeta:
                    return orb.R
            raise ValueError("Could parse the negative R value")

        orbs = []
        for nl in dic:
            if nl not in _shell_order:
                continue

            n, l = int(nl[0]), 'spdfg'.index(nl[1])
            # Now we are sure we are dealing with valence shells
            basis = dic[nl].get("basis", {})

            opt_nl = NotNonePropertyDict()
            orbs_nl = []

            # Now read through the entries
            for key, entry in basis.items():
                if key in ("charge-confinement", "charge-conf"):
                    opt_nl["charge"] = [
                        parse_variable(entry.get("charge")).value,
                        parse_variable(entry.get("yukawa"),
                                       unit='1/Ang').value,
                        parse_variable(entry.get("width"), unit='Ang').value
                    ]
                elif key in ("soft-confinement", "soft-conf"):
                    opt_nl["soft"] = [
                        parse_variable(entry.get("V0"), unit='eV').value,
                        parse_variable(entry.get("ri"), unit='Ang').value
                    ]
                elif key in ("filter", ):
                    opt_nl["filter"] = parse_variable(entry, unit='eV').value
                elif key in ("split-norm", "split"):
                    opt_nl["split"] = parse_variable(entry).value
                elif key in ("polarization", "pol"):
                    opt_nl["pol"] = parse_variable(entry).value
                elif key.startswith("zeta"):
                    # cutoff of zeta
                    zeta = int(key[4:])
                    R = parse_variable(entry, unit='Ang').value
                    if R < 0:
                        R *= -get_radius(orbs_nl, zeta - 1)
                    orbs_nl.append(
                        si.AtomicOrbital(n=n, l=l, m=0, zeta=zeta, R=R))

            if len(orbs_nl) > 0:
                opts[(n, l)] = opt_nl
                orbs.extend(orbs_nl)

        atom = si.Atom(element, orbs, mass=mass, tag=tag)
        return cls(atom, opts)
Пример #8
0
    def from_block(cls, block):
        """ Return an `Atom` for a specified basis block

        Parameters
        ----------
        block : list or str
           the PAO.basis block (as read from an fdf file).
           Should be a list of lines.
        """
        if isinstance(block, str):
            block = block.splitlines()
        else:
            # store local list
            block = list(block)

        def blockline():
            nonlocal block
            out = ""
            while len(out) == 0:
                out = block.pop(0).split('#')[0].strip()
            return out

        # define global opts
        opts = {}

        specie = blockline()
        specie = specie.split()
        if len(specie) == 4:
            # we have Symbol, nl, type, ionic_charge
            symbol, nl, opts["type"], opts["ion_charge"] = specie
        elif len(specie) == 3:
            # we have Symbol, nl, type
            # or
            # we have Symbol, nl, ionic_charge
            symbol, nl, opt = specie
            try:
                opts["ion_charge"] = float(opt)
            except:
                opts["type"] = opt
        elif len(specie) == 2:
            # we have Symbol, nl
            symbol, nl = specie
            type = None

        # now loop orbitals
        orbs = []
        for _ in range(int(nl)):
            # we have 2 or 3 lines
            nl_line = blockline()
            rc_line = blockline()
            # check if we have contraction in the line
            # This is not perfect, but should grab
            # contration lines rather than next orbital line.
            # This is because the first n=<integer> should never
            # contain a ".", whereas the contraction *should*.
            if len(block) > 0:
                if '.' in block[0].split()[0]:
                    contract_line = blockline()

            # remove n=
            nl_line = nl_line.replace("n=", "").split()

            # first 3 are n, l, Nzeta
            n = int(nl_line.pop(0))
            l = int(nl_line.pop(0))
            nzeta = int(nl_line.pop(0))
            # assign defaults
            nlopts = {}

            while len(nl_line) > 0:
                opt = nl_line.pop(0)
                if opt == "P":
                    try:
                        npol = int(nl_line[0])
                        nl_line.pop(0)
                        nlopts["pol"] = npol
                    except:
                        nlopts["pol"] = 1
                elif opt == "S":
                    nlopts["split"] = float(nl_line.pop(0))
                elif opt == "F":
                    nlopts["filter"] = float(nl_line.pop(0)) / _eV2Ry
                elif opt == "E":
                    # 1 or 2 values
                    V0 = float(nl_line.pop(0)) / _eV2Ry
                    try:
                        ri = float(nl_line[0]) / _Ang2Bohr
                        nl_line.pop(0)
                    except:
                        # default to None (uses siesta default)
                        ri = None
                    nlopts["soft"] = [V0, ri]
                elif opt == "Q":
                    # 1, 2 or 3 values
                    charge = float(nl_line.pop(0))
                    try:
                        # this is in Bohr-1
                        yukawa = float(nl_line[0]) * _Ang2Bohr
                        nl_line.pop(0)
                    except:
                        # default to None (uses siesta default)
                        yukawa = None
                    try:
                        width = float(nl_line[0]) / _Ang2Bohr
                        nl_line.pop(0)
                    except:
                        # default to None (uses siesta default)
                        width = None
                    nlopts["charge"] = [charge, yukawa, width]

            # now we have everything to build the orbitals etc.
            for izeta, rc in enumerate(map(float, rc_line.split()), 1):
                if rc > 0:
                    rc /= _Ang2Bohr
                elif rc < 0 and izeta > 1:
                    rc *= -orbs[-1].R
                elif rc == 0 and izeta > 1:
                    # this is ok, the split-norm will be used to
                    # calculate the radius
                    pass
                else:
                    raise ValueError(
                        f"Could not parse the PAO.Basis block for the zeta ranges {rc_line}."
                    )
                orb = si.AtomicOrbital(n=n, l=l, m=0, zeta=izeta, R=rc)
                nzeta -= 1
                orbs.append(orb)

            # In case the final orbitals hasn't been defined.
            # They really should be defined in this one, but sometimes it may be
            # useful to leave the rc's definitions out.
            rc = orbs[-1].R
            for izeta in range(nzeta):
                orb = si.AtomicOrbital(n=n,
                                       l=l,
                                       m=0,
                                       zeta=orbs[-1].zeta + 1,
                                       R=rc)
                orbs.append(orb)
            opts[(n, l)] = nlopts

        # Now create the atom
        atom = si.Atom(symbol, orbs)
        return cls(atom, opts)
Пример #9
0
    def from_input(cls, inp):
        """ Return atom object respecting the input

        Parameters
        ----------
        inp : list or str
           create `AtomInput` from the content of `inp`
        """
        def _get_content(f):
            if f.is_file():
                return open(f, 'r').readlines()
            return None

        if isinstance(inp, (tuple, list)):
            # it is already in correct format
            pass
        elif isinstance(inp, (str, Path)):
            # convert to path
            inp = Path(inp)

            # Check if it is a path or an input
            content = _get_content(inp)
            if content is None:
                content = _get_content(inp / "INP")
            if content is None:
                raise ValueError(
                    f"Could not find any input file in {str(inp)} or {str(inp / 'INP')}"
                )
            inp = content

        else:
            raise ValueError(f"Unknown input format inp={inp}?")

        # Now read lines
        defines = []
        opts = PropertyDict()

        def bypass_comments(inp):
            if inp[0].startswith("#"):
                inp.pop(0)
                bypass_comments(inp)

        def bypass(inp, defines):
            bypass_comments(inp)
            if inp[0].startswith("%define"):
                line = inp.pop(0)
                defines.append(line.split()[1].strip())
                bypass(inp, defines)

        bypass(inp, defines)

        # Now prepare reading
        # First line has to contain the *type* of calculation
        # pg|pe|ae|pt <comment>
        line = inp.pop(0).strip()
        if line.startswith("pg"):
            opts.cc = False
        elif line.startswith("pe"):
            opts.cc = True

        # <flavor> logr?
        line = inp.pop(0).strip().split()
        opts.flavor = line[0]
        if len(line) >= 2:
            opts.logr = float(line[1]) / _Ang2Bohr

        # <element> <xc>' rs'?
        line = inp.pop(0)
        symbol = line.split()[0]
        # now get xc equation
        if len(line) >= 11:
            opts.equation = line[10:10]
        opts.xc = line[:10].split()[1]
        line = line.split()
        if len(line) >= 3:
            opts.libxc = int(line[2])

        # currently not used line
        inp.pop(0)

        # core, valence
        core, valence = inp.pop(0).split()
        opts.core = int(core)
        valence = int(valence)

        orbs = []
        for _ in range(valence):
            n, l, *occ = inp.pop(0).split()
            orb = PropertyDict()
            orb.n = int(n)
            orb.l = int(l)
            # currently we don't distinguish between up/down
            orb.q0 = sum(map(float, occ))
            orbs.append(orb)

        # now we read the line with rc's and core-correction
        rcs = inp.pop(0).split()
        if len(rcs) >= 6:
            # core-correction
            opts.rcore = float(rcs[5]) / _Ang2Bohr

        for orb in orbs:
            orb.R = float(rcs[orb.l]) / _Ang2Bohr

        # Now create orbitals
        orbs = [si.AtomicOrbital(**orb, m=0, zeta=1) for orb in orbs]
        # now re-arrange ensuring we have correct order of l shells
        orbs = sorted(orbs, key=lambda orb: orb.l)
        atom = si.Atom(symbol, orbs)
        return cls(atom, defines, **opts)
Пример #10
0
import plotly.graph_objs as go
import numpy as np

import sisl

r = np.linspace(0, 3.5, 50)
f = np.exp(-r)

orb = sisl.AtomicOrbital('2pzZ', (r, f))
geom = sisl.geom.graphene(orthogonal=True, atoms=sisl.Atom(6, orb))
geom = geom.move([0, 0, 5])
H = sisl.Hamiltonian(geom)
H.construct([(0.1, 1.44), (0, -2.7)], )


def test_eigenstate_wf():

    plot = H.eigenstate()[0].plot.wavefunction(geometry=H.geometry)

    assert len(plot.data) > 0
    assert isinstance(plot.data[0], go.Isosurface)


def test_hamiltonian_wf():

    # Check if it works for 3D plots
    plot = H.plot.wavefunction(2)
    assert isinstance(plot.data[0], go.Isosurface)

    # Check that setting plot geom to True adds data traces
    plot.update_settings(plot_geom=False)
Пример #11
0
def sp2(ext_geom,
        t1=2.7,
        t2=0.2,
        t3=0.18,
        eB=3.,
        eN=-3.,
        s0=1.0,
        s1=0,
        s2=0,
        s3=0,
        dq=0,
        dim=2):
    """ Function to create a Tight Binding Hamiltoninan for sp2 Carbon systems

    It takes advantage of the `sisl` class for building sparse Hamiltonian matrices,
    `sisl.physics.Hamiltonian`

    It obtains the Hamiltonian for ``ext_geom`` (which must be a `sisl.Geometry` instance)
    with the parameters for first, second and third nearest neighbors (``t1``, ``t2``, ``t3``).

    One can also use a non-orthogonal basis of atomic orbitals by passing the parameters for the
    overlap matrix between first, second and third nearest neighbors (``s1``, ``s2``, ``s3``).

    The function will also take into account the possible presence of Boron or Nitrogen atoms,
    for which one would need to specify the on-site energy for those atoms (``eB`` and ``eN``)

    Returns
    -------
    H: sisl.physics.Hamiltonian
        tight-binding Hamiltonian for the sp2 structure of ``dim=2`` (for the two spin channels)
    """

    # Determine pz sites
    aux = []
    sp3 = []
    for ia, atom in enumerate(ext_geom.atoms.iter()):
        # Append non C-type atoms in aux list
        if atom.Z not in [5, 6, 7]:
            aux.append(ia)
        idx = ext_geom.close(ia, R=[0.1, 1.6])
        if len(idx[1]) == 4:  # Search for atoms with 4 neighbors
            if atom.Z == 6:
                sp3.append(ia)

    # Remove all sites not carbon-type
    pi_geom = ext_geom.remove(aux + sp3)
    pi_geom.reduce()

    # Iterate over atomic species to set initial charge
    maxR = 20
    r = np.linspace(0, maxR, 700)
    # In Slater-type orbitals (Hydrogen-like atom solution), the radial function is ~exp(-Zr/2a)
    # where a=0.529 \AA is the Bohr radius and Z is the atomic number.
    # We use the effective nuclear charge instead, which for Carbon atoms is approximately Zeff~3.
    func = np.exp(-3 * r)
    for atom, _ in pi_geom.atoms.iter(True):
        pz = sisl.AtomicOrbital('pz', (r, func), R=maxR, q0=atom.Z - 5 + dq)
        atom.orbitals[0] = pz

    # Construct Hamiltonian
    if s1 != 0:
        orthogonal = False
    else:
        orthogonal = True
    H = sisl.Hamiltonian(pi_geom, orthogonal=orthogonal, dim=dim)

    # Radii defining 1st, 2nd, and 3rd neighbors
    R = [0.1, 1.6, 2.6, 3.1]
    # Build hamiltonian for backbone

    for ia in pi_geom:
        idx = pi_geom.close(ia, R=R)
        # NB: I found that ':' is necessary in the following lines, but I don't understand why...
        if pi_geom.atoms[ia].Z == 5:
            H[ia, ia, :] = eB  # set onsite for B sites
        elif pi_geom.atoms[ia].Z == 7:
            H[ia, ia, :] = eN  # set onsite for N sites
        # set hoppings
        H[ia, idx[1], :] = -t1
        if t2 != 0:
            H[ia, idx[2], :] = -t2
        if t3 != 0:
            H[ia, idx[3], :] = -t3
        if not H.orthogonal:
            H.S[ia, ia] = s0
            H.S[ia, idx[1]] = s1
            H.S[ia, idx[2]] = s2
            H.S[ia, idx[3]] = s3

    return H