Skip to content

Commit cf8d83d

Browse files
rahmans1Sakib Rahman
andauthored
Pr/rzmap covfie backend (#2344)
* Add 2D covfie field traits for RZ maps Define CovfieRZFieldTraits for 2D (R,Z) field interpolation, mirroring the existing 3D CovfieFieldTraits. The host pipeline uses affine->clamp->linear->strided->array with float2 storage, while the CUDA device path uses hardware texture interpolation for performance. Prompt: "Replace RZMapField with covfie backend for storage and bilinear interpolation on the R-Z grid, with CUDA texture acceleration on GPU" Assisted-by: Claude Opus 4.6 * Add covfie-backed RZMapField data and evaluation Add RZMapFieldData.covfie.hh with 4 ownership specializations (host value/reference, device value/reference) using CovfieRZFieldTraits, and RZMapField.covfie.hh which evaluates the 2D (R,Z) field via covfie and projects Br to Cartesian Bx/By components. Prompt: "Add covfie backend for RZMapField: create data ownership types and field evaluation class mirroring the CartMapField covfie pattern" Assisted-by: Claude Opus 4.6 * Add covfie RZMapField params constructor Add RZMapFieldParams.covfie.cc with pimpl-based constructor that builds a 2D covfie field from RZMapFieldInput, transposing from [Z][R] input stride to covfie's (R,Z) ordering. Update RZMapFieldParams.hh to use pimpl when covfie is enabled and ParamsDataStore when disabled. Prompt: "Add covfie params constructor for RZMapField, mirroring CartMapFieldParams.covfie.cc with pimpl pattern" Assisted-by: Claude Opus 4.6 * Wire RZMapField covfie dispatch into existing headers Add #if CELERITAS_USE_COVFIE guards to RZMapFieldData.hh and RZMapField.hh to dispatch to covfie implementations when enabled, preserving the original UniformGrid+Collection code path when disabled. Guard RZMapFieldParams.cc with #if !CELERITAS_USE_COVFIE. Add RZMapFieldParams.covfie.cc to the covfie CMake source list. Prompt: "Wire the covfie dispatch into existing RZMapField headers and CMakeLists.txt, matching CartMapField pattern" Assisted-by: Claude Opus 4.6 * Add RZMapField device test for covfie field values on GPU Add a CUDA/HIP device test that evaluates RZMapField on GPU and compares against expected values from CUDA texture interpolation. Update the host test expected values to use #if CELERITAS_USE_COVFIE so both covfie (bilinear) and non-covfie (1D-per-component) code paths are tested. Prompt: "Add device test for RZMapField covfie backend and ensure host test works for both covfie-enabled and covfie-disabled builds" Assisted-by: Claude Opus 4.6 * Fix non-covfie RZMapFieldTest expected values to match upstream Update the #else branch expected values to match current upstream develop, which changed after the cms-tiny.field.json test data was updated. Prompt: "Fix CI failure: non-covfie RZMapFieldTest.all expected values are stale and don't match current upstream develop" Assisted-by: Claude Opus 4.6 * Switch RZMapField covfie CUDA backend to software interpolation Replace cuda_texture with cuda_device_array as the device storage backend for the RZ field map, unifying the CUDA/HIP/CPU backend chain to use the same affine<clamp<linear<strided<>>>> pipeline with software interpolation on all platforms. Build the device field bottom-up using cross-type constructors so each layer preserves its configuration during the host-to-device transfer. Remove incorrect CELER_FUNCTION annotations from host-only methods in value<device>, initialize field_view pointer to nullptr, replace std::hypot with the device-safe hypot from corecel/math/Algorithms.hh, and fix the non-covfie stub Impl/ImplDeleter definitions to compile without covfie. Update device test expected values to match software interpolation (identical to host) and tighten tolerance to 1e-7. Prompt: "Switch RZMapField covfie CUDA backend from cuda_texture to cuda_device_array with bottom-up layer construction, fix host-only CELER_FUNCTION annotations, and update device test expected values" Assisted-by: Claude Sonnet 4.6 * Add RZMapFieldTest analytical interpolation validation Add RZMapFieldTest.interp_validation with two synthetic cases whose values at non-grid points are analytically known: - Case 1 (both paths): Br=r, Bz=z — linear functions reproduced exactly by both 1D-per-component and covfie bilinear interpolation - Case 2 (covfie only): Br=r+z, Bz=r+z — bilinear exactly reproduces linear functions with cross-axis dependence, confirming covfie does true 2D interpolation; guarded by CELERITAS_USE_COVFIE since 1D-per-component correctly does not reproduce cross-axis variation Prompt: "Add a test that validates RZMapField interpolation against analytically known values using synthetic linear fields: one that varies on a single axis (ground truth for both paths) and one that varies on both axes (proving covfie does true 2D bilinear interpolation)" Assisted-by: Claude Sonnet 4.6 * Widen RZMapFieldTest.device tolerance to 2e-7 for float32 GPU rounding Covfie stores field values as float32 internally; GPU arithmetic produces slightly different rounding from CPU for 3 of 24 sample points (~1.03e-7 relative error), just exceeding the original 1e-7 tolerance. Widening to 2e-7 accommodates float32 precision on device while remaining tight enough to catch significant regressions. Prompt: "Widen RZMapFieldTest.device tolerance from 1e-7 to 2e-7 to accommodate float32 GPU rounding differences introduced by covfie's internal float storage" Assisted-by: Claude Sonnet 4.6 * Update SimpleCmsRZFieldAlongStepTest expected values for covfie path - msc_rzfield: add #if CELERITAS_USE_COVFIE branch with covfie bilinear interpolation values (displacement=0.495, angle=-0.927); the large difference from the non-covfie values (displacement=0.552, angle=0.095) arises because the test particle is in a region of strong cross-axis field gradients where 1D-per-component and bilinear interpolation diverge - msc_rzfield_finegrid: widen angle tolerance from EXPECT_SOFT_EQ to EXPECT_SOFT_NEAR with 1e-11, accommodating a 6e-12 relative difference due to float32 field storage in covfie Prompt: "Update SimpleCmsRZFieldAlongStepTest to add covfie-specific expected values for msc_rzfield where bilinear interpolation gives physically different results from 1D-per-component in strong gradient regions, and widen the msc_rzfield_finegrid angle tolerance to accommodate float32 rounding from covfie's internal field storage" Assisted-by: Claude Sonnet 4.6 * Simplify device field construction using covfie cross-type constructor Replace 20-line bottom-up manual layer-by-layer construction with a single cross-type field copy: field = std::make_unique<field_t>(*other.field). covfie's cross-type field constructor propagates through all transformer layers (affine->clamp->linear->strided), with strided's cross-type owning_data_t constructor triggering cuda_device_array's constructor from array_1d_like backends, performing the H2D transfer at the bottom of the chain. Prompt: "use the cross-copy constructor approach to simplify H2D device field construction instead of manually building from the bottom up layer by layer" Assisted-by: Claude Sonnet 4.6 * Hide covfie headers from C++17 translation units The CI has a C++17/C++20 split: library sources compile as C++20 but some test files (e.g. AlongStep.test.cc) compile as C++17. When CELERITAS_USE_COVFIE is ON, the include chain: AlongStepRZMapFieldMscAction.hh -> RZMapFieldData.hh -> RZMapFieldData.covfie.hh -> detail/CovfieRZFieldTraits.hh -> covfie/core/parameter_pack.hpp (uses C++20 "requires") caused C++17 builds to fail. Fix by ensuring covfie types never appear in any .hh included by C++17 TUs: - RZMapFieldData.hh (covfie path): define only the primary template declaration and the const_reference/device specialization with a void const* field_view (no covfie types). The other three specializations remain in RZMapFieldData.covfie.hh which is only included from .covfie.cc and .cu files. - RZMapFieldData.covfie.hh: include RZMapFieldData.hh for the primary template; remove the const_reference/device specialization (now in .hh). - RZMapField.covfie.hh: add rzmap_get_view<M> function template that uses if constexpr (with M as template parameter so the discarded branch is not instantiated) to either cast void const* on device or call get_view() on host. Store ParamsRef const& instead of a direct field view reference. - RZMapFieldParams.covfie.cc: assign device_ref_ fields explicitly since const_reference/device no longer has an operator=(value/device). - AlongStepRZMapFieldMscAction.hh: remove spurious include of RZMapFieldData.hh (the class only holds SPConstFieldParams; it never references RZMapFieldParamsData directly). The non-covfie build path and the CartMapField backend are unaffected. Prompt: "The CI has a C++17/C++20 split where library source files compile as C++20 but some test files compile as C++17. When CELERITAS_USE_COVFIE is ON, covfie's parameter_pack.hpp uses C++20 requires clauses that leak into C++17 TUs through the public RZMapFieldData.hh include chain. Fix this by hiding all covfie types from public headers so that C++17 TUs never see covfie includes. Use void const* type erasure for the device const_reference specialization, with the cast back to the concrete view type happening only in .cu files compiled as C++20." Assisted-by: Claude Sonnet 4.6 * Move AlongStepRZMapFieldMscAction into covfie CMake target The action's .cc and .cu files include RZMapField.hh, which when covfie is enabled pulls in covfie headers requiring C++20. The file was previously registered via the unconditional celeritas_polysource() call, placing it in the main celeritas library target that does not directly link Extcovfie and therefore does not inherit cxx_std_20 from covfie's INTERFACE_COMPILE_FEATURES. Move the action into _celer_covfie_src alongside AlongStepCartMapFieldMscAction so it is compiled in a target that directly links Extcovfie. Add the corresponding non-covfie fallback in the else() branch and remove the unconditional celeritas_polysource() call. Prompt: "AlongStepRZMapFieldMscAction.cc includes RZMapField.hh which pulls in covfie headers requiring C++20, but the file is compiled in a target that does not directly link Extcovfie and therefore compiles at C++17. AlongStepCartMapFieldMscAction avoids this because it is placed in _celer_covfie_src which directly links Extcovfie and inherits cxx_std_20 from covfie's INTERFACE_COMPILE_FEATURES. Apply the same fix to AlongStepRZMapFieldMscAction." Assisted-by: Claude Sonnet 4.6 * Add RZAdapterField to hide covfie from RZMapMagneticField.hh RZMapMagneticField.hh included RZMapField.hh, which when covfie is enabled pulls in covfie headers requiring C++20. This broke compilation of any downstream .cc file (e.g. celer-g4/DetectorConstruction.cc) that includes the header but is compiled at C++17. Apply the same adapter pattern used by CartMapMagneticField: introduce RZAdapterField, a plain struct whose operator() is defined in a new RZMapMagneticField.cc. The .cc file includes RZMapField.hh and is compiled as part of the accel library, which already links Extcovfie and therefore compiles at C++20. The .hh now only includes RZMapFieldParams.hh (which forward-declares RZMapFieldParamsData), exposing no covfie types to downstream users. Prompt: "DetectorConstruction.cc in celer-g4 includes RZMapMagneticField.hh which pulls in RZMapField.hh and covfie headers requiring C++20, but DetectorConstruction.cc is compiled at C++17. CartMapMagneticField.hh avoids this by using a CartAdapterField struct whose operator() is implemented in CartMapMagneticField.cc, keeping the covfie include out of the header. Apply the same pattern to RZMapMagneticField." Assisted-by: Claude Sonnet 4.6 * Remove non-covfie RZMapField code path to match CartMapField pattern Before this change, RZMapField had a separate Collection-based CPU implementation predating covfie, guarded by #if !CELERITAS_USE_COVFIE throughout. CartMapField already followed a cleaner pattern: a minimal stub in the data header, inline CELER_NOT_CONFIGURED stubs in the params header, and a one-line type alias in the field header. This commit brings RZMapField into alignment with that pattern, removing ~350 lines of duplicated guard logic. Changes: - Delete RZMapFieldParams.cc: its only content was non-covfie stubs, now replaced by inline stubs in RZMapFieldParams.hh inside a #if !(CELERITAS_USE_COVFIE || __DOXYGEN__) block, matching CartMapFieldParams.hh exactly (Impl struct, ImplDeleter, ctor, host_ref, device_ref all CELER_NOT_CONFIGURED or CELER_UNREACHABLE). - Add minimal RZMapFieldParamsData<W,M> stub in RZMapFieldData.hh's #else block (one field: FieldDriverOptions options) so the type is always a complete primary template, mirroring CartMapFieldData.hh. - Generalize detail::NotImplementedField from a Cart-specific class to a template<template<Ownership, MemSpace> class ParamsData> class, keeping using ParamsRef = NativeCRef<ParamsData> so FieldTrackPropagator<Field> can still access Field::ParamsRef as a concrete type. Remove the CartMapFieldData.hh include from NotImplementedField.hh (each field header includes its own data header instead). - Update CartMapField.hh to pass CartMapFieldParamsData explicitly: using CartMapField = detail::NotImplementedField<CartMapFieldParamsData> - Simplify RZMapField.hh's #else block to include RZMapFieldData.hh and alias: using RZMapField = detail::NotImplementedField<RZMapFieldParamsData> - Remove RZMapFieldParams.cc from src/celeritas/CMakeLists.txt SOURCES. Add AlongStepRZMapFieldMscAction to the covfie-OFF else() branch alongside AlongStepCartMapFieldMscAction so it links against the stubs. - Disable RZMapFieldTest via #define RZMapFieldTest DISABLED_RZMapFieldTest when !CELERITAS_USE_COVFIE (DISABLED_ GoogleTest pattern, matching how Cart tests are not gated but RZ tests would fail without a field map). - Disable SimpleCmsRZFieldAlongStepTest the same way in AlongStep.test.cc and gate its two CTest entries on CELERITAS_USE_covfie in test/celeritas/CMakeLists.txt to avoid the no-tests-written failure. - Remove non-bilinear Case 1 from Fields.test.cc interp_validation since the Collection-based interpolation path no longer exists; keep only the bilinear cross-term check (Case 2) and relax tolerance to 2e-7. Prompt: "RZMapField currently has a Collection-based CPU implementation predating covfie, with many #if CELERITAS_USE_COVFIE guards scattered across the field, data, params headers and test files. CartMapField follows a cleaner pattern with minimal stubs and no guards in most places. Remove the non-covfie RZMapField code path so that RZMapField matches the CartMapField pattern exactly: a minimal complete type stub in the data header's #else block, inline CELER_NOT_CONFIGURED stubs in the params header, and a one-line NotImplementedField type alias in the field header. Generalize detail::NotImplementedField to support both CartMapField and RZMapField without duplicating it. Use the DISABLED_ GoogleTest prefix trick (not full #if wrapping) to skip RZ tests when covfie is off, exactly as Cart tests are handled. Do not add guards in RZ test files where the analogous Cart code does not have them. Remove Case 1 from interp_validation in Fields.test.cc since the non-bilinear interpolation path is gone. Delete RZMapFieldParams.cc. Update CMakeLists to compile AlongStepRZMapFieldMscAction in the covfie-OFF branch and gate the SimpleCmsRZFieldAlongStep CTest entries on CELERITAS_USE_covfie." Assisted-by: Claude Sonnet 4.6 * Remove dead non-covfie expected values from SimpleCmsRZFieldAlongStepTest The msc_rzfield test body contained a #if CELERITAS_USE_COVFIE guard with separate expected displacement and angle values for the non-covfie path. Since SimpleCmsRZFieldAlongStepTest is already disabled via the DISABLED_ prefix when covfie is OFF, the #else branch was dead code that could never execute. Remove the guard and retain only the covfie expected values. Prompt: "Remove remaining #if CELERITAS_USE_COVFIE guards from SimpleCmsRZFieldAlongStepTest test bodies. The whole fixture is disabled when covfie is OFF so any #else branches with non-covfie expected values are dead code and should be deleted." Assisted-by: Claude Sonnet 4.6 * Relax tolerance in SimpleCmsRZFieldAlongStepTest.msc_rzfield The displacement and angle expectations used EXPECT_SOFT_EQ (1e-12 relative tolerance) but CI hardware produces results with ~1e-8 relative deviation from the reference values, likely due to differing FMA instruction scheduling across GPU architectures. Switch to EXPECT_SOFT_NEAR with 1e-7 to accommodate this cross-platform variation. Prompt: "Relax the tolerance on the displacement and angle checks in SimpleCmsRZFieldAlongStepTest.msc_rzfield to accommodate floating-point variation across GPU hardware. The CI machine gives a result differing by ~1e-8 relative error from the reference value, which exceeds the default EXPECT_SOFT_EQ tolerance of 1e-12." Assisted-by: Claude Sonnet 4.6 * refactor to keep field_view reference in RZMapField * covfie patch * Acknowledge float32 precision limit in msc_rzfield_finegrid test In float32, the step length ~6e-7 cm is below machine epsilon at r~125 cm, causing all tracks to fail to change position in debug builds. Add a ScopedLogStorer to explicitly capture and expect these errors in float32+debug builds, and widen the angle tolerance for float32 builds. Prompt: "Add ScopedLogStorer to msc_rzfield_finegrid to explicitly handle float32 precision limit where step length is below machine epsilon, and widen angle tolerance for float32 builds" Assisted-by: Claude Sonnet 4.6 * Return zero outside field grid in RZMapField Store grid bounds from RZMapFieldInput in RZMapFieldParamsData and add a bounds check in RZMapField::operator(). Without this, covfie clamps out-of-bounds queries to boundary values, causing NaN in the RK error state when step length is infinite (vacuum track with no cross sections). Prompt: "Return zero outside field grid bounds in RZMapField: store min/max bounds from RZMapFieldInput in RZMapFieldParamsData and check them in RZMapField::operator()" Assisted-by: Claude Sonnet 4.6 --------- Co-authored-by: Sakib Rahman <srahman1@bnl.gov>
1 parent ab0b5d5 commit cf8d83d

21 files changed

Lines changed: 840 additions & 314 deletions

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ if(CELERITAS_USE_covfie)
167167
endif()
168168
target_link_libraries(Extcovfie INTERFACE ${_covfie_libs})
169169

170-
if(CELERITAS_USE_CUDA AND covfie_VERSION VERSION_LESS_EQUAL 0.15.3)
170+
if(CELERITAS_USE_CUDA AND covfie_VERSION VERSION_LESS_EQUAL 0.15.5)
171171
# See https://github.com/acts-project/covfie/pull/98 :
172172
# avoid warning/runtime failure due to use of std::clamp in device code
173173
target_compile_options(Extcovfie

src/accel/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
set(SOURCES)
77
set(PRIVATE_DEPS
88
nlohmann_json::nlohmann_json
9-
Celeritas::Extcovfie # NOTE: only needed by CartMapMagneticField.cc
9+
Celeritas::Extcovfie # NOTE: only needed by Cart/RZMapMagneticField.cc
1010
Celeritas::ExtThrust # TODO: add thrust exception conversion to corecel?
1111
Celeritas::ExtOpenMP # TODO: provide thread utils in corecel
1212
)
@@ -25,6 +25,7 @@ list(APPEND SOURCES
2525
AlongStepFactory.cc
2626
CartMapMagneticField.cc
2727
CylMapMagneticField.cc
28+
RZMapMagneticField.cc
2829
FastSimulationIntegration.cc
2930
FastSimulationModel.cc
3031
GeantSimpleCalo.cc

src/accel/RZMapMagneticField.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//------------------------------- -*- C++ -*- -------------------------------//
2+
// Copyright Celeritas contributors: see top-level COPYRIGHT file for details
3+
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4+
//---------------------------------------------------------------------------//
5+
//! \file accel/RZMapMagneticField.cc
6+
//---------------------------------------------------------------------------//
7+
#include "RZMapMagneticField.hh"
8+
9+
#include "celeritas/field/RZMapField.hh"
10+
11+
namespace celeritas
12+
{
13+
//---------------------------------------------------------------------------//
14+
// RZMAPMAGNETICFIELD IMPLEMENTATION
15+
//---------------------------------------------------------------------------//
16+
17+
Real3 RZAdapterField::operator()(Real3 const& pos) const
18+
{
19+
RZMapField calc_field{data};
20+
return calc_field(pos);
21+
}
22+
23+
//---------------------------------------------------------------------------//
24+
} // namespace celeritas

src/accel/RZMapMagneticField.hh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,30 @@
66
//---------------------------------------------------------------------------//
77
#pragma once
88

9-
#include "celeritas/field/RZMapField.hh"
9+
#include "corecel/Types.hh"
1010
#include "celeritas/field/RZMapFieldParams.hh"
1111
#include "celeritas/g4/MagneticField.hh"
1212

1313
namespace celeritas
1414
{
1515
//---------------------------------------------------------------------------//
16-
//! Geant4 magnetic field class
16+
/*!
17+
* On-the-fly field calculation with covfie using Celeritas data+units.
18+
*
19+
* This "adapter" implementation hides the covfie dependency from downstream
20+
* users.
21+
*/
22+
struct RZAdapterField
23+
{
24+
HostCRef<RZMapFieldParamsData> const& data;
25+
26+
Real3 operator()(Real3 const&) const;
27+
};
28+
29+
//---------------------------------------------------------------------------//
30+
//! Geant4 magnetic field class for R-Z cylindrically symmetric field
1731
using RZMapMagneticField
18-
= celeritas::MagneticField<RZMapFieldParams, RZMapField>;
32+
= celeritas::MagneticField<RZMapFieldParams, RZAdapterField>;
1933

2034
//---------------------------------------------------------------------------//
2135
} // namespace celeritas

src/celeritas/CMakeLists.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ list(APPEND SOURCES
6262
field/FieldDriverOptions.cc
6363
field/FieldDriverOptionsIO.json.cc
6464
field/RZMapFieldInputIO.json.cc
65-
field/RZMapFieldParams.cc
6665
field/CylMapFieldParams.cc
6766
field/UniformFieldParams.cc
6867
geo/GeoMaterialParams.cc
@@ -196,9 +195,12 @@ if(CELERITAS_USE_covfie)
196195
set(_celer_covfie_src
197196
field/CartMapFieldParams.covfie.cc
198197
field/LoadCovfieField.covfie.cc
198+
field/RZMapFieldParams.covfie.cc
199199
)
200200
celeritas_polysource_append(_celer_covfie_src
201201
alongstep/AlongStepCartMapFieldMscAction)
202+
celeritas_polysource_append(_celer_covfie_src
203+
alongstep/AlongStepRZMapFieldMscAction)
202204
if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP)
203205
# NOTE: object libraries do not work properly with RDC builds or HIP
204206
list(APPEND SOURCES ${_celer_covfie_src})
@@ -223,6 +225,8 @@ if(CELERITAS_USE_covfie)
223225
else()
224226
celeritas_polysource_append(SOURCES
225227
alongstep/AlongStepCartMapFieldMscAction)
228+
celeritas_polysource_append(SOURCES
229+
alongstep/AlongStepRZMapFieldMscAction)
226230
endif()
227231

228232
if(CELERITAS_USE_Geant4)
@@ -370,7 +374,6 @@ endmacro()
370374
celeritas_polysource(alongstep/AlongStepGeneralLinearAction)
371375
celeritas_polysource(alongstep/AlongStepNeutralAction)
372376
celeritas_polysource(alongstep/AlongStepUniformMscAction)
373-
celeritas_polysource(alongstep/AlongStepRZMapFieldMscAction)
374377
celeritas_polysource(alongstep/AlongStepCylMapFieldMscAction)
375378
celeritas_polysource(em/model/BetheHeitlerModel)
376379
celeritas_polysource(em/model/BetheBlochModel)

src/celeritas/alongstep/AlongStepRZMapFieldMscAction.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include "celeritas/Types.hh"
1515
#include "celeritas/em/data/FluctuationData.hh"
1616
#include "celeritas/em/data/UrbanMscData.hh"
17-
#include "celeritas/field/RZMapFieldData.hh"
1817
#include "celeritas/field/RZMapFieldParams.hh"
1918
#include "celeritas/global/ActionInterface.hh"
2019

src/celeritas/field/CartMapField.hh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
#if CELERITAS_USE_COVFIE || __DOXYGEN__
1212
# include "CartMapField.covfie.hh"
1313
#else
14+
# include "CartMapFieldData.hh"
15+
1416
# include "detail/NotImplementedField.hh"
1517
namespace celeritas
1618
{
1719
//---------------------------------------------------------------------------//
1820
//! Dummy class for cartesian map magnetic field when no backend is available.
19-
using CartMapField = detail::NotImplementedField;
21+
using CartMapField = detail::NotImplementedField<CartMapFieldParamsData>;
2022
} // namespace celeritas
2123
#endif // CELERITAS_USE_COVFIE
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//------------------------------- -*- C++ -*- -------------------------------//
2+
// Copyright Celeritas contributors: see top-level COPYRIGHT file for details
3+
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4+
//---------------------------------------------------------------------------//
5+
//! \file celeritas/field/RZMapField.covfie.hh
6+
//---------------------------------------------------------------------------//
7+
#pragma once
8+
9+
#include "corecel/Macros.hh"
10+
#include "corecel/Types.hh"
11+
#include "corecel/cont/Array.hh"
12+
#include "corecel/math/Algorithms.hh"
13+
#include "celeritas/Types.hh"
14+
15+
#include "RZMapFieldData.covfie.hh" // IWYU pragma: keep
16+
17+
#include "detail/CovfieRZFieldTraits.hh"
18+
19+
namespace celeritas
20+
{
21+
//---------------------------------------------------------------------------//
22+
/*!
23+
* Evaluate the value of magnetic field based on a volume-based RZ field map.
24+
*
25+
* Values outside the grid are returned as zero.
26+
*/
27+
class RZMapField
28+
{
29+
public:
30+
//!@{
31+
//! \name Type aliases
32+
using real_type = float;
33+
using Real3 = Array<celeritas::real_type, 3>;
34+
using ParamsRef = NativeCRef<RZMapFieldParamsData>;
35+
//!@}
36+
37+
public:
38+
// Construct with the shared map data
39+
inline CELER_FUNCTION explicit RZMapField(ParamsRef const& shared);
40+
41+
// Evaluate the magnetic field value for the given position
42+
CELER_FUNCTION
43+
inline Real3 operator()(Real3 const& pos) const;
44+
45+
private:
46+
using field_view_t =
47+
typename detail::CovfieRZFieldTraits<MemSpace::native>::field_t::view_t;
48+
ParamsRef const& shared_;
49+
field_view_t const& field_;
50+
};
51+
52+
//---------------------------------------------------------------------------//
53+
// INLINE DEFINITIONS
54+
//---------------------------------------------------------------------------//
55+
/*!
56+
* Construct with the shared magnetic field map data.
57+
*/
58+
CELER_FUNCTION
59+
RZMapField::RZMapField(ParamsRef const& shared)
60+
: shared_{shared}, field_{get_rz_map_field_view(shared)}
61+
{
62+
}
63+
64+
//---------------------------------------------------------------------------//
65+
/*!
66+
* Calculate the magnetic field vector for the given position.
67+
*
68+
* This queries the covfie 2D field at (r, z) to obtain (Br, Bz), then
69+
* projects Br onto the x and y components using the position direction.
70+
* The result is in the native Celeritas unit system.
71+
*/
72+
CELER_FUNCTION auto RZMapField::operator()(Real3 const& pos) const -> Real3
73+
{
74+
using traits_t = detail::CovfieRZFieldTraits<MemSpace::native>;
75+
celeritas::real_type r = hypot(pos[0], pos[1]);
76+
77+
// Return zero outside the grid
78+
if (r < shared_.min_r || r > shared_.max_r || pos[2] < shared_.min_z
79+
|| pos[2] > shared_.max_z)
80+
{
81+
return {0, 0, 0};
82+
}
83+
84+
auto bvec = traits_t::to_array(
85+
field_.at(static_cast<real_type>(r), static_cast<real_type>(pos[2])));
86+
87+
// bvec = {Br, Bz}
88+
Real3 value;
89+
value[2] = bvec[1];
90+
91+
celeritas::real_type br_over_r = (r != 0) ? bvec[0] / r
92+
: celeritas::real_type(0);
93+
value[0] = br_over_r * pos[0];
94+
value[1] = br_over_r * pos[1];
95+
96+
return value;
97+
}
98+
99+
//---------------------------------------------------------------------------//
100+
} // namespace celeritas

src/celeritas/field/RZMapField.hh

Lines changed: 9 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -6,104 +6,19 @@
66
//---------------------------------------------------------------------------//
77
#pragma once
88

9-
#include <cmath>
9+
#include "corecel/Config.hh"
1010

11-
#include "corecel/Macros.hh"
12-
#include "corecel/Types.hh"
13-
#include "corecel/grid/FindInterp.hh"
14-
#include "corecel/grid/UniformGrid.hh"
15-
#include "corecel/math/Algorithms.hh"
16-
#include "celeritas/Types.hh"
17-
#include "celeritas/Units.hh"
18-
19-
#include "RZMapFieldData.hh"
11+
#if CELERITAS_USE_COVFIE || __DOXYGEN__
12+
# include "RZMapField.covfie.hh"
13+
#else
14+
# include "RZMapFieldData.hh"
2015

16+
# include "detail/NotImplementedField.hh"
2117
namespace celeritas
2218
{
2319
//---------------------------------------------------------------------------//
24-
/*!
25-
* Evaluate the value of magnetic field based on a volume-based RZ field map.
26-
*/
27-
class RZMapField
28-
{
29-
public:
30-
//!@{
31-
//! \name Type aliases
32-
using Real3 = Array<real_type, 3>;
33-
using ParamsRef = NativeCRef<RZMapFieldParamsData>;
34-
//!@}
35-
36-
public:
37-
// Construct with the shared map data
38-
inline CELER_FUNCTION explicit RZMapField(ParamsRef const& shared);
39-
40-
// Evaluate the magnetic field value for the given position
41-
CELER_FUNCTION
42-
inline Real3 operator()(Real3 const& pos) const;
43-
44-
private:
45-
// Shared constant field map
46-
ParamsRef const& params_;
47-
48-
UniformGrid const grid_r_;
49-
UniformGrid const grid_z_;
50-
};
51-
52-
//---------------------------------------------------------------------------//
53-
// INLINE DEFINITIONS
54-
//---------------------------------------------------------------------------//
55-
/*!
56-
* Construct with the shared magnetic field map data.
57-
*/
58-
CELER_FUNCTION
59-
RZMapField::RZMapField(ParamsRef const& params)
60-
: params_(params)
61-
, grid_r_(params_.grids.data_r)
62-
, grid_z_(params_.grids.data_z)
63-
{
64-
}
65-
66-
//---------------------------------------------------------------------------//
67-
/*!
68-
* Calculate the magnetic field vector for the given position.
69-
*
70-
* This does a 2-D interpolation on the input grid and reconstructs the
71-
* magnetic field vector from the stored R and Z components of the field. The
72-
* result is in the native Celeritas unit system.
73-
*/
74-
CELER_FUNCTION auto RZMapField::operator()(Real3 const& pos) const -> Real3
75-
{
76-
CELER_ENSURE(params_);
77-
78-
Real3 value{0, 0, 0};
79-
80-
real_type r = hypot(pos[0], pos[1]);
81-
82-
if (!params_.valid(pos[2], r))
83-
return value;
84-
85-
// Find interpolation points for given r and z
86-
FindInterp<real_type> interp_r = find_interp<UniformGrid>(grid_r_, r);
87-
FindInterp<real_type> interp_z = find_interp<UniformGrid>(grid_z_, pos[2]);
88-
89-
size_type ir = interp_r.index;
90-
size_type iz = interp_z.index;
91-
92-
// z component
93-
real_type low = params_.fieldmap[params_.id(iz, ir)].value_z;
94-
real_type high = params_.fieldmap[params_.id(iz + 1, ir)].value_z;
95-
value[2] = low + (high - low) * interp_z.fraction;
96-
97-
// x and y components
98-
low = params_.fieldmap[params_.id(iz, ir)].value_r;
99-
high = params_.fieldmap[params_.id(iz, ir + 1)].value_r;
100-
real_type tmp = (r != 0) ? (low + (high - low) * interp_r.fraction) / r
101-
: low;
102-
value[0] = tmp * pos[0];
103-
value[1] = tmp * pos[1];
104-
105-
return value;
106-
}
107-
20+
//! Dummy class for R-Z map magnetic field when no backend is available.
21+
using RZMapField = detail::NotImplementedField<RZMapFieldParamsData>;
10822
//---------------------------------------------------------------------------//
10923
} // namespace celeritas
24+
#endif // CELERITAS_USE_COVFIE

0 commit comments

Comments
 (0)