diff --git a/src/orange/orangeinp/CsgTreeUtils.cc b/src/orange/orangeinp/CsgTreeUtils.cc index 4a13453467..7bea1d3c63 100644 --- a/src/orange/orangeinp/CsgTreeUtils.cc +++ b/src/orange/orangeinp/CsgTreeUtils.cc @@ -173,7 +173,7 @@ void simplify(CsgTree* tree, NodeId start) * This is required if the tree's logic expression is used with * \c InfixEvaluator as negated joins are not supported. */ -CsgTree transform_negated_joins(CsgTree const& tree) +DeMorganSimplifierResult transform_negated_joins(CsgTree const& tree) { return detail::DeMorganSimplifier{tree}(); } diff --git a/src/orange/orangeinp/CsgTreeUtils.hh b/src/orange/orangeinp/CsgTreeUtils.hh index e2ffe7aa76..fd749f3138 100644 --- a/src/orange/orangeinp/CsgTreeUtils.hh +++ b/src/orange/orangeinp/CsgTreeUtils.hh @@ -11,6 +11,7 @@ #include #include "orange/OrangeTypes.hh" +#include "orange/orangeinp/detail/DeMorganSimplifier.hh" #include "CsgTypes.hh" @@ -33,7 +34,8 @@ orangeinp::NodeId simplify_up(CsgTree* tree, orangeinp::NodeId start); void simplify(CsgTree* tree, orangeinp::NodeId start); // Replace ~&(xs...) with |(~xs...) and ~|(xs...) with &(~xs...) -[[nodiscard]] CsgTree transform_negated_joins(CsgTree const& tree); +[[nodiscard]] DeMorganSimplifierResult +transform_negated_joins(CsgTree const& tree); // Transform a CSG node into a string expression [[nodiscard]] std::string diff --git a/src/orange/orangeinp/UnitProto.cc b/src/orange/orangeinp/UnitProto.cc index 03f13abb90..817d272ae5 100644 --- a/src/orange/orangeinp/UnitProto.cc +++ b/src/orange/orangeinp/UnitProto.cc @@ -457,7 +457,7 @@ auto UnitProto::build(Tol const& tol, BBox const& bbox) const -> Unit ", ", write_node_labels); } - + unit_builder.simplifiy_joins(); /*! \todo We can sometimes eliminate CSG surfaces and nodes that aren't * used by the actual volumes> */ diff --git a/src/orange/orangeinp/detail/CsgUnitBuilder.cc b/src/orange/orangeinp/detail/CsgUnitBuilder.cc index a356da6afa..468610da98 100644 --- a/src/orange/orangeinp/detail/CsgUnitBuilder.cc +++ b/src/orange/orangeinp/detail/CsgUnitBuilder.cc @@ -11,6 +11,7 @@ #include "corecel/io/Logger.hh" #include "corecel/io/StreamableVariant.hh" #include "orange/OrangeData.hh" +#include "orange/orangeinp/CsgTreeUtils.hh" #include "orange/transform/TransformIO.hh" #include "orange/transform/TransformSimplifier.hh" @@ -193,6 +194,35 @@ void CsgUnitBuilder::fill_volume(LocalVolumeId v, CELER_ENSURE(is_filled(unit_->fills[v.unchecked_get()])); } +//---------------------------------------------------------------------------// +/*! + * Simplify negated joins for Infix evaluation + */ +void CsgUnitBuilder::simplifiy_joins() +{ + auto& tree = unit_->tree; + auto simplification = transform_negated_joins(tree); + CELER_EXPECT(tree.size() == simplification.equivalent_nodes.size()); + std::vector> md; + md.resize(simplification.tree.size()); + + std::map regions; + + for (auto node_id : range(tree.size())) + { + if (auto equivalent_node = simplification.equivalent_nodes[node_id]; + equivalent_node) + { + CELER_EXPECT(equivalent_node < md.size()); + md[equivalent_node.unchecked_get()] = unit_->metadata[node_id]; + regions[equivalent_node] = unit_->regions[NodeId{node_id}]; + } + } + unit_->metadata = std::move(md); + unit_->regions = std::move(regions); + unit_->tree = std::move(simplification.tree); +} + //---------------------------------------------------------------------------// /*! * Get a variant surface from a node ID. diff --git a/src/orange/orangeinp/detail/CsgUnitBuilder.hh b/src/orange/orangeinp/detail/CsgUnitBuilder.hh index 81d8afb9d9..665aae4142 100644 --- a/src/orange/orangeinp/detail/CsgUnitBuilder.hh +++ b/src/orange/orangeinp/detail/CsgUnitBuilder.hh @@ -107,6 +107,9 @@ class CsgUnitBuilder void fill_volume(LocalVolumeId, UniverseId, VariantTransform const& transform); + // Simplify negated joins for Infix evaluation + void simplifiy_joins(); + private: CsgUnit* unit_; Tol tol_; diff --git a/src/orange/orangeinp/detail/DeMorganSimplifier.cc b/src/orange/orangeinp/detail/DeMorganSimplifier.cc index d95b6ada8d..8e11f1bbc6 100644 --- a/src/orange/orangeinp/detail/DeMorganSimplifier.cc +++ b/src/orange/orangeinp/detail/DeMorganSimplifier.cc @@ -88,10 +88,18 @@ DeMorganSimplifier::DeMorganSimplifier(CsgTree const& tree) * Perform the simplification. The state of the instance isn't cleared, so only * call this once. */ -CsgTree DeMorganSimplifier::operator()() +DeMorganSimplifierResult DeMorganSimplifier::operator()() { this->find_join_negations(); - return this->build_simplified_tree(); + auto simplified_tree{this->build_simplified_tree()}; + std::vector equivalent_nodes; + equivalent_nodes.reserve(tree_.size()); + for (auto node_id : range(tree_.size())) + { + equivalent_nodes.push_back( + node_ids_translation_[node_id].equivalent_node()); + } + return {simplified_tree, equivalent_nodes}; } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/detail/DeMorganSimplifier.hh b/src/orange/orangeinp/detail/DeMorganSimplifier.hh index 15800cc1ac..82d55267e9 100644 --- a/src/orange/orangeinp/detail/DeMorganSimplifier.hh +++ b/src/orange/orangeinp/detail/DeMorganSimplifier.hh @@ -17,6 +17,17 @@ namespace celeritas { namespace orangeinp { +//---------------------------------------------------------------------------// +/*! + * Result of a DeMorgan simplification. + */ +struct DeMorganSimplifierResult +{ + CsgTree tree; + std::vector equivalent_nodes; +}; + +//---------------------------------------------------------------------------// namespace detail { //---------------------------------------------------------------------------// @@ -46,7 +57,7 @@ class DeMorganSimplifier explicit DeMorganSimplifier(CsgTree const&); // Perform the simplification - CsgTree operator()(); + DeMorganSimplifierResult operator()(); private: //! CsgTree node 0 is always True{} and can't be the parent of any node diff --git a/test/orange/orangeinp/CsgTreeUtils.test.cc b/test/orange/orangeinp/CsgTreeUtils.test.cc index ba9f91e3cb..1d7290f4f6 100644 --- a/test/orange/orangeinp/CsgTreeUtils.test.cc +++ b/test/orange/orangeinp/CsgTreeUtils.test.cc @@ -391,7 +391,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) auto s1 = this->insert(Surface{S{1}}); auto n0 = this->insert(Negated{s1}); auto j0 = this->insert(Joined{op_and, {s0, n0}}); - auto simplified = transform_negated_joins(tree_); + auto simplified = transform_negated_joins(tree_).tree; // Check a well-formed tree EXPECT_EQ( @@ -414,7 +414,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Check an easy case with just a single negated operand - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "any{3,4}, }", @@ -429,7 +429,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) // Check that the non-negated operand maps to correct new node_ids and // that not{2} is not deleted - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "not{4}, 6: any{3,4}, 7: any{2,5}, }", @@ -443,7 +443,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Check that the two operands are transformed, removing dangling operators - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "any{3,4}, 6: all{3,4}, }", @@ -459,7 +459,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Check that disjoint trees are correctly handled - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "any{3,4}, 6: all{3,4}, 7: surface 2, 8: not{7}, }", @@ -474,7 +474,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Add a non-transformed operand with suboperands - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "not{4}, 6: any{3,4}, 7: all{2,5}, 8: all{3,4}, 9: any{2,5}, 10: " @@ -491,7 +491,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) // Top-level operand is negated and should be simplified, no need to // duplicate intermediary Joined nodes - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "any{3,4}, 6: all{3,4}, 7: surface 2, 8: not{7}, 9: any{5,6}, }", @@ -506,7 +506,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Top-level joined has Negated{Joined{}} chldrens - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "any{3,4}, 6: all{3,4}, 7: surface 2, 8: not{7}, 9: any{5,6}, 10: " @@ -522,7 +522,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Complex case with a negated join with negated join as children - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "not{4}, 6: any{3,4}, 7: all{2,5}, 8: all{3,4}, 9: any{2,5}, 10: " @@ -549,7 +549,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) to_string(tree_)); // Complex case with a negated join with negated children - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: surface 1, 4: all{2,3}, 5: " "surface 2, 6: not{5}, 7: all{4,6}, }", @@ -567,7 +567,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins) "{0: true, 1: not{0}, 2: surface 0, 3: surface 1, 4: not{2}, 5: " "not{3}, 6: all{4,5}, 7: any{4,5}, 8: not{7}, }", to_string(tree_)); - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: surface 1, 4: not{2}, 5: " "not{3}, 6: all{4,5}, 7: all{2,3}, }", @@ -596,7 +596,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins_with_volumes) to_string(tree_)); // Complex case with a negated join with negated children - auto simplified = transform_negated_joins(tree_); + auto simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: surface 1, 4: not{2}, 5: " "not{3}, 6: all{2,3}, 7: any{4,5}, 8: surface 2, 9: not{8}, 10: " @@ -625,7 +625,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins_with_volumes) this->insert(Joined{op_and, {bdy_outer, mz, below_pz}}); this->insert(Joined{op_and, {mz, below_pz}}); tree_.insert_volume(inner_cyl); - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "not{4}, 6: surface 2, 7: not{6}, 8: any{3,4,6}, 9: all{2,5,7}, 10: " @@ -687,7 +687,7 @@ TEST_F(CsgTreeUtilsTest, transform_negated_joins_with_volumes) to_string(tree_)); tree_.insert_volume(N{16}); - simplified = transform_negated_joins(tree_); + simplified = transform_negated_joins(tree_).tree; EXPECT_EQ( "{0: true, 1: not{0}, 2: surface 0, 3: not{2}, 4: surface 1, 5: " "not{4}, 6: surface 2, 7: not{6}, 8: any{3,4,6}, 9: all{2,5,7}, 10: "