Actual source code: ex10.c

  1: static char help[] = "Tests implementation of PetscSpace_Sum by solving the Poisson equations using a PetscSpace_Poly and a PetscSpace_Sum and checking that \
  2:   solutions agree up to machine precision.\n\n";

  4: #include <petscdmplex.h>
  5: #include <petscds.h>
  6: #include <petscfe.h>
  7: #include <petscsnes.h>
  8: /* We are solving the system of equations:
  9:  * \vec{u} = -\grad{p}
 10:  * \div{u} = f
 11:  */

 13: /* Exact solutions for linear velocity
 14:    \vec{u} = \vec{x};
 15:    p = -0.5*(\vec{x} \cdot \vec{x});
 16:    */
 17: static PetscErrorCode linear_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx)
 18: {
 19:   PetscInt c;

 21:   for (c = 0; c < Nc; ++c) u[c] = x[c];
 22:   return PETSC_SUCCESS;
 23: }

 25: static PetscErrorCode linear_p(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx)
 26: {
 27:   u[0] = 0.;
 28:   for (PetscInt d = 0; d < dim; ++d) u[0] += -0.5 * x[d] * x[d];
 29:   return PETSC_SUCCESS;
 30: }

 32: static PetscErrorCode linear_divu(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx)
 33: {
 34:   u[0] = dim;
 35:   return PETSC_SUCCESS;
 36: }

 38: /* fx_v are the residual functions for the equation \vec{u} = \grad{p}. f0_v is the term <v,u>.*/
 39: static void f0_v(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
 40: {
 41:   for (PetscInt i = 0; i < dim; ++i) f0[i] = u[uOff[0] + i];
 42: }

 44: /* f1_v is the term <v,-\grad{p}> but we integrate by parts to get <\grad{v}, -p*I> */
 45: static void f1_v(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f1[])
 46: {
 47:   PetscInt c;

 49:   for (c = 0; c < dim; ++c) {
 50:     for (PetscInt d = 0; d < dim; ++d) f1[c * dim + d] = (c == d) ? -u[uOff[1]] : 0;
 51:   }
 52: }

 54: /* Residual function for enforcing \div{u} = f. */
 55: static void f0_q_linear(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
 56: {
 57:   PetscScalar rhs, divu = 0;

 59:   (void)linear_divu(dim, t, x, dim, &rhs, NULL);
 60:   for (PetscInt i = 0; i < dim; ++i) divu += u_x[uOff_x[0] + i * dim + i];
 61:   f0[0] = divu - rhs;
 62: }

 64: /* Boundary residual. Dirichlet boundary for u means u_bdy=p*n */
 65: static void f0_bd_u_linear(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], const PetscReal n[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
 66: {
 67:   PetscScalar pressure;

 69:   (void)linear_p(dim, t, x, dim, &pressure, NULL);
 70:   for (PetscInt d = 0; d < dim; ++d) f0[d] = pressure * n[d];
 71: }

 73: /* gx_yz are the jacobian functions obtained by taking the derivative of the y residual w.r.t z*/
 74: static void g0_vu(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[])
 75: {
 76:   for (PetscInt c = 0; c < dim; ++c) g0[c * dim + c] = 1.0;
 77: }

 79: static void g1_qu(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g1[])
 80: {
 81:   for (PetscInt c = 0; c < dim; ++c) g1[c * dim + c] = 1.0;
 82: }

 84: static void g2_vp(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g2[])
 85: {
 86:   for (PetscInt c = 0; c < dim; ++c) g2[c * dim + c] = -1.0;
 87: }

 89: typedef struct {
 90:   PetscInt dummy;
 91: } AppCtx;

 93: static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *ctx, DM *mesh)
 94: {
 95:   PetscFunctionBegin;
 96:   PetscCall(DMCreate(comm, mesh));
 97:   PetscCall(DMSetType(*mesh, DMPLEX));
 98:   PetscCall(DMSetFromOptions(*mesh));
 99:   PetscCall(DMSetApplicationContext(*mesh, ctx));
100:   PetscCall(DMViewFromOptions(*mesh, NULL, "-dm_view"));
101:   PetscFunctionReturn(PETSC_SUCCESS);
102: }

104: /* Setup the system of equations that we wish to solve */
105: static PetscErrorCode SetupProblem(DM dm, AppCtx *ctx)
106: {
107:   PetscDS        ds;
108:   DMLabel        label;
109:   PetscWeakForm  wf;
110:   const PetscInt id = 1;
111:   PetscInt       bd;

113:   PetscFunctionBegin;
114:   PetscCall(DMGetDS(dm, &ds));
115:   /* All of these are independent of the user's choice of solution */
116:   PetscCall(PetscDSSetResidual(ds, 0, f0_v, f1_v));
117:   PetscCall(PetscDSSetResidual(ds, 1, f0_q_linear, NULL));
118:   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_vu, NULL, NULL, NULL));
119:   PetscCall(PetscDSSetJacobian(ds, 0, 1, NULL, NULL, g2_vp, NULL));
120:   PetscCall(PetscDSSetJacobian(ds, 1, 0, NULL, g1_qu, NULL, NULL));

122:   PetscCall(DMGetLabel(dm, "marker", &label));
123:   PetscCall(PetscDSAddBoundary(ds, DM_BC_NATURAL, "Boundary Integral", label, 1, &id, 0, 0, NULL, (PetscFortranCallbackFn *)NULL, NULL, ctx, &bd));
124:   PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
125:   PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, 1, 0, 0, 0, f0_bd_u_linear, 0, NULL));

