numerics 0.1.0
Loading...
Searching...
No Matches
fields.cpp
Go to the documentation of this file.
1/// @file src/pde/fields.cpp
2/// @brief Implementations for num::ScalarField3D, VectorField3D, FieldSolver,
3/// MagneticSolver.
4#include "pde/fields.hpp"
6#include "pde/stencil.hpp"
7#include <algorithm>
8#include <unordered_map>
9
10namespace num {
11
12// ScalarField3D
13
15 int ny,
16 int nz,
17 float dx,
18 float ox,
19 float oy,
20 float oz)
21 : grid_(nx, ny, nz, static_cast<double>(dx)),
22 ox_(ox),
23 oy_(oy),
24 oz_(oz) {
25}
26
27float ScalarField3D::sample(float x, float y, float z) const {
28 const float gx = (x - ox_) / dx();
29 const float gy = (y - oy_) / dx();
30 const float gz = (z - oz_) / dx();
31
32 if (gx < 0 || gx >= nx() - 1 || gy < 0 || gy >= ny() - 1 || gz < 0 || gz >= nz() - 1)
33 return 0.0f;
34
35 const int i0 = static_cast<int>(gx);
36 const int j0 = static_cast<int>(gy);
37 const int k0 = static_cast<int>(gz);
38 const float tx = gx - i0, ty = gy - j0, tz = gz - k0;
39
40 auto v = [&](int di, int dj, int dk) {
41 return static_cast<float>(grid_(i0 + di, j0 + dj, k0 + dk));
42 };
43 return (1 - tz)
44 * ((1 - ty) * ((1 - tx) * v(0, 0, 0) + tx * v(1, 0, 0))
45 + ty * ((1 - tx) * v(0, 1, 0) + tx * v(1, 1, 0)))
46 + tz
47 * ((1 - ty) * ((1 - tx) * v(0, 0, 1) + tx * v(1, 0, 1))
48 + ty * ((1 - tx) * v(0, 1, 1) + tx * v(1, 1, 1)));
49}
50
51// VectorField3D
52
54 int ny,
55 int nz,
56 float dx,
57 float ox,
58 float oy,
59 float oz)
60 : x(nx, ny, nz, dx, ox, oy, oz),
61 y(nx, ny, nz, dx, ox, oy, oz),
62 z(nx, ny, nz, dx, ox, oy, oz) {
63}
64
65std::array<float, 3> VectorField3D::sample(float px, float py, float pz) const {
66 return {x.sample(px, py, pz), y.sample(px, py, pz), z.sample(px, py, pz)};
67}
68
69void VectorField3D::scale(float s) {
70 auto sc = [&](Grid3D& g) {
71 auto v = g.to_vector();
72 num::scale(v, static_cast<real>(s));
73 g.from_vector(v);
74 };
75 sc(x.grid());
76 sc(y.grid());
77 sc(z.grid());
78}
79
80// FieldSolver
81
83 const ScalarField3D& source,
84 double tol,
85 int max_iter) {
86 const Grid3D& g = phi.grid();
87 const int nx = g.nx(), ny = g.ny(), nz = g.nz();
88 const double inv_dx2 = 1.0 / (g.dx() * g.dx());
89 const int N = nx * ny * nz;
90
91 auto flat = [&](int i, int j, int k) -> idx {
92 return static_cast<idx>(k * ny * nx + j * nx + i);
93 };
94
95 Vector b(N, 0.0);
96 for (int k = 1; k < nz - 1; ++k)
97 for (int j = 1; j < ny - 1; ++j)
98 for (int i = 1; i < nx - 1; ++i)
99 b[flat(i, j, k)] = -source.grid()(i, j, k);
100
101 Vector xv = phi.grid().to_vector();
102 auto matvec = [&](const Vector& v, Vector& Av) {
103 neg_laplacian_3d(v, Av, nx, ny, nz, inv_dx2);
104 };
105 auto A = operators::make_op(matvec, static_cast<idx>(N));
106 auto result = num::cg(A, b, xv, tol, static_cast<idx>(max_iter));
107 phi.grid().from_vector(xv);
108 return result;
109}
110
112 const ScalarField3D& coeff,
113 const std::vector<DirichletBC>& bcs,
114 double tol,
115 int max_iter) {
116 const Grid3D& g = phi.grid();
117 const Grid3D& cg = coeff.grid();
118 const int nx = g.nx(), ny = g.ny(), nz = g.nz();
119 const int N = nx * ny * nz;
120 const double inv_dx2 = 1.0 / (g.dx() * g.dx());
121
122 auto flat = [&](int i, int j, int k) -> idx {
123 return static_cast<idx>(k * ny * nx + j * nx + i);
124 };
125
126 std::unordered_map<int, double> bc_map;
127 bc_map.reserve(bcs.size());
128 for (const auto& e : bcs)
129 bc_map[e.flat_idx] = e.value;
130
131 constexpr int DI[6] = {1, -1, 0, 0, 0, 0};
132 constexpr int DJ[6] = {0, 0, 1, -1, 0, 0};
133 constexpr int DK[6] = {0, 0, 0, 0, 1, -1};
134 constexpr double penalty = 1e10;
135
136 // Symmetric penalty elimination: for each BC node, add coeff*phi_bc to
137 // the RHS of neighbouring free nodes so the system stays SPD.
138 Vector b(N, 0.0);
139 for (const auto& e : bcs) {
140 b[e.flat_idx] = penalty * e.value;
141 const int ei = e.flat_idx % nx;
142 const int ej = (e.flat_idx / nx) % ny;
143 const int ek = e.flat_idx / (nx * ny);
144 for (int d = 0; d < 6; ++d) {
145 int ni = ei + DI[d], nj = ej + DJ[d], nk = ek + DK[d];
146 if (ni < 0 || ni >= nx || nj < 0 || nj >= ny || nk < 0 || nk >= nz)
147 continue;
148 int nidx = flat(ni, nj, nk);
149 if (bc_map.count(nidx))
150 continue;
151 double sigma_face = 0.5 * (cg(ei, ej, ek) + cg(ni, nj, nk));
152 b[nidx] += sigma_face * inv_dx2 * e.value;
153 }
154 }
155
156 auto matvec = [&](const Vector& v, Vector& Av) {
157 for (int k = 0; k < nz; ++k)
158 for (int j = 0; j < ny; ++j)
159 for (int i = 0; i < nx; ++i) {
160 int id = flat(i, j, k);
161 if (bc_map.count(id)) {
162 Av[id] = penalty * v[id];
163 continue;
164 }
165 double Av_ijk = 0.0;
166 for (int d = 0; d < 6; ++d) {
167 int ni = std::max(0, std::min(i + DI[d], nx - 1));
168 int nj = std::max(0, std::min(j + DJ[d], ny - 1));
169 int nk = std::max(0, std::min(k + DK[d], nz - 1));
170 int nidx = flat(ni, nj, nk);
171 double c_face = 0.5 * (cg(i, j, k) + cg(ni, nj, nk));
172 double v_nb = bc_map.count(nidx) ? 0.0 : v[nidx];
173 Av_ijk += c_face * inv_dx2 * (v[id] - v_nb);
174 }
175 Av[id] = Av_ijk;
176 }
177 };
178
179 Vector xv = phi.grid().to_vector();
180 auto A = operators::make_op(matvec, static_cast<idx>(N));
181 auto result = num::cg(A, b, xv, tol, static_cast<idx>(max_iter));
182 phi.grid().from_vector(xv);
183 return result;
184}
185
188 out(phi.nx(), phi.ny(), phi.nz(), phi.dx(), phi.ox(), phi.oy(), phi.oz());
189 gradient_3d(phi.grid(), out.x.grid(), out.y.grid(), out.z.grid());
190 return out;
191}
192
195 out(f.x.nx(), f.x.ny(), f.x.nz(), f.x.dx(), f.x.ox(), f.x.oy(), f.x.oz());
196 divergence_3d(f.x.grid(), f.y.grid(), f.z.grid(), out.grid());
197 return out;
198}
199
201 VectorField3D B(A.x.nx(), A.x.ny(), A.x.nz(), A.x.dx(), A.x.ox(), A.x.oy(), A.x.oz());
202 curl_3d(A.x.grid(), A.y.grid(), A.z.grid(), B.x.grid(), B.y.grid(), B.z.grid());
203 return B;
204}
205
206// MagneticSolver
207
209 const ScalarField3D& phi) {
211 const Grid3D& sg = sigma.grid();
212 const int nx = sg.nx(), ny = sg.ny(), nz = sg.nz();
213 for (int k = 0; k < nz; ++k)
214 for (int j = 0; j < ny; ++j)
215 for (int i = 0; i < nx; ++i) {
216 const double neg_s = -sg(i, j, k);
217 J.x.grid()(i, j, k) *= neg_s;
218 J.y.grid()(i, j, k) *= neg_s;
219 J.z.grid()(i, j, k) *= neg_s;
220 }
221 return J;
222}
223
225 double tol,
226 int max_iter) {
227 const int nx = J.x.nx(), ny = J.x.ny(), nz = J.x.nz();
228 const float dx = J.x.dx(), ox = J.x.ox(), oy = J.x.oy(), oz = J.x.oz();
229
230 auto make_source = [&](const ScalarField3D& Jc) {
231 ScalarField3D src(nx, ny, nz, dx, ox, oy, oz);
232 const Grid3D& gJ = Jc.grid();
233 Grid3D& gs = src.grid();
234 for (int k = 0; k < nz; ++k)
235 for (int j = 0; j < ny; ++j)
236 for (int i = 0; i < nx; ++i)
237 gs(i, j, k) = -MU0 * gJ(i, j, k);
238 return src;
239 };
240
241 ScalarField3D Ax(nx, ny, nz, dx, ox, oy, oz);
242 ScalarField3D Ay(nx, ny, nz, dx, ox, oy, oz);
243 ScalarField3D Az(nx, ny, nz, dx, ox, oy, oz);
244
245 FieldSolver::solve_poisson(Ax, make_source(J.x), tol, max_iter);
246 FieldSolver::solve_poisson(Ay, make_source(J.y), tol, max_iter);
247 FieldSolver::solve_poisson(Az, make_source(J.z), tol, max_iter);
248
249 VectorField3D A(nx, ny, nz, dx, ox, oy, oz);
250 A.x = Ax;
251 A.y = Ay;
252 A.z = Az;
253 return FieldSolver::curl(A);
254}
255
256} // namespace num
static VectorField3D gradient(const ScalarField3D &phi)
Compute .
Definition fields.cpp:186
static SolverResult solve_var_poisson(ScalarField3D &phi, const ScalarField3D &coeff, const std::vector< DirichletBC > &bcs, double tol=1e-6, int max_iter=500)
Solve with Dirichlet data.
Definition fields.cpp:111
static SolverResult solve_poisson(ScalarField3D &phi, const ScalarField3D &source, double tol=1e-6, int max_iter=500)
Solve with zero Dirichlet boundaries.
Definition fields.cpp:82
static VectorField3D curl(const VectorField3D &A)
Definition fields.cpp:200
static ScalarField3D divergence(const VectorField3D &f)
Compute .
Definition fields.cpp:193
double dx() const
Definition grid3d.hpp:24
int ny() const
Definition grid3d.hpp:22
int nz() const
Definition grid3d.hpp:23
int nx() const
Definition grid3d.hpp:21
static VectorField3D solve_magnetic_field(const VectorField3D &J, double tol=1e-6, int max_iter=500)
Definition fields.cpp:224
static VectorField3D current_density(const ScalarField3D &sigma, const ScalarField3D &phi)
Compute current density J = -sigma*grad(phi) [A/m^2].
Definition fields.cpp:208
static constexpr double MU0
mu_0 [H/m]
Definition fields.hpp:135
float oy() const
Definition fields.hpp:43
float ox() const
Definition fields.hpp:42
int nx() const
Definition fields.hpp:38
float dx() const
Definition fields.hpp:41
float sample(float x, float y, float z) const
Definition fields.cpp:27
ScalarField3D(int nx, int ny, int nz, float dx, float ox=0.0f, float oy=0.0f, float oz=0.0f)
Definition fields.cpp:14
int ny() const
Definition fields.hpp:39
Grid3D & grid()
Definition fields.hpp:35
float oz() const
Definition fields.hpp:44
int nz() const
Definition fields.hpp:40
CallableOp< F > make_op(F f, idx rows, idx cols)
Definition callable.hpp:39
double real
Definition types.hpp:10
constexpr real phi
Golden ratio.
Definition math.hpp:45
void curl_3d(const Grid3D &ax, const Grid3D &ay, const Grid3D &az, Grid3D &bx, Grid3D &by, Grid3D &bz)
Compute with central differences.
Definition stencil.hpp:294
void neg_laplacian_3d(const Vector &x, Vector &y, int nx, int ny, int nz, double inv_dx2)
Compute on a 3D grid.
Definition stencil.hpp:232
std::size_t idx
Definition types.hpp:11
void matvec(const Matrix &A, const Vector &x, Vector &y, Backend b=default_backend)
y = A * x
Definition matrix.cpp:45
void scale(Vector &v, real alpha, Backend b=default_backend)
Compute .
Definition vector.cpp:15
constexpr real e
Definition math.hpp:44
void divergence_3d(const Grid3D &fx, const Grid3D &fy, const Grid3D &fz, Grid3D &out)
Compute with central differences.
Definition stencil.hpp:274
void gradient_3d(const Grid3D &phi, Grid3D &gx, Grid3D &gy, Grid3D &gz)
Compute with central differences.
Definition stencil.hpp:258
SolverResult cg(const Matrix &A, const Vector &b, Vector &x, real tol=1e-10, idx max_iter=1000, Backend backend=default_backend)
Definition cg.cpp:8
3D scalar and vector fields on Cartesian grids, with PDE field solvers.
Umbrella include for all linear solvers.
Higher-order stencil and grid-sweep utilities.
std::array< float, 3 > sample(float px, float py, float pz) const
Trilinear-interpolated field vector at world position.
Definition fields.cpp:65
ScalarField3D z
x, y, z components on the same grid layout
Definition fields.hpp:80
VectorField3D(int nx, int ny, int nz, float dx, float ox=0.0f, float oy=0.0f, float oz=0.0f)
Definition fields.cpp:53
ScalarField3D x
Definition fields.hpp:80
void scale(float s)
Multiply all components by scalar s.
Definition fields.cpp:69
ScalarField3D y
Definition fields.hpp:80