mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-26 21:25:36 -04:00
Add truncated normal distribution support (#3761)
This commit is contained in:
parent
a3426cf833
commit
8198e6021d
8 changed files with 385 additions and 28 deletions
|
|
@ -92,3 +92,103 @@ TEST_CASE("Test construction of SpatialBox with parameters")
|
|||
REQUIRE(box.upper_right() == openmc::Position {30, 15, 5});
|
||||
REQUIRE_FALSE(box.only_fissionable());
|
||||
}
|
||||
|
||||
TEST_CASE("Test Normal distribution")
|
||||
{
|
||||
// Test untruncated normal distribution
|
||||
openmc::Normal normal_unbounded(0.0, 1.0);
|
||||
|
||||
// Check PDF at mean (should be 1/sqrt(2*pi) ≈ 0.3989)
|
||||
REQUIRE_THAT(
|
||||
normal_unbounded.evaluate(0.0), Catch::Matchers::WithinRel(0.3989, 0.001));
|
||||
|
||||
// Check that it's not truncated
|
||||
REQUIRE_FALSE(normal_unbounded.is_truncated());
|
||||
|
||||
// Check accessors
|
||||
REQUIRE(normal_unbounded.mean_value() == 0.0);
|
||||
REQUIRE(normal_unbounded.std_dev() == 1.0);
|
||||
REQUIRE(normal_unbounded.lower() == -openmc::INFTY);
|
||||
REQUIRE(normal_unbounded.upper() == openmc::INFTY);
|
||||
}
|
||||
|
||||
TEST_CASE("Test truncated Normal distribution")
|
||||
{
|
||||
// Create a truncated normal: mean=0, std=1, bounds=[-1, 1]
|
||||
openmc::Normal normal_truncated(0.0, 1.0, -1.0, 1.0);
|
||||
|
||||
// Check that it's truncated
|
||||
REQUIRE(normal_truncated.is_truncated());
|
||||
|
||||
// Check accessors
|
||||
REQUIRE(normal_truncated.lower() == -1.0);
|
||||
REQUIRE(normal_truncated.upper() == 1.0);
|
||||
|
||||
// PDF should be zero outside bounds
|
||||
REQUIRE(normal_truncated.evaluate(-2.0) == 0.0);
|
||||
REQUIRE(normal_truncated.evaluate(2.0) == 0.0);
|
||||
|
||||
// PDF inside bounds should be higher than untruncated (due to
|
||||
// renormalization)
|
||||
openmc::Normal normal_unbounded(0.0, 1.0);
|
||||
REQUIRE(normal_truncated.evaluate(0.0) > normal_unbounded.evaluate(0.0));
|
||||
|
||||
// The truncated PDF at mean should be approximately 0.3989 / 0.6827 ≈ 0.584
|
||||
// (0.6827 is the probability mass of N(0,1) in [-1,1])
|
||||
REQUIRE_THAT(
|
||||
normal_truncated.evaluate(0.0), Catch::Matchers::WithinRel(0.584, 0.01));
|
||||
}
|
||||
|
||||
TEST_CASE("Test truncated Normal sampling")
|
||||
{
|
||||
constexpr int n_samples = 10000;
|
||||
openmc::Normal normal_truncated(0.0, 1.0, -1.0, 1.0);
|
||||
uint64_t seed = openmc::init_seed(0, 0);
|
||||
|
||||
// Sample and verify all samples are within bounds
|
||||
for (int i = 0; i < n_samples; ++i) {
|
||||
auto [x, w] = normal_truncated.sample(&seed);
|
||||
REQUIRE(x >= -1.0);
|
||||
REQUIRE(x <= 1.0);
|
||||
REQUIRE(w == 1.0); // Unbiased sampling should have weight 1
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test one-sided truncated Normal")
|
||||
{
|
||||
// Test lower-bounded only (positive half-normal)
|
||||
openmc::Normal lower_bounded(0.0, 1.0, 0.0, openmc::INFTY);
|
||||
REQUIRE(lower_bounded.is_truncated());
|
||||
REQUIRE(lower_bounded.evaluate(-1.0) == 0.0);
|
||||
REQUIRE(lower_bounded.evaluate(1.0) > 0.0);
|
||||
|
||||
// PDF at 0 should be approximately 2 * 0.3989 ≈ 0.798 (half-normal)
|
||||
REQUIRE_THAT(
|
||||
lower_bounded.evaluate(0.0), Catch::Matchers::WithinRel(0.798, 0.01));
|
||||
|
||||
// Test upper-bounded only
|
||||
openmc::Normal upper_bounded(0.0, 1.0, -openmc::INFTY, 0.0);
|
||||
REQUIRE(upper_bounded.is_truncated());
|
||||
REQUIRE(upper_bounded.evaluate(1.0) == 0.0);
|
||||
REQUIRE(upper_bounded.evaluate(-1.0) > 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE("Test Normal XML constructor with truncation")
|
||||
{
|
||||
// XML doc node for truncated Normal
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_node energy = doc.append_child("energy");
|
||||
energy.append_child("type")
|
||||
.append_child(pugi::node_pcdata)
|
||||
.set_value("normal");
|
||||
energy.append_child("parameters")
|
||||
.append_child(pugi::node_pcdata)
|
||||
.set_value("1.0e6 1.0e5 0.8e6 1.2e6");
|
||||
|
||||
openmc::Normal dist(energy);
|
||||
REQUIRE(dist.mean_value() == 1.0e6);
|
||||
REQUIRE(dist.std_dev() == 1.0e5);
|
||||
REQUIRE(dist.lower() == 0.8e6);
|
||||
REQUIRE(dist.upper() == 1.2e6);
|
||||
REQUIRE(dist.is_truncated());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue