7#include <itertools/enumerate.hpp>
8#include <triqs/operators/many_body_operator.hpp>
16 auto n(
auto const &x) {
return operators::n(x.first, x.second); };
17 auto c_dag(
auto const &x) {
return operators::c_dag(x.first, x.second); };
18 auto c(
auto const &x) {
return operators::c(x.first, x.second); };
22 double three_j_symbol(std::tuple<long, long, long, long, long, long> jms) {
23 auto [j1, m1, j2, m2, j3, m3] = jms;
24 auto fact = [](
auto x) {
return std::tgamma(x + 1); };
25 if ((m1 + m2 + m3 != 0) || (m1 < -j1) || (m1 > j1) || (m2 < -j2) || (m2 > j2) || (m3 < -j3) || (m3 > j3) || (j3 > j1 + j2)
26 || j3 < std::abs(j1 - j2)) {
29 auto three_j_sym = ((j1 - j2 - m3) % 2) ? -1.0 : 1.0;
30 three_j_sym *= sqrt((fact(j1 + j2 - j3) * fact(j1 - j2 + j3) * fact(-j1 + j2 + j3)) / fact(j1 + j2 + j3 + 1));
31 three_j_sym *= sqrt(fact(j1 - m1) * fact(j1 + m1) * fact(j2 - m2) * fact(j2 + m2) * fact(j3 - m3) * fact(j3 + m3));
33 auto t_min = std::max({j2 - j3 - m1, j1 - j3 + m2,
static_cast<long>(0)});
34 auto t_max = std::min({j1 - m1, j2 + m2, j1 + j2 - j3});
36 for (
auto t : range(t_min, t_max + 1)) {
37 t_sum += ((t % 2) ? -1.0 : 1.0)
38 / (fact(t) * fact(j3 - j2 + m1 + t) * fact(j3 - j1 - m2 + t) * fact(j1 + j2 - j3 - t) * fact(j1 - m1 - t) * fact(j2 + m2 - t));
45 double angular_matrix_element(
long l,
long k,
long m1,
long m2,
long m3,
long m4) {
46 auto squared = [](
auto x) {
return x * x; };
48 auto ang_mat_ele = 0.0;
49 for (
auto q : range(-k, k + 1)) {
50 ang_mat_ele += three_j_symbol({l, -m1, k, q, l, m3}) * three_j_symbol({l, -m2, k, -q, l, m4}) * (((m1 + q + m2) % 2) ? -1.0 : 1.0);
52 ang_mat_ele *= squared(2.0 *
static_cast<double>(l) + 1) * squared(three_j_symbol({l, 0, k, 0, l, 0}));
56 nda::array<double, 1> radial_integrals(
long l,
double U_int,
double J_hund) {
57 auto F = nda::zeros<double>(l + 1);
63 F(1) = 14.0 * J_hund / (1.0 + 0.63);
67 F(1) = (6435.0 * J_hund) / (286.0 + 195.0 * 451.0 / 675.0 + 250.0 * 1001.0 / 2025.0);
68 F(2) = 451.0 * F(1) / 675.0;
69 F(3) = 1001.0 * F(1) / 2025.0;
75 std::map<op_name, op_name>
make_op_map(
auto const &spin_names,
auto const &orb_dims) {
76 std::map<op_name, op_name> op_map;
77 for (
auto const &spin : spin_names) {
79 for (
auto [bl_idx, bl_size] : enumerate(orb_dims)) {
80 for (
auto orb : range(bl_size)) { op_map[{spin, flat_idx++}] = {fmt::format(
"{}_{}", spin, bl_idx), orb}; }
86 operators::many_body_operator
rename_op(operators::many_body_operator
const &op, std::map<op_name, op_name>
const &op_map) {
88 auto rename_monomial = [&](
auto const &m) -> operators::many_body_operator {
89 operators::many_body_operator nop = operators::many_body_operator(1.0);
90 for (
auto const &b : m) {
91 auto name = op_map.at({std::get<std::string>(b.indices[0]), std::get<long>(b.indices[1])});
92 nop *= (b.dagger) ? detail::c_dag(name) : detail::c(name);
97 auto new_op = operators::many_body_operator();
98 for (
auto const &term : op) { new_op += term.coef * rename_monomial(term.monomial); }
104 std::pair<nda::matrix<double>, nda::matrix<double>>
U_matrix_kanamori(
long n_orb,
double U_int,
double U_prime,
double J_hund) {
105 auto Umat = nda::zeros<double>(n_orb, n_orb);
106 auto Upmat = nda::zeros<double>(n_orb, n_orb);
107 for (
auto &&[m, mp] : product(range(n_orb), range(n_orb))) {
109 Upmat(m, mp) = U_int;
111 Umat(m, mp) = U_prime - J_hund;
112 Upmat(m, mp) = U_prime;
115 return {Umat, Upmat};
119 auto U_matrix = nda::zeros<double>(2 * l + 1, 2 * l + 1, 2 * l + 1, 2 * l + 1);
120 auto m_range = nda::range(-l, l + 1);
121 auto radial_integrals = detail::radial_integrals(l, U_int, J_hund);
122 for (
auto [n, F] : itertools::enumerate(radial_integrals)) {
124 for (
auto [m1, m2, m3, m4] : product(m_range, m_range, m_range, m_range)) {
125 U_matrix(m1 + l, m2 + l, m3 + l, m4 + l) += F * detail::angular_matrix_element(l, k, m1, m2, m3, m4);
131 nda::array<dcomplex, 4>
rotate_U_matrix_slater(nda::array<double, 4>
const &Uspherical, nda::matrix<dcomplex> sph_to_local) {
132 auto s2lC = conj(sph_to_local);
133 auto s2lT = transpose(sph_to_local);
134 auto N = Uspherical.extent(0);
135 auto U_local = nda::zeros<dcomplex>(N, N, N, N);
136 for (
auto [i, k, n, p, j, q, m, o] : product(range(N), range(N), range(N), range(N), range(N), range(N), range(N), range(N))) {
137 auto left = s2lC(i, j) * s2lC(k, q);
138 auto right = (s2lT(m, n)) * s2lT(o, p);
139 U_local(i, k, n, p) += left * Uspherical(j, q, m, o) * right;
144 nda::array<dcomplex, 4>
U_matrix_slater_local(
long l, nda::matrix<dcomplex> sph_to_local,
double U_int,
double J_hund) {
149 operators::many_body_operator
h_int_kanamori(nda::matrix<double>
const &Umat, nda::matrix<double>
const &Upmat,
double const &J_hund,
150 int const &n_orb, std::vector<std::string>
const &spin_names,
kanamori_params const ¶ms) {
152 auto h_int = operators::many_body_operator();
155 for (
auto &&[s1, s2] : itertools::product(spin_names, spin_names)) {
156 for (
auto &&[a1, a2] : product(range(n_orb), range(n_orb))) {
157 h_int += 0.5 * ((s1 == s2) ? Umat(a1, a2) : Upmat(a1, a2)) * detail::n(std::make_pair(s1, a1)) * detail::n(std::make_pair(s2, a2));
163 for (
auto &&[s1, s2] : itertools::product(spin_names, spin_names)) {
164 if (s1 == s2)
continue;
165 for (
auto &&[a1, a2] : product(range(n_orb), range(n_orb))) {
166 if (a1 == a2)
continue;
167 h_int += -0.5 * J_hund * detail::c_dag(std::make_pair(s1, a1)) * detail::c(std::make_pair(s2, a1)) * detail::c_dag(std::make_pair(s2, a2))
168 * detail::c(std::make_pair(s1, a2));
175 for (
auto &&[s1, s2] : itertools::product(spin_names, spin_names)) {
176 if (s1 == s2)
continue;
177 for (
auto &&[a1, a2] : product(range(n_orb), range(n_orb))) {
178 if (a1 == a2)
continue;
179 h_int += 0.5 * J_hund * detail::c_dag(std::make_pair(s1, a1)) * detail::c_dag(std::make_pair(s2, a1)) * detail::c(std::make_pair(s2, a2))
180 * detail::c(std::make_pair(s1, a2));
187 operators::many_body_operator
h_int_density(nda::matrix<double>
const &Umat, nda::matrix<double>
const &Upmat,
double const &J_hund,
188 int const &n_orb, std::vector<std::string>
const &spin_names) {
190 return h_int_kanamori(Umat, Upmat, J_hund, n_orb, spin_names, params);
195 operators::many_body_operator
h_int_slater(nda::array<dcomplex, 4>
const &Umatrix,
int const &n_orb, std::vector<std::string>
const &spin_names) {
196 auto h_int = operators::many_body_operator();
197 for (
auto &&[s1, s2] : itertools::product(spin_names, spin_names)) {
198 for (
auto [m1, m2, m3, m4] : product(range(n_orb), range(n_orb), range(n_orb), range(n_orb))) {
199 h_int += 0.5 * real(Umatrix(m1, m2, m3, m4)) * detail::c_dag(std::make_pair(s1, m1)) * detail::c_dag(std::make_pair(s2, m2))
200 * detail::c(std::make_pair(s2, m4)) * detail::c(std::make_pair(s1, m3));
207 operators::many_body_operator
make_kanamori(std::vector<std::string>
const &tau_names, std::vector<long>
const &dim_gamma,
double U_int,
208 double U_prime,
double J_hund,
bool spin_flip,
bool pair_hopping) {
211 long n_orb = stdr::fold_left(dim_gamma, 0, std::plus<>());
226 operators::many_body_operator
make_density_density(
const std::vector<std::string> &tau_names,
const std::vector<long> &dim_gamma,
double U_int,
227 double U_prime,
double J_hund) {
229 return make_kanamori(tau_names, dim_gamma, U_int, U_prime, J_hund,
false,
false);
232 operators::many_body_operator
make_slater(std::vector<std::string>
const &tau_names, std::vector<long>
const &dim_gamma,
double U_int,
233 double J_hund, nda::matrix<dcomplex>
const &spherical_to_dft,
234 std::optional<nda::matrix<dcomplex>>
const &dft_to_local) {
237 long n_orb = stdr::fold_left(dim_gamma, 0, std::plus<>());
243 long l = (n_orb - 1) / 2;
244 auto transform = (dft_to_local) ? spherical_to_dft * dft_to_local.value() : spherical_to_dft;
std::pair< nda::matrix< double >, nda::matrix< double > > U_matrix_kanamori(long n_orb, double U_int, double U_prime, double J_hund)
construct the Kanamori two-index interaction matrix for parallel and anti-parallel spins.
operators::many_body_operator make_density_density(const std::vector< std::string > &tau_names, const std::vector< long > &dim_gamma, double U_int, double U_prime, double J_hund)
Construct a density-density interaction Hamiltonian.
operators::many_body_operator make_slater(std::vector< std::string > const &tau_names, std::vector< long > const &dim_gamma, double U_int, double J_hund, nda::matrix< dcomplex > const &spherical_to_dft, std::optional< nda::matrix< dcomplex > > const &dft_to_local)
Construct a Slater Hamiltonian.
nda::array< double, 4 > U_matrix_slater_spherical(long l, double U_int, double J_hund)
Construct a four-index Coulomb tensor in the basis of spherical harmonics.
nda::array< dcomplex, 4 > U_matrix_slater_local(long l, nda::matrix< dcomplex > sph_to_local, double U_int, double J_hund)
Construct a four-index Coulomb tensor in a specific orbital basis.
operators::many_body_operator make_kanamori(std::vector< std::string > const &tau_names, std::vector< long > const &dim_gamma, double U_int, double U_prime, double J_hund, bool spin_flip, bool pair_hopping)
Construct a Hubbard-Kanamori Hamiltonian.
operators::many_body_operator rename_op(operators::many_body_operator const &op, std::map< op_name, op_name > const &op_map)
operators::many_body_operator h_int_slater(nda::array< dcomplex, 4 > const &Umatrix, int const &n_orb, std::vector< std::string > const &spin_names)
operators::many_body_operator h_int_density(nda::matrix< double > const &Umat, nda::matrix< double > const &Upmat, double const &J_hund, int const &n_orb, std::vector< std::string > const &spin_names)
std::map< op_name, op_name > make_op_map(auto const &spin_names, auto const &orb_dims)
operators::many_body_operator h_int_kanamori(nda::matrix< double > const &Umat, nda::matrix< double > const &Upmat, double const &J_hund, int const &n_orb, std::vector< std::string > const &spin_names, kanamori_params const ¶ms)
nda::array< dcomplex, 4 > rotate_U_matrix_slater(nda::array< double, 4 > const &Uspherical, nda::matrix< dcomplex > sph_to_local)