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 .cvf format is: affine (mm → fractional grid index) ∘ clamp (grid index
11// to the mapped box) ∘ interpolator ∘ strided ∘ float3 array. The reader
12// interpolates trilinearly; 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.
16
17#include <array>
18#include <cmath>
19#include <covfie/core/algebra/affine.hpp>
20#include <covfie/core/backend/primitive/array.hpp>
21#include <covfie/core/backend/transformer/affine.hpp>
22#include <covfie/core/backend/transformer/clamp.hpp>
23#include <covfie/core/backend/transformer/linear.hpp>
24#include <covfie/core/backend/transformer/nearest_neighbour.hpp>
25#include <covfie/core/backend/transformer/strided.hpp>
26#include <covfie/core/field.hpp>
27#include <covfie/core/field_view.hpp>
28#include <covfie/core/parameter_pack.hpp>
29#include <covfie/core/vector.hpp>
30#include <cstddef>
31#include <stdexcept>
32#include <string>
33#include <utility>
34#include <variant>
35
36namespace ship::detail {
37
39 covfie::backend::strided<covfie::vector::size3, covfie::backend::array<covfie::vector::float3>>;
40
41using reader_field_t = covfie::field<
42 covfie::backend::affine<covfie::backend::clamp<covfie::backend::linear<stored_backend_t>>>>;
43
44using writer_field_t = covfie::field<covfie::backend::affine<
45 covfie::backend::clamp<covfie::backend::nearest_neighbour<stored_backend_t>>>>;
46
48struct GridSpec {
49 std::array<float, 3> min;
50 std::array<float, 3> max;
51 std::array<std::size_t, 3> n;
52};
53
58inline void validate(GridSpec const& spec) {
59 for (std::size_t i = 0; i < 3; ++i) {
60 if (spec.n[i] < 2) {
61 throw std::invalid_argument("GridSpec: axis " + std::to_string(i) +
62 " needs at least 2 samples");
63 }
64 if (!(spec.max[i] > spec.min[i])) {
65 throw std::invalid_argument("GridSpec: axis " + std::to_string(i) + " needs max > min");
66 }
67 }
68}
69
74[[nodiscard]] inline float grid_pos(float min, float max, std::size_t n, std::size_t i) {
75 return min + static_cast<float>(i) * (max - min) / static_cast<float>(n - 1);
76}
77
80[[nodiscard]] inline writer_field_t make_writer_field(GridSpec const& spec) {
81 validate(spec);
82
83 covfie::algebra::affine<3> translation =
84 covfie::algebra::affine<3>::translation(-spec.min[0], -spec.min[1], -spec.min[2]);
85 covfie::algebra::affine<3> scaling = covfie::algebra::affine<3>::scaling(
86 static_cast<float>(spec.n[0] - 1) / (spec.max[0] - spec.min[0]),
87 static_cast<float>(spec.n[1] - 1) / (spec.max[1] - spec.min[1]),
88 static_cast<float>(spec.n[2] - 1) / (spec.max[2] - spec.min[2]));
89
90 // Clamp fractional grid indices to the mapped box. The upper bound sits
91 // one float ULP below n-1 because the trilinear interpolator always reads
92 // cell i+1; nearest_neighbour still rounds it back to n-1, so writer
93 // fills at the max corner are unaffected.
94 using clamp_cfg_t = writer_field_t::backend_t::backend_t::configuration_t;
95 clamp_cfg_t clamp_cfg{};
96 for (std::size_t i = 0; i < 3; ++i) {
97 clamp_cfg.min[i] = 0.0f;
98 clamp_cfg.max[i] = std::nextafter(static_cast<float>(spec.n[i] - 1), 0.0f);
99 }
100
101 return writer_field_t(covfie::make_parameter_pack(
102 writer_field_t::backend_t::configuration_t(scaling * translation), std::move(clamp_cfg),
103 std::monostate{},
104 writer_field_t::backend_t::backend_t::backend_t::backend_t::configuration_t{
105 spec.n[0], spec.n[1], spec.n[2]}));
106}
107
108} // namespace ship::detail
Definition covfie_chains.h:36
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:58
covfie::backend::strided< covfie::vector::size3, covfie::backend::array< covfie::vector::float3 > > stored_backend_t
Definition covfie_chains.h:39
covfie::field< covfie::backend::affine< covfie::backend::clamp< covfie::backend::nearest_neighbour< stored_backend_t > > > > writer_field_t
Definition covfie_chains.h:45
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:74
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:80
covfie::field< covfie::backend::affine< covfie::backend::clamp< covfie::backend::linear< stored_backend_t > > > > reader_field_t
Definition covfie_chains.h:42
Axis-aligned grid: n[i] samples spanning [min[i], max[i]] (mm).
Definition covfie_chains.h:48
std::array< std::size_t, 3 > n
Definition covfie_chains.h:51
std::array< float, 3 > min
Definition covfie_chains.h:49
std::array< float, 3 > max
Definition covfie_chains.h:50