Skip to content

Commit

Permalink
Add ScintillationGenerator and related data structures (#1086)
Browse files Browse the repository at this point in the history
  • Loading branch information
whokion committed Jan 26, 2024
1 parent 5339583 commit ff984ca
Show file tree
Hide file tree
Showing 11 changed files with 627 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/celeritas/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ list(APPEND SOURCES
mat/MaterialParamsOutput.cc
mat/detail/Utils.cc
optical/CerenkovParams.cc
optical/ScintillationParams.cc
phys/CutoffParams.cc
phys/ImportedModelAdapter.cc
phys/ImportedProcessAdapter.cc
Expand Down
8 changes: 4 additions & 4 deletions src/celeritas/optical/CerenkovGenerator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "celeritas/random/distribution/GenerateCanonical.hh"
#include "celeritas/random/distribution/UniformRealDistribution.hh"

#include "CerenkovDistributionData.hh"
#include "OpticalDistributionData.hh"
#include "CerenkovDndxCalculator.hh"
#include "OpticalPrimary.hh"
#include "OpticalPropertyData.hh"
Expand Down Expand Up @@ -53,7 +53,7 @@ class CerenkovGenerator
inline CELER_FUNCTION
CerenkovGenerator(NativeCRef<OpticalPropertyData> const& properties,
NativeCRef<CerenkovData> const& shared,
CerenkovDistributionData const& dist,
OpticalDistributionData const& dist,
Span<OpticalPrimary> photons);

// Sample Cerenkov photons from the distribution
Expand All @@ -67,7 +67,7 @@ class CerenkovGenerator

//// DATA ////

CerenkovDistributionData const& dist_;
OpticalDistributionData const& dist_;
Span<OpticalPrimary> photons_;
GenericCalculator calc_refractive_index_;
UniformRealDist sample_phi_;
Expand Down Expand Up @@ -98,7 +98,7 @@ CELER_FUNCTION
CerenkovGenerator::CerenkovGenerator(
NativeCRef<OpticalPropertyData> const& properties,
NativeCRef<CerenkovData> const& shared,
CerenkovDistributionData const& dist,
OpticalDistributionData const& dist,
Span<OpticalPrimary> photons)
: dist_(dist)
, photons_(photons)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file celeritas/optical/CerenkovDistributionData.hh
//! \file celeritas/optical/OpticalDistributionData.hh
//---------------------------------------------------------------------------//
#pragma once

Expand All @@ -17,26 +17,26 @@ namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Pre- and post-step data for sampling Cerenkov photons.
* Pre- and post-step data for sampling optical photons.
*/
struct CerenkovStepData
struct OpticalStepData
{
units::LightSpeed speed;
Real3 pos{};
};

//---------------------------------------------------------------------------//
/*!
* Input data for sampling Cerenkov photons.
* Input data for sampling optical photons.
*/
struct CerenkovDistributionData
struct OpticalDistributionData
{
size_type num_photons{}; //!< Sampled number of photons to generate
real_type time{}; //!< Pre-step time
real_type step_length{};
units::ElementaryCharge charge;
OpticalMaterialId material{};
EnumArray<StepPoint, CerenkovStepData> points;
EnumArray<StepPoint, OpticalStepData> points;

//! Check whether the data are assigned
explicit CELER_FUNCTION operator bool() const
Expand Down
1 change: 1 addition & 0 deletions src/celeritas/optical/OpticalPrimary.hh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "corecel/Types.hh"
#include "orange/Types.hh"
#include "celeritas/Quantities.hh"
#include "celeritas/Types.hh"

namespace celeritas
Expand Down
83 changes: 83 additions & 0 deletions src/celeritas/optical/ScintillationData.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//----------------------------------*-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 celeritas/optical/ScintillationData.hh
//---------------------------------------------------------------------------//
#pragma once

#include "corecel/Macros.hh"
#include "corecel/Types.hh"
#include "corecel/data/Collection.hh"
#include "celeritas/Types.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Material dependent scintillation property.
*/
struct ScintillationComponent
{
real_type yield_prob{}; //!< Probability of the yield
real_type lambda_mean{}; //!< Mean wavelength
real_type lambda_sigma{}; //!< Standard dev. of wavelength
real_type rise_time{}; //!< Rise time
real_type fall_time{}; //!< Decay time

//! Whether all data are assigned and valid
explicit CELER_FUNCTION operator bool() const
{
return yield_prob > 0 && lambda_mean > 0 && lambda_sigma > 0
&& rise_time >= 0 && fall_time > 0;
}
};

//---------------------------------------------------------------------------//
/*!
* A collection range of scintillation components.
*/
struct ScintillationSpectrum
{
ItemRange<ScintillationComponent> components;
};

//---------------------------------------------------------------------------//
/*!
* Scintillation data tabulated with the optical material id.
*/
template<Ownership W, MemSpace M>
struct ScintillationData
{
template<class T>
using Items = Collection<T, W, M>;
template<class T>
using OpticalMaterialItems = Collection<T, W, M, OpticalMaterialId>;

//// MEMBER DATA ////

Items<ScintillationComponent> components;
OpticalMaterialItems<ScintillationSpectrum> spectra;

//// MEMBER FUNCTIONS ////

//! Whether all data are assigned and valid
explicit CELER_FUNCTION operator bool() const
{
return !components.empty() && !spectra.empty();
}

//! Assign from another set of data
template<Ownership W2, MemSpace M2>
ScintillationData& operator=(ScintillationData<W2, M2> const& other)
{
CELER_EXPECT(other);
components = other.components;
spectra = other.spectra;
return *this;
}
};

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

#include "corecel/Assert.hh"
#include "corecel/Macros.hh"
#include "corecel/Types.hh"
#include "corecel/data/Collection.hh"
#include "corecel/math/ArrayOperators.hh"
#include "corecel/math/ArrayUtils.hh"
#include "celeritas/random/distribution/BernoulliDistribution.hh"
#include "celeritas/random/distribution/ExponentialDistribution.hh"
#include "celeritas/random/distribution/GenerateCanonical.hh"
#include "celeritas/random/distribution/NormalDistribution.hh"
#include "celeritas/random/distribution/UniformRealDistribution.hh"

#include "OpticalDistributionData.hh"
#include "OpticalPrimary.hh"
#include "ScintillationData.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Sample Scintillation photons.
*
* \note This performs the same sampling routine as in G4Scintillation class
* of the Geant4 release 11.2 with some modifications.
*/
class ScintillationGenerator
{
public:
//!@{
//! \name Type aliases
using Energy = units::MevEnergy;
//!@}

public:
// Construct from scintillation data and distribution parameters
inline CELER_FUNCTION
ScintillationGenerator(OpticalDistributionData const& dist,
NativeCRef<ScintillationData> const& shared,
Span<OpticalPrimary> photons);

// Sample Scintillation photons from the distribution
template<class Generator>
inline CELER_FUNCTION Span<OpticalPrimary> operator()(Generator& rng);

private:
//// TYPES ////

using UniformRealDist = UniformRealDistribution<real_type>;
using ExponentialDist = ExponentialDistribution<real_type>;

//// DATA ////

OpticalDistributionData const& dist_;
NativeCRef<ScintillationData> const& shared_;
Span<OpticalPrimary> photons_;

UniformRealDist sample_cost_;
UniformRealDist sample_phi_;

bool is_neutral_{};
units::LightSpeed delta_speed_{};
Real3 delta_pos_{};
};

//---------------------------------------------------------------------------//
// INLINE DEFINITIONS
//---------------------------------------------------------------------------//
/*!
* Construct from shared scintillation data and distribution parameters.
*/
CELER_FUNCTION
ScintillationGenerator::ScintillationGenerator(
OpticalDistributionData const& dist,
NativeCRef<ScintillationData> const& shared,
Span<OpticalPrimary> photons)
: dist_(dist)
, shared_(shared)
, photons_(photons)
, sample_cost_(-1, 1)
, sample_phi_(0, 2 * constants::pi)
, is_neutral_{dist_.charge == zero_quantity()}
{
CELER_EXPECT(dist_);
CELER_EXPECT(shared_);
CELER_EXPECT(photons_.size() == dist_.num_photons);

auto const& pre_step = dist_.points[StepPoint::pre];
auto const& post_step = dist_.points[StepPoint::post];
delta_pos_ = post_step.pos - pre_step.pos;
delta_speed_ = post_step.speed - pre_step.speed;
}

//---------------------------------------------------------------------------//
/*!
* Sample scintillation photons from optical property data and step data.
*
* The optical photons are generated evenly along the step and are emitted
* uniformly over the entire solid angle with a random linear polarization.
* The photon energy is calculated by the scintillation emission wavelength
* \f[
E = \frac{hc}{\lambda},
* \f]
* where \f$ h \f$ is the Planck constant and \f$ c \f$ is the speed of light,
* and \f$ \lambda \f$ is sampled by the normal distribution with the mean of
* scintillation emission spectrum and the standard deviation. The emitted time
* is simulated according to empirical shapes of the material-dependent
* scintillation time structure with one or double exponentials.
*/
template<class Generator>
CELER_FUNCTION Span<OpticalPrimary>
ScintillationGenerator::operator()(Generator& rng)
{
// Loop for generating scintillation photons
size_type num_generated{0};

ScintillationSpectrum const& spectrum = shared_.spectra[dist_.material];

for (auto sid : spectrum.components)
{
ScintillationComponent component = shared_.components[sid];

// Calculate the number of photons to generate for this component
size_type num_photons
= sid.get() + 1 == spectrum.components.size()
? dist_.num_photons - num_generated
: static_cast<size_type>(dist_.num_photons
* component.yield_prob);

CELER_EXPECT(num_generated + num_photons <= dist_.num_photons);

// Sample photons for each scintillation component
NormalDistribution<real_type> sample_lambda(component.lambda_mean,
component.lambda_sigma);
ExponentialDist sample_time(real_type{1} / component.fall_time);

for (size_type i : range(num_generated, num_generated + num_photons))
{
// Sample wavelength and convert to energy
real_type wave_length = sample_lambda(rng);
CELER_EXPECT(wave_length > 0);
photons_[i].energy = native_value_to<Energy>(
constants::h_planck * constants::c_light / wave_length);

// Sample direction
real_type cost = sample_cost_(rng);
real_type phi = sample_phi_(rng);
photons_[i].direction = from_spherical(cost, phi);

// Sample polarization perpendicular to the photon direction
Real3 temp = from_spherical(
(cost > 0 ? -1 : 1) * std::sqrt(1 - ipow<2>(cost)), phi);
Real3 perp = {-std::sin(phi), std::cos(phi), 0};
real_type sinphi, cosphi;
sincospi(UniformRealDist{0, 1}(rng), &sinphi, &cosphi);
for (int j = 0; j < 3; ++j)
{
photons_[i].polarization[j] = cosphi * temp[j]
+ sinphi * perp[j];
}
photons_[i].polarization
= make_unit_vector(photons_[i].polarization);

// Sample position
real_type u = (is_neutral_) ? 1 : generate_canonical(rng);
photons_[i].position = dist_.points[StepPoint::pre].pos
+ u * delta_pos_;

// Sample time
real_type delta_time
= u * dist_.step_length
/ (native_value_from(dist_.points[StepPoint::pre].speed)
+ u * real_type(0.5) * native_value_from(delta_speed_));

if (component.rise_time == 0)
{
delta_time -= component.fall_time
* std::log(generate_canonical(rng));
}
else
{
real_type scint_time{};
real_type envelop{};
do
{
scint_time = sample_time(rng);
envelop = -std::expm1(-scint_time / component.rise_time);
} while (!BernoulliDistribution(envelop)(rng));
delta_time += scint_time;
}
CELER_ASSERT(delta_time >= 0);
photons_[i].time = dist_.time + delta_time;
}
num_generated += num_photons;
}
CELER_ASSERT(num_generated == dist_.num_photons);

return photons_;
}

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

0 comments on commit ff984ca

Please sign in to comment.