Skip to content

Commit 2e3d8ab

Browse files
committed
Refactor: move types and coordinate systems to own header files
1 parent d70ef2a commit 2e3d8ab

File tree

8 files changed

+519
-442
lines changed

8 files changed

+519
-442
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// SPDX-FileCopyrightText: 2025 VTT Technical Research Centre of Finland Ltd
2+
// SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
#pragma once
5+
6+
#include "types.hpp"
7+
8+
namespace pfc {
9+
namespace csys {
10+
11+
using pfc::types::Bool3;
12+
using pfc::types::Int3;
13+
using pfc::types::Real3;
14+
15+
/**
16+
* @brief Primary template for defining coordinate systems by tag.
17+
*
18+
* @details
19+
* The `CoordinateSystem<Tag>` template provides a mechanism to define coordinate
20+
* systems in a modular and extensible way, where each `Tag` corresponds to a
21+
* specific geometry (e.g., Cartesian, Polar, Cylindrical).
22+
*
23+
* Specializations of this template should define:
24+
* - The internal parameters of the coordinate system (e.g., offset, spacing)
25+
* - Methods to map between index space and physical space:
26+
* - `to_physical(const Int3&) -> Real3`
27+
* - `to_index(const Real3&) -> Int3`
28+
*
29+
* This design decouples geometry from logic and enables user-defined coordinate
30+
* systems to integrate cleanly with the simulation framework. It also avoids
31+
* inheritance or runtime polymorphism, favoring compile-time specialization and
32+
* inlining for performance-critical transformations.
33+
*
34+
* Example usage:
35+
* @code
36+
* struct CartesianTag {};
37+
*
38+
* template <>
39+
* struct CoordinateSystem<CartesianTag> {
40+
* Real3 offset;
41+
* Real3 spacing;
42+
*
43+
* Real3 to_physical(const Int3& idx) const noexcept;
44+
* Int3 to_index(const Real3& pos) const noexcept;
45+
* };
46+
* @endcode
47+
*
48+
* Coordinate systems are used by the `World<Tag>` class and related infrastructure
49+
* to define how grid indices map to real-world physical coordinates.
50+
*
51+
* @tparam Tag A user-defined type that uniquely identifies the coordinate system.
52+
*/
53+
template <typename Tag> struct CoordinateSystem;
54+
55+
/**
56+
* @brief Trait class for providing default parameters for coordinate systems.
57+
*
58+
* @details
59+
* This traits template defines default values (e.g., offset, spacing, periodicity)
60+
* for coordinate systems identified by their tag type `CoordTag`.
61+
*
62+
* The primary template is left undefined, and users are expected to specialize
63+
* this trait for their own coordinate system tags to provide meaningful defaults.
64+
*
65+
* This mechanism allows external extension of coordinate system behavior
66+
* without modifying the core library, enabling user-defined systems to
67+
* seamlessly integrate with generic `World` construction and other infrastructure.
68+
*
69+
* Example specialization:
70+
* @code
71+
* struct MyCoordTag {};
72+
*
73+
* template <>
74+
* struct CoordinateSystemDefaults<MyCoordTag> {
75+
* static constexpr Real3 offset = {0.0, 0.0, 0.0};
76+
* static constexpr Real3 spacing = {1.0, 1.0, 1.0};
77+
* static constexpr Bool3 periodicity = {true, false, false};
78+
* };
79+
* @endcode
80+
*/
81+
template <typename CoordTag>
82+
struct CoordinateSystemDefaults; // intentionally left undefined
83+
84+
// Coordinate system tags.
85+
// struct LineTag {};
86+
// struct PlaneTag {};
87+
// struct PolarTag {};
88+
// struct CylindricalTag {};
89+
// struct SphericalTag {};
90+
// struct Polar2DTag {};
91+
// struct Toroidal2DTag {};
92+
// struct LogPolar3DTag {};
93+
94+
/**
95+
* @brief Tag type for the 3D Cartesian coordinate system.
96+
*
97+
* @details
98+
* This tag represents a standard right-handed Cartesian coordinate system in 3D,
99+
* which is the default and most commonly used geometry in OpenPFC simulations.
100+
*
101+
* The associated coordinate system maps discrete grid indices (i, j, k) to
102+
* continuous physical space using a uniform, axis-aligned grid defined by:
103+
* - `offset`: the physical position corresponding to index (0, 0, 0)
104+
* - `spacing`: the distance between adjacent grid points along each axis
105+
*
106+
* This coordinate system assumes:
107+
* - The grid is regular (uniform spacing)
108+
* - Axes are orthogonal and aligned with the simulation dimensions
109+
*
110+
* It provides a simple and efficient mapping suitable for a wide range of
111+
* physical simulations where a Euclidean space is appropriate.
112+
*/
113+
struct CartesianTag {};
114+
115+
/**
116+
* @brief Default parameters for the 3D Cartesian coordinate system.
117+
*
118+
* @details
119+
* This specialization of `CoordinateSystemDefaults` provides the default values
120+
* used when constructing a 3D Cartesian coordinate system (`CartesianTag`)
121+
* without explicitly specifying offset, spacing, or periodicity.
122+
*
123+
* These defaults are consistent with standard simulation conventions:
124+
* - `offset = {0.0, 0.0, 0.0}` places the (0, 0, 0) index at the physical origin.
125+
* - `spacing = {1.0, 1.0, 1.0}` defines unit grid spacing in all dimensions.
126+
* - `periodicity = {true, true, true}` models a fully periodic domain.
127+
*
128+
* These values are used by factory functions and world constructors when
129+
* coordinate system parameters are not explicitly provided by the user.
130+
*/
131+
template <> struct CoordinateSystemDefaults<CartesianTag> {
132+
static constexpr Real3 offset = {0.0, 0.0, 0.0};
133+
static constexpr Real3 spacing = {1.0, 1.0, 1.0};
134+
static constexpr Bool3 periodic = {true, true, true};
135+
std::size_t dimensions = 3; ///< Number of dimensions in the coordinate system
136+
};
137+
138+
/**
139+
* @brief Specialization of the coordinate system for 3D Cartesian space.
140+
*
141+
* @details
142+
* This structure defines a uniform, axis-aligned Cartesian coordinate system
143+
* in three dimensions. It maps discrete grid indices (i, j, k) to continuous
144+
* physical coordinates using a regular grid geometry.
145+
*
146+
* The coordinate system is defined by three key parameters:
147+
* - `m_offset`: the physical position corresponding to the grid index (0, 0, 0)
148+
* - `m_spacing`: the uniform distance between adjacent grid points in each dimension
149+
* - `m_periodic`: flags indicating periodicity along each axis
150+
*
151+
* This specialization is used internally by the `World<CartesianTag>` type
152+
* and can be constructed directly or via factory methods. It is designed to
153+
* support fast, inlined coordinate transformations for performance-critical
154+
* simulation kernels.
155+
*
156+
* @constructor
157+
* Constructs a Cartesian coordinate system with the specified offset, spacing,
158+
* and periodicity. Throws `std::invalid_argument` if any spacing component
159+
* is non-positive.
160+
*
161+
* @param offset Physical position of the index (0, 0, 0)
162+
* @param spacing Grid spacing in each dimension (must be > 0)
163+
* @param periodic Periodicity flags for {x, y, z}
164+
*/
165+
template <> struct CoordinateSystem<CartesianTag> {
166+
const Real3 m_offset; ///< Physical coordinate of grid index (0, 0, 0)
167+
const Real3 m_spacing; ///< Physical spacing between grid points
168+
const Bool3 m_periodic; ///< Periodicity flags for each dimension
169+
170+
/// Constructs a 3D Cartesian coordinate system
171+
CoordinateSystem(
172+
const Real3 &offset = CoordinateSystemDefaults<CartesianTag>::offset,
173+
const Real3 &spacing = CoordinateSystemDefaults<CartesianTag>::spacing,
174+
const Bool3 &periodic = CoordinateSystemDefaults<CartesianTag>::periodic)
175+
: m_offset(offset), m_spacing(spacing), m_periodic(periodic) {
176+
for (std::size_t i = 0; i < 3; ++i) {
177+
if (spacing[i] <= 0.0) {
178+
throw std::invalid_argument("Spacing must be positive.");
179+
}
180+
};
181+
};
182+
};
183+
184+
using CartesianCS = CoordinateSystem<CartesianTag>;
185+
186+
/**
187+
* @brief Get the offset of the coordinate system.
188+
* @param cs Coordinate system object.
189+
* @return The offset of the coordinate system.
190+
*/
191+
inline const Real3 &get_offset(const CartesianCS &cs) noexcept {
192+
return cs.m_offset;
193+
};
194+
195+
/**
196+
* @brief Get the offset of the coordinate system in a specific dimension.
197+
* @param cs Coordinate system object.
198+
* @param i Dimension index.
199+
* @return The offset in the specified dimension.
200+
* @throws std::out_of_range if i is not in [0, 2].
201+
*/
202+
inline double get_offset(const CartesianCS &cs, int i) { return cs.m_offset.at(i); }
203+
204+
/**
205+
* @brief Get the spacing of the coordinate system.
206+
* @param cs Coordinate system object.
207+
* @return The spacing of the coordinate system.
208+
*/
209+
inline const Real3 &get_spacing(const CartesianCS &cs) noexcept {
210+
return cs.m_spacing;
211+
};
212+
213+
/**
214+
* @brief Get the spacing of the coordinate system in a specific dimension.
215+
* @param cs Coordinate system object.
216+
* @param i Dimension index.
217+
* @return The spacing in the specified dimension.
218+
* @throws std::out_of_range if i is not in [0, 2].
219+
*/
220+
inline double get_spacing(const CartesianCS &cs, int i) {
221+
return cs.m_spacing.at(i);
222+
}
223+
224+
/**
225+
* @brief Get the periodicity of the coordinate system.
226+
* @param cs Coordinate system object.
227+
* @return The periodicity flags.
228+
*/
229+
inline const Bool3 &get_periodic(const CartesianCS &cs) noexcept {
230+
return cs.m_periodic;
231+
};
232+
233+
/**
234+
* @brief Check if the coordinate system is periodic in a specific dimension.
235+
* @param cs Coordinate system object.
236+
* @param i Dimension index.
237+
* @return True if periodic, false otherwise.
238+
* @throws std::out_of_range if i is not in [0, 2].
239+
*/
240+
inline bool is_periodic(const CartesianCS &cs, int i) { return cs.m_periodic.at(i); }
241+
242+
/**
243+
* @brief Convert grid indices to physical coordinates.
244+
* @param cs Coordinate system object.
245+
* @param idx Grid indices.
246+
* @return The physical coordinates.
247+
*/
248+
inline const Real3 to_coords(const CartesianCS &cs, const Int3 &idx) noexcept {
249+
Real3 xyz;
250+
const auto &offset = get_offset(cs);
251+
const auto &spacing = get_spacing(cs);
252+
for (int i = 0; i < 3; ++i) {
253+
xyz[i] = offset[i] + idx[i] * spacing[i];
254+
}
255+
return xyz;
256+
}
257+
258+
/**
259+
* @brief Convert physical coordinates to grid indices.
260+
* @param cs Coordinate system object.
261+
* @param xyz Physical coordinates.
262+
* @return The grid indices.
263+
*/
264+
inline const Int3 to_index(const CartesianCS &cs, const Real3 &xyz) noexcept {
265+
Int3 idx;
266+
const auto &offset = get_offset(cs);
267+
const auto &spacing = get_spacing(cs);
268+
for (int i = 0; i < 3; ++i) {
269+
idx[i] = static_cast<int>((xyz[i] - offset[i]) / spacing[i]);
270+
}
271+
return idx;
272+
}
273+
274+
} // namespace csys
275+
} // namespace pfc

0 commit comments

Comments
 (0)