diff --git a/include/openmc/cell.h b/include/openmc/cell.h index 9dfe2339ab6..c708f6823bb 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -106,6 +106,9 @@ class Region { //! Get a vector of the region expression in postfix notation vector generate_postfix(int32_t cell_id) const; + //! Get a vector of the region expression in infix notation + vector generate_infix(vector rpn) const; + //! Determine if a particle is inside the cell for a simple cell (only //! intersection operators) bool contains_simple(Position r, Direction u, int32_t on_surface) const; diff --git a/src/cell.cpp b/src/cell.cpp index fd459cdcc70..03d19fa2526 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -673,6 +673,10 @@ Region::Region(std::string region_spec, int32_t cell_id) it--; } } + } else { + // Simplify expression by going to rpn and back + auto postfix = generate_postfix(cell_id); + expression_ = generate_infix(postfix); } expression_.shrink_to_fit(); @@ -879,6 +883,61 @@ vector Region::generate_postfix(int32_t cell_id) const return rpn; } +//============================================================================== +//! Convert Reverse Polish Notation (RPN) to infix using minimal parentheses. +//============================================================================== + +vector Region::generate_infix(vector rpn) const +{ + + vector> stack; + vector pstack; + std::unordered_map precedence = { + {OP_UNION, 1}, {OP_INTERSECTION, 2}, {OP_COMPLEMENT, 3}}; + + int HIGH = 4; + + for (auto it = rpn.begin(); it != rpn.end(); ++it) { + auto token = *it; + if (precedence.find(token) != precedence.end()) { + auto p_c = precedence[token]; + + auto v_r = stack.back(); + stack.pop_back(); + auto p_r = pstack.back(); + pstack.pop_back(); + + auto v_l = stack.back(); + stack.pop_back(); + auto p_l = pstack.back(); + pstack.pop_back(); + + if (p_l != p_c && p_l != HIGH) { + v_l.emplace(v_l.begin(), OP_LEFT_PAREN); + v_l.emplace_back(OP_RIGHT_PAREN); + } + v_l.emplace_back(token); + + if ((p_r != p_c && p_r != HIGH) || + ((p_r == p_c) && (token == OP_COMPLEMENT))) { + v_r.emplace(v_r.begin(), OP_LEFT_PAREN); + v_r.emplace_back(OP_RIGHT_PAREN); + } + + vector merged = v_l; + merged.reserve(v_l.size() + v_r.size()); + merged.insert(merged.end(), v_r.begin(), v_r.end()); + + stack.emplace_back(merged); + pstack.emplace_back(p_c); + } else { + stack.emplace_back(vector {token}); + pstack.emplace_back(HIGH); + } + } + return stack.back(); +} + //============================================================================== std::string Region::str() const diff --git a/tests/cpp_unit_tests/CMakeLists.txt b/tests/cpp_unit_tests/CMakeLists.txt index 5f87db9eac2..20341b420f8 100644 --- a/tests/cpp_unit_tests/CMakeLists.txt +++ b/tests/cpp_unit_tests/CMakeLists.txt @@ -6,6 +6,7 @@ set(TEST_NAMES test_math test_mcpl_stat_sum test_mesh + test_region # Add additional unit test files here ) diff --git a/tests/cpp_unit_tests/test_region.cpp b/tests/cpp_unit_tests/test_region.cpp new file mode 100644 index 00000000000..f4ae330b100 --- /dev/null +++ b/tests/cpp_unit_tests/test_region.cpp @@ -0,0 +1,27 @@ +#include + +#include "openmc/cell.h" +#include "openmc/surface.h" + +#include + +TEST_CASE("Test region simplification") +{ + pugi::xml_document doc; + pugi::xml_node surf_node = doc.append_child("surface"); + surf_node.set_name("surface"); + surf_node.append_attribute("id") = "0"; + surf_node.append_attribute("type") = "x-plane"; + surf_node.append_attribute("coeffs") = "1"; + + for (int i = 1; i < 7; ++i) { + surf_node.attribute("id") = i; + openmc::model::surfaces.push_back( + std::make_unique(surf_node)); + openmc::model::surface_map[i] = i - 1; + } + auto region = openmc::Region("(-1 2 (-3 4) | (-5 6))", 0); + auto ref_val = " ( -1 2 -3 4 ) | ( -5 6 )"; + auto test_val = region.str(); + REQUIRE(test_val == ref_val); +}