Skip to content

Commit

Permalink
Construct proto-universe hierarchy from converted volumes (#1179)
Browse files Browse the repository at this point in the history
* Update pointer testing utils
* Implement proto constructor
* Add manual name construction to unit proto
* Add interior box test
* Construct 'fill' cells explicitly when there are 2 or less daughters
* Clear isotopes and elements when resetting geometry
* Turn on simple safety for orangeinp-created background
  • Loading branch information
sethrj committed Apr 16, 2024
1 parent 5646bd3 commit bbd64da
Show file tree
Hide file tree
Showing 18 changed files with 961 additions and 112 deletions.
27 changes: 27 additions & 0 deletions src/geocel/GeantGeoUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
//---------------------------------------------------------------------------//
#include "GeantGeoUtils.hh"

#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <unordered_set>
#include <G4Element.hh>
#include <G4GDMLParser.hh>
#include <G4GDMLWriteStructure.hh>
#include <G4Isotope.hh>
#include <G4LogicalVolume.hh>
#include <G4LogicalVolumeStore.hh>
#include <G4Material.hh>
#include <G4PhysicalVolumeStore.hh>
#include <G4ReflectionFactory.hh>
#include <G4RegionStore.hh>
Expand Down Expand Up @@ -90,6 +94,25 @@ load_geant_geometry_impl(std::string const& filename, bool strip_pointer_ext)
return result;
}

//---------------------------------------------------------------------------//
/*!
* Free all pointers in a table.
*
* Geant4 requires "new"ing and *not* "delete"ing classes whose new/delete
* operators modify an entry in a global table.
*/
template<class T>
void free_and_clear(std::vector<T*>* table)
{
for (auto* ptr : *table)
{
delete ptr;
}
CELER_ASSERT(std::all_of(
table->begin(), table->end(), [](T* ptr) { return ptr == nullptr; }));
table->clear();
}

//---------------------------------------------------------------------------//
} // namespace

Expand Down Expand Up @@ -191,6 +214,10 @@ void reset_geant_geometry()
#if G4VERSION_NUMBER >= 1100
G4ReflectionFactory::Instance()->Clean();
#endif
free_and_clear(G4Material::GetMaterialTable());
free_and_clear(G4Element::GetElementTable());
free_and_clear(const_cast<std::vector<G4Isotope*>*>(
G4Isotope::GetIsotopeTable()));
msg = scoped_log.str();
}
if (!msg.empty())
Expand Down
1 change: 1 addition & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double")
set( _cg4org_sources
g4org/LogicalVolumeConverter.cc
g4org/PhysicalVolumeConverter.cc
g4org/ProtoConstructor.cc
g4org/SolidConverter.cc
)
celeritas_get_g4libs(_cg4org_libs geometry)
Expand Down
3 changes: 2 additions & 1 deletion src/orange/detail/UnitInserter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ VolumeRecord UnitInserter::insert_volume(SurfacesRecord const& surf_record,
std::end(nowhere_logic)));
CELER_EXPECT(!v.bbox);
CELER_EXPECT(v.flags & VolumeRecord::implicit_vol);
simple_safety = false;
// Rely on incoming flags for "simple_safety": false from .org.json,
// maybe true if built from GDML
}

VolumeRecord output;
Expand Down
223 changes: 223 additions & 0 deletions src/orange/g4org/ProtoConstructor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
//----------------------------------*-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/g4org/ProtoConstructor.cc
//---------------------------------------------------------------------------//
#include "ProtoConstructor.hh"

#include <iostream>

#include "corecel/io/StreamableVariant.hh"
#include "orange/orangeinp/CsgObject.hh"
#include "orange/orangeinp/Transformed.hh"
#include "orange/transform/TransformIO.hh"

#include "Volume.hh"

namespace celeritas
{
namespace g4org
{
namespace
{
//---------------------------------------------------------------------------//
using SPConstObject = ProtoConstructor::SPConstObject;

//---------------------------------------------------------------------------//
/*!
* Construct an explicit "background" cell.
*/
SPConstObject make_explicit_background(LogicalVolume const& lv,
VariantTransform const& transform)
{
using namespace orangeinp;

std::vector<SPConstObject> children;
for (auto const& child_pv : lv.children)
{
auto child_transform = apply_transform(transform, child_pv.transform);
children.push_back(
Transformed::or_object(child_pv.lv->solid, child_transform));
}

return Transformed::or_object(
orangeinp::make_subtraction(
std::string{lv.name},
lv.solid,
orangeinp::AnyObjects::or_object(lv.name + ".children",
std::move(children))),
transform);
}

//---------------------------------------------------------------------------//
} // namespace

//---------------------------------------------------------------------------//
/*!
* Construct a proto-universe from a logical volume.
*/
auto ProtoConstructor::operator()(LogicalVolume const& lv) -> SPUnitProto
{
ProtoInput input;
input.boundary.interior = lv.solid;
input.label = lv.name;

if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << "* New proto: " << lv.name
<< " with shape " << to_string(*lv.solid) << std::endl;
}

// Add children first
for (PhysicalVolume const& pv : lv.children)
{
++depth_;
this->place_pv(NoTransformation{}, pv, &input);
--depth_;
}

// Heuristic: if LV has fewer than N daughters in the input, use an
// explicit background cell
if (lv.children.size() <= fill_daughter_threshold())
{
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " - explicit background"
<< std::endl;
}
// Create explicit "fill" for this logical volume
orangeinp::UnitProto::MaterialInput background;
background.interior = make_explicit_background(lv, NoTransformation{});
background.label = Label::from_geant(lv.name);
background.fill = lv.material_id;
input.boundary.zorder = ZOrder::media;
input.materials.push_back(std::move(background));
}
else
{
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " - implicit background"
<< std::endl;
}
input.background.fill = lv.material_id;
input.background.label = Label::from_geant(lv.name);
}

