PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Data Structures | Macros | Functions | Variables
poisson.c File Reference

Pressure-Poisson projection: operator, right-hand side, multigrid solve, pressure update, and velocity correction. More...

#include "poisson.h"
#include "logging.h"
#include "setup.h"
Include dependency graph for poisson.c:

Go to the source code of this file.

Data Structures

struct  PoissonTransverseDifference
 Transverse difference used at one face. More...
 
struct  PoissonFaceGradient
 Everything needed to evaluate the gradient flux through one face. More...
 
struct  PoissonFaceMetrics
 Read-only face metric arrays: metric[n][b] is F_b on the n-faces. More...
 

Macros

#define POISSON_SOLID_THRESHOLD   0.1
 Cells whose nvert exceeds this value are solid.
 
#define __FUNCT__   "AssemblePoissonOperator"
 
#define __FUNCT__   "ComputePoissonRHS"
 
#define __FUNCT__   "UpdatePressure"
 
#define __FUNCT__   "ProjectVelocity"
 
#define __FUNCT__   "PoissonMultigrid_Build"
 
#define __FUNCT__   "PoissonSolver_Multigrid"
 

Functions

static PetscInt PoissonOperator_StencilSlot (const PetscInt d[3])
 Maps the 19 stencil offsets to their slots; corners of the 3x3x3 block are -1.
 
static PetscReal PoissonOperator_At (const PetscReal ***field, const PetscInt c[3], const PetscInt d[3])
 Value of a cell-centred array at cell c displaced by d.
 
static void PoissonOperator_PeriodicAxes (const UserCtx *user, PetscBool periodic[3])
 Records which axes are periodic, from the negative face of each axis.
 
static PoissonTransverseDifference PoissonOperator_TransverseDifference (const PetscReal ***nvert, const PetscInt c[3], PetscInt n, PetscInt t, const PetscInt m[3], const PetscBool periodic[3])
 Chooses the transverse difference along axis t at the face between cell c and c + e_n.
 
static PoissonFaceGradient PoissonOperator_FaceGradientStencil (const PoissonFaceMetrics *metrics, const PetscReal ***nvert, const PetscInt c[3], PetscInt n, const PetscInt m[3], const PetscBool periodic[3])
 Gathers the metric coefficients and transverse differences of the gradient flux through the face between cell c and c + e_n.
 
static PetscErrorCode PoissonOperator_GetFaceMetrics (UserCtx *user, PoissonFaceMetrics *metrics)
 Borrows read access to the face metric arrays of user.
 
static PetscErrorCode PoissonOperator_RestoreFaceMetrics (UserCtx *user, PoissonFaceMetrics *metrics)
 Returns the arrays borrowed by PoissonOperator_GetFaceMetrics().
 
static PetscInt PoissonOperator_NeighborIndex (PetscInt v, PetscInt d, PetscInt m, PetscBool periodic)
 Index of the neighbour at offset d (-1, 0, +1) from v on an axis of m points, wrapping between the interior layers 1 and m-2 when periodic.
 
static void PoissonOperator_AddFaceFlux (const PoissonFaceGradient *face, const PetscInt own[3], PetscInt n, PetscReal sign, PetscScalar coefficients[19])
 Adds the signed gradient flux through one face to a row's stencil coefficients.
 
PetscErrorCode AssemblePoissonOperator (UserCtx *user)
 Implementation of AssemblePoissonOperator().
 
PetscErrorCode ComputePoissonRHS (UserCtx *user, Vec B)
 Implementation of ComputePoissonRHS().
 
PetscErrorCode UpdatePressure (UserCtx *user)
 Implementation of UpdatePressure().
 
PetscErrorCode ProjectVelocity (UserCtx *user)
 Implementation of ProjectVelocity().
 
static PetscErrorCode PoissonMultigrid_RemoveNullSpace (MatNullSpace nullsp, Vec X, void *ctx)
 Removes the null space of the Neumann pressure problem from a level vector.
 
static void PoissonMultigrid_InterpolationParent (PetscInt f, PetscInt m, PetscInt semi, PetscInt *coarse, PetscInt *direction)
 Coarse cell and interpolation direction along one axis for fine index f.
 
static PetscErrorCode PoissonMultigrid_Interpolate (Mat P, Vec X, Vec F)
 Prolongs a coarse-level correction to the next finer level (MatShell multiply).
 
static PetscErrorCode PoissonMultigrid_Restrict (Mat R, Vec X, Vec F)
 Restricts a fine-level residual to the next coarser level (MatShell multiply).
 
static PetscErrorCode PoissonMultigrid_ShiftBlockFactors (KSP level_ksp)
 Gives each block factor of a block-Jacobi level solver a small diagonal shift, so the factorization of a nearly singular Neumann block does not fail on a zero pivot.
 
static PetscErrorCode PoissonMultigrid_Build (UserMG *usermg, PetscInt bi)
 Builds the multigrid solver for block bi and stores it in the finest level.
 
static PetscErrorCode PoissonMultigrid_OpenConvergenceLog (KSP ksp, SimCtx *simCtx, PetscInt bi)
 Prepares the convergence monitor for one solve and opens its log file on rank 0.
 
static PetscErrorCode PoissonMultigrid_CloseConvergenceLog (KSP ksp)
 Closes the log file opened by PoissonMultigrid_OpenConvergenceLog().
 
PetscErrorCode PoissonSolver_Multigrid (UserMG *usermg)
 Implementation of PoissonSolver_Multigrid().
 

Variables

static const PetscInt POISSON_STENCIL_OFFSETS [19][3]
 Offsets of the 19 stencil points, in the column order used to insert each row.
 
static const FieldId POISSON_FACE_METRIC_FIELDS [3][3]
 Field IDs of the face metric vectors, indexed as PoissonFaceMetrics.
 
static const FieldId POISSON_FACE_AJ_FIELDS [3] = {FIELD_ID_IAJ, FIELD_ID_JAJ, FIELD_ID_KAJ}
 

Detailed Description

Pressure-Poisson projection: operator, right-hand side, multigrid solve, pressure update, and velocity correction.

Grid layout. Cell-centred quantities live at DMDA indices 1..m-2 on each axis; indices 0 and m-1 are dummy layers that carry no unknown. On a periodic axis the interior wraps from m-2 to 1. The contravariant flux Ucont[k][j][i].x sits on the face between cells i and i+1, and likewise for the other two components.

Face gradient. The flux of grad(Phi) through the face between cell c and c + e_n is

sum_b g_nb D_b(Phi),   g_nb = (F_b . F_n) * aj_n,

where F_b are the contravariant base vectors stored on the n-faces, aj_n is the inverse Jacobian there, D_n is the difference across the face, and D_b (b != n) is the transverse difference chosen by PoissonOperator_TransverseDifference(). The operator assembles the divergence of this flux, and the projection subtracts it from Ucont, so the two are consistent by construction.

Definition in file poisson.c.


Data Structure Documentation

◆ PoissonTransverseDifference

struct PoissonTransverseDifference

Transverse difference used at one face.

The difference is weight * (sum of Phi on row hi - sum of Phi on row lo), where a row is the pair of cells on either side of the face, displaced by the given offset along the transverse axis.

Definition at line 45 of file poisson.c.

Data Fields
PetscInt lo Transverse offset of the subtracted row pair.
PetscInt hi Transverse offset of the added row pair.
PetscReal weight 0.25 central, 0.5 one-sided, 0 when no fluid side remains.

◆ PoissonFaceGradient

struct PoissonFaceGradient

Everything needed to evaluate the gradient flux through one face.

Definition at line 52 of file poisson.c.

Collaboration diagram for PoissonFaceGradient:
[legend]
Data Fields
PetscReal dot[3] F_b .

F_n on the face, b = 0..2.

PetscReal aj Inverse Jacobian on the face.
PoissonTransverseDifference diff[3] Transverse differences; diff[n] is unused.

◆ PoissonFaceMetrics

struct PoissonFaceMetrics

Read-only face metric arrays: metric[n][b] is F_b on the n-faces.

Definition at line 59 of file poisson.c.

Collaboration diagram for PoissonFaceMetrics:
[legend]
Data Fields
const Cmpnts *** metric[3][3]
const PetscReal *** aj[3]

Macro Definition Documentation

◆ POISSON_SOLID_THRESHOLD

#define POISSON_SOLID_THRESHOLD   0.1

Cells whose nvert exceeds this value are solid.

Definition at line 26 of file poisson.c.

◆ __FUNCT__ [1/6]

#define __FUNCT__   "AssemblePoissonOperator"

Definition at line 244 of file poisson.c.

◆ __FUNCT__ [2/6]

#define __FUNCT__   "ComputePoissonRHS"

Definition at line 244 of file poisson.c.

◆ __FUNCT__ [3/6]

