SHiP Field Service
Framework-agnostic C++20 library exposing SHiP magnetic field maps.
Loading...
Searching...
No Matches
covfie_chains.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 CERN for the benefit of the SHiP Collaboration
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4#pragma once
5
6// Canonical covfie backend chains for SHiP field maps. Internal — covfie is a
7// PRIVATE dependency of the core library, so this header must not be included
8// from installed public headers.
9//
10// The chain is: affine (mm → fractional grid index) ∘ clamp (grid index to the
11// mapped box) ∘ interpolator ∘ strided ∘ float3 array. The reader interpolates
12// trilinearly. The legacy .cvf writers use nearest_neighbour instead so
13// `view.at(...)` returns an assignable lvalue during construction; linear and
14// nearest_neighbour serialise nothing of their own, so files written with the
15// writer chain load with the reader chain. Maps in the SHiP field-map format
16// skip serialisation entirely: make_reader_field adopts their values.
17
18#include <array>
19#include <cmath>
20#include <covfie/core/algebra/affine.hpp>
21#include <covfie/core/backend/primitive/array.hpp>
22#include <covfie/core/backend/transformer/affine.hpp>
23#include <covfie/core/backend/transformer/clamp.hpp>
24#include <covfie/core/backend/transformer/linear.hpp>
25#include <covfie/core/backend/transformer/nearest_neighbour.hpp>
26#include <covfie/core/backend/transformer/strided.hpp>
27#include <covfie/core/field.hpp>
28#include <covfie/core/field_view.hpp>
29#include <covfie/core/parameter_pack.hpp>
30#include <covfie/core/vector.hpp>
31#include <cstddef>
32#include <memory>
33#include <stdexcept>
34#include <string>
35#include <utility>
36#include <variant>
37
38namespace SHiP::detail {
39
40using array_backend_t = covfie::backend::array<covfie::vector::float3>;
41
43using stored_vector_t = array_backend_t::vector_t;
44
45using stored_backend_t = covfie::backend::strided<covfie::vector::size3, array_backend_t>;
46
47using reader_field_t = covfie::field<
48 covfie::backend::affine<covfie::backend::clamp<covfie::backend::linear<stored_backend_t>>>>;
49
50using writer_field_t = covfie::field<covfie::backend::affine<
51 covfie::backend::clamp<covfie::backend::nearest_neighbour<stored_backend_t>>>>;
52
54struct GridSpec {
55 std::array<float, 3> min;
56 std::array<float, 3> max;
57 std::array<std::size_t, 3> n;
58};
59
64inline void validate(GridSpec const& spec) {
65 for (std::size_t i = 0; i < 3; ++i) {
66 if (spec.n[i] < 2) {
67 throw std::invalid_argument("GridSpec: axis " + std::to_string(i) +
68 " needs at least 2 samples");
69 }
70 if (!(spec.max[i] > spec.min[i])) {
71 throw std::invalid_argument("GridSpec: axis " + std::to_string(i) + " needs max > min");
72 }
73 }
74}
75
80[[nodiscard]] inline float grid_pos(float min, float max, std::size_t n, std::size_t i) {
81 return min + static_cast<float>(i) * (max - min) / static_cast<float>(n - 1);
82}
83
85[[nodiscard]] inline covfie::algebra::affine<3> make_affine(GridSpec const& spec) {
86 covfie::algebra::affine<3> translation =
87 covfie::algebra::affine<3>::translation(-spec.min[0], -spec.min[1], -spec.min[2]);
88 covfie::algebra::affine<3> scaling = covfie::algebra::affine<3>::scaling(
89 static_cast<float>(spec.n[0] - 1) / (spec.max[0] - spec.min[0]),
90 static_cast<float>(spec.n[1] - 1) / (spec.max[1] - spec.min[1]),
91 static_cast<float>(spec.n[2] - 1) / (spec.max[2] - spec.min[2]));
92 return scaling * translation;
93}
94
99template <typename clamp_cfg_t>
100[[nodiscard]] clamp_cfg_t make_clamp(GridSpec const& spec) {
101 clamp_cfg_t clamp_cfg{};
102 for (std::size_t i = 0; i < 3; ++i) {
103 clamp_cfg.min[i] = 0.0f;
104 clamp_cfg.max[i] = std::nextafter(static_cast<float>(spec.n[i] - 1), 0.0f);
105 }
106 return clamp_cfg;
107}
108
111[[nodiscard]] inline writer_field_t make_writer_field(GridSpec const& spec) {
112 validate(spec);
113 using clamp_cfg_t = writer_field_t::backend_t::backend_t::configuration_t;
114 return writer_field_t(covfie::make_parameter_pack(
115 writer_field_t::backend_t::configuration_t(make_affine(spec)),
116 make_clamp<clamp_cfg_t>(spec), std::monostate{},
117 writer_field_t::backend_t::backend_t::backend_t::backend_t::configuration_t{
118 spec.n[0], spec.n[1], spec.n[2]}));
119}
120
124[[nodiscard]] inline reader_field_t make_reader_field(GridSpec const& spec,
125 std::unique_ptr<stored_vector_t[]> values) {
126 validate(spec);
127 using affine_t = reader_field_t::backend_t;
128 using clamp_t = affine_t::backend_t;
129 using linear_t = clamp_t::backend_t;
130 using strided_t = linear_t::backend_t;
131 std::size_t const size = spec.n[0] * spec.n[1] * spec.n[2];
132 strided_t::owning_data_t stored(strided_t::configuration_t{spec.n[0], spec.n[1], spec.n[2]},
133 array_backend_t::owning_data_t(size, std::move(values)));
134 linear_t::owning_data_t linear(std::monostate{}, std::move(stored));
135 clamp_t::owning_data_t clamp(make_clamp<clamp_t::configuration_t>(spec), std::move(linear));
136 return reader_field_t(affine_t::owning_data_t(make_affine(spec), std::move(clamp)));
137}
138
139} // namespace SHiP::detail
Definition covfie_chains.h:38
array_backend_t::vector_t stored_vector_t
One stored node: {Bx, By, Bz} in Tesla.
Definition covfie_chains.h:43
covfie::algebra::affine< 3 > make_affine(GridSpec const &spec)
World (mm) to fractional grid index: node i on each axis maps to i.
Definition covfie_chains.h:85
covfie::backend::array< covfie::vector::float3 > array_backend_t
Definition covfie_chains.h:40
covfie::field< covfie::backend::affine< covfie::backend::clamp< covfie::backend::nearest_neighbour< stored_backend_t > > > > writer_field_t
Definition covfie_chains.h:51
clamp_cfg_t make_clamp(GridSpec const &spec)
Clamp fractional grid indices to the mapped box.
Definition covfie_chains.h:100
float grid_pos(float min, float max, std::size_t n, std::size_t i)
Position of sample i on an axis with n samples spanning [min, max].
Definition covfie_chains.h:80
void validate(GridSpec const &spec)
Throw if spec cannot produce a well-formed grid: every axis needs at least two samples (n - 1 underfl...
Definition covfie_chains.h:64
covfie::backend::strided< covfie::vector::size3, array_backend_t > stored_backend_t
Definition covfie_chains.h:45
reader_field_t make_reader_field(GridSpec const &spec, std::unique_ptr< stored_vector_t[]> values)
Build a reader-chain field over spec that adopts values: one node per grid point, n[0] * n[1] * n[2] ...
Definition covfie_chains.h:124
covfie::field< covfie::backend::affine< covfie::backend::clamp< covfie::backend::linear< stored_backend_t > > > > reader_field_t
Definition covfie_chains.h:48
writer_field_t make_writer_field(GridSpec const &spec)
Build an empty writer-chain field over spec, ready to be filled via writer_field_t::view_t::at(x,...
Definition covfie_chains.h:111
Axis-aligned grid: n[i] samples spanning [min[i], max[i]] (mm).
Definition covfie_chains.h:54
std::array< float, 3 > min
Definition covfie_chains.h:55
std::array< std::size_t, 3 > n
Definition covfie_chains.h:57
std::array< float, 3 > max
Definition covfie_chains.h:56