Skip to content

Commit

Permalink
Complete ORANGE construction from CSG objects (celeritas-project#1166)
Browse files Browse the repository at this point in the history
* Add input builder class
* Add utility typedefs for CSG objects
* Add unit proto construction
* Improve warning messages by adding md before region, downgrade to debug
* Add missing 'patty'
* Fix loading of multiple geometry from same filename
* Fix crashing safety calculation at the center of a sphere/cyl
* Use infinity instead of zero to give more realistic rather than overconservative behavior
  • Loading branch information
sethrj authored Apr 2, 2024
1 parent 1ecda17 commit 6d3aada
Show file tree
Hide file tree
Showing 21 changed files with 2,221 additions and 70 deletions.
2 changes: 2 additions & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ list(APPEND SOURCES
orangeinp/CsgTree.cc
orangeinp/CsgTypes.cc
orangeinp/CsgTreeUtils.cc
orangeinp/ProtoInterface.cc
orangeinp/Shape.cc
orangeinp/Solid.cc
orangeinp/Transformed.cc
Expand All @@ -39,6 +40,7 @@ list(APPEND SOURCES
orangeinp/detail/BuildConvexRegion.cc
orangeinp/detail/ConvexSurfaceState.cc
orangeinp/detail/CsgUnitBuilder.cc
orangeinp/detail/InputBuilder.cc
orangeinp/detail/InternalSurfaceFlagger.cc
orangeinp/detail/LocalSurfaceInserter.cc
orangeinp/detail/NodeSimplifier.cc
Expand Down
9 changes: 4 additions & 5 deletions src/orange/orangeinp/CsgObject.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ void JoinObjects<Op>::output(JsonPimpl* j) const
*/
std::shared_ptr<AllObjects const>
make_subtraction(std::string&& label,
std::shared_ptr<ObjectInterface const> const& minuend,
std::shared_ptr<ObjectInterface const> const& subtrahend)
SPConstObject const& minuend,
SPConstObject const& subtrahend)
{
CELER_EXPECT(!label.empty());
CELER_EXPECT(minuend && subtrahend);
Expand All @@ -146,9 +146,8 @@ make_subtraction(std::string&& label,
* The Region Definition Vector is the SCALE way for defining media,
* boundaries, etc. It must not be empty.
*/
std::shared_ptr<AllObjects const> make_rdv(
std::string&& label,
std::vector<std::pair<Sense, std::shared_ptr<ObjectInterface const>>>&& inp)
std::shared_ptr<AllObjects const>
make_rdv(std::string&& label, VecSenseObj&& inp)
{
CELER_EXPECT(!label.empty());
CELER_EXPECT(!inp.empty());
Expand Down
14 changes: 9 additions & 5 deletions src/orange/orangeinp/CsgObject.hh
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,24 @@ using AnyObjects = JoinObjects<op_or>;
//! Intersection of the given objects
using AllObjects = JoinObjects<op_and>;

//! Shared pointer to an object
using SPConstObject = std::shared_ptr<ObjectInterface const>;
//! Type used for defining a Region Definition Vector (RDV)
using VecSenseObj = std::vector<std::pair<Sense, SPConstObject>>;

//---------------------------------------------------------------------------//
// FREE FUNCTIONS
//---------------------------------------------------------------------------//

// Make a new object that is the second object subtracted from the first
std::shared_ptr<AllObjects const>
make_subtraction(std::string&& label,
std::shared_ptr<ObjectInterface const> const& minuend,
std::shared_ptr<ObjectInterface const> const& subtrahend);
SPConstObject const& minuend,
SPConstObject const& subtrahend);

// Make a combination of possibly negated objects
std::shared_ptr<AllObjects const> make_rdv(
std::string&& label,
std::vector<std::pair<Sense, std::shared_ptr<ObjectInterface const>>>&&);
std::shared_ptr<AllObjects const>
make_rdv(std::string&& label, VecSenseObj&& rdv);

//---------------------------------------------------------------------------//
} // namespace orangeinp
Expand Down
37 changes: 37 additions & 0 deletions src/orange/orangeinp/ProtoInterface.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/orangeinp/ProtoInterface.cc
//---------------------------------------------------------------------------//
#include "ProtoInterface.hh"

#include "detail/InputBuilder.hh"

namespace celeritas
{
namespace orangeinp
{
//---------------------------------------------------------------------------//
/*!
* Construct all universes.
*/
OrangeInput build_input(Tolerance<> const& tol, ProtoInterface const& global)
{
OrangeInput result;
detail::ProtoMap const protos{global};
CELER_ASSERT(protos.find(&global) == orange_global_universe);
detail::InputBuilder builder(&result, tol, protos);
for (auto uid : range(UniverseId{protos.size()}))
{
protos.at(uid)->build(builder);
}

CELER_ENSURE(result);
return result;
}

//---------------------------------------------------------------------------//
} // namespace orangeinp
} // namespace celeritas
7 changes: 7 additions & 0 deletions src/orange/orangeinp/ProtoInterface.hh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

namespace celeritas
{
//---------------------------------------------------------------------------//
struct OrangeInput;

namespace orangeinp
{
class ObjectInterface;
Expand Down Expand Up @@ -69,6 +72,10 @@ class ProtoInterface
//!@}
};

//---------------------------------------------------------------------------//
// Construct an ORANGE input from a global proto-universe
OrangeInput build_input(Tolerance<> const& tol, ProtoInterface const& global);

//---------------------------------------------------------------------------//
} // namespace orangeinp
} // namespace celeritas
185 changes: 178 additions & 7 deletions src/orange/orangeinp/UnitProto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "UnitProto.hh"

#include <algorithm>
#include <numeric>

#include "orange/OrangeData.hh"
#include "orange/OrangeInput.hh"
Expand All @@ -19,6 +20,9 @@

#include "detail/CsgUnit.hh"
#include "detail/CsgUnitBuilder.hh"
#include "detail/InputBuilder.hh"
#include "detail/InternalSurfaceFlagger.hh"
#include "detail/PostfixLogicBuilder.hh"
#include "detail/VolumeBuilder.hh"

namespace celeritas
Expand Down Expand Up @@ -89,13 +93,180 @@ auto UnitProto::daughters() const -> VecProto
* Construction is done from highest masking precedence to lowest (reverse
* zorder): exterior, then holes, then arrays, then media.
*/
void UnitProto::build(InputBuilder&) const
void UnitProto::build(InputBuilder& input) const
{
// Transform CsgUnit to OrangeInput
// - Map universe IDs (index in daughter list to actual universe ID)
// - Remap surface indices, removing unused surfaces
// - Set up "interior" cell if needed (build volume and "all surfaces")
CELER_NOT_IMPLEMENTED("global builder");
// Build CSG unit
auto csg_unit = this->build(input.tol(),
input.next_id() == orange_global_universe
? ExteriorBoundary::is_global
: ExteriorBoundary::is_daughter);
CELER_ASSERT(csg_unit);

// Get the list of all surfaces actually used
auto const sorted_local_surfaces = calc_surfaces(csg_unit.tree);
bool const has_background = csg_unit.background != MaterialId{};

UnitInput result;
result.label = input_.label;

// Save unit's bounding box (inverted bounding zone of exterior)
{
NodeId node_id = csg_unit.volumes[orange_exterior_volume.get()];
auto region_iter = csg_unit.regions.find(node_id);
CELER_ASSERT(region_iter != csg_unit.regions.end());
auto const& bz = region_iter->second.bounds;
if (bz.negated)
{
result.bbox = bz.interior;
}
}

// Save surfaces
result.surfaces.reserve(sorted_local_surfaces.size());
for (auto const& lsid : sorted_local_surfaces)
{
result.surfaces.emplace_back(csg_unit.surfaces[lsid.get()]);
}

// Save surface labels
result.surface_labels.resize(result.surfaces.size());
for (auto node_id : range(NodeId{csg_unit.tree.size()}))
{
if (auto* surf_node = std::get_if<Surface>(&csg_unit.tree[node_id]))
{
LocalSurfaceId old_lsid = surf_node->id;
auto idx = static_cast<size_type>(
find_sorted(sorted_local_surfaces.begin(),
sorted_local_surfaces.end(),
old_lsid)
- sorted_local_surfaces.begin());
CELER_ASSERT(idx < result.surface_labels.size());

// NOTE: surfaces may be created more than once. Our primitive
// "input" allows association with only one surface, so we'll
// arbitrarily choose the lexicographically sorted "first" surface
// name in the list.
CELER_ASSERT(!csg_unit.metadata[node_id.get()].empty());
auto const& label = *csg_unit.metadata[node_id.get()].begin();
result.surface_labels[idx] = label;
}
}

// Loop over all volumes to construct
detail::PostfixLogicBuilder build_logic{csg_unit.tree,
sorted_local_surfaces};
detail::InternalSurfaceFlagger has_internal_surfaces{csg_unit.tree};
result.volumes.reserve(csg_unit.volumes.size() + has_background);

for (auto vol_idx : range(csg_unit.volumes.size()))
{
NodeId node_id = csg_unit.volumes[vol_idx];
VolumeInput vi;

// Construct logic and faces with remapped surfaces
auto&& [faces, logic] = build_logic(node_id);
vi.faces = std::move(faces);
vi.logic = std::move(logic);

// Set bounding box
auto region_iter = csg_unit.regions.find(node_id);
CELER_ASSERT(region_iter != csg_unit.regions.end());
vi.bbox = get_exterior_bbox(region_iter->second.bounds);
/* TODO: "simple safety" flag is set inside
* "unit inserter" (move here once we stop importing from SCALE via
* OrangeInput)
*/
if (has_internal_surfaces(node_id))
{
vi.flags |= VolumeRecord::internal_surfaces;
}

vi.zorder = ZOrder::media;
result.volumes.emplace_back(std::move(vi));
}

if (has_background)
{
// "Background" should be unreachable: 'nowhere' logic, null bbox
// but it has to have all the surfaces that connect to an interior
// volume
VolumeInput vi;
vi.faces.resize(sorted_local_surfaces.size());
std::iota(vi.faces.begin(), vi.faces.end(), LocalSurfaceId{0});
vi.logic = {logic::ltrue, logic::lnot};
vi.bbox = {}; // XXX: input converter changes to infinite bbox
vi.zorder = ZOrder::background;
vi.flags = VolumeRecord::implicit_vol;
result.volumes.emplace_back(std::move(vi));
}
CELER_ASSERT(result.volumes.size()
== csg_unit.volumes.size() + has_background);

// Set labels and other attributes.
// NOTE: this means we're entirely ignoring the "metadata" from the CSG
// nodes for the region, because we can't know which ones have the
// user-supplied volume names
// TODO: add JSON output to the input builder that includes the CSG
// metadata
auto vol_iter = result.volumes.begin();

// Save attributes for exterior volume
if (input.next_id() != orange_global_universe)
{
vol_iter->zorder = ZOrder::implicit_exterior;
vol_iter->flags |= VolumeRecord::implicit_vol;
}
else
{
vol_iter->zorder = input_.boundary.zorder;
}
vol_iter->label = {"[EXTERIOR]", input_.label};
++vol_iter;

for (auto const& d : input_.daughters)
{
LocalVolumeId const vol_id{
static_cast<size_type>(vol_iter - result.volumes.begin())};

// Save daughter volume attributes
vol_iter->label
= Label{std::string{d.fill->label()}, std::string{this->label()}};
vol_iter->zorder = d.zorder;
/* TODO: the "embedded_universe" flag is *also* set by the unit
* builder. Move that here. */
++vol_iter;

// Add daughter to map
auto&& [iter, inserted] = result.daughter_map.insert({vol_id, {}});
CELER_ASSERT(inserted);
// Convert proto pointer to universe ID
iter->second.universe_id = input.find_universe_id(d.fill.get());

// Save the transform
auto const* fill = std::get_if<Daughter>(&csg_unit.fills[vol_id.get()]);
CELER_ASSERT(fill);
auto transform_id = fill->transform_id;
CELER_ASSERT(transform_id < csg_unit.transforms.size());
iter->second.transform = csg_unit.transforms[transform_id.get()];
}

// Save attributes from materials
for (auto const& m : input_.materials)
{
vol_iter->label = std::string{m.interior->label()};
vol_iter->zorder = ZOrder::media;
++vol_iter;
}

if (input_.fill)
{
vol_iter->label = {input_.label, "bg"};
++vol_iter;
}
CELER_EXPECT(vol_iter == result.volumes.end());

// TODO: save material IDs as well
input.insert(std::move(result));
}

//---------------------------------------------------------------------------//
Expand Down Expand Up @@ -140,7 +311,7 @@ auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit
CELER_NOT_IMPLEMENTED("volume masking using different z orders");
}
auto lv = build_volume(*d.make_interior());
unit_builder.fill_volume(lv, daughter_id++);
unit_builder.fill_volume(lv, daughter_id++, d.transform);
}

// Build materials
Expand Down
2 changes: 1 addition & 1 deletion src/orange/orangeinp/detail/CsgUnit.hh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace detail
* reference frame, not relative to any other CSG node nor to any parent
* universe. (TODO: add bounds and transforms only for finite regions)
*
* TODO: map of SP object to detailed provenance?
* TODO (?) map nodes to set of object pointers, for detailed provenance?
*/
struct CsgUnit
{
Expand Down
Loading

0 comments on commit 6d3aada

Please sign in to comment.