#define __FUNCT__   "UpdatePressure"

Definition at line 244 of file poisson.c.

◆ __FUNCT__ [4/6]

#define __FUNCT__   "ProjectVelocity"

Definition at line 244 of file poisson.c.

◆ __FUNCT__ [5/6]

#define __FUNCT__   "PoissonMultigrid_Build"

Definition at line 244 of file poisson.c.

◆ __FUNCT__ [6/6]

#define __FUNCT__   "PoissonSolver_Multigrid"

Definition at line 244 of file poisson.c.

Function Documentation

◆ PoissonOperator_StencilSlot()

static PetscInt PoissonOperator_StencilSlot ( const PetscInt  d[3])
static

Maps the 19 stencil offsets to their slots; corners of the 3x3x3 block are -1.

Definition at line 75 of file poisson.c.

76{
77 for (PetscInt s = 0; s < 19; s++) {
78 if (POISSON_STENCIL_OFFSETS[s][0] == d[0] && POISSON_STENCIL_OFFSETS[s][1] == d[1] &&
79 POISSON_STENCIL_OFFSETS[s][2] == d[2]) return s;
80 }
81 return -1;
82}
static const PetscInt POISSON_STENCIL_OFFSETS[19][3]
Offsets of the 19 stencil points, in the column order used to insert each row.
Definition poisson.c:29
Here is the caller graph for this function:

◆ PoissonOperator_At()

static PetscReal PoissonOperator_At ( const PetscReal ***  field,
const PetscInt  c[3],
const PetscInt  d[3] 
)
inlinestatic

Value of a cell-centred array at cell c displaced by d.

Definition at line 85 of file poisson.c.

87{
88 return field[c[2] + d[2]][c[1] + d[1]][c[0] + d[0]];
89}
Here is the caller graph for this function:

◆ PoissonOperator_PeriodicAxes()

static void PoissonOperator_PeriodicAxes ( const UserCtx *  user,
PetscBool  periodic[3] 
)
static

Records which axes are periodic, from the negative face of each axis.

Definition at line 92 of file poisson.c.

93{
94 periodic[0] = (PetscBool)(user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC);
95 periodic[1] = (PetscBool)(user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC);
96 periodic[2] = (PetscBool)(user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC);
97}
@ PERIODIC
Definition variables.h:318
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:1099
BCType mathematical_type
Definition variables.h:392
@ BC_FACE_NEG_X
Definition variables.h:288
@ BC_FACE_NEG_Z
Definition variables.h:290
@ BC_FACE_NEG_Y
Definition variables.h:289
Here is the caller graph for this function:

◆ PoissonOperator_TransverseDifference()

static PoissonTransverseDifference PoissonOperator_TransverseDifference ( const PetscReal ***  nvert,
const PetscInt  c[3],
PetscInt  n,
PetscInt  t,
const PetscInt  m[3],
const PetscBool  periodic[3] 
)
static

Chooses the transverse difference along axis t at the face between cell c and c + e_n.

The central difference averages the two cells beside the face over rows -1 and +1. When the +1 row is a non-periodic boundary layer or touches a solid cell, the difference falls back to rows -1 and 0, and symmetrically to rows 0 and +1; with neither side available the transverse term vanishes.

Definition at line 108 of file poisson.c.

111{
112 PoissonTransverseDifference diff = {0, 0, 0.0};
113 PetscInt plus[3] = {0, 0, 0}, plus_n[3] = {0, 0, 0};
114 PetscInt minus[3] = {0, 0, 0}, minus_n[3] = {0, 0, 0};
115 const PetscInt s = c[t];
116
117 plus[t] = 1; plus_n[t] = 1; plus_n[n] = 1;
118 minus[t] = -1; minus_n[t] = -1; minus_n[n] = 1;
119 const PetscReal solid_plus = PoissonOperator_At(nvert, c, plus) + PoissonOperator_At(nvert, c, plus_n);
120 const PetscReal solid_minus = PoissonOperator_At(nvert, c, minus) + PoissonOperator_At(nvert, c, minus_n);
121
122 if ((s == m[t] - 2 && !periodic[t]) || solid_plus > POISSON_SOLID_THRESHOLD) {
123 if (solid_minus < POISSON_SOLID_THRESHOLD && (s != 1 || periodic[t])) {
124 diff.lo = -1; diff.hi = 0; diff.weight = 0.5;
125 }
126 } else if ((s == 1 && !periodic[t]) || solid_minus > POISSON_SOLID_THRESHOLD) {
127 if (solid_plus < POISSON_SOLID_THRESHOLD) {
128 diff.lo = 0; diff.hi = 1; diff.weight = 0.5;
129 }
130 } else {
131 diff.lo = -1; diff.hi = 1; diff.weight = 0.25;
132 }
133 return diff;
134}
PetscReal weight
0.25 central, 0.5 one-sided, 0 when no fluid side remains.
Definition poisson.c:48
PetscInt hi
Transverse offset of the added row pair.
Definition poisson.c:47
#define POISSON_SOLID_THRESHOLD
Cells whose nvert exceeds this value are solid.
Definition poisson.c:26
static PetscReal PoissonOperator_At(const PetscReal ***field, const PetscInt c[3], const PetscInt d[3])
Value of a cell-centred array at cell c displaced by d.
Definition poisson.c:85
PetscInt lo
Transverse offset of the subtracted row pair.
Definition poisson.c:46
Transverse difference used at one face.
Definition poisson.c:45
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonOperator_FaceGradientStencil()

static PoissonFaceGradient PoissonOperator_FaceGradientStencil ( const PoissonFaceMetrics *  metrics,
const PetscReal ***  nvert,
const PetscInt  c[3],
PetscInt  n,
const PetscInt  m[3],
const PetscBool  periodic[3] 
)
static

Gathers the metric coefficients and transverse differences of the gradient flux through the face between cell c and c + e_n.

Definition at line 140 of file poisson.c.

143{
145 const Cmpnts normal = metrics->metric[n][n][c[2]][c[1]][c[0]];
146
147 face.aj = metrics->aj[n][c[2]][c[1]][c[0]];
148 for (PetscInt b = 0; b < 3; b++) {
149 const Cmpnts base = metrics->metric[n][b][c[2]][c[1]][c[0]];
150 face.dot[b] = base.x * normal.x + base.y * normal.y + base.z * normal.z;
151 if (b == n) {
152 face.diff[b].lo = 0; face.diff[b].hi = 0; face.diff[b].weight = 0.0;
153 } else {
154 face.diff[b] = PoissonOperator_TransverseDifference(nvert, c, n, b, m, periodic);
155 }
156 }
157 return face;
158}
const PetscReal *** aj[3]
Definition poisson.c:61
static PoissonTransverseDifference PoissonOperator_TransverseDifference(const PetscReal ***nvert, const PetscInt c[3], PetscInt n, PetscInt t, const PetscInt m[3], const PetscBool periodic[3])
Chooses the transverse difference along axis t at the face between cell c and c + e_n.
Definition poisson.c:108
PetscReal aj
Inverse Jacobian on the face.
Definition poisson.c:54
const Cmpnts *** metric[3][3]
Definition poisson.c:60
PoissonTransverseDifference diff[3]
Transverse differences; diff[n] is unused.
Definition poisson.c:55
PetscReal dot[3]
F_b .
Definition poisson.c:53
Everything needed to evaluate the gradient flux through one face.
Definition poisson.c:52
PetscScalar x
Definition variables.h:122
PetscScalar z
Definition variables.h:122
PetscScalar y
Definition variables.h:122
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonOperator_GetFaceMetrics()

static PetscErrorCode PoissonOperator_GetFaceMetrics ( UserCtx *  user,
PoissonFaceMetrics *  metrics 
)
static

Borrows read access to the face metric arrays of user.

Definition at line 161 of file poisson.c.

162{
163 FieldView view;
164
165 PetscFunctionBeginUser;
166 for (PetscInt n = 0; n < 3; n++) {
167 for (PetscInt b = 0; b < 3; b++) {
168 PetscCall(FieldGetView(user, POISSON_FACE_METRIC_FIELDS[n][b], &view));
169 PetscCall(DMDAVecGetArrayRead(view.dm, view.local_vec, (void *)&metrics->metric[n][b]));
170 }
171 PetscCall(FieldGetView(user, POISSON_FACE_AJ_FIELDS[n], &view));
172 PetscCall(DMDAVecGetArrayRead(view.dm, view.local_vec, (void *)&metrics->aj[n]));
173 }
174 PetscFunctionReturn(0);
175}
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
Non-owning runtime objects resolved for one field and UserCtx.
static const FieldId POISSON_FACE_AJ_FIELDS[3]
Definition poisson.c:70
static const FieldId POISSON_FACE_METRIC_FIELDS[3][3]
Field IDs of the face metric vectors, indexed as PoissonFaceMetrics.
Definition poisson.c:65
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonOperator_RestoreFaceMetrics()

