Source code for CADMium.pssolver.pssolver

"""
pssolver.py
"""

import numpy as np
from scipy.sparse import spdiags
from pydantic import BaseModel

from .calc_orbitals import calc_orbitals
from .calc_density import calc_density
from .calc_energy import calc_energy
from .calc_response import calc_response
from .iter_orbitals import iter_orbitals
from .normalize_orbitals import normalize_orbitals
from .get_homo import get_homo

from .calc_ked_WFI import calc_ked_WFI
from .get_ked_WFI import get_ked_WFI

eps = np.finfo(float).eps

class SolverOptions(BaseModel):
    fractional      : bool = False
    sym             : bool = False
    iter_lin_solver : bool = True
    disp            : bool = True
    tol_orbital     : float = 1e-15
    tol_lin_solver  : float = 1e-4

[docs]def Pssolver(grid, Nmo, N, optSolver={}): """ Generates an array of solvers of shape Nmo[0]xNmo[1] """ Nmo = np.array(Nmo) N = np.array(N) solver = np.empty((Nmo.shape[0], Nmo.shape[1]), dtype=object) for i in range(Nmo.shape[0]): for j in range(Nmo.shape[1]): solver[i,j] = i_solver(grid, Nmo[i,j], N[i,j], i, Nmo.shape[1], optSolver) return solver
class i_solver(): """ Keeps track of eigenvalues/vectors and constructs densities and responses. """ def __init__(self, grid, Nmo, N, Nlvls, pol, optSolver): optSolver = {k.lower(): v for k, v in optSolver.items()} for i in optSolver.keys(): if i not in SolverOptions().dict().keys(): raise ValueError(f"{i} is not a valid option for KohnSham") optSolver = SolverOptions(**optSolver) self.optSolver = optSolver self.grid = grid self.Nmo = Nmo self.N = N #Polarization of electrons handled by this solver self.pol = pol self.veff = None # Effective potential self.H0 = None # Base Hamiltonian self.phi = None # Molecular Orbitals self.eig = None # Eigenvalues self.e0 = -20 # Estimate of lowest energy value self.eks = None # KohnSham energy self.Vs = None # Kohn Sham potential self.Ts = None # Kohn Sham kinetic energy self.n = None # Electron density self.chi = None # Change in density from given change in potential self.homo = None # Highest occupied Molecular Orbital #kinetic energy densities | Not uniquely defined self.ked_WFI = None #Use laplacian self.kedWFII = None #Use gradient self.v0 = np.ones(self.grid.Nelem) # self.default_e0 = -20.0 self.opt = {"tol" : 1e-16, "v0":np.ones((self.grid.Nelem, 1))} self.Nlvls = Nlvls self.pol = pol if self.N == -1: if self.polarization == 1: self.N = 2 * self.Nmo else: self.N = self.Nmo self.m = Nlvls def hamiltonian(self): """ Construct basic Hamiltonian H_0 Includes effective potential due to angular momentum around bond axis """ Nelem = self.grid.Nelem #Inverse volume element and angular momentum potential W = spdiags(data=self.grid.w, diags=0, m=Nelem, n=Nelem) f = spdiags(data=self.grid.f, diags=0, m=Nelem, n=Nelem) #Choose the correct symmetry for m if np.mod(self.m, 2) == 0: #Even symmetry eT = -0.5 * self.grid.elap self.H0 = eT + self.m ** 2 * W @ f else: #Odd symmetry oT = -0.5 * self.grid.olap self.H0 = oT + self.m ** 2 * W @ f def calc_orbitals(self, solver_id=None, return_dict=None): calc_orbitals(self, solver_id, return_dict) def calc_response(self): calc_response(self) def iter_orbitals(self, solver_id=None, return_dict=None): iter_orbitals(self, solver_id, return_dict) def normalize_orbitals(self): normalize_orbitals(self) def calc_density(self): calc_density(self) def calc_energy(self): calc_energy(self) def get_homo(self): homo = get_homo(self) return homo def get_ked_WFI(self): ked = get_ked_WFI(self) def calc_ked_WFI(self): calc_ked_WFI(self) # def get_Ts(self): # """ # Get total kinetic energy # """ # Ts = 0.0 # self.calc_energy() # for i in range(self.solver.shape[0]): # for j in range(self.solver.shape[1]): # Ts += np.sum( self.solver[i,j].Ts ) def setveff(self, veff): """ Distribute effective potential to solver object Size of Potential array should match polarization Assert solver(0).pol == len(veff, 1) """ self.veff = veff