xref: /petsc/src/snes/tutorials/ex26.c (revision 750b007cd8d816cecd9de99077bb0a703b4cf61a)
1 static char help[] = "'Good Cop' Helmholtz Problem in 2d and 3d with finite elements.\n\
2 We solve the 'Good Cop' Helmholtz problem in a rectangular\n\
3 domain, using a parallel unstructured mesh (DMPLEX) to discretize it.\n\
4 This example supports automatic convergence estimation\n\
5 and coarse space adaptivity.\n\n\n";
6 
7 /*
8    The model problem:
9       Solve "Good Cop" Helmholtz equation on the unit square: (0,1) x (0,1)
10           - \Delta u + u = f,
11            where \Delta = Laplace operator
12       Dirichlet b.c.'s on all sides
13 
14 */
15 
16 #include <petscdmplex.h>
17 #include <petscsnes.h>
18 #include <petscds.h>
19 #include <petscconvest.h>
20 
21 typedef struct {
22   PetscBool trig; /* Use trig function as exact solution */
23 } AppCtx;
24 
25 /* For Primal Problem */
26 static void g0_uu(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[]) {
27   PetscInt d;
28   for (d = 0; d < dim; ++d) g0[0] = 1.0;
29 }
30 
31 static void g3_uu(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 g3[]) {
32   PetscInt d;
33   for (d = 0; d < dim; ++d) g3[d * dim + d] = 1.0;
34 }
35 
36 static PetscErrorCode trig_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx) {
37   PetscInt d;
38   *u = 0.0;
39   for (d = 0; d < dim; ++d) *u += PetscSinReal(2.0 * PETSC_PI * x[d]);
40   return 0;
41 }
42 
43 static PetscErrorCode quad_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx) {
44   PetscInt d;
45   *u = 1.0;
46   for (d = 0; d < dim; ++d) *u += (d + 1) * PetscSqr(x[d]);
47   return 0;
48 }
49 
50 static void f0_trig_u(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[]) {
51   PetscInt d;
52   f0[0] += u[0];
53   for (d = 0; d < dim; ++d) f0[0] -= 4.0 * PetscSqr(PETSC_PI) * PetscSinReal(2.0 * PETSC_PI * x[d]) + PetscSinReal(2.0 * PETSC_PI * x[d]);
54 }
55 
56 static void f0_quad_u(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[]) {
57   PetscInt d;
58   switch (dim) {
59   case 1: f0[0] = 1.0; break;
60   case 2: f0[0] = 5.0; break;
61   case 3: f0[0] = 11.0; break;
62   default: f0[0] = 5.0; break;
63   }
64   f0[0] += u[0];
65   for (d = 0; d < dim; ++d) f0[0] -= (d + 1) * PetscSqr(x[d]);
66 }
67 
68 static void f1_u(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[]) {
69   PetscInt d;
70   for (d = 0; d < dim; ++d) f1[d] = u_x[d];
71 }
72 
73 static PetscErrorCode ProcessOptions(DM dm, AppCtx *options) {
74   MPI_Comm comm;
75   PetscInt dim;
76 
77   PetscFunctionBeginUser;
78   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
79   PetscCall(DMGetDimension(dm, &dim));
80   options->trig = PETSC_FALSE;
81 
82   PetscOptionsBegin(comm, "", "Helmholtz Problem Options", "DMPLEX");
83   PetscCall(PetscOptionsBool("-exact_trig", "Use trigonometric exact solution (better for more complex finite elements)", "ex26.c", options->trig, &options->trig, NULL));
84   PetscOptionsEnd();
85 
86   PetscFunctionReturn(0);
87 }
88 
89 static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm) {
90   PetscFunctionBeginUser;
91   PetscCall(DMCreate(comm, dm));
92   PetscCall(DMSetType(*dm, DMPLEX));
93   PetscCall(DMSetFromOptions(*dm));
94 
95   PetscCall(PetscObjectSetName((PetscObject)*dm, "Mesh"));
96   PetscCall(DMSetApplicationContext(*dm, user));
97   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
98 
99   PetscFunctionReturn(0);
100 }
101 
102 static PetscErrorCode SetupPrimalProblem(DM dm, AppCtx *user) {
103   PetscDS        ds;
104   DMLabel        label;
105   const PetscInt id = 1;
106 
107   PetscFunctionBeginUser;
108   PetscCall(DMGetDS(dm, &ds));
109   PetscCall(DMGetLabel(dm, "marker", &label));
110   if (user->trig) {
111     PetscCall(PetscDSSetResidual(ds, 0, f0_trig_u, f1_u));
112     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_uu, NULL, NULL, g3_uu));
113     PetscCall(PetscDSSetExactSolution(ds, 0, trig_u, user));
114     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, &id, 0, 0, NULL, (void (*)(void))trig_u, NULL, user, NULL));
115     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Trig Exact Solution\n"));
116   } else {
117     PetscCall(PetscDSSetResidual(ds, 0, f0_quad_u, f1_u));
118     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_uu, NULL, NULL, g3_uu));
119     PetscCall(PetscDSSetExactSolution(ds, 0, quad_u, user));
120     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, &id, 0, 0, NULL, (void (*)(void))quad_u, NULL, user, NULL));
121   }
122   PetscFunctionReturn(0);
123 }
124 
125 static PetscErrorCode SetupDiscretization(DM dm, const char name[], PetscErrorCode (*setup)(DM, AppCtx *), AppCtx *user) {
126   DM             cdm = dm;
127   PetscFE        fe;
128   DMPolytopeType ct;
129   PetscBool      simplex;
130   PetscInt       dim, cStart;
131   char           prefix[PETSC_MAX_PATH_LEN];
132 
133   PetscFunctionBeginUser;
134   PetscCall(DMGetDimension(dm, &dim));
135 
136   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
137   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
138   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
139   /* Create finite element */
140   PetscCall(PetscSNPrintf(prefix, PETSC_MAX_PATH_LEN, "%s_", name));
141   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, name ? prefix : NULL, -1, &fe));
142   PetscCall(PetscObjectSetName((PetscObject)fe, name));
143   /* Set discretization and boundary conditions for each mesh */
144   PetscCall(DMSetField(dm, 0, NULL, (PetscObject)fe));
145   PetscCall(DMCreateDS(dm));
146   PetscCall((*setup)(dm, user));
147   while (cdm) {
148     PetscCall(DMCopyDisc(dm, cdm));
149     PetscCall(DMGetCoarseDM(cdm, &cdm));
150   }
151   PetscCall(PetscFEDestroy(&fe));
152   PetscFunctionReturn(0);
153 }
154 
155 int main(int argc, char **argv) {
156   DM      dm; /* Problem specification */
157   PetscDS ds;
158   SNES    snes; /* Nonlinear solver */
159   Vec     u;    /* Solutions */
160   AppCtx  user; /* User-defined work context */
161 
162   PetscFunctionBeginUser;
163   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
164   /* Primal system */
165   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
166   PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm));
167   PetscCall(ProcessOptions(dm, &user));
168   PetscCall(SNESSetDM(snes, dm));
169   PetscCall(SetupDiscretization(dm, "potential", SetupPrimalProblem, &user));
170   PetscCall(DMCreateGlobalVector(dm, &u));
171   PetscCall(VecSet(u, 0.0));
172   PetscCall(PetscObjectSetName((PetscObject)u, "potential"));
173   PetscCall(DMPlexSetSNESLocalFEM(dm, &user, &user, &user));
174   PetscCall(SNESSetFromOptions(snes));
175   PetscCall(DMSNESCheckFromOptions(snes, u));
176 
177   /* Looking for field error */
178   PetscInt Nfields;
179   PetscCall(DMGetDS(dm, &ds));
180   PetscCall(PetscDSGetNumFields(ds, &Nfields));
181   PetscCall(SNESSolve(snes, NULL, u));
182   PetscCall(SNESGetSolution(snes, &u));
183   PetscCall(VecViewFromOptions(u, NULL, "-potential_view"));
184 
185   /* Cleanup */
186   PetscCall(VecDestroy(&u));
187   PetscCall(SNESDestroy(&snes));
188   PetscCall(DMDestroy(&dm));
189   PetscCall(PetscFinalize());
190   return 0;
191 }
192 
193 /*TEST
194 test:
195   # L_2 convergence rate: 1.9
196   suffix: 2d_p1_conv
197   requires: triangle
198   args: -potential_petscspace_degree 1 -snes_convergence_estimate -dm_refine 2 -convest_num_refine 3 -pc_type lu
199 test:
200   # L_2 convergence rate: 1.9
201   suffix: 2d_q1_conv
202   args: -dm_plex_simplex 0 -potential_petscspace_degree 1 -snes_convergence_estimate -dm_refine 2 -convest_num_refine 3 -pc_type lu
203 test:
204   # Using -convest_num_refine 3 we get L_2 convergence rate: -1.5
205   suffix: 3d_p1_conv
206   requires: ctetgen
207   args: -dm_plex_dim 3 -dm_refine 2 -potential_petscspace_degree 1 -snes_convergence_estimate -convest_num_refine 1 -pc_type lu
208 test:
209   # Using -dm_refine 2 -convest_num_refine 3 we get L_2 convergence rate: -1.2
210   suffix: 3d_q1_conv
211   args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_refine 2 -potential_petscspace_degree 1 -snes_convergence_estimate -convest_num_refine 1 -pc_type lu
212 test:
213   # L_2 convergence rate: 1.9
214   suffix: 2d_p1_trig_conv
215   requires: triangle
216   args: -potential_petscspace_degree 1 -snes_convergence_estimate -dm_refine 2 -convest_num_refine 3 -pc_type lu -exact_trig
217 test:
218   # L_2 convergence rate: 1.9
219   suffix: 2d_q1_trig_conv
220   args: -dm_plex_simplex 0 -potential_petscspace_degree 1 -snes_convergence_estimate -dm_refine 2 -convest_num_refine 3 -pc_type lu -exact_trig
221 test:
222   # Using -convest_num_refine 3 we get L_2 convergence rate: -1.5
223   suffix: 3d_p1_trig_conv
224   requires: ctetgen
225   args: -dm_plex_dim 3 -dm_refine 2 -potential_petscspace_degree 1 -snes_convergence_estimate -convest_num_refine 1 -pc_type lu -exact_trig
226 test:
227   # Using -dm_refine 2 -convest_num_refine 3 we get L_2 convergence rate: -1.2
228   suffix: 3d_q1_trig_conv
229   args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_refine 2 -potential_petscspace_degree 1 -snes_convergence_estimate -convest_num_refine 1 -pc_type lu -exact_trig
230 test:
231   suffix: 2d_p1_gmg_vcycle
232   requires: triangle
233   args: -potential_petscspace_degree 1 -dm_plex_box_faces 2,2 -dm_refine_hierarchy 3 \
234     -ksp_type cg -ksp_rtol 1e-10 -pc_type mg \
235     -mg_levels_ksp_max_it 1 \
236     -mg_levels_pc_type jacobi -snes_monitor -ksp_monitor
237 test:
238   suffix: 2d_p1_gmg_fcycle
239   requires: triangle
240   args: -potential_petscspace_degree 1 -dm_plex_box_faces 2,2 -dm_refine_hierarchy 3 \
241     -ksp_type cg -ksp_rtol 1e-10 -pc_type mg -pc_mg_type full \
242     -mg_levels_ksp_max_it 2 \
243     -mg_levels_pc_type jacobi -snes_monitor -ksp_monitor
244 test:
245   suffix: 2d_p1_gmg_vcycle_trig
246   requires: triangle
247   args: -exact_trig -potential_petscspace_degree 1 -dm_plex_box_faces 2,2 -dm_refine_hierarchy 3 \
248     -ksp_type cg -ksp_rtol 1e-10 -pc_type mg \
249     -mg_levels_ksp_max_it 1 \
250     -mg_levels_pc_type jacobi -snes_monitor -ksp_monitor
251 test:
252   suffix: 2d_q3_cgns
253   requires: cgns
254   args: -dm_plex_simplex 0 -dm_plex_dim 2 -dm_plex_box_faces 3,3 -snes_view_solution cgns:sol.cgns -potential_petscspace_degree 3 -dm_coord_petscspace_degree 3
255 TEST*/
256