2 * @file VolumeEstimatorMC.tpp
3 * @brief VolumeEstimatorMC template implementation.
4 * @details Implements hit-or-miss volume estimation with parallel sampling
5 * and standard error computation.
7// VolumeEstimatorMC.tpp
9// Created by Giacomo Merlo on 12/01/26.
19#include "montecarlo/rng/rng_factory.hpp"
21namespace mc::estimators {
23template <std::size_t dim>
24static geom::Point<dim>
25sample_uniform_in_box(const geom::Bounds<dim>& b, std::mt19937& rng)
28 for (std::size_t k = 0; k < dim; ++k) {
29 std::uniform_real_distribution<double> unif(b[k].first, b[k].second);
35template <std::size_t dim>
37VolumeEstimatorMC<dim>::estimate(const mc::domains::IntegrationDomain<dim>& domain,
39 std::size_t n_samples) const
41 if (n_samples == 0) throw std::invalid_argument("VolumeEstimatorMC: n_samples must be > 0.");
43 const geom::Bounds<dim> bounds = domain.getBounds();
44 const double boxV = domain.getBoxVolume();
46 const int T = omp_get_max_threads();
47 const std::size_t base = n_samples / static_cast<std::size_t>(T);
48 const std::size_t rem = n_samples % static_cast<std::size_t>(T);
50 std::size_t inside = 0;
52#pragma omp parallel for reduction(+:inside)
53 for (int tid = 0; tid < T; ++tid) {
54 const std::size_t n_local = base + (static_cast<std::size_t>(tid) < rem ? 1u : 0u);
56 auto rng = mc::rng::make_engine_with_seed(std::optional<std::uint32_t>{seed},
57 static_cast<std::uint64_t>(tid));
59 for (std::size_t i = 0; i < n_local; ++i) {
60 const auto x = sample_uniform_in_box<dim>(bounds, rng);
61 if (domain.isInside(x)) ++inside;
65 const double p = static_cast<double>(inside) / static_cast<double>(n_samples);
66 const double var_p = (p * (1.0 - p)) / static_cast<double>(n_samples);
67 const double se_p = std::sqrt(std::max(0.0, var_p));
69 VolumeEstimate<dim> out;
70 out.n_samples = n_samples;
72 out.volume = boxV * p;
73 out.stderr = boxV * se_p;
77} // namespace mc::integrators