8#include <unordered_map>
16 : grid_(nx, ny, nz, static_cast<double>(dx)), ox_(ox), oy_(oy), oz_(oz) {}
19 const float gx = (x - ox_) /
dx();
20 const float gy = (y - oy_) /
dx();
21 const float gz = (z - oz_) /
dx();
23 if (gx < 0 || gx >=
nx() - 1 || gy < 0 || gy >=
ny() - 1 || gz < 0 ||
27 const int i0 =
static_cast<int>(gx);
28 const int j0 =
static_cast<int>(gy);
29 const int k0 =
static_cast<int>(gz);
30 const float tx = gx - i0, ty = gy - j0, tz = gz - k0;
32 auto v = [&](
int di,
int dj,
int dk) {
33 return static_cast<float>(grid_(i0 + di, j0 + dj, k0 + dk));
35 return (1 - tz) * ((1 - ty) * ((1 - tx) * v(0, 0, 0) + tx * v(1, 0, 0)) +
36 ty * ((1 - tx) * v(0, 1, 0) + tx * v(1, 1, 0))) +
37 tz * ((1 - ty) * ((1 - tx) * v(0, 0, 1) + tx * v(1, 0, 1)) +
38 ty * ((1 - tx) * v(0, 1, 1) + tx * v(1, 1, 1)));
45 : x(nx, ny, nz, dx, ox, oy, oz), y(nx, ny, nz, dx, ox, oy, oz),
46 z(nx, ny, nz, dx, ox, oy, oz) {}
54 auto v = g.to_vector();
69 const int nx = g.
nx(), ny = g.
ny(), nz = g.
nz();
70 const double inv_dx2 = 1.0 / (g.
dx() * g.
dx());
71 const int N = nx * ny * nz;
73 auto flat = [&](
int i,
int j,
int k) ->
idx {
74 return static_cast<idx>(k * ny * nx + j * nx + i);
78 for (
int k = 1; k < nz - 1; ++k)
79 for (
int j = 1; j < ny - 1; ++j)
80 for (
int i = 1; i < nx - 1; ++i)
81 b[flat(i, j, k)] = -source.
grid()(i, j, k);
88 phi.grid().from_vector(xv);
94 const std::vector<DirichletBC> &bcs,
95 double tol,
int max_iter) {
98 const int nx = g.
nx(), ny = g.
ny(), nz = g.
nz();
99 const int N = nx * ny * nz;
100 const double inv_dx2 = 1.0 / (g.
dx() * g.
dx());
102 auto flat = [&](
int i,
int j,
int k) ->
idx {
103 return static_cast<idx>(k * ny * nx + j * nx + i);
106 std::unordered_map<int, double> bc_map;
107 bc_map.reserve(bcs.size());
108 for (
const auto &
e : bcs)
109 bc_map[
e.flat_idx] =
e.value;
111 constexpr int DI[6] = {1, -1, 0, 0, 0, 0};
112 constexpr int DJ[6] = {0, 0, 1, -1, 0, 0};
113 constexpr int DK[6] = {0, 0, 0, 0, 1, -1};
114 constexpr double penalty = 1e10;
119 for (
const auto &
e : bcs) {
120 b[
e.flat_idx] = penalty *
e.value;
121 const int ei =
e.flat_idx % nx;
122 const int ej = (
e.flat_idx / nx) % ny;
123 const int ek =
e.flat_idx / (nx * ny);
124 for (
int d = 0; d < 6; ++d) {
125 int ni = ei + DI[d], nj = ej + DJ[d], nk = ek + DK[d];
126 if (ni < 0 || ni >= nx || nj < 0 || nj >= ny || nk < 0 || nk >= nz)
128 int nidx = flat(ni, nj, nk);
129 if (bc_map.count(nidx))
131 double sigma_face = 0.5 * (
cg(ei, ej, ek) +
cg(ni, nj, nk));
132 b[nidx] += sigma_face * inv_dx2 *
e.value;
137 for (
int k = 0; k < nz; ++k)
138 for (
int j = 0; j < ny; ++j)
139 for (
int i = 0; i < nx; ++i) {
140 int id = flat(i, j, k);
141 if (bc_map.count(
id)) {
142 Av[id] = penalty * v[id];
146 for (
int d = 0; d < 6; ++d) {
147 int ni = std::max(0, std::min(i + DI[d], nx - 1));
148 int nj = std::max(0, std::min(j + DJ[d], ny - 1));
149 int nk = std::max(0, std::min(k + DK[d], nz - 1));
150 int nidx = flat(ni, nj, nk);
151 double c_face = 0.5 * (
cg(i, j, k) +
cg(ni, nj, nk));
152 double v_nb = bc_map.count(nidx) ? 0.0 : v[nidx];
153 Av_ijk += c_face * inv_dx2 * (v[id] - v_nb);
161 phi.grid().from_vector(xv);
193 const int nx = sg.
nx(), ny = sg.
ny(), nz = sg.
nz();
194 for (
int k = 0; k < nz; ++k)
195 for (
int j = 0; j < ny; ++j)
196 for (
int i = 0; i < nx; ++i) {
197 const double neg_s = -sg(i, j, k);
198 J.
x.
grid()(i, j, k) *= neg_s;
199 J.
y.
grid()(i, j, k) *= neg_s;
200 J.
z.
grid()(i, j, k) *= neg_s;
206 double tol,
int max_iter) {
207 const int nx = J.
x.
nx(), ny = J.
x.
ny(), nz = J.
x.
nz();
208 const float dx = J.
x.
dx(), ox = J.
x.
ox(), oy = J.
x.
oy(), oz = J.
x.
oz();
212 const Grid3D &gJ = Jc.grid();
214 for (
int k = 0; k < nz; ++k)
215 for (
int j = 0; j < ny; ++j)
216 for (
int i = 0; i < nx; ++i)
217 gs(i, j, k) = -
MU0 * gJ(i, j, k);
static VectorField3D gradient(const ScalarField3D &phi)
static SolverResult solve_var_poisson(ScalarField3D &phi, const ScalarField3D &coeff, const std::vector< DirichletBC > &bcs, double tol=1e-6, int max_iter=500)
static SolverResult solve_poisson(ScalarField3D &phi, const ScalarField3D &source, double tol=1e-6, int max_iter=500)
static VectorField3D curl(const VectorField3D &A)
static ScalarField3D divergence(const VectorField3D &f)
Compute div(f) via central finite differences.
static VectorField3D solve_magnetic_field(const VectorField3D &J, double tol=1e-6, int max_iter=500)
static VectorField3D current_density(const ScalarField3D &sigma, const ScalarField3D &phi)
Compute current density J = -sigma*grad(phi) [A/m^2].
static constexpr double MU0
mu_0 [H/m]
float sample(float x, float y, float z) const
ScalarField3D(int nx, int ny, int nz, float dx, float ox=0.0f, float oy=0.0f, float oz=0.0f)
constexpr real phi
Golden ratio.
void curl_3d(const Grid3D &ax, const Grid3D &ay, const Grid3D &az, Grid3D &bx, Grid3D &by, Grid3D &bz)
void neg_laplacian_3d(const Vector &x, Vector &y, int nx, int ny, int nz, double inv_dx2)
void matvec(const Matrix &A, const Vector &x, Vector &y, Backend b=default_backend)
y = A * x
void scale(Vector &v, real alpha, Backend b=default_backend)
v *= alpha
void divergence_3d(const Grid3D &fx, const Grid3D &fy, const Grid3D &fz, Grid3D &out)
void gradient_3d(const Grid3D &phi, Grid3D &gx, Grid3D &gy, Grid3D &gz)
SolverResult cg_matfree(MatVecFn matvec, const Vector &b, Vector &x, real tol=1e-6, idx max_iter=1000)
Matrix-free conjugate gradient for Ax = b where A is SPD.
SolverResult cg(const Matrix &A, const Vector &b, Vector &x, real tol=1e-10, idx max_iter=1000, Backend backend=default_backend)
Conjugate gradient solver for Ax = b.
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.
ScalarField3D z
x, y, z components on the same grid layout
VectorField3D(int nx, int ny, int nz, float dx, float ox=0.0f, float oy=0.0f, float oz=0.0f)
void scale(float s)
Multiply all components by scalar s.