static PetscErrorCode PoissonOperator_RestoreFaceMetrics ( UserCtx *  user,
PoissonFaceMetrics *  metrics 
)
static

Returns the arrays borrowed by PoissonOperator_GetFaceMetrics().

Definition at line 178 of file poisson.c.

179{
180 FieldView view;
181
182 PetscFunctionBeginUser;
183 for (PetscInt n = 0; n < 3; n++) {
184 for (PetscInt b = 0; b < 3; b++) {
185 PetscCall(FieldGetView(user, POISSON_FACE_METRIC_FIELDS[n][b], &view));
186 PetscCall(DMDAVecRestoreArrayRead(view.dm, view.local_vec, (void *)&metrics->metric[n][b]));
187 }
188 PetscCall(FieldGetView(user, POISSON_FACE_AJ_FIELDS[n], &view));
189 PetscCall(DMDAVecRestoreArrayRead(view.dm, view.local_vec, (void *)&metrics->aj[n]));
190 }
191 PetscFunctionReturn(0);
192}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonOperator_NeighborIndex()

static PetscInt PoissonOperator_NeighborIndex ( PetscInt  v,
PetscInt  d,
PetscInt  m,
PetscBool  periodic 
)
inlinestatic

Index of the neighbour at offset d (-1, 0, +1) from v on an axis of m points, wrapping between the interior layers 1 and m-2 when periodic.

Definition at line 198 of file poisson.c.

199{
200 if (periodic && d == 1 && v == m - 2) return 1;
201 if (periodic && d == -1 && v == 1) return m - 2;
202 return v + d;
203}
Here is the caller graph for this function:

◆ PoissonOperator_AddFaceFlux()

static void PoissonOperator_AddFaceFlux ( const PoissonFaceGradient *  face,
const PetscInt  own[3],
PetscInt  n,
PetscReal  sign,
PetscScalar  coefficients[19] 
)
static

Adds the signed gradient flux through one face to a row's stencil coefficients.

Parameters
[in]faceFace gradient from PoissonOperator_FaceGradientStencil().
[in]ownOffset of the face's lower cell from the row cell.
[in]nNormal axis of the face.
[in]sign+1 for the row's upper face on the axis, -1 for its lower face.
[in,out]coefficientsThe row's 19 coefficients.

Definition at line 214 of file poisson.c.

216{
217 for (PetscInt b = 0; b < 3; b++) {
218 const PetscReal g = face->dot[b] * face->aj;
219 PetscInt lower[3] = {own[0], own[1], own[2]};
220 PetscInt upper[3] = {own[0], own[1], own[2]};
221
222 upper[n] += 1;
223 if (b == n) {
224 coefficients[PoissonOperator_StencilSlot(lower)] += sign * (-g);
225 coefficients[PoissonOperator_StencilSlot(upper)] += sign * g;
226 continue;
227 }
228
229 const PoissonTransverseDifference diff = face->diff[b];
230 if (diff.weight == 0.0) continue;
231 const PetscReal term = g * diff.weight;
232 PetscInt hi_lower[3] = {lower[0], lower[1], lower[2]}, hi_upper[3] = {upper[0], upper[1], upper[2]};
233 PetscInt lo_lower[3] = {lower[0], lower[1], lower[2]}, lo_upper[3] = {upper[0], upper[1], upper[2]};
234 hi_lower[b] += diff.hi; hi_upper[b] += diff.hi;
235 lo_lower[b] += diff.lo; lo_upper[b] += diff.lo;
236 coefficients[PoissonOperator_StencilSlot(hi_lower)] += sign * term;
237 coefficients[PoissonOperator_StencilSlot(hi_upper)] += sign * term;
238 coefficients[PoissonOperator_StencilSlot(lo_lower)] += sign * (-term);
239 coefficients[PoissonOperator_StencilSlot(lo_upper)] += sign * (-term);
240 }
241}
static PetscInt PoissonOperator_StencilSlot(const PetscInt d[3])
Maps the 19 stencil offsets to their slots; corners of the 3x3x3 block are -1.
Definition poisson.c:75
Here is the call graph for this function:
Here is the caller graph for this function:

◆ AssemblePoissonOperator()

PetscErrorCode AssemblePoissonOperator ( UserCtx *  user)

Implementation of AssemblePoissonOperator().

Assembles the pressure-correction operator on one multigrid level.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/poisson.h.

See also
AssemblePoissonOperator()

Definition at line 251 of file poisson.c.

252{
253 const DMDALocalInfo info = user->info;
254 const PetscInt m[3] = {info.mx, info.my, info.mz};
255 PetscBool periodic[3];
256 PoissonFaceMetrics metrics;
257 const PetscReal ***nvert, ***aj;
258 AO ao;
259
260 PetscFunctionBeginUser;
262
263 if (!user->A) {
264 PetscInt local_rows;
265 PetscCall(VecGetLocalSize(user->Phi, &local_rows));
266 PetscCall(MatCreateAIJ(PETSC_COMM_WORLD, local_rows, local_rows, m[0] * m[1] * m[2], m[0] * m[1] * m[2],
267 19, NULL, 19, NULL, &user->A));
268 }
269 PetscCall(MatZeroEntries(user->A));
270
271 PoissonOperator_PeriodicAxes(user, periodic);
272 PetscCall(DMDAGetAO(user->da, &ao));
273 PetscCall(PoissonOperator_GetFaceMetrics(user, &metrics));
274 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, (void *)&nvert));
275 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, (void *)&aj));
276
277 for (PetscInt k = info.zs; k < info.zs + info.zm; k++) {
278 for (PetscInt j = info.ys; j < info.ys + info.ym; j++) {
279 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
280 const PetscInt c[3] = {i, j, k};
281 PetscInt row = i + j * m[0] + k * m[0] * m[1];
282 PetscInt columns[19];
283 PetscScalar coefficients[19] = {0.0};
284
285 PetscCall(AOApplicationToPetsc(ao, 1, &row));
286 if (i == 0 || i == m[0] - 1 || j == 0 || j == m[1] - 1 || k == 0 || k == m[2] - 1) {
287 const PetscScalar one = 1.0;
288 PetscCall(MatSetValues(user->A, 1, &row, 1, &row, &one, INSERT_VALUES));
289 continue;
290 }
291
292 for (PetscInt s = 0; s < 19; s++) {
293 const PetscInt *d = POISSON_STENCIL_OFFSETS[s];
294 columns[s] = PoissonOperator_NeighborIndex(i, d[0], m[0], periodic[0]) +
295 PoissonOperator_NeighborIndex(j, d[1], m[1], periodic[1]) * m[0] +
296 PoissonOperator_NeighborIndex(k, d[2], m[2], periodic[2]) * m[0] * m[1];
297 }
298 PetscCall(AOApplicationToPetsc(ao, 19, columns));
299
300 if (nvert[k][j][i] > POISSON_SOLID_THRESHOLD) {
301 /* Solid rows keep the fluid row's structure with zero couplings, so a
302 solid field that changes during a run reassembles in place. */
303 coefficients[0] = 1.0;
304 PetscCall(MatSetValues(user->A, 1, &row, 19, columns, coefficients, INSERT_VALUES));
305 continue;
306 }
307
308 /* Faces in the order east, west, north, south, top, bottom. Non-periodic
309 boundary faces carry no flux (homogeneous Neumann). */
310 for (PetscInt n = 0; n < 3; n++) {
311 const PetscInt first = periodic[n] ? 0 : 1;
312 const PetscInt last = periodic[n] ? m[n] - 1 : m[n] - 2;
313 for (PetscInt side = 0; side < 2; side++) {
314 PetscInt across[3] = {0, 0, 0}, own[3] = {0, 0, 0}, face_cell[3] = {i, j, k};
315 const PetscBool upper = (PetscBool)(side == 0);
316
317 across[n] = upper ? 1 : -1;
318 if (PoissonOperator_At(nvert, c, across) >= POISSON_SOLID_THRESHOLD) continue;
319 if (c[n] == (upper ? last : first)) continue;
320 if (!upper) { own[n] = -1; face_cell[n] -= 1; }
321
322 const PoissonFaceGradient face =
323 PoissonOperator_FaceGradientStencil(&metrics, nvert, face_cell, n, m, periodic);
324 PoissonOperator_AddFaceFlux(&face, own, n, upper ? 1.0 : -1.0, coefficients);
325 }
326 }
327
328 for (PetscInt s = 0; s < 19; s++) coefficients[s] *= -aj[k][j][i];
329 PetscCall(MatSetValues(user->A, 1, &row, 19, columns, coefficients, INSERT_VALUES));
330 }
331 }
332 }
333
334 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, (void *)&aj));
335 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, (void *)&nvert));
336 PetscCall(PoissonOperator_RestoreFaceMetrics(user, &metrics));
337 PetscCall(MatAssemblyBegin(user->A, MAT_FINAL_ASSEMBLY));
338 PetscCall(MatAssemblyEnd(user->A, MAT_FINAL_ASSEMBLY));
339
340 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Poisson operator assembled on level %d.\n", user->thislevel);
342 PetscFunctionReturn(0);
343}
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:894
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:885
static PetscInt PoissonOperator_NeighborIndex(PetscInt v, PetscInt d, PetscInt m, PetscBool periodic)
Index of the neighbour at offset d (-1, 0, +1) from v on an axis of m points, wrapping between the in...
Definition poisson.c:198
static PetscErrorCode PoissonOperator_RestoreFaceMetrics(UserCtx *user, PoissonFaceMetrics *metrics)
Returns the arrays borrowed by PoissonOperator_GetFaceMetrics().
Definition poisson.c:178
static void PoissonOperator_AddFaceFlux(const PoissonFaceGradient *face, const PetscInt own[3], PetscInt n, PetscReal sign, PetscScalar coefficients[19])
Adds the signed gradient flux through one face to a row's stencil coefficients.
Definition poisson.c:214
static PetscErrorCode PoissonOperator_GetFaceMetrics(UserCtx *user, PoissonFaceMetrics *metrics)
Borrows read access to the face metric arrays of user.
Definition poisson.c:161
static void PoissonOperator_PeriodicAxes(const UserCtx *user, PetscBool periodic[3])
Records which axes are periodic, from the negative face of each axis.
Definition poisson.c:92
static PoissonFaceGradient PoissonOperator_FaceGradientStencil(const PoissonFaceMetrics *metrics, const PetscReal ***nvert, const PetscInt c[3], PetscInt n, const PetscInt m[3], const PetscBool periodic[3])
Gathers the metric coefficients and transverse differences of the gradient flux through the face betw...
Definition poisson.c:140
Read-only face metric arrays: metric[n][b] is F_b on the n-faces.
Definition poisson.c:59
Vec lNvert
Definition variables.h:1113
PetscInt thislevel
Definition variables.h:1165
DMDALocalInfo info
Definition variables.h:1086
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputePoissonRHS()

