from functools import partial import numpy as np import scipy from typing import Optional, Callable from meanfi.params.rparams import rparams_to_tb, tb_to_rparams from meanfi.tb.tb import add_tb, _tb_type from meanfi.model import Model from meanfi.tb.utils import fermi_energy def cost(mf_param: np.ndarray, model: Model, nk: int = 20) -> np.ndarray: """Defines the cost function for root solver. The cost function is the difference between the computed and inputted mean-field. Parameters ---------- mf_param : 1D real array that parametrises the mean-field correction. Model : Interacting tight-binding problem definition. nk : Number of k-points in a grid to sample the Brillouin zone along each dimension. If the system is 0-dimensional (finite), this parameter is ignored. Returns ------- : 1D real array that is the difference between the computed and inputted mean-field parametrisations """ shape = model._ndof mf = rparams_to_tb(mf_param, list(model.h_int), shape) mf_new = model.mfield(mf, nk=nk) mf_params_new = tb_to_rparams(mf_new) return mf_params_new - mf_param def solver( model: Model, mf_guess: np.ndarray, nk: int = 20, optimizer: Optional[Callable] = scipy.optimize.anderson, optimizer_kwargs: Optional[dict[str, str]] = {"M": 0}, ) -> _tb_type: """Solve for the mean-field correction through self-consistent root finding. Parameters ---------- model : Interacting tight-binding problem definition. mf_guess : The initial guess for the mean-field correction in the tight-binding dictionary format. nk : Number of k-points in a grid to sample the Brillouin zone along each dimension. If the system is 0-dimensional (finite), this parameter is ignored. optimizer : The solver used to solve the fixed point iteration. Default uses `scipy.optimize.anderson`. optimizer_kwargs : The keyword arguments to pass to the optimizer. Returns ------- : Mean-field correction solution in the tight-binding dictionary format. """ shape = model._ndof mf_params = tb_to_rparams(mf_guess) f = partial(cost, model=model, nk=nk) result = rparams_to_tb( optimizer(f, mf_params, **optimizer_kwargs), list(model.h_int), shape ) fermi = fermi_energy(add_tb(model.h_0, result), model.filling, nk=nk) return add_tb(result, {model._local_key: -fermi * np.eye(model._ndof)})