Skip to content

Commit

Permalink
Add Cerenkov distribution and generator (celeritas-project#1080)
Browse files Browse the repository at this point in the history
  • Loading branch information
amandalund committed Jan 17, 2024
1 parent 0b80505 commit b1e52f2
Show file tree
Hide file tree
Showing 13 changed files with 1,068 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/celeritas/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ list(APPEND SOURCES
mat/MaterialParams.cc
mat/MaterialParamsOutput.cc
mat/detail/Utils.cc
optical/CerenkovParams.cc
phys/CutoffParams.cc
phys/ImportedModelAdapter.cc
phys/ImportedProcessAdapter.cc
Expand Down
3 changes: 3 additions & 0 deletions src/celeritas/Types.hh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ using ElementComponentId = OpaqueId<struct MatElementComponent>;
//! Opaque index to one isotopic component datum in a particular element
using IsotopeComponentId = OpaqueId<struct ElIsotopeComponent>;

//! Opaque index to a material with optical properties
using OpticalMaterialId = OpaqueId<struct OpticalMaterial_>;

//! Opaque index of a process applicable to a single particle type
using ParticleProcessId = OpaqueId<ProcessId>;

Expand Down
31 changes: 21 additions & 10 deletions src/celeritas/grid/GenericCalculator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ class GenericCalculator
// Get the tabulated y value at a particular index.
CELER_FORCEINLINE_FUNCTION real_type operator[](size_type index) const;

// Get the tabulated x values
CELER_FORCEINLINE_FUNCTION NonuniformGrid<real_type> const& grid() const;

private:
GenericGridData const& data_;
Values const& reals_;
NonuniformGrid<real_type> x_grid_;
};

//---------------------------------------------------------------------------//
Expand All @@ -57,7 +61,7 @@ class GenericCalculator
CELER_FUNCTION
GenericCalculator::GenericCalculator(GenericGridData const& grid,
Values const& values)
: data_(grid), reals_(values)
: data_(grid), reals_(values), x_grid_(data_.grid, reals_)
{
CELER_EXPECT(data_);
}
Expand All @@ -68,31 +72,29 @@ GenericCalculator::GenericCalculator(GenericGridData const& grid,
*/
CELER_FUNCTION real_type GenericCalculator::operator()(real_type x) const
{
NonuniformGrid<real_type> const x_grid(data_.grid, reals_);

// Snap out-of-bounds values to closest grid points
size_type lower_idx;
real_type result;
if (x <= x_grid.front())
if (x <= x_grid_.front())
{
lower_idx = 0;
result = (*this)[lower_idx];
}
else if (x >= x_grid.back())
else if (x >= x_grid_.back())
{
lower_idx = x_grid.size() - 1;
lower_idx = x_grid_.size() - 1;
result = (*this)[lower_idx];
}
else
{
// Locate the x bin
lower_idx = x_grid.find(x);
CELER_ASSERT(lower_idx + 1 < x_grid.size());
lower_idx = x_grid_.find(x);
CELER_ASSERT(lower_idx + 1 < x_grid_.size());

// Interpolate *linearly* on x using the bin data.
LinearInterpolator<real_type> interpolate_xs(
{x_grid[lower_idx], (*this)[lower_idx]},
{x_grid[lower_idx + 1], (*this)[lower_idx + 1]});
{x_grid_[lower_idx], (*this)[lower_idx]},
{x_grid_[lower_idx + 1], (*this)[lower_idx + 1]});
result = interpolate_xs(x);
}

Expand All @@ -109,5 +111,14 @@ CELER_FUNCTION real_type GenericCalculator::operator[](size_type index) const
return reals_[data_.value[index]];
}

//---------------------------------------------------------------------------//
/*!
* Get the tabulated x values.
*/
CELER_FUNCTION NonuniformGrid<real_type> const& GenericCalculator::grid() const
{
return x_grid_;
}

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

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

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Cerenkov angle integrals tablulated as a function of photon energy.
*/
template<Ownership W, MemSpace M>
struct CerenkovData
{
template<class T>
using Items = Collection<T, W, M>;
template<class T>
using OpticalMaterialItems = Collection<T, W, M, OpticalMaterialId>;

//// MEMBER DATA ////

Items<real_type> reals;
OpticalMaterialItems<GenericGridData> angle_integral;

//// MEMBER FUNCTIONS ////

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

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

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

#include "corecel/Macros.hh"
#include "corecel/Types.hh"
#include "corecel/cont/EnumArray.hh"
#include "celeritas/Quantities.hh"
#include "celeritas/Types.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Pre- and post-step data for sampling Cerenkov photons.
*/
struct CerenkovStepData
{
units::LightSpeed speed;
Real3 pos{};
};

//---------------------------------------------------------------------------//
/*!
* Input data for sampling Cerenkov photons.
*/
struct CerenkovDistributionData
{
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;

//! Check whether the data are assigned
explicit CELER_FUNCTION operator bool() const
{
return num_photons > 0 && step_length > 0 && material;
}
};

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

#include <cmath>

#include "corecel/Assert.hh"
#include "corecel/Macros.hh"
#include "corecel/Types.hh"
#include "celeritas/Quantities.hh"
#include "celeritas/grid/GenericCalculator.hh"
#include "celeritas/grid/GenericGridData.hh"
#include "celeritas/grid/VectorUtils.hh"

#include "CerenkovData.hh"
#include "OpticalPropertyData.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Calculate the mean number of Cerenkov photons produced per unit length.
*
* The average number of photons produced is given by
* \f[
\dif N = \frac{\alpha z^2}{\hbar c}\sin^2\theta \dif\epsilon \dif x =
\frac{\alpha z^2}{\hbar c}\left(1 - \frac{1}{n^2\beta^2}\right) \dif\epsilon
\dif x,
* \f]
* where \f$ n \f$ is the refractive index of the material, \f$ \epsilon \f$
* is the photon energy, and \f$ \theta \f$ is the angle of the emitted photons
* with respect to the incident particle direction, given by \f$ \cos\theta = 1
* / (\beta n) \f$. Note that in a dispersive medium, the index of refraction
* is an inreasing function of photon energy. The mean number of photons per
* unit length is given by
* \f[
\dif N/\dif x = \frac{\alpha z^2}{\hbar c}
\int_{\epsilon_\text{min}}^{\epsilon_\text{max}} \dif\epsilon \left(1 -
\frac{1}{n^2\beta^2} \right) = \frac{\alpha z^2}{\hbar c}
\left[\epsilon_\text{max} - \epsilon_\text{min} - \frac{1}{\beta^2}
\int_{\epsilon_\text{min}}^{\epsilon_\text{max}}
\frac{\dif\epsilon}{n^2(\epsilon)} \right].
* \f]
*/
class CerenkovDndxCalculator
{
public:
// Construct from optical properties and Cerenkov angle integrals
inline CELER_FUNCTION
CerenkovDndxCalculator(NativeCRef<OpticalPropertyData> const& properties,
NativeCRef<CerenkovData> const& shared,
OpticalMaterialId material,
units::ElementaryCharge charge);

// Calculate the mean number of Cerenkov photons produced per unit length
inline CELER_FUNCTION real_type operator()(units::LightSpeed beta);

private:
NativeCRef<OpticalPropertyData> const& properties_;
NativeCRef<CerenkovData> const& shared_;
OpticalMaterialId material_;
real_type zsq_;
};

//---------------------------------------------------------------------------//
// INLINE DEFINITIONS
//---------------------------------------------------------------------------//
/*!
* Construct from optical properties and Cerenkov angle integrals.
*/
CELER_FUNCTION
CerenkovDndxCalculator::CerenkovDndxCalculator(
NativeCRef<OpticalPropertyData> const& properties,
NativeCRef<CerenkovData> const& shared,
OpticalMaterialId material,
units::ElementaryCharge charge)
: properties_(properties)
, shared_(shared)
, material_(material)
, zsq_(ipow<2>(charge.value()))
{
CELER_EXPECT(properties_);
CELER_EXPECT(shared_);
}

//---------------------------------------------------------------------------//
/*!
* Calculate the mean number of Cerenkov photons produced per unit length.
*/
CELER_FUNCTION real_type
CerenkovDndxCalculator::operator()(units::LightSpeed beta)
{
CELER_EXPECT(beta.value() > 0 && beta.value() <= 1);

if (!shared_.angle_integral[material_])
{
// No optical properties for this material
return 0;
}

CELER_ASSERT(material_ < properties_.refractive_index.size());
real_type inv_beta = 1 / beta.value();
GenericCalculator calc_refractive_index(
properties_.refractive_index[material_], properties_.reals);
real_type energy_max = calc_refractive_index.grid().back();
if (inv_beta > calc_refractive_index(energy_max))
{
// Incident particle energy is below the threshold for Cerenkov
// emission (i.e., beta < 1 / n_max)
return 0;
}

// Calculate the Cerenkov angle integral [MeV]
GenericCalculator calc_integral(shared_.angle_integral[material_],
shared_.reals);

// Calculate \f$ \int_{\epsilon_\text{min}}^{\epsilon_\text{max}}
// \dif\epsilon \left(1 - \frac{1}{n^2\beta^2}\right) \f$
real_type energy;
if (inv_beta < calc_refractive_index[0])
{
energy = energy_max - calc_refractive_index.grid().front()
- calc_integral(energy_max) * ipow<2>(inv_beta);
}
else
{
// TODO: Check that refractive index is monotonically increasing when
// grids are imported

// Find the energy where the refractive index is equal to 1 / beta.
// Both energy and refractive index are monotonically increasing, so
// the grid and values can be swapped and the energy can be calculated
// from a given index of refraction
auto grid_data = properties_.refractive_index[material_];
trivial_swap(grid_data.grid, grid_data.value);
real_type energy_min
= GenericCalculator(grid_data, properties_.reals)(inv_beta);
energy = energy_max - energy_min
- (calc_integral(energy_max) - calc_integral(energy_min))
* ipow<2>(inv_beta);
}

// Calculate number of photons. This may be negative if the incident
// particle energy is very close to (just above) the Cerenkov production
// threshold
return clamp_to_nonneg(zsq_ * constants::alpha_fine_structure
/ (constants::hbar_planck * constants::c_light)
* native_value_from(units::MevEnergy(energy)));
}

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

0 comments on commit b1e52f2

Please sign in to comment.