PetscErrorCode ComputePoissonRHS ( UserCtx *  user,
Vec  B 
)

Implementation of ComputePoissonRHS().

Forms the right-hand side of the pressure-correction equation.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/poisson.h.

See also
ComputePoissonRHS()

Definition at line 353 of file poisson.c.

354{
355 SimCtx *simCtx = user->simCtx;
356 const DMDALocalInfo info = user->info;
357 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
358 const PetscReal dt = simCtx->dt;
359 const Cmpnts ***ucont;
360 const PetscReal ***nvert, ***aj;
361 PetscReal ***rhs;
362 PetscReal local_sum = 0.0, global_sum = 0.0;
363
364 PetscFunctionBeginUser;
366 PetscCall(DMDAVecGetArray(user->da, B, &rhs));
367 PetscCall(DMDAVecGetArrayRead(user->fda, user->lUcont, (void *)&ucont));
368 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, (void *)&nvert));
369 PetscCall(DMDAVecGetArrayRead(user->da, user->lAj, (void *)&aj));
370
371 for (PetscInt k = info.zs; k < info.zs + info.zm; k++) {
372 for (PetscInt j = info.ys; j < info.ys + info.ym; j++) {
373 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
374 if (i == 0 || i == mx - 1 || j == 0 || j == my - 1 || k == 0 || k == mz - 1 ||
375 nvert[k][j][i] > POISSON_SOLID_THRESHOLD) {
376 rhs[k][j][i] = 0.0;
377 } else {
378 rhs[k][j][i] = -(ucont[k][j][i].x - ucont[k][j][i-1].x +
379 ucont[k][j][i].y - ucont[k][j-1][i].y +
380 ucont[k][j][i].z - ucont[k-1][j][i].z) / dt * aj[k][j][i] * COEF_TIME_ACCURACY;
381 }
382 }
383 }
384 }
385
386 /* The integral of the right-hand side is the net volume flux into the domain carried
387 by the uncorrected velocity. With Neumann pressure boundaries it must vanish for
388 the equation to have a solution. */
389 for (PetscInt k = info.zs; k < info.zs + info.zm; k++) {
390 for (PetscInt j = info.ys; j < info.ys + info.ym; j++) {
391 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
392 local_sum += rhs[k][j][i] / aj[k][j][i] * dt / COEF_TIME_ACCURACY;
393 }
394 }
395 }
396 PetscCallMPI(MPI_Allreduce(&local_sum, &global_sum, 1, MPIU_REAL, MPI_SUM, PetscObjectComm((PetscObject)B)));
397 simCtx->poissonSourceImbalance = global_sum;
398 LOG_ALLOW(GLOBAL, LOG_INFO, "Poisson source imbalance: %le\n", (double)global_sum);
399
400 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lAj, (void *)&aj));
401 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, (void *)&nvert));
402 PetscCall(DMDAVecRestoreArrayRead(user->fda, user->lUcont, (void *)&ucont));
403 PetscCall(DMDAVecRestoreArray(user->da, B, &rhs));
405 PetscFunctionReturn(0);
406}
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1077
PetscReal poissonSourceImbalance
Definition variables.h:1026
PetscReal dt
Definition variables.h:874
Vec lUcont
Definition variables.h:1113
#define COEF_TIME_ACCURACY
Coefficient controlling the temporal accuracy scheme (e.g., 1.5 for 2nd Order Backward Difference).
Definition variables.h:75
The master context for the entire simulation.
Definition variables.h:859
Here is the caller graph for this function:

◆ UpdatePressure()

PetscErrorCode UpdatePressure ( UserCtx *  user)

Implementation of UpdatePressure().

Adds the pressure correction to the pressure, P += Phi, and refreshes both fields' periodic images and ghosts.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/poisson.h.

See also
UpdatePressure()

Definition at line 416 of file poisson.c.

417{
418 const FieldId periodic_fields[] = {FIELD_ID_P, FIELD_ID_PHI};
419
420 PetscFunctionBeginUser;
422 PetscCall(VecAXPY(user->P, 1.0, user->Phi));
423 PetscCall(SynchronizePeriodicCellFields(user, 2, periodic_fields));
424 PetscCall(UpdateLocalGhosts(user, FIELD_ID_P));
425 PetscCall(UpdateLocalGhosts(user, FIELD_ID_PHI));
427 PetscFunctionReturn(0);
428}
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_PHI
@ FIELD_ID_P
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:2489
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProjectVelocity()

PetscErrorCode ProjectVelocity ( UserCtx *  user)

Implementation of ProjectVelocity().

Corrects the contravariant flux with the gradient of Phi.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/poisson.h.

See also
ProjectVelocity()

Definition at line 438 of file poisson.c.

