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
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)
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
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
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
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
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)
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)
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)
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)
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