232 static constexpr std::array<double, 24> GL24_X {{
233 -0.99518721999702131, -0.97472855597130947, -0.93827455200273280, -0.88641552700440107,
234 -0.82000198597390295, -0.74012419157855436, -0.64809365193697555, -0.54542147138883956,
235 -0.43379350762604513, -0.31504267969616340, -0.19111886747361631, -0.06405689286260563,
236 0.06405689286260563, 0.19111886747361631, 0.31504267969616340, 0.43379350762604513,
237 0.54542147138883956, 0.64809365193697555, 0.74012419157855436, 0.82000198597390295,
238 0.88641552700440107, 0.93827455200273280, 0.97472855597130947, 0.99518721999702131
240 static constexpr std::array<double, 24> GL24_W {{
241 0.01234122979998869, 0.02853138862893356, 0.04427743881741941, 0.05929858491543636,
242 0.07334648141108016, 0.08619016153195321, 0.09761865210411393, 0.10744427011596556,
243 0.11550566805372552, 0.12167047292780329, 0.12583745634682825, 0.12793819534675202,
244 0.12793819534675202, 0.12583745634682825, 0.12167047292780329, 0.11550566805372552,
245 0.10744427011596556, 0.09761865210411393, 0.08619016153195321, 0.07334648141108016,
246 0.05929858491543636, 0.04427743881741941, 0.02853138862893356, 0.01234122979998869
262 const auto&
bins = this->
bins.value();
263 const size_t nbins =
bins.size();
265 auto clear_and_resize = [&] (std::array<std::vector<double>, 6>& dest) {
266 for (
auto& v : dest) {
267 v.assign(nbins, std::numeric_limits<double>::quiet_NaN());
273 cache.
bin_widths.assign(nbins, std::numeric_limits<double>::quiet_NaN());
275 for (
size_t i = 0; i < nbins; ++i) {
283 auto eval_amplitudes = [&] (
double q2,
bool bar) -> AmpSet {
284 const double beta =
beta_l(q2);
299 auto eval_integrands = [&] (
double q2,
bool bar) -> std::array<double, 6> {
300 const AmpSet a = eval_amplitudes(q2, bar);
302 const double norm_ALpar1 = std::norm(a.ALpar1);
303 const double norm_ALperp1 = std::norm(a.ALperp1);
304 const double norm_ARpar1 = std::norm(a.ARpar1);
305 const double norm_ARperp1 = std::norm(a.ARperp1);
306 const double norm_ALpar0 = std::norm(a.ALpar0);
307 const double norm_ALperp0 = std::norm(a.ALperp0);
308 const double norm_ARpar0 = std::norm(a.ARpar0);
309 const double norm_ARperp0 = std::norm(a.ARperp0);
311 const double one_plus_beta2 = 1.0 + a.beta2;
312 const double one_minus_beta2 = 1.0 - a.beta2;
313 const double alpha_L = cache.
alpha_L;
316 a.ARpar1 * std::conj(a.ALpar1)
317 + a.ARperp1 * std::conj(a.ALperp1)
318 + a.ARpar0 * std::conj(a.ALpar0)
319 + a.ARperp0 * std::conj(a.ALperp0);
322 a.ARperp1 * std::conj(a.ALpar1)
323 + a.ARpar1 * std::conj(a.ALperp1)
324 + a.ARperp0 * std::conj(a.ALpar0)
325 + a.ARpar0 * std::conj(a.ALperp0);
327 const double k1ss = std::real(
328 0.25 * (norm_ALpar1 + norm_ALperp1 + norm_ARpar1 + norm_ARperp1)
329 + 0.25 * one_plus_beta2 * (norm_ALpar0 + norm_ALperp0 + norm_ARpar0 + norm_ARperp0)
330 + 0.5 * one_minus_beta2 * k1_cross
333 const double k1cc = std::real(
334 0.25 * one_plus_beta2 * (norm_ARpar1 + norm_ARperp1 + norm_ALpar1 + norm_ALperp1)
335 + 0.25 * one_minus_beta2 * (norm_ARpar0 + norm_ARperp0 + norm_ALpar0 + norm_ALperp0)
336 + 0.5 * one_minus_beta2 * k1_cross
339 const double k1c = -a.beta * std::real(
340 a.ARperp1 * std::conj(a.ARpar1)
341 - a.ALperp1 * std::conj(a.ALpar1)
344 const double k2ss = 0.5 * alpha_L * std::real(
345 a.ARperp1 * std::conj(a.ARpar1)
346 + a.ALperp1 * std::conj(a.ALpar1)
347 + one_plus_beta2 * (a.ARperp0 * std::conj(a.ARpar0) + a.ALperp0 * std::conj(a.ALpar0))
348 + one_minus_beta2 * k2_cross
351 const double k2cc = 0.5 * alpha_L * std::real(
352 one_plus_beta2 * (a.ARperp1 * std::conj(a.ARpar1) + a.ALperp1 * std::conj(a.ALpar1))
353 + one_minus_beta2 * (a.ARpar0 * std::conj(a.ARperp0) + a.ALpar0 * std::conj(a.ALperp0))
354 + one_minus_beta2 * k2_cross
357 const double k2c = -0.5 * alpha_L * a.beta * (
358 norm_ARpar1 + norm_ARperp1 - norm_ALpar1 - norm_ALperp1
361 return {{k1ss, k1cc, k1c, k2ss, k2cc, k2c}};
364 auto integrate_bin = [&] (
double q2_l,
double q2_u,
bool bar) -> std::array<double, 6> {
365 std::array<double, 6> acc {};
367 const double width = q2_u - q2_l;
368 if (!std::isfinite(width) || width <= 0.0) {
369 acc.fill(std::numeric_limits<double>::quiet_NaN());
373 const double center = 0.5 * (q2_l + q2_u);
374 const double half_width = 0.5 * width;
376 for (
size_t i = 0; i < GL24_X.size(); ++i) {
377 const double q2 = center + half_width * GL24_X[i];
378 const auto vals = eval_integrands(q2, bar);
379 for (
size_t k = 0; k < acc.size(); ++k) {
380 acc[k] += GL24_W[i] * vals[k];
384 for (
double& v : acc) {
391 auto compute_and_store_bin = [&] (
size_t bin_idx) {
392 const auto& [q2_l, q2_u] =
bins[bin_idx];
394 const auto integ = integrate_bin(q2_l, q2_u,
false);
395 const auto integ_bar = integrate_bin(q2_l, q2_u,
true);
397 for (
size_t k = 0; k < cache.
K_i_binned.size(); ++k) {
404 if (requested_threads == 0u) {
405 requested_threads = std::thread::hardware_concurrency();
407 if (requested_threads == 0u) {
408 requested_threads = 1u;
411 const size_t nworkers = std::min<size_t>(requested_threads, nbins);
413 if (nworkers <= 1u) {
414 for (
size_t i = 0; i < nbins; ++i) {
415 compute_and_store_bin(i);
420 std::vector<std::thread> workers;
421 workers.reserve(nworkers);
423 std::exception_ptr first_exception =
nullptr;
424 std::mutex exception_mutex;
426 auto worker = [&] (
size_t begin,
size_t end) {
428 for (
size_t i = begin; i < end; ++i) {
429 compute_and_store_bin(i);
432 std::lock_guard<std::mutex> lock(exception_mutex);
433 if (!first_exception) {
434 first_exception = std::current_exception();
439 const size_t chunk = (nbins + nworkers - 1) / nworkers;
440 for (
size_t w = 0; w < nworkers; ++w) {
441 const size_t begin = w * chunk;
442 const size_t end = std::min(nbins, begin + chunk);
446 workers.emplace_back(worker, begin, end);
449 for (
auto& th : workers) {
453 if (first_exception) {
454 std::rethrow_exception(first_exception);