Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete ORANGE construction from CSG objects #1166

Merged
merged 16 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 an RDV
sethrj marked this conversation as resolved.
Show resolved Hide resolved
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
Loading