127:   PetscCall(PetscDSSetExactSolution(ds, 0, linear_u, NULL));
128:   PetscCall(PetscDSSetExactSolution(ds, 1, linear_p, NULL));
129:   PetscFunctionReturn(PETSC_SUCCESS);
130: }

132: /* Create the finite element spaces we will use for this system */
133: static PetscErrorCode SetupDiscretization(DM mesh, DM mesh_sum, PetscErrorCode (*setup)(DM, AppCtx *), AppCtx *ctx)
134: {
135:   DM        cdm = mesh, cdm_sum = mesh_sum;
136:   PetscDS   ds;
137:   PetscFE   u, divu, u_sum, divu_sum;
138:   PetscInt  dim;
139:   PetscBool simplex;

141:   PetscFunctionBegin;
142:   PetscCall(DMGetDimension(mesh, &dim));
143:   PetscCall(DMPlexIsSimplex(mesh, &simplex));

145:   {
146:     PetscBool force;
147:     // Turn off automatic quadrature selection as a test
148:     PetscCall(DMGetDS(mesh_sum, &ds));
149:     PetscCall(PetscDSGetForceQuad(ds, &force));
150:     if (force) PetscCall(PetscDSSetForceQuad(ds, PETSC_FALSE));
151:   }

153:   /* Create FE objects and give them names so that options can be set from
154:    * command line. Each field gets 2 instances (i.e. velocity and velocity_sum)created twice so that we can compare between approaches. */
155:   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh), dim, dim, simplex, "velocity_", -1, &u));
156:   PetscCall(PetscObjectSetName((PetscObject)u, "velocity"));
157:   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh_sum), dim, dim, simplex, "velocity_sum_", -1, &u_sum));
158:   PetscCall(PetscObjectSetName((PetscObject)u_sum, "velocity_sum"));
159:   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh), dim, 1, simplex, "divu_", -1, &divu));
160:   PetscCall(PetscObjectSetName((PetscObject)divu, "divu"));
161:   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh_sum), dim, 1, simplex, "divu_sum_", -1, &divu_sum));
162:   PetscCall(PetscObjectSetName((PetscObject)divu_sum, "divu_sum"));

164:   PetscCall(PetscFECopyQuadrature(u, divu));
165:   PetscCall(PetscFECopyQuadrature(u_sum, divu_sum));

167:   /* Associate the FE objects with the mesh and setup the system */
168:   PetscCall(DMSetField(mesh, 0, NULL, (PetscObject)u));
169:   PetscCall(DMSetField(mesh, 1, NULL, (PetscObject)divu));
170:   PetscCall(DMCreateDS(mesh));
171:   PetscCall((*setup)(mesh, ctx));

173:   PetscCall(DMSetField(mesh_sum, 0, NULL, (PetscObject)u_sum));
174:   PetscCall(DMSetField(mesh_sum, 1, NULL, (PetscObject)divu_sum));
175:   PetscCall(DMCreateDS(mesh_sum));
176:   PetscCall((*setup)(mesh_sum, ctx));

178:   while (cdm) {
179:     PetscCall(DMCopyDisc(mesh, cdm));
180:     PetscCall(DMGetCoarseDM(cdm, &cdm));
181:   }

183:   while (cdm_sum) {
184:     PetscCall(DMCopyDisc(mesh_sum, cdm_sum));
185:     PetscCall(DMGetCoarseDM(cdm_sum, &cdm_sum));
186:   }

188:   /* The Mesh now owns the fields, so we can destroy the FEs created in this
189:    * function */
190:   PetscCall(PetscFEDestroy(&u));
191:   PetscCall(PetscFEDestroy(&divu));
192:   PetscCall(PetscFEDestroy(&u_sum));
193:   PetscCall(PetscFEDestroy(&divu_sum));
194:   PetscCall(DMDestroy(&cdm));
195:   PetscCall(DMDestroy(&cdm_sum));
196:   PetscFunctionReturn(PETSC_SUCCESS);
197: }

199: int main(int argc, char **argv)
200: {
201:   AppCtx          ctx;
202:   DM              dm, dm_sum;
203:   SNES            snes, snes_sum;
204:   Vec             u, u_sum;
205:   PetscReal       errNorm;
206:   const PetscReal errTol = PETSC_SMALL;

208:   PetscFunctionBeginUser;
209:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));

211:   /* Set up a snes for the standard approach, one space with 2 components */
212:   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
213:   PetscCall(CreateMesh(PETSC_COMM_WORLD, &ctx, &dm));
214:   PetscCall(SNESSetDM(snes, dm));