439{
440 SimCtx *simCtx = user->simCtx;
441 const DMDALocalInfo info = user->info;
442 const PetscInt m[3] = {info.mx, info.my, info.mz};
443 const PetscInt start[3] = {info.xs, info.ys, info.zs};
444 const PetscInt end[3] = {info.xs + info.xm, info.ys + info.ym, info.zs + info.zm};
445 const PetscReal scale = simCtx->dt / COEF_TIME_ACCURACY;
446 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
447 PetscBool periodic[3];
448 PetscInt interior_start[3], interior_end[3];
449 PoissonFaceMetrics metrics;
450 const PetscReal ***nvert, ***phi;
451 Cmpnts ***ucont;
452
453 PetscFunctionBeginUser;
455 PoissonOperator_PeriodicAxes(user, periodic);
456 for (PetscInt a = 0; a < 3; a++) {
457 interior_start[a] = (start[a] == 0) ? 1 : start[a];
458 interior_end[a] = (end[a] == m[a]) ? m[a] - 1 : end[a];
459 }
460
461 PetscCall(PoissonOperator_GetFaceMetrics(user, &metrics));
462 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, (void *)&nvert));
463 PetscCall(DMDAVecGetArrayRead(user->da, user->lPhi, (void *)&phi));
464 PetscCall(DMDAVecGetArray(user->fda, user->Ucont, &ucont));
465
466 /* One pass per face orientation. Faces between two interior cells are corrected on
467 every axis; a periodic axis also corrects its seam face at index 0. */
468 for (PetscInt n = 0; n < 3; n++) {
469 PetscInt lo[3], hi[3];
470 for (PetscInt a = 0; a < 3; a++) { lo[a] = interior_start[a]; hi[a] = interior_end[a]; }
471 if (periodic[n] && start[n] == 0) lo[n] = 0;
472 hi[n] = PetscMin(hi[n], periodic[n] ? m[n] - 1 : m[n] - 2);
473
474 for (PetscInt k = lo[2]; k < hi[2]; k++) {
475 for (PetscInt j = lo[1]; j < hi[1]; j++) {
476 for (PetscInt i = lo[0]; i < hi[0]; i++) {
477 const PetscInt c[3] = {i, j, k};
478 PetscInt across[3] = {0, 0, 0};
479 PetscReal difference[3];
480
481 across[n] = 1;
482 if (nvert[k][j][i] > POISSON_SOLID_THRESHOLD ||
483 PoissonOperator_At(nvert, c, across) > POISSON_SOLID_THRESHOLD) continue;
484
485 const PoissonFaceGradient face =
486 PoissonOperator_FaceGradientStencil(&metrics, nvert, c, n, m, periodic);
487 for (PetscInt b = 0; b < 3; b++) {
488 if (b == n) {
489 difference[b] = PoissonOperator_At(phi, c, across) - phi[k][j][i];
490 continue;
491 }
492 const PoissonTransverseDifference diff = face.diff[b];
493 PetscInt hi_lower[3] = {0, 0, 0}, hi_upper[3] = {0, 0, 0};
494 PetscInt lo_lower[3] = {0, 0, 0}, lo_upper[3] = {0, 0, 0};
495 hi_lower[b] = diff.hi; hi_upper[b] = diff.hi; hi_upper[n] = 1;
496 lo_lower[b] = diff.lo; lo_upper[b] = diff.lo; lo_upper[n] = 1;
497 difference[b] = (diff.weight == 0.0) ? 0.0 :
498 (PoissonOperator_At(phi, c, hi_lower) + PoissonOperator_At(phi, c, hi_upper) -
499 PoissonOperator_At(phi, c, lo_lower) - PoissonOperator_At(phi, c, lo_upper)) * diff.weight;
500 }
501
502 const PetscReal flux = difference[0] * face.dot[0] * face.aj +
503 difference[1] * face.dot[1] * face.aj +
504 difference[2] * face.dot[2] * face.aj;
505 const PetscReal correction = flux * scale;
506 if (n == 0) ucont[k][j][i].x -= correction;
507 else if (n == 1) ucont[k][j][i].y -= correction;
508 else ucont[k][j][i].z -= correction;
509 }
510 }
511 }
512 }
513
514 PetscCall(DMDAVecRestoreArray(user->fda, user->Ucont, &ucont));
515 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lPhi, (void *)&phi));
516 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, (void *)&nvert));
517 PetscCall(PoissonOperator_RestoreFaceMetrics(user, &metrics));
518
519 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, staggered_fields));
520 PetscCall(Contra2Cart(user));
521 PetscCall(FinalizePostProjectionCellFields(user));
523 PetscFunctionReturn(0);
524}
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode FinalizePostProjectionCellFields(UserCtx *user)
Finalizes cell-centered fields after the projection step.
@ FIELD_ID_UCONT
PetscErrorCode Contra2Cart(UserCtx *user)
Reconstructs Cartesian velocity (Ucat) at cell centers from contravariant velocity (Ucont) defined on...
Definition setup.c:3300
Vec Ucont
Definition variables.h:1113
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonMultigrid_RemoveNullSpace()

static PetscErrorCode PoissonMultigrid_RemoveNullSpace ( MatNullSpace  nullsp,
Vec  X,
void *  ctx 
)
static

Removes the null space of the Neumann pressure problem from a level vector.

PETSc first removes the global constant. This callback then subtracts the mean over the interior fluid cells and zeroes the dummy layers and solid cells, which carry no unknown.

Definition at line 533 of file poisson.c.

534{
535 UserCtx *user = (UserCtx *)ctx;
536 const DMDALocalInfo info = user->info;
537 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
538 const PetscInt xs = info.xs, xe = info.xs + info.xm;
539 const PetscInt ys = info.ys, ye = info.ys + info.ym;
540 const PetscInt zs = info.zs, ze = info.zs + info.zm;
541 const PetscInt lxs = (xs == 0) ? 1 : xs, lxe = (xe == mx) ? mx - 1 : xe;
542 const PetscInt lys = (ys == 0) ? 1 : ys, lye = (ye == my) ? my - 1 : ye;
543 const PetscInt lzs = (zs == 0) ? 1 : zs, lze = (ze == mz) ? mz - 1 : ze;
544 const PetscReal ***nvert;
545 PetscReal ***x;
546 PetscReal local[2] = {0.0, 0.0}, global[2];
547 MPI_Comm comm = PetscObjectComm((PetscObject)X);
548
549 PetscFunctionBeginUser;
550 (void)nullsp;
551 PetscCall(DMDAVecGetArray(user->da, X, &x));
552 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, (void *)&nvert));
553
554 for (PetscInt k = lzs; k < lze; k++) {
555 for (PetscInt j = lys; j < lye; j++) {
556 for (PetscInt i = lxs; i < lxe; i++) {
557 if (nvert[k][j][i] < POISSON_SOLID_THRESHOLD) {
558 local[0] += x[k][j][i];
559 local[1] += 1.0;
560 }
561 }
562 }
563 }
564 PetscCallMPI(MPI_Allreduce(&local[0], &global[0], 1, MPIU_REAL, MPI_SUM, comm));
565 PetscCallMPI(MPI_Allreduce(&local[1], &global[1], 1, MPIU_REAL, MPI_SUM, comm));
566 const PetscReal shift = global[0] / (-1.0 * global[1]);
567 for (PetscInt k = lzs; k < lze; k++) {
568 for (PetscInt j = lys; j < lye; j++) {
569 for (PetscInt i = lxs; i < lxe; i++) {
570 if (nvert[k][j][i] < POISSON_SOLID_THRESHOLD) x[k][j][i] += shift;
571 }
572 }
573 }
574
575 for (PetscInt k = zs; k < ze; k++) {
576 for (PetscInt j = ys; j < ye; j++) {
577 for (PetscInt i = xs; i < xe; i++) {
578 if (i == 0 || i == mx - 1 || j == 0 || j == my - 1 || k == 0 || k == mz - 1 ||
579 nvert[k][j][i] > POISSON_SOLID_THRESHOLD) x[k][j][i] = 0.0;
580 }
581 }
582 }
583
584 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, (void *)&nvert));
585 PetscCall(DMDAVecRestoreArray(user->da, X, &x));
586 PetscFunctionReturn(0);
587}
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1074
Here is the caller graph for this function:

◆ PoissonMultigrid_InterpolationParent()

static void PoissonMultigrid_InterpolationParent ( PetscInt  f,
PetscInt  m,
PetscInt  semi,
PetscInt *  coarse,
PetscInt *  direction 
)
static

Coarse cell and interpolation direction along one axis for fine index f.

Each fine cell takes 3/4 of its parent coarse cell and 1/4 of the coarse neighbour on the side it lies towards. The first and last interior cells and semi-coarsened axes use the parent only, as does a neighbour that is solid on the coarse grid.

Definition at line 596 of file poisson.c.

598{
599 if (semi) {
600 *coarse = f;
601 *direction = 0;
602 return;
603 }
604 *coarse = (f + 1) / 2;
605 *direction = (f - 2 * (*coarse)) == 0 ? 1 : -1;
606 if (f == 1 || f == m - 2) *direction = 0;
607}
Here is the caller graph for this function:

◆ PoissonMultigrid_Interpolate()

static PetscErrorCode PoissonMultigrid_Interpolate ( Mat  P,
Vec  X,
Vec  F 
)
static

Prolongs a coarse-level correction to the next finer level (MatShell multiply).

The shell context is the fine-level UserCtx.

Definition at line 613 of file poisson.c.

