3#include "../utility/adaptive.hpp"
4#include "../utility/integrator.hpp"
7#include <itertools/itertools.hpp>
22namespace triqs::experimental::lattice {
24 using namespace triqs::gfs;
40 constexpr nda::clef::placeholder<0>
kx;
42 constexpr nda::clef::placeholder<1>
ky;
44 constexpr nda::clef::placeholder<2>
kz;
46 constexpr nda::clef::placeholder<3>
w;
70 std::array<long, 3>
k_grid = {10, 10, 10};
80 int deduce_dim_from_expression(
auto const &f_kw) {
82 auto f_w_temp = eval(f_kw, ph::kx = 0., ph::ky = 0., ph::kz = 0., ph::w = 0);
83 static_assert(not nda::clef::is_lazy<
decltype(f_w_temp)>,
"Integration expects a proper expression with placeholders.");
86 if constexpr (
requires { f_w_temp.shape(); }) {
87 return f_w_temp.shape()[0];
112 template <
typename T>
113 requires(std::convertible_to<T, dcomplex> or std::convertible_to<T, double>)
114 nda::array<dcomplex, 3>
integrate_ptr(
auto const &f_kw, std::vector<T>
const &omega_values, std::array<long, 3>
const &k_grid,
115 mpi::communicator comm) {
117 for (
auto i : {0, 1, 2}) {
118 if (k_grid[i] <= 0)
throw std::runtime_error{
"Cannot use PTR integration with kgrid dim <= 0."};
122 int block_dim = detail::deduce_dim_from_expression(f_kw);
123 auto result = nda::zeros<dcomplex>(omega_values.size(), block_dim, block_dim);
126 auto mpi_chunk_max = [&k_grid, comm](
int kdim) {
128 if (kdim == std::distance(k_grid.begin(), std::ranges::max_element(k_grid))) {
return mpi::chunk(nda::range(k_grid[kdim]), comm); }
129 return itertools::slice(nda::range(k_grid[kdim]), 0,
134#pragma omp declare reduction(array_add_c_3 : nda::array<dcomplex, 3> : omp_out += omp_in) \
135 initializer(omp_priv = nda::array<dcomplex, 3>(omp_orig.shape()))
137#pragma omp parallel for collapse(3) reduction(array_add_c_3 : result) default(none) \
138 shared(k_grid, omega_values, f_kw, ph::kx, ph::ky, ph::kz, ph::w, mpi_chunk_max)
139 for (
auto ikx : mpi_chunk_max(0)) {
140 for (
auto iky : mpi_chunk_max(1)) {
141 for (
auto ikz : mpi_chunk_max(2)) {
142 double kx = ikx / double(k_grid[0]);
143 double ky = iky / double(k_grid[1]);
144 double kz = ikz / double(k_grid[2]);
145 auto f_w = eval(f_kw, ph::kx = kx, ph::ky = ky, ph::kz = kz);
146 for (
auto &&[n, omega] : itertools::enumerate(omega_values)) result(n, nda::range::all, nda::range::all) += eval(f_w, ph::w = omega);
150 result = mpi::all_reduce(result, comm);
153 result /= double(k_grid[0] * k_grid[1] * k_grid[2]);
173 template <
typename Mesh>
auto integrate_ptr(
auto const &f_kw, Mesh
const &w_mesh, std::array<long, 3>
const &k_grid, mpi::communicator comm = {}) {
175 int dim = detail::deduce_dim_from_expression(f_kw);
176 auto g_out =
gf{w_mesh, {dim, dim}};
177 std::vector<typename Mesh::mesh_point_t> mesh_points(w_mesh.begin(), w_mesh.end());
178 auto ptr_result =
integrate_ptr(f_kw, mesh_points, k_grid, comm);
180 for (
auto &&[n, w] : itertools::enumerate(mpi::chunk(w_mesh, comm))) { g_out[w] = calc(w); }
200 auto f_value = nda::make_regular(eval(f_kw, ph::kx = 0., ph::ky = 0., ph::kz = 0., ph::w = 0));
204 return [expr_kw = std::move(f_kw), int_1d_adapt](
auto om) {
205 std::pair<double, double> k_domain = {0, 1};
206 auto expr_k = eval(expr_kw, ph::w = om);
228 int dim = detail::deduce_dim_from_expression(f_kw);
229 auto g_out =
gf{w_mesh, {dim, dim}};
231 mpi::communicator comm = {};
233 for (
auto &&[n, w] : itertools::enumerate(mpi::chunk(w_mesh, comm))) { g_out[w] = calc(w); }
255 template <
typename Mesh>
258 namespace ph = placeholders;
262 auto kgrid_above_max = [&](
auto &k_grid) {
263 for (
auto ik : {0, 1, 2})
264 if (k_grid[ik] >= opt.
k_grid_max[ik]) {
return true; }
270 if (opt.
tolerance <= 0) {
throw std::runtime_error(
"Must provide a positive tolerance."); }
272 throw std::runtime_error(
"Must choose at least one of run_ptr or run_adaptive for BZ integration to work.");
275 if (!std::all_of(k_grid.begin(), k_grid.end(), [&](
int k) { return k > 0; })) {
276 throw std::runtime_error(
"Cannot run integraton with k_grid <= 0. ");
280 throw std::runtime_error(
"delta_k_grid cannot be negative.");
283 if (std::all_of(opt.
delta_k_grid.begin(), opt.
delta_k_grid.end(), [&](
int k) { return k == 0; }) and !kgrid_above_max(k_grid)) {
284 throw std::runtime_error(
"delta_k_grid can only be zero if k_grid >= k_grid_max.");
288 int dim = detail::deduce_dim_from_expression(f_kw);
289 auto g_out =
gf{w_mesh, {dim, dim}};
292 std::vector<bool> ptr_converged(w_mesh.size(),
false);
293 auto all_converged = [&]() {
return std::ranges::all_of(ptr_converged, std::identity()); };
296 std::vector<typename Mesh::mesh_point_t> mesh_points(w_mesh.begin(), w_mesh.end());
303 int remaining_ptr =
static_cast<int>(ptr_converged.size()) - std::reduce(ptr_converged.begin(), ptr_converged.end());
304 std::cout <<
"Points remaining unconverged: " << remaining_ptr <<
", now running with k-grid " << k_grid[0] <<
" " << k_grid[1] <<
" "
305 << k_grid[2] << std::endl;
309 auto ptr_result =
integrate_ptr(f_kw, mesh_points, k_grid, comm);
312 for (
auto &&[n, w] : itertools::enumerate(mesh_points)) {
313 ptr_converged[
w.data_index()] = (max_element(abs(ptr_result(n, nda::range::all, nda::range::all) - g_out[w])) < opt.
tolerance);
314 g_out[
w] = ptr_result(n, nda::range::all, nda::range::all);
319 std::vector<typename Mesh::mesh_point_t> unconv_mesh_points;
320 for (
auto w : mesh_points) {
321 if (!ptr_converged[
w.data_index()]) unconv_mesh_points.emplace_back(w);
323 mesh_points = unconv_mesh_points;
326 for (
auto ik : {0, 1, 2}) k_grid[ik] += opt.
delta_k_grid[ik];
328 }
while (!all_converged() and !kgrid_above_max(k_grid));
334 if (opt.
verbose) std::cout <<
"Running adaptive integration on remaining points." << std::endl;
338 for (
auto &&[n, w] : itertools::enumerate(mpi::chunk(g_out.mesh(), comm))) {
339 if (not ptr_converged[n]) g_out[
w] = calc(w);
Adaptive one-dimensional integrator based on a 13-point Gauss-Kronrod-Lobatto rule.
The owning Green's function container.
Umbrella header for the Green's function library.
nda::array< dcomplex, 3 > integrate_ptr(auto const &f_kw, std::vector< T > const &omega_values, std::array< long, 3 > const &k_grid, mpi::communicator comm)
Integrate an expression over the Brillouin zone on a fixed k-grid (PTR) for a list of frequencies,...
auto integrate_adaptive(auto const &f_kw, adaptive_options const &opt)
Build a callable that adaptively integrates an expression over the Brillouin zone for a given frequen...
gf< Mesh, matrix_valued > integrate_bz(auto const &f_kw, Mesh const &w_mesh, bz_int_options const &opt, mpi::communicator comm={})
Integrate an expression over the Brillouin zone for all frequencies, combining PTR and adaptive integ...
auto integrate(auto const &integrator1d, auto expr_to_integrate, nda::clef::pair< I, D > const &p)
Integrate a CLEF expression over one of its placeholders using an arbitrary one-dimensional integrato...
CLEF placeholders for the momentum and frequency arguments of integrable expressions.
constexpr nda::clef::placeholder< 0 > kx
Placeholder for the first momentum component .
constexpr nda::clef::placeholder< 1 > ky
Placeholder for the second momentum component .
constexpr nda::clef::placeholder< 2 > kz
Placeholder for the third momentum component .
constexpr nda::clef::placeholder< 3 > w
Placeholder for the frequency .
Options controlling the adaptive Brillouin-zone integration.
double tolerance
Target absolute error of the adaptive integration.
Options controlling the combined PTR and adaptive Brillouin-zone integration.
std::array< long, 3 > k_grid_max
Maximum number of k-points along each direction.
bool run_adaptive
Whether to run adaptive integration on the remaining points.
std::array< long, 3 > delta_k_grid
Increment of the k-grid size at each refinement step.
std::array< long, 3 > k_grid
Initial number of PTR k-points along each direction.
bool verbose
Whether to print the convergence progress.
double tolerance
Absolute tolerance of the integrated quantity.
bool run_ptr
Whether to run PTR integration before the adaptive step.