CELER_ENSURE(input);
return std::make_shared<orangeinp::UnitProto>(std::move(input));
}

//---------------------------------------------------------------------------//
/*!
* Place this physical volume into the proto.
*/
void ProtoConstructor::place_pv(VariantTransform const& parent_transform,
PhysicalVolume const& pv,
ProtoInput* proto)
{
CELER_EXPECT(proto);

using namespace orangeinp;

// Transform for this PV, whether as a "top level" volume or as a volume
// that's subtracted from an inlined LV
auto transform = apply_transform(parent_transform, pv.transform);

if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << "- Add pv " << pv.name
<< " use_count=" << pv.lv.use_count()
<< ", num_children=" << pv.lv->children.size() << ", at "
<< StreamableVariant{transform} << " to " << proto->label
<< std::endl;
}

auto add_material = [proto, &lv = *pv.lv](SPConstObject&& obj) {
CELER_EXPECT(obj);
UnitProto::MaterialInput mat;
mat.interior = std::move(obj);
mat.fill = lv.material_id;
mat.label = Label::from_geant(lv.name);
proto->materials.push_back(std::move(mat));
};

if (pv.lv->children.empty())
{
// No children! This LV is just a material.
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " -> "
<< "material at " << StreamableVariant{pv.transform}
<< std::endl;
std::clog << std::string(depth_, ' ') << " "
<< to_string(*pv.lv->solid) << std::endl;
}

add_material(
Transformed::or_object(pv.lv->solid, std::move(transform)));
}
else if (pv.lv.use_count() == 1)
{
// Child can be inlined into the parent because it's used only once
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " -> "
<< "inline the child at "
<< StreamableVariant{pv.transform} << std::endl;
}

// Subtract *its* children from this shape
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " : subtracted "
<< pv.lv->children.size() << " children from "
<< to_string(*pv.lv->solid) << std::endl;
}

add_material(make_explicit_background(*pv.lv, transform));

// Now build its daghters
++depth_;
for (auto const& child_pv : pv.lv->children)
{
// Note: place_pv incorporates child's transform
this->place_pv(transform, child_pv, proto);
}
--depth_;
}
else
{
// LV is referenced more than once *AND* has children
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " -> "
<< "new universe at " << StreamableVariant{pv.transform}
<< std::endl;
}

auto [iter, inserted] = protos_.insert({pv.lv.get(), nullptr});
if (inserted)
{
++depth_;
// Construct pv proto
iter->second = (*this)(*pv.lv);
--depth_;
}
CELER_ASSERT(iter->second);
proto->daughters.push_back({iter->second, transform, ZOrder::media});

if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " : "
<< "daughter shape is "
<< to_string(*proto->daughters.back().make_interior());
}
}
}

//---------------------------------------------------------------------------//
} // namespace g4org
} // namespace celeritas
68 changes: 68 additions & 0 deletions src/orange/g4org/ProtoConstructor.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//----------------------------------*-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/g4org/ProtoConstructor.hh
//---------------------------------------------------------------------------//
#pragma once

#include <memory>
#include <unordered_map>

#include "orange/orangeinp/ObjectInterface.hh"
#include "orange/orangeinp/UnitProto.hh"

#include "Volume.hh"

namespace celeritas
{
namespace g4org
{
//---------------------------------------------------------------------------//
/*!
* Recursively build ORANGE proto-universes from a \c LogicalVolume .
*
* The input to this function is the output of \c LogicalVolumeConverter . This
* class is responsible for "placing" the converted \c PhysicalVolume by
* transforming its children. Depending on heuristics, the children are
* directly inserted into a \c UnitProto as volumes (specifically, the logical
* volume becomes a \c UnitProto::MaterialInput), or a \c LogicalVolume is
* turned into a \em new \c UnitProto that can be used in multiple locations.
*/
class ProtoConstructor
{
public:
//!@{
//! \name Type aliases
using SPConstObject = std::shared_ptr<orangeinp::ObjectInterface const>;
using ObjLv = std::pair<SPConstObject, LogicalVolume const*>;
using SPUnitProto = std::shared_ptr<orangeinp::UnitProto>;
using ProtoInput = orangeinp::UnitProto::Input;
//!@}

public:
//! Construct with verbosity setting
explicit ProtoConstructor(bool verbose) : verbose_{verbose} {}

// Construct a proto from a logical volume
SPUnitProto operator()(LogicalVolume const& pv);

private:
std::unordered_map<LogicalVolume const*, SPUnitProto> protos_;
int depth_{0};
bool verbose_{false};

// Place a physical volume into the given unconstructed proto
void place_pv(VariantTransform const& parent_transform,
PhysicalVolume const& pv,
ProtoInput* proto);

// (TODO: make this configurable)
//! Number of daughters above which we use a "fill" material
static constexpr int fill_daughter_threshold() { return 2; }
};

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

#include "corecel/io/ScopedTimeLog.hh"
#include "corecel/sys/ScopedMem.hh"
#include "corecel/sys/ScopedProfiling.hh"

#include "detail/InputBuilder.hh"

namespace celeritas
Expand All @@ -19,6 +23,10 @@ namespace orangeinp
*/
OrangeInput build_input(Tolerance<> const& tol, ProtoInterface const& global)
{
ScopedProfiling profile_this{"build-orange-geo"};
ScopedMem record_mem("orangeinp::build_input");
ScopedTimeLog scoped_time;

OrangeInput result;
detail::ProtoMap const protos{global};
CELER_ASSERT(protos.find(&global) == orange_global_universe);
Expand Down

0 comments on commit bbd64da

Please sign in to comment.