216:   /* Set up a snes for the sum space approach, where each subspace of the sum space represents one component */
217:   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes_sum));
218:   PetscCall(CreateMesh(PETSC_COMM_WORLD, &ctx, &dm_sum));
219:   PetscCall(SNESSetDM(snes_sum, dm_sum));
220:   PetscCall(SetupDiscretization(dm, dm_sum, SetupProblem, &ctx));

222:   /* Set up and solve the system using standard approach. */
223:   PetscCall(DMCreateGlobalVector(dm, &u));
224:   PetscCall(PetscObjectSetName((PetscObject)u, "solution"));
225:   PetscCall(DMPlexSetSNESLocalFEM(dm, PETSC_FALSE, &ctx));
226:   PetscCall(SNESSetFromOptions(snes));
227:   PetscCall(DMSNESCheckFromOptions(snes, u));
228:   PetscCall(SNESSolve(snes, NULL, u));
229:   PetscCall(SNESGetSolution(snes, &u));
230:   PetscCall(VecViewFromOptions(u, NULL, "-solution_view"));

232:   /* Set up and solve the sum space system */
233:   PetscCall(DMCreateGlobalVector(dm_sum, &u_sum));
234:   PetscCall(PetscObjectSetName((PetscObject)u_sum, "solution_sum"));
235:   PetscCall(DMPlexSetSNESLocalFEM(dm_sum, PETSC_FALSE, &ctx));
236:   PetscCall(SNESSetFromOptions(snes_sum));
237:   PetscCall(DMSNESCheckFromOptions(snes_sum, u_sum));
238:   PetscCall(SNESSolve(snes_sum, NULL, u_sum));
239:   PetscCall(SNESGetSolution(snes_sum, &u_sum));
240:   PetscCall(VecViewFromOptions(u_sum, NULL, "-solution_sum_view"));

242:   /* Check if standard solution and sum space solution match to machine precision */
243:   PetscCall(VecAXPY(u_sum, -1, u));
244:   PetscCall(VecNorm(u_sum, NORM_2, &errNorm));
245:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Sum space provides the same solution as a regular space: %s", (errNorm < errTol) ? "true" : "false"));

247:   /* Cleanup */
248:   PetscCall(VecDestroy(&u_sum));
249:   PetscCall(VecDestroy(&u));
250:   PetscCall(SNESDestroy(&snes_sum));
251:   PetscCall(SNESDestroy(&snes));
252:   PetscCall(DMDestroy(&dm_sum));
253:   PetscCall(DMDestroy(&dm));
254:   PetscCall(PetscFinalize());
255:   return 0;
256: }

258: /*TEST
259:   test:
260:     suffix: 2d_lagrange
261:     requires: triangle
262:     args: -velocity_petscspace_degree 1 \
263:       -velocity_petscspace_type poly \
264:       -velocity_petscspace_components 2\
265:       -velocity_petscdualspace_type lagrange \
266:       -divu_petscspace_degree 0 \
267:       -divu_petscspace_type poly \
268:       -divu_petscdualspace_lagrange_continuity false \
269:       -velocity_sum_petscfe_default_quadrature_order 1 \
270:       -velocity_sum_petscspace_degree 1 \
271:       -velocity_sum_petscspace_type sum \
272:       -velocity_sum_petscspace_variables 2 \
273:       -velocity_sum_petscspace_components 2 \
274:       -velocity_sum_petscspace_sum_spaces 2 \
275:       -velocity_sum_petscspace_sum_concatenate true \
276:       -velocity_sum_petscdualspace_type lagrange \
277:       -velocity_sum_sumcomp_0_petscspace_type poly \
278:       -velocity_sum_sumcomp_0_petscspace_degree 1 \
279:       -velocity_sum_sumcomp_0_petscspace_variables 2 \
280:       -velocity_sum_sumcomp_0_petscspace_components 1 \
281:       -velocity_sum_sumcomp_1_petscspace_type poly \
282:       -velocity_sum_sumcomp_1_petscspace_degree 1 \
283:       -velocity_sum_sumcomp_1_petscspace_variables 2 \
284:       -velocity_sum_sumcomp_1_petscspace_components 1 \
285:       -divu_sum_petscspace_degree 0 \
286:       -divu_sum_petscspace_type sum \
287:       -divu_sum_petscspace_variables 2 \
288:       -divu_sum_petscspace_components 1 \
289:       -divu_sum_petscspace_sum_spaces 1 \
290:       -divu_sum_petscspace_sum_concatenate true \
291:       -divu_sum_petscdualspace_lagrange_continuity false \
292:       -divu_sum_sumcomp_0_petscspace_type poly \
293:       -divu_sum_sumcomp_0_petscspace_degree 0 \
294:       -divu_sum_sumcomp_0_petscspace_variables 2 \
295:       -divu_sum_sumcomp_0_petscspace_components 1 \
296:       -dm_refine 0 \
297:       -snes_error_if_not_converged \
298:       -ksp_rtol 1e-10 \
299:       -ksp_error_if_not_converged \
300:       -pc_type fieldsplit\
301:       -pc_fieldsplit_type schur\
302:       -divu_sum_petscdualspace_lagrange_continuity false \
303:       -pc_fieldsplit_schur_precondition full
304: TEST*/