Skip to content

Commit

Permalink
Complete GDML-to-ORANGE geometry converter (#1180)
Browse files Browse the repository at this point in the history
* Add geometry converter
* Add ORANGE geometry constructor
* Don't inline child if it's transformed
  • Loading branch information
sethrj committed Apr 17, 2024
1 parent e1c7aed commit fe26115
Show file tree
Hide file tree
Showing 17 changed files with 642 additions and 63 deletions.
8 changes: 0 additions & 8 deletions app/celer-g4/GlobalSetup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,6 @@ void GlobalSetup::ReadInput(std::string const& filename)
CELER_NOT_CONFIGURED("nlohmann_json");
#endif

// Input options
if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE)
{
// To allow ORANGE to work for testing purposes, pass the GDML
// input filename to Celeritas
options_->geometry_file = input_.geometry_file;
}

// Output options
options_->output_file = input_.output_file;
options_->physics_output_file = input_.physics_output_file;
Expand Down
7 changes: 1 addition & 6 deletions app/celer-g4/test-harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,7 @@ def strtobool(text):
j = json.loads(out_text)
except json.decoder.JSONDecodeError as e:
print(f"error ({e}): expected a JSON object but got the following stdout:")
with open(out_file, 'w') as f:
f.write(out_text)
print("Wrote text to", out_file)
if result.returncode:
print("fatal:", str(e))
exit(result.returncode)
print(out_text)
else:
with open(out_file, 'w') as f:
json.dump(j, f, indent=1)
Expand Down
2 changes: 0 additions & 2 deletions src/accel/detail/HitProcessor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ HitProcessor::HitProcessor(SPConstVecLV detector_volumes,
tracks_.back()->SetParentID(-1);
}

// Create secondary vector if using track data

// Convert logical volumes (global) to sensitive detectors (thread local)
CELER_LOG_LOCAL(debug) << "Setting up " << detector_volumes_->size()
<< " sensitive detectors";
Expand Down
1 change: 1 addition & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ endif()

if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double")
set( _cg4org_sources
g4org/Converter.cc
g4org/LogicalVolumeConverter.cc
g4org/PhysicalVolumeConverter.cc
g4org/ProtoConstructor.cc
Expand Down
64 changes: 43 additions & 21 deletions src/orange/OrangeParams.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
#include "corecel/io/ScopedTimeLog.hh"
#include "corecel/io/StringUtils.hh"
#include "geocel/BoundingBox.hh"
#include "geocel/GeantGeoUtils.hh"

#include "OrangeData.hh" // IWYU pragma: associated
#include "OrangeInput.hh"
#include "OrangeTypes.hh"
#include "g4org/Converter.hh"
#include "univ/detail/LogicStack.hh"

#include "detail/DepthCalculator.hh"
Expand All @@ -48,7 +50,7 @@ namespace
{
//---------------------------------------------------------------------------//
/*!
* Load a geometry from the given filename.
* Load a geometry from the given JSON file.
*/
OrangeInput input_from_json(std::string filename)
{
Expand All @@ -58,18 +60,6 @@ OrangeInput input_from_json(std::string filename)
CELER_LOG(info) << "Loading ORANGE geometry from JSON at " << filename;
ScopedTimeLog scoped_time;

if (ends_with(filename, ".gdml"))
{
CELER_LOG(warning) << "Using ORANGE geometry with GDML suffix: trying "
"`.org.json` instead";
filename.erase(filename.end() - 5, filename.end());
filename += ".org.json";
}
else if (!ends_with(filename, ".json"))
{
CELER_LOG(warning) << "Expected '.json' extension for JSON input";
}

OrangeInput result;

#if CELERITAS_USE_JSON
Expand All @@ -83,6 +73,40 @@ OrangeInput input_from_json(std::string filename)
return result;
}

//---------------------------------------------------------------------------//
/*!
* Load a geometry from the given filename.
*/
OrangeInput input_from_file(std::string filename)
{
if (ends_with(filename, ".gdml"))
{
if (CELERITAS_USE_GEANT4)
{
// Load with Geant4: must *not* be using run manager
auto* world = ::celeritas::load_geant_geometry_native(filename);
auto result = g4org::Converter{}(world).input;
::celeritas::reset_geant_geometry();
return result;
}
else
{
CELER_LOG(warning) << "Using ORANGE geometry with GDML suffix "
"when Geant4 is disabled: trying "
"`.org.json` instead";
filename.erase(filename.end() - 5, filename.end());
filename += ".org.json";
}
}
else
{
CELER_VALIDATE(ends_with(filename, ".json"),
<< "expected JSON extension for ORANGE input '"
<< filename << "'");
}
return input_from_json(std::move(filename));
}

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

Expand All @@ -93,20 +117,20 @@ OrangeInput input_from_json(std::string filename)
* The JSON format is defined by the SCALE ORANGE exporter (not currently
* distributed).
*/
OrangeParams::OrangeParams(std::string const& json_filename)
: OrangeParams(input_from_json(json_filename))
OrangeParams::OrangeParams(std::string const& filename)
: OrangeParams(input_from_file(filename))
{
}

//---------------------------------------------------------------------------//
/*!
* Construct in-memory from a Geant4 geometry (not implemented).
* Construct in-memory from a Geant4 geometry.
*
* Perhaps someday we'll implement in-memory translation...
* TODO: expose options? Fix volume mappings?
*/
OrangeParams::OrangeParams(G4VPhysicalVolume const*)
OrangeParams::OrangeParams(G4VPhysicalVolume const* world)
: OrangeParams(std::move(g4org::Converter{}(world).input))
{
CELER_NOT_IMPLEMENTED("Geant4->VecGeom geometry translation");
}

//---------------------------------------------------------------------------//
Expand All @@ -118,8 +142,6 @@ OrangeParams::OrangeParams(G4VPhysicalVolume const*)
OrangeParams::OrangeParams(OrangeInput&& input)
{
CELER_VALIDATE(input, << "input geometry is incomplete");
CELER_VALIDATE(!input.universes.empty(),
<< "input geometry has no universes");

// Save global bounding box
bbox_ = [&input] {
Expand Down
4 changes: 2 additions & 2 deletions src/orange/OrangeParams.hh
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class OrangeParams final : public GeoParamsSurfaceInterface,
public ParamsDataInterface<OrangeParamsData>
{
public:
// Construct from a JSON file (if JSON is enabled)
explicit OrangeParams(std::string const& json_filename);
// Construct from a JSON or GDML file (if JSON or Geant4 are enabled)
explicit OrangeParams(std::string const& filename);

// Construct in-memory from Geant4 (not implemented)
explicit OrangeParams(G4VPhysicalVolume const*);
Expand Down
73 changes: 73 additions & 0 deletions src/orange/g4org/Converter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//----------------------------------*-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/Converter.cc
//---------------------------------------------------------------------------//
#include "Converter.hh"

#include "corecel/io/Logger.hh"
#include "geocel/detail/LengthUnits.hh"

#include "PhysicalVolumeConverter.hh"
#include "ProtoConstructor.hh"

namespace celeritas
{
namespace g4org
{
//---------------------------------------------------------------------------//
/*!
* Construct with options.
*/
Converter::Converter(Options&& opts) : opts_{std::move(opts)}
{
if (!opts_.tol)
{
opts_.tol = Tolerance<>::from_default(lengthunits::millimeter);
}

if (real_type{1} - ipow<2>(opts_.tol.rel) == real_type{1})
{
CELER_LOG(warning)
<< "Requested relative tolerance (" << opts_.tol.rel
<< ") for ORANGE is very small: tracking errors may result due to "
"incomplete geometry simplification";
}

CELER_ENSURE(opts_.tol);
}

//---------------------------------------------------------------------------//
/*!
* Convert the world.
*/
auto Converter::operator()(arg_type g4world) -> result_type
{
CELER_EXPECT(g4world);

CELER_LOG(debug) << "Converting Geant4 geometry elements";

// Convert solids, logical volumes, physical volumes
PhysicalVolumeConverter::Options options;
options.verbose = opts_.verbose;
PhysicalVolumeConverter convert_pv(std::move(options));
PhysicalVolume world = convert_pv(*g4world);
CELER_VALIDATE(std::holds_alternative<NoTransformation>(world.transform),
<< "world volume should not have a transformation");

CELER_LOG(debug) << "Building protos";
// Convert logical volumes into protos
auto global_proto = ProtoConstructor{opts_.verbose}(*world.lv);

CELER_LOG(debug) << "Building universes";
// Build universes from protos
result_type result;
result.input = build_input(opts_.tol, *global_proto);
return result;
}

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

#include <memory>
#include <unordered_map>

#include "celeritas_config.h"
#include "orange/OrangeInput.hh"
#include "orange/OrangeTypes.hh"

//---------------------------------------------------------------------------//
// Forward declarations
//---------------------------------------------------------------------------//

class G4LogicalVolume;
class G4VPhysicalVolume;

namespace celeritas
{
namespace g4org
{
//---------------------------------------------------------------------------//
/*!
* Create an ORANGE geometry model from an in-memory Geant4 model.
*
* Return the new world volume and a mapping of Geant4 logical volumes to
* VecGeom-based volume IDs.
*
* The default Geant4 "tolerance" (often used as surface "thickness") is 1e-9
* mm, and the relative tolerance when specifying a length scale is 1e-11 (so
* the default macro length scale is expected to be 100 mm = 10 cm).
* That relative tolerance is *much* too small for any quadric operations or
* angular rotations to be differentiated, so for now we'll stick with the
* ORANGE default tolerance of 1e-8 relative, and we assume a 1mm length scale.
*/
class Converter
{
public:
//!@{
//! \name Type aliases
using arg_type = G4VPhysicalVolume const*;
using MapLvVolId = std::unordered_map<G4LogicalVolume const*, VolumeId>;
//!@}

//! Input options for the conversion
struct Options
{
//! Write output about volumes being converted
bool verbose{false};
//! Manually specify a tracking/construction tolerance
Tolerance<> tol;
};

struct result_type
{
OrangeInput input;
MapLvVolId volumes; //! TODO
};

public:
// Construct with options
explicit Converter(Options&&);

//! Construct with default options
Converter() : Converter{Options{}} {}

// Convert the world
result_type operator()(arg_type);

private:
Options opts_;
};

//---------------------------------------------------------------------------//

#if !(CELERITAS_USE_GEANT4 \
&& CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE)
inline Converter::Converter(Options&&)
{
CELER_DISCARD(opts_);
}

inline auto Converter::operator()(arg_type) -> result_type
{
CELER_NOT_CONFIGURED("Geant4 with double-precision real_type");
}
#endif

//---------------------------------------------------------------------------//
} // namespace g4org
} // namespace celeritas
1 change: 0 additions & 1 deletion src/orange/g4org/PhysicalVolumeConverter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ auto PhysicalVolumeConverter::operator()(arg_type g4world) -> result_type
CELER_EXPECT(!g4world.GetRotation());
CELER_EXPECT(g4world.GetTranslation() == G4ThreeVector(0, 0, 0));

CELER_LOG(status) << "Converting Geant4 geometry";
ScopedProfiling profile_this{"import-geant-geo"};
ScopedMem record_mem("PhysicalVolumeConverter.convert");
ScopedTimeLog scoped_time;
Expand Down
7 changes: 5 additions & 2 deletions src/orange/g4org/ProtoConstructor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,11 @@ void ProtoConstructor::place_pv(VariantTransform const& parent_transform,
add_material(
Transformed::or_object(pv.lv->solid, std::move(transform)));
}
else if (pv.lv.use_count() == 1)
else if (pv.lv.use_count() == 1
&& std::holds_alternative<NoTransformation>(pv.transform))
{
// Child can be inlined into the parent because it's used only once
// *and* it doesn't have a transform relative to the parent
if (CELER_UNLIKELY(verbose_))
{
std::clog << std::string(depth_, ' ') << " -> "
Expand Down Expand Up @@ -213,7 +215,8 @@ void ProtoConstructor::place_pv(VariantTransform const& parent_transform,
{
std::clog << std::string(depth_, ' ') << " : "
<< "daughter shape is "
<< to_string(*proto->daughters.back().make_interior());
<< to_string(*proto->daughters.back().make_interior())
<< std::endl;
}
}
}
Expand Down

0 comments on commit fe26115

Please sign in to comment.