614{
615 UserCtx *user, *coarse;
616 DMDALocalInfo info;
617 Vec lX;
618 const PetscReal ***x, ***nvert, ***nvert_c;
619 PetscReal ***f;
620
621 PetscFunctionBeginUser;
622 PetscCall(MatShellGetContext(P, &user));
623 coarse = user->user_c;
624 info = user->info;
625 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
626 const PetscInt xs = info.xs, xe = info.xs + info.xm;
627 const PetscInt ys = info.ys, ye = info.ys + info.ym;
628 const PetscInt zs = info.zs, ze = info.zs + info.zm;
629 const PetscInt lxs = (xs == 0) ? 1 : xs, lxe = (xe == mx) ? mx - 1 : xe;
630 const PetscInt lys = (ys == 0) ? 1 : ys, lye = (ye == my) ? my - 1 : ye;
631 const PetscInt lzs = (zs == 0) ? 1 : zs, lze = (ze == mz) ? mz - 1 : ze;
632
633 PetscCall(DMGetLocalVector(coarse->da, &lX));
634 PetscCall(DMGlobalToLocalBegin(coarse->da, X, INSERT_VALUES, lX));
635 PetscCall(DMGlobalToLocalEnd(coarse->da, X, INSERT_VALUES, lX));
636 PetscCall(DMDAVecGetArrayRead(coarse->da, lX, (void *)&x));
637 PetscCall(DMDAVecGetArrayRead(coarse->da, coarse->lNvert, (void *)&nvert_c));
638 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, (void *)&nvert));
639 PetscCall(DMDAVecGetArray(user->da, F, &f));
640
641 for (PetscInt k = lzs; k < lze; k++) {
642 for (PetscInt j = lys; j < lye; j++) {
643 for (PetscInt i = lxs; i < lxe; i++) {
644 PetscInt ic, jc, kc, ia, ja, ka;
645
646 PoissonMultigrid_InterpolationParent(i, mx, user->isc, &ic, &ia);
647 PoissonMultigrid_InterpolationParent(j, my, user->jsc, &jc, &ja);
648 PoissonMultigrid_InterpolationParent(k, mz, user->ksc, &kc, &ka);
649 if (ka == -1 && nvert_c[kc-1][jc][ic] > POISSON_SOLID_THRESHOLD) ka = 0;
650 else if (ka == 1 && nvert_c[kc+1][jc][ic] > POISSON_SOLID_THRESHOLD) ka = 0;
651 if (ja == -1 && nvert_c[kc][jc-1][ic] > POISSON_SOLID_THRESHOLD) ja = 0;
652 else if (ja == 1 && nvert_c[kc][jc+1][ic] > POISSON_SOLID_THRESHOLD) ja = 0;
653 if (ia == -1 && nvert_c[kc][jc][ic-1] > POISSON_SOLID_THRESHOLD) ia = 0;
654 else if (ia == 1 && nvert_c[kc][jc][ic+1] > POISSON_SOLID_THRESHOLD) ia = 0;
655
656 f[k][j][i] = (x[kc ][jc ][ic ] * 9 +
657 x[kc ][jc+ja][ic ] * 3 +
658 x[kc ][jc ][ic+ia] * 3 +
659 x[kc ][jc+ja][ic+ia]) * 3./64. +
660 (x[kc+ka][jc ][ic ] * 9 +
661 x[kc+ka][jc+ja][ic ] * 3 +
662 x[kc+ka][jc ][ic+ia] * 3 +
663 x[kc+ka][jc+ja][ic+ia]) / 64.;
664 }
665 }
666 }
667 for (PetscInt k = zs; k < ze; k++) {
668 for (PetscInt j = ys; j < ye; j++) {
669 for (PetscInt i = xs; i < xe; i++) {
670 if (i == 0 || i == mx - 1 || j == 0 || j == my - 1 || k == 0 || k == mz - 1 ||
671 nvert[k][j][i] > POISSON_SOLID_THRESHOLD) f[k][j][i] = 0.0;
672 }
673 }
674 }
675
676 PetscCall(DMDAVecRestoreArray(user->da, F, &f));
677 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, (void *)&nvert));
678 PetscCall(DMDAVecRestoreArrayRead(coarse->da, coarse->lNvert, (void *)&nvert_c));
679 PetscCall(DMDAVecRestoreArrayRead(coarse->da, lX, (void *)&x));
680 PetscCall(DMRestoreLocalVector(coarse->da, &lX));
681 PetscFunctionReturn(0);
682}
static void PoissonMultigrid_InterpolationParent(PetscInt f, PetscInt m, PetscInt semi, PetscInt *coarse, PetscInt *direction)
Coarse cell and interpolation direction along one axis for fine index f.
Definition poisson.c:596
PetscInt isc
Definition variables.h:1092
PetscInt ksc
Definition variables.h:1092
PetscInt jsc
Definition variables.h:1092
UserCtx * user_c
Definition variables.h:1166
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonMultigrid_Restrict()

static PetscErrorCode PoissonMultigrid_Restrict ( Mat  R,
Vec  X,
Vec  F 
)
static

Restricts a fine-level residual to the next coarser level (MatShell multiply).

Each coarse cell averages the eight fine cells it covers, weighting each by its fluid fraction; coarse dummy and solid cells receive zero. The shell context is the coarse-level UserCtx.

Definition at line 691 of file poisson.c.

692{
693 UserCtx *user, *fine;
694 DMDALocalInfo info;
695 Vec lX;
696 const PetscReal ***x, ***nvert, ***nvert_f;
697 PetscReal ***f;
698
699 PetscFunctionBeginUser;
700 PetscCall(MatShellGetContext(R, &user));
701 fine = user->user_f;
702 info = user->info;
703 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
704 const PetscInt ia = user->isc ? 0 : 1, ja = user->jsc ? 0 : 1, ka = user->ksc ? 0 : 1;
705
706 PetscCall(DMGetLocalVector(fine->da, &lX));
707 PetscCall(DMGlobalToLocalBegin(fine->da, X, INSERT_VALUES, lX));
708 PetscCall(DMGlobalToLocalEnd(fine->da, X, INSERT_VALUES, lX));
709 PetscCall(DMDAVecGetArrayRead(fine->da, lX, (void *)&x));
710 PetscCall(DMDAVecGetArrayRead(fine->da, fine->lNvert, (void *)&nvert_f));
711 PetscCall(DMDAVecGetArrayRead(user->da, user->lNvert, (void *)&nvert));
712 PetscCall(DMDAVecGetArray(user->da, F, &f));
713
714 for (PetscInt k = info.zs; k < info.zs + info.zm; k++) {
715 for (PetscInt j = info.ys; j < info.ys + info.ym; j++) {
716 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
717 if (i == 0 || i == mx - 1 || j == 0 || j == my - 1 || k == 0 || k == mz - 1 ||
718 nvert[k][j][i] > POISSON_SOLID_THRESHOLD) {
719 f[k][j][i] = 0.0;
720 continue;
721 }
722 const PetscInt ih = user->isc ? i : 2 * i;
723 const PetscInt jh = user->jsc ? j : 2 * j;
724 const PetscInt kh = user->ksc ? k : 2 * k;
725 f[k][j][i] = 0.125 *
726 (x[kh ][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih ]) +
727 x[kh ][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh ][ih-ia]) +
728 x[kh ][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih ]) +
729 x[kh-ka][jh ][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih ]) +
730 x[kh ][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh ][jh-ja][ih-ia]) +
731 x[kh-ka][jh-ja][ih ] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih ]) +
732 x[kh-ka][jh ][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh ][ih-ia]) +
733 x[kh-ka][jh-ja][ih-ia] * PetscMax(0., 1 - nvert_f[kh-ka][jh-ja][ih-ia]));
734 }
735 }
736 }
737
738 PetscCall(DMDAVecRestoreArray(user->da, F, &f));
739 PetscCall(DMDAVecRestoreArrayRead(user->da, user->lNvert, (void *)&nvert));
740 PetscCall(DMDAVecRestoreArrayRead(fine->da, fine->lNvert, (void *)&nvert_f));
741 PetscCall(DMDAVecRestoreArrayRead(fine->da, lX, (void *)&x));
742 PetscCall(DMRestoreLocalVector(fine->da, &lX));
743 PetscFunctionReturn(0);
744}
UserCtx * user_f
Definition variables.h:1166
Here is the caller graph for this function:

◆ PoissonMultigrid_ShiftBlockFactors()

static PetscErrorCode PoissonMultigrid_ShiftBlockFactors ( KSP  level_ksp)
static

Gives each block factor of a block-Jacobi level solver a small diagonal shift, so the factorization of a nearly singular Neumann block does not fail on a zero pivot.

Does nothing for any other preconditioner.

Definition at line 751 of file poisson.c.

752{
753 PC level_pc;
754 PCType level_pc_type;
755 PetscBool is_bjacobi = PETSC_FALSE;
756 KSP *block_ksp;
757 PetscInt nblocks;
758
759 PetscFunctionBeginUser;
760 PetscCall(KSPGetPC(level_ksp, &level_pc));
761 PetscCall(PCGetType(level_pc, &level_pc_type));
762 if (level_pc_type) PetscCall(PetscStrcmp(level_pc_type, PCBJACOBI, &is_bjacobi));
763 if (!is_bjacobi) PetscFunctionReturn(0);
764
765 PetscCall(KSPSetUp(level_ksp));
766 PetscCall(PCBJacobiGetSubKSP(level_pc, &nblocks, NULL, &block_ksp));
767 for (PetscInt b = 0; b < nblocks; b++) {
768 PC block_pc;
769 PetscCall(KSPGetPC(block_ksp[b], &block_pc));
770 PetscCall(PCFactorSetShiftAmount(block_pc, 1.e-10));
771 }
772 PetscFunctionReturn(0);
773}
Here is the caller graph for this function:

◆ PoissonMultigrid_Build()

static PetscErrorCode PoissonMultigrid_Build ( UserMG *  usermg,
PetscInt  bi 
)
static

Builds the multigrid solver for block bi and stores it in the finest level.

Assembles the operator on every level, then configures the outer ps_ Krylov solver with a multiplicative V-cycle PCMG: shell restriction and interpolation between levels, block-Jacobi smoothers by default, a coarse solve limited to 40 iterations at relative tolerance 1e-8, and the Neumann null space on every level. PETSc options override these defaults.

Each smoother runs pre_sweeps iterations before the coarse correction and post_sweeps after it. When the two differ, the post-smoother is a separate solver that starts as a copy of the configured pre-smoother and reads further options under ps_mg_levels_N_up_. Everything built here depends only on the grid metrics, the solid field, and the boundary types; calling it again after one of those changes rebuilds the solver.

Definition at line 793 of file poisson.c.

794{
795 MGCtx *mgctx = usermg->mgctx;
796 const PetscInt levels = usermg->mglevels;
797 UserCtx *finest = &mgctx[levels - 1].user[bi];
798 SimCtx *simCtx = finest->simCtx;
799 DualMonitorCtx *monitor;
800 KSP ksp;
801 PC pc;
802
803 PetscFunctionBeginUser;
805 LOG_ALLOW(GLOBAL, LOG_INFO, "Block %d: building the multigrid Poisson solver on %d levels.\n", bi, levels);
806
807 for (PetscInt l = levels - 1; l >= 0; l--) PetscCall(AssemblePoissonOperator(&mgctx[l].user[bi]));
808
809 PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
810 PetscCall(KSPAppendOptionsPrefix(ksp, "ps_"));
811
812 /* The convergence log is opened and closed around every solve; see
813 PoissonMultigrid_OpenConvergenceLog(). The monitor owns its context. */
814 PetscCall(PetscNew(&monitor));
815 monitor->block_id = bi;
816 monitor->file_handle = NULL;
817 PetscCall(KSPMonitorSet(ksp, DualKSPMonitor, monitor, DualMonitorDestroy));
818
819 PetscCall(KSPGetPC(ksp, &pc));
820 PetscCall(PCSetType(pc, PCMG));
821 PetscCall(PCMGSetLevels(pc, levels, NULL));
822 PetscCall(PCMGSetCycleType(pc, PC_MG_CYCLE_V));
823 PetscCall(PCMGSetType(pc, PC_MG_MULTIPLICATIVE));
824 PetscCall(PCMGSetNumberSmooth(pc, simCtx->mg_preItr));
825
826 for (PetscInt l = levels - 1; l > 0; l--) {
827 UserCtx *fine = &mgctx[l].user[bi];
828 UserCtx *coarse = &mgctx[l - 1].user[bi];
829 const PetscInt m_c = coarse->info.xm * coarse->info.ym * coarse->info.zm;
830 const PetscInt m_f = fine->info.xm * fine->info.ym * fine->info.zm;
831 const PetscInt M_c = coarse->info.mx * coarse->info.my * coarse->info.mz;
832 const PetscInt M_f = fine->info.mx * fine->info.my * fine->info.mz;
833
834 PetscCall(MatCreateShell(PETSC_COMM_WORLD, m_c, m_f, M_c, M_f, coarse, &fine->MR));
835 PetscCall(MatCreateShell(PETSC_COMM_WORLD, m_f, m_c, M_f, M_c, fine, &fine->MP));
836 PetscCall(MatShellSetOperation(fine->MR, MATOP_MULT, (void (*)(void))PoissonMultigrid_Restrict));
837 PetscCall(MatShellSetOperation(fine->MP, MATOP_MULT, (void (*)(void))PoissonMultigrid_Interpolate));
838 PetscCall(PCMGSetRestriction(pc, l, fine->MR));
839 PetscCall(PCMGSetInterpolation(pc, l, fine->MP));
840 }
841
842 for (PetscInt l = levels - 1; l >= 0; l--) {
843 UserCtx *level = &mgctx[l].user[bi];
844 KSP level_ksp;
845 PC level_pc;
846
847 if (l > 0) {
848 PetscCall(PCMGGetSmoother(pc, l, &level_ksp));
849 } else {
850 PetscCall(PCMGGetCoarseSolve(pc, &level_ksp));
851 PetscCall(KSPSetTolerances(level_ksp, 1.e-8, PETSC_DEFAULT, PETSC_DEFAULT, 40));
852 }
853 PetscCall(KSPSetOperators(level_ksp, level->A, level->A));
854 PetscCall(KSPGetPC(level_ksp, &level_pc));
855 PetscCall(PCSetType(level_pc, PCBJACOBI));
856 PetscCall(KSPSetFromOptions(level_ksp));
857 PetscCall(PoissonMultigrid_ShiftBlockFactors(level_ksp));
858
859 PetscCall(MatNullSpaceCreate(PETSC_COMM_WORLD, PETSC_TRUE, 0, NULL, &level->nullsp));
860 PetscCall(MatNullSpaceSetFunction(level->nullsp, PoissonMultigrid_RemoveNullSpace, level));
861 PetscCall(MatSetNullSpace(level->A, level->nullsp));
862 PetscCall(PCMGSetResidual(pc, l, PCMGResidualDefault, level->A));
863 PetscCall(KSPSetUp(level_ksp));
864
865 if (l > 0 && simCtx->mg_preItr != simCtx->mg_poItr) {
866 KSP post_smoother;
867
868 /* PETSc creates the post-smoother as a copy of the pre-smoother's type,
869 preconditioner and tolerances as configured so far. */
870 PetscCall(PCMGGetSmootherUp(pc, l, &post_smoother));
871 PetscCall(KSPAppendOptionsPrefix(post_smoother, "up_"));
872 PetscCall(KSPSetOperators(post_smoother, level->A, level->A));
873 PetscCall(KSPSetTolerances(post_smoother, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT, simCtx->mg_poItr));
874 PetscCall(KSPSetFromOptions(post_smoother));
875 PetscCall(PoissonMultigrid_ShiftBlockFactors(post_smoother));
876 PetscCall(KSPSetUp(post_smoother));
877 }
878
879 if (l < levels - 1) {
880 PetscCall(MatCreateVecs(level->A, &level->R, NULL));
881 PetscCall(PCMGSetRhs(pc, l, level->R));
882 }
883 }
884
885 PetscCall(KSPSetOperators(ksp, finest->A, finest->A));
886 PetscCall(MatSetNullSpace(finest->A, finest->nullsp));
887 PetscCall(KSPSetFromOptions(ksp));
888 PetscCall(KSPSetUp(ksp));
889 PetscCall(VecDuplicate(finest->P, &finest->B));
890 finest->ksp = ksp;
891
893 PetscFunctionReturn(0);
894}
PetscErrorCode DualMonitorDestroy(void **ctx)
Destroys the DualMonitorCtx.
Definition logging.c:926
PetscErrorCode DualKSPMonitor(KSP ksp, PetscInt it, PetscReal rnorm, void *ctx)
A custom KSP monitor that logs to a file and optionally to the console.
Definition logging.c:965
FILE * file_handle
Definition logging.h:57
PetscInt block_id
Definition logging.h:61
Context for a dual-purpose KSP monitor.
Definition logging.h:56
static PetscErrorCode PoissonMultigrid_Restrict(Mat R, Vec X, Vec F)
Restricts a fine-level residual to the next coarser level (MatShell multiply).
Definition poisson.c:691
PetscErrorCode AssemblePoissonOperator(UserCtx *user)
Implementation of AssemblePoissonOperator().
Definition poisson.c:251
static PetscErrorCode PoissonMultigrid_ShiftBlockFactors(KSP level_ksp)
Gives each block factor of a block-Jacobi level solver a small diagonal shift, so the factorization o...
Definition poisson.c:751
static PetscErrorCode PoissonMultigrid_Interpolate(Mat P, Vec X, Vec F)
Prolongs a coarse-level correction to the next finer level (MatShell multiply).
Definition poisson.c:613
static PetscErrorCode PoissonMultigrid_RemoveNullSpace(MatNullSpace nullsp, Vec X, void *ctx)
Removes the null space of the Neumann pressure problem from a level vector.
Definition poisson.c:533
UserCtx * user
Definition variables.h:729
MatNullSpace nullsp
Definition variables.h:1142
PetscInt mg_poItr
Definition variables.h:902
PetscInt mglevels
Definition variables.h:736
MGCtx * mgctx
Definition variables.h:739
PetscInt mg_preItr
Definition variables.h:902
Context for Multigrid operations.
Definition variables.h:728
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PoissonMultigrid_OpenConvergenceLog()

static PetscErrorCode PoissonMultigrid_OpenConvergenceLog ( KSP  ksp,
SimCtx *  simCtx,
PetscInt  bi 
)
static

Prepares the convergence monitor for one solve and opens its log file on rank 0.

The first step of a fresh run truncates the log; every other step appends, and the first step of a continued run records where it resumed.

Definition at line 902 of file poisson.c.

903{
904 DualMonitorCtx *monitor = NULL;
905 const PetscBool first_step = (PetscBool)(simCtx->step == simCtx->StartStep + 1);
906
907 PetscFunctionBeginUser;
908 PetscCall(KSPGetMonitorContext(ksp, &monitor));
909 monitor->step = simCtx->step;
911 monitor->file_handle = NULL;
912 if (simCtx->rank == 0) {
913 char filename[PETSC_MAX_PATH_LEN + 128];
914
915 PetscCall(PetscSNPrintf(filename, sizeof(filename),
916 "%s/Poisson_Solver_Convergence_History_Block_%d.log", simCtx->log_dir, bi));
917 monitor->file_handle = fopen(filename, (first_step && !simCtx->continueMode) ? "w" : "a");
918 PetscCheck(monitor->file_handle, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
919 "Could not open KSP monitor log file: %s", filename);
920 if (simCtx->continueMode && first_step) {
921 PetscCall(PetscFPrintf(PETSC_COMM_SELF, monitor->file_handle,
922 "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep));
923 }
924 PetscCall(PetscFPrintf(PETSC_COMM_SELF, monitor->file_handle,
925 "--- Convergence for Timestep %d, Block %d ---\n", (int)simCtx->step, bi));
926 }
927 PetscFunctionReturn(0);
928}
PetscBool log_to_console
Definition logging.h:58
PetscInt step
Definition logging.h:60
PetscBool continueMode
Definition variables.h:876
PetscMPIInt rank
Definition variables.h:862
PetscInt StartStep
Definition variables.h:869
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:882
PetscInt step
Definition variables.h:867
PetscBool ps_ksp_pic_monitor_true_residual
Definition variables.h:915
Here is the caller graph for this function:

◆ PoissonMultigrid_CloseConvergenceLog()

static PetscErrorCode PoissonMultigrid_CloseConvergenceLog ( KSP  ksp)
static

Closes the log file opened by PoissonMultigrid_OpenConvergenceLog().

Definition at line 931 of file poisson.c.

932{
933 DualMonitorCtx *monitor = NULL;
934
935 PetscFunctionBeginUser;
936 PetscCall(KSPGetMonitorContext(ksp, &monitor));
937 if (monitor->file_handle) {
938 fclose(monitor->file_handle);
939 monitor->file_handle = NULL;
940 }
941 PetscFunctionReturn(0);
942}
Here is the caller graph for this function:

◆ PoissonSolver_Multigrid()

PetscErrorCode PoissonSolver_Multigrid ( UserMG *  usermg)

Implementation of PoissonSolver_Multigrid().

Solves the pressure-correction equation for every block with geometric multigrid.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/poisson.h.

See also
PoissonSolver_Multigrid()

Definition at line 952 of file poisson.c.

953{
954 SimCtx *simCtx = usermg->mgctx[0].user[0].simCtx;
955 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
956
957 PetscFunctionBeginUser;
959 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting Multigrid Poisson Solve...\n");
960
961 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
962 UserCtx *user = &usermg->mgctx[usermg->mglevels - 1].user[bi];
963 KSPConvergedReason reason;
964
965 if (!user->ksp) PetscCall(PoissonMultigrid_Build(usermg, bi));
966
967 PetscCall(SynchronizePeriodicStaggeredFields(user, 1, staggered_fields));
968 PetscCall(ComputePoissonRHS(user, user->B));
969
970 PetscCall(PoissonMultigrid_OpenConvergenceLog(user->ksp, simCtx, bi));
971 PetscCall(KSPSolve(user->ksp, user->B, user->Phi));
973
974 /* A non-finite residual or a preconditioner that could not be built leaves Phi
975 meaningless, and the projection would carry it into the next momentum step,
976 where it surfaces as a failure of the wrong solver. Stopping at max_it is this
977 solve's normal mode and stays silent; any other divergence is reported, because
978 the projection proceeds on that Phi. */
979 PetscCall(KSPGetConvergedReason(user->ksp, &reason));
980 PetscCheck(reason != KSP_DIVERGED_NANORINF && reason != KSP_DIVERGED_PC_FAILED,
981 PETSC_COMM_WORLD, PETSC_ERR_NOT_CONVERGED,
982 "Pressure Poisson solve on block %" PetscInt_FMT " failed at step %" PetscInt_FMT
983 " (KSP reason %s). Known causes: a multigrid hierarchy coarsened too far "
984 "(reduce poisson_solver.multigrid.levels or refine the grid), or a momentum "
985 "field that has already diverged, such as an explicit time step beyond its "
986 "stability limit.",
987 bi, simCtx->step, KSPConvergedReasons[reason]);
988 if (reason < 0 && reason != KSP_DIVERGED_ITS) {
989 LOG(GLOBAL, LOG_WARNING, "Pressure Poisson solve on block %" PetscInt_FMT
990 " diverged at step %" PetscInt_FMT " (KSP reason %s); the projection uses the last iterate.\n",
991 bi, simCtx->step, KSPConvergedReasons[reason]);
992 }
993 }
994
995 LOG_ALLOW(GLOBAL, LOG_INFO, "Multigrid Poisson Solve complete.\n");
997 PetscFunctionReturn(0);
998}
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
static PetscErrorCode PoissonMultigrid_CloseConvergenceLog(KSP ksp)
Closes the log file opened by PoissonMultigrid_OpenConvergenceLog().
Definition poisson.c:931
static PetscErrorCode PoissonMultigrid_OpenConvergenceLog(KSP ksp, SimCtx *simCtx, PetscInt bi)
Prepares the convergence monitor for one solve and opens its log file on rank 0.
Definition poisson.c:902
PetscErrorCode ComputePoissonRHS(UserCtx *user, Vec B)
Implementation of ComputePoissonRHS().
Definition poisson.c:353
static PetscErrorCode PoissonMultigrid_Build(UserMG *usermg, PetscInt bi)
Builds the multigrid solver for block bi and stores it in the finest level.
Definition poisson.c:793
PetscInt block_number
Definition variables.h:952
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ POISSON_STENCIL_OFFSETS

const PetscInt POISSON_STENCIL_OFFSETS[19][3]
static
Initial value:
= {
{ 0, 0, 0},
{ 1, 0, 0}, {-1, 0, 0}, { 0, 1, 0}, { 0, -1, 0},
{ 0, 0, 1}, { 0, 0, -1},
{ 1, 1, 0}, { 1, -1, 0}, {-1, 1, 0}, {-1, -1, 0},
{ 0, 1, 1}, { 0, 1, -1}, { 0, -1, 1}, { 0, -1, -1},
{ 1, 0, 1}, { 1, 0, -1}, {-1, 0, 1}, {-1, 0, -1},
}

Offsets of the 19 stencil points, in the column order used to insert each row.

Definition at line 29 of file poisson.c.

29 {
30 { 0, 0, 0}, /* centre */
31 { 1, 0, 0}, {-1, 0, 0}, { 0, 1, 0}, { 0, -1, 0}, /* faces */
32 { 0, 0, 1}, { 0, 0, -1},
33 { 1, 1, 0}, { 1, -1, 0}, {-1, 1, 0}, {-1, -1, 0}, /* edges */
34 { 0, 1, 1}, { 0, 1, -1}, { 0, -1, 1}, { 0, -1, -1},
35 { 1, 0, 1}, { 1, 0, -1}, {-1, 0, 1}, {-1, 0, -1},
36};

◆ POISSON_FACE_METRIC_FIELDS

const FieldId POISSON_FACE_METRIC_FIELDS[3][3]
static
Initial value:
= {
}
@ FIELD_ID_JETA
@ FIELD_ID_KETA
@ FIELD_ID_KZET
@ FIELD_ID_IETA
@ FIELD_ID_ICSI
@ FIELD_ID_JCSI
@ FIELD_ID_JZET
@ FIELD_ID_IZET
@ FIELD_ID_KCSI

Field IDs of the face metric vectors, indexed as PoissonFaceMetrics.

Definition at line 65 of file poisson.c.

◆ POISSON_FACE_AJ_FIELDS

const FieldId POISSON_FACE_AJ_FIELDS[3] = {FIELD_ID_IAJ, FIELD_ID_JAJ, FIELD_ID_KAJ}
static

Definition at line 70 of file poisson.c.

@ FIELD_ID_IAJ
@ FIELD_ID_JAJ
@ FIELD_ID_KAJ