xref: /petsc/src/snes/tutorials/ex27.c (revision 57d508425293f0bb93f59574d14951d8faac9af8)
1 static char help[] = "Poisson Problem in 2d and 3d with simplicial finite elements in both primal and mixed form.\n\
2 We solve the Poisson problem in a rectangular\n\
3 domain, using a parallel unstructured mesh (DMPLEX) to discretize it.\n\
4 This example solves mixed form equation to get the flux field to calculate flux norm. We then use that for adaptive mesh refinement. \n\n\n";
5 
6 /*
7 The primal (or original) Poisson problem, in the strong form, is given by,
8 
9 \begin{align}
10   - \nabla \cdot ( \nabla u ) = f
11 \end{align}
12 where $u$ is potential.
13 
14 The weak form of this equation is
15 
16 \begin{align}
17   < \nabla v, \nabla u > - < v, \nabla u \cdot \hat{n} >_\Gamma - < v, f > = 0
18 \end{align}
19 
20 The mixed Poisson problem, in the strong form, is given by,
21 
22 \begin{align}
23   q - \nabla u &= 0 \\
24   - \nabla \cdot q &= f
25 \end{align}
26 where $u$ is the potential and $q$ is the flux.
27 
28 The weak form of this equation is
29 
30 \begin{align}
31   < t, q > + < \nabla \cdot t, u > - < t \cdot \hat{n}, u >_\Gamma &= 0 \\
32   <v, \nabla \cdot q> - < v, f > &= 0
33 \end{align}
34 
35 We solve both primal and mixed problem and calculate the error in the flux norm, namely || e || = || q^m_h - \nabla u^p_h ||. Here superscript 'm' represents field from mixed form and 'p' represents field from the primal form.
36 
37 The following boundary conditions are prescribed.
38 
39 Primal problem:
40 \begin{align}
41   u = u_0                    on \Gamma_D
42   \nabla u \cdot \hat{n} = g on \Gamma_N
43 \end{align}
44 
45 Mixed problem:
46 \begin{align}
47   u = u_0             on \Gamma_D
48   q \cdot \hat{n} = g on \Gamma_N
49 \end{align}
50         __________\Gamma_D_____________
51         |                              |
52         |                              |
53         |                              |
54 \Gamma_N                               \Gamma_N
55         |                              |
56         |                              |
57         |                              |
58         |_________\Gamma_D_____________|
59 
60 To visualize the automated adaptation
61 
62   -dm_adapt_pre_view draw -dm_adapt_view draw -draw_pause -1 -geometry 0,0,1024,1024
63 
64 and to compare with a naice gradient estimator use
65 
66   -adaptor_type gradient
67 
68 To see a sequence of adaptations
69 
70   -snes_adapt_sequence 8 -adaptor_monitor_error draw::draw_lg
71   -dm_adapt_pre_view draw -dm_adapt_iter_view draw -dm_adapt_view draw -draw_pause 1 -geometry 0,0,1024,1024
72 
73 To get a better view of the by-hand process, use
74 
75   -dm_view hdf5:${PWD}/mesh.h5
76   -primal_sol_vec_view hdf5:${PWD}/mesh.h5::append
77   -flux_error_vec_view hdf5:${PWD}/mesh.h5::append
78   -exact_error_vec_view hdf5:${PWD}/mesh.h5::append
79   -refine_vec_view hdf5:${PWD}/mesh.h5::append
80   -adapt_dm_view draw -draw_pause -1
81 
82 This is also possible with the automated path
83 
84   -dm_view hdf5:${PWD}/mesh.h5
85   -adapt_primal_sol_vec_view hdf5:${PWD}/mesh.h5::append
86   -adapt_error_vec_view hdf5:${PWD}/mesh.h5::append
87   -adapt_vec_view hdf5:${PWD}/mesh.h5::append
88 */
89 
90 #include <petsc/private/petscfeimpl.h>
91 #include <petscdmplex.h>
92 #include <petscsnes.h>
93 #include <petscdmadaptor.h>
94 #include <petscds.h>
95 #include <petscviewerhdf5.h>
96 #include <petscbag.h>
97 
98 PETSC_EXTERN PetscErrorCode SetupMixed(DMAdaptor, DM);
99 
100 typedef enum {
101   SOL_QUADRATIC,
102   SOL_TRIG,
103   SOL_SENSOR,
104   SOL_UNKNOWN,
105   NUM_SOL_TYPE
106 } SolType;
107 const char *SolTypeNames[NUM_SOL_TYPE + 4] = {"quadratic", "trig", "sensor", "unknown", "SolType", "SOL_", NULL};
108 
109 typedef struct {
110   PetscBag  param;
111   SolType   solType;
112   PetscBool byHand;
113   PetscInt  numAdapt;
114 } AppCtx;
115 
116 typedef struct {
117   PetscReal k;
118 } Parameter;
119 
120 /* Exact solution: u = x^2 + y^2 */
121 static PetscErrorCode quadratic_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
122 {
123   PetscInt d;
124 
125   u[0] = 0.0;
126   for (d = 0; d < dim; ++d) u[0] += x[d] * x[d];
127   return PETSC_SUCCESS;
128 }
129 /* Exact solution: q = (2x, 2y) */
130 static PetscErrorCode quadratic_q(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
131 {
132   PetscInt c;
133   for (c = 0; c < Nc; ++c) u[c] = 2.0 * x[c];
134   return PETSC_SUCCESS;
135 }
136 
137 /* Exact solution: u = sin( n \pi x ) * sin( n \pi y ) */
138 static PetscErrorCode trig_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
139 {
140   const PetscReal n = 5.5;
141 
142   u[0] = 1.0;
143   for (PetscInt d = 0; d < dim; ++d) u[0] *= PetscSinReal(n * PETSC_PI * x[d]);
144   return PETSC_SUCCESS;
145 }
146 static PetscErrorCode trig_q(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
147 {
148   const PetscReal n = 5.5;
149 
150   for (PetscInt c = 0; c < Nc; c++) u[c] = n * PETSC_PI * PetscCosReal(n * PETSC_PI * x[c]) * PetscSinReal(n * PETSC_PI * x[Nc - c - 1]);
151   return PETSC_SUCCESS;
152 }
153 
154 /*
155 Classic hyperbolic sensor function for testing multi-scale anisotropic mesh adaptation:
156 
157   f:[-1, 1]x[-1, 1] \to R,
158     f(x, y) = sin(50xy)/100 if |xy| > 2\pi/50 else sin(50xy)
159 
160 (mapped to have domain [0,1] x [0,1] in this case).
161 */
162 static PetscErrorCode sensor_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nf, PetscScalar u[], void *ctx)
163 {
164   const PetscReal xref = 2. * x[0] - 1.;
165   const PetscReal yref = 2. * x[1] - 1.;
166   const PetscReal xy   = xref * yref;
167 
168   u[0] = PetscSinReal(50. * xy);
169   if (PetscAbsReal(xy) > 2. * PETSC_PI / 50.) u[0] *= 0.01;
170 
171   return PETSC_SUCCESS;
172 }
173 
174 /* Flux is (cos(50xy) * 50y/100, cos(50xy) * 50x/100) if |xy| > 2\pi/50 else (cos(50xy) * 50y, cos(50xy) * 50x) */
175 static PetscErrorCode sensor_q(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nf, PetscScalar u[], void *ctx)
176 {
177   const PetscReal xref = 2. * x[0] - 1.;
178   const PetscReal yref = 2. * x[1] - 1.;
179   const PetscReal xy   = xref * yref;
180 
181   u[0] = 50. * yref * PetscCosReal(50. * xy) * 2.0;
182   u[1] = 50. * xref * PetscCosReal(50. * xy) * 2.0;
183   if (PetscAbsReal(xy) > 2. * PETSC_PI / 50.) {
184     u[0] *= 0.01;
185     u[1] *= 0.01;
186   }
187   return PETSC_SUCCESS;
188 }
189 
190 /* We set up residuals and Jacobians for the primal problem. */
191 static void f0_quadratic_primal(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[])
192 {
193   f0[0] = 4.0;
194 }
195 
196 static void f0_trig_primal(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[])
197 {
198   const PetscReal n = 5.5;
199 
200   f0[0] = -2.0 * PetscSqr(n * PETSC_PI) * PetscSinReal(n * PETSC_PI * x[0]) * PetscSinReal(n * PETSC_PI * x[1]);
201 }
202 
203 static void f0_sensor_primal(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[])
204 {
205   const PetscReal xref = 2. * x[0] - 1.;
206   const PetscReal yref = 2. * x[1] - 1.;
207   const PetscReal xy   = xref * yref;
208 
209   f0[0] = -2500.0 * PetscSinReal(50. * xy) * (xref * xref + yref * yref) * 4.0;
210   if (PetscAbsReal(xy) > 2. * PETSC_PI / 50.) f0[0] *= 0.01;
211 }
212 
213 static void f1_primal(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[])
214 {
215   const PetscReal k = PetscRealPart(constants[0]);
216 
217   for (PetscInt d = 0; d < dim; ++d) f1[d] = k * u_x[uOff_x[0] + d];
218 }
219 
220 static void f0_quadratic_bd_primal(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[])
221 {
222   const PetscReal k = PetscRealPart(constants[0]);
223   PetscScalar     flux;
224 
225   PetscCallAbort(PETSC_COMM_SELF, quadratic_q(dim, t, x, dim, &flux, NULL));
226   for (PetscInt d = 0; d < dim; ++d) f0[d] = -k * flux * n[d];
227 }
228 
229 static void f0_trig_bd_primal(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[])
230 {
231   const PetscReal k = PetscRealPart(constants[0]);
232   PetscScalar     flux;
233 
234   PetscCallAbort(PETSC_COMM_SELF, trig_q(dim, t, x, dim, &flux, NULL));
235   for (PetscInt d = 0; d < dim; ++d) f0[d] = -k * flux * n[d];
236 }
237 
238 static void f0_sensor_bd_primal(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[])
239 {
240   const PetscReal k = PetscRealPart(constants[0]);
241   PetscScalar     flux[2];
242 
243   PetscCallAbort(PETSC_COMM_SELF, sensor_q(dim, t, x, dim, flux, NULL));
244   for (PetscInt d = 0; d < dim; ++d) f0[d] = -k * flux[d] * n[d];
245 }
246 
247 static void g3_primal_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[])
248 {
249   const PetscReal k = PetscRealPart(constants[0]);
250 
251   for (PetscInt d = 0; d < dim; ++d) g3[d * dim + d] = k;
252 }
253 
254 /* Now we set up the residuals and Jacobians mixed problem. */
255 static void f0_mixed_quadratic_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[])
256 {
257   f0[0] = 4.0;
258   for (PetscInt d = 0; d < dim; ++d) f0[0] += -u_x[uOff_x[0] + d * dim + d];
259 }
260 static void f0_mixed_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[])
261 {
262   PetscReal n = 5.5;
263 
264   f0[0] = -2.0 * PetscSqr(n * PETSC_PI) * PetscSinReal(n * PETSC_PI * x[0]) * PetscSinReal(n * PETSC_PI * x[1]);
265   for (PetscInt d = 0; d < dim; ++d) f0[0] += -u_x[uOff_x[0] + d * dim + d];
266 }
267 static void f0_mixed_sensor_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[])
268 {
269   const PetscReal xref = 2. * x[0] - 1.;
270   const PetscReal yref = 2. * x[1] - 1.;
271   const PetscReal xy   = xref * yref;
272 
273   f0[0] = -2500.0 * PetscSinReal(50. * xy) * (xref * xref + yref * yref) * 4.0;
274   if (PetscAbsReal(xy) > 2. * PETSC_PI / 50.) f0[0] *= 0.01;
275   for (PetscInt d = 0; d < dim; ++d) f0[0] += -u_x[uOff_x[0] + d * dim + d];
276 }
277 
278 static void f0_mixed_q(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[])
279 {
280   for (PetscInt d = 0; d < dim; d++) f0[d] = u[uOff[0] + d];
281 }
282 
283 static void f1_mixed_q(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[])
284 {
285   const PetscReal k = PetscRealPart(constants[0]);
286 
287   for (PetscInt d = 0; d < dim; d++) f1[d * dim + d] = k * u[uOff[1]];
288 }
289 
290 static void f0_quadratic_bd_mixed_q(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[])
291 {
292   const PetscReal k = PetscRealPart(constants[0]);
293   PetscScalar     potential;
294 
295   PetscCallAbort(PETSC_COMM_SELF, quadratic_u(dim, t, x, dim, &potential, NULL));
296   for (PetscInt d = 0; d < dim; ++d) f0[d] = -k * potential * n[d];
297 }
298 
299 static void f0_trig_bd_mixed_q(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[])
300 {
301   const PetscReal k = PetscRealPart(constants[0]);
302   PetscScalar     potential;
303 
304   PetscCallAbort(PETSC_COMM_SELF, trig_u(dim, t, x, dim, &potential, NULL));
305   for (PetscInt d = 0; d < dim; ++d) f0[d * dim + d] = -k * potential * n[d];
306 }
307 
308 static void f0_sensor_bd_mixed_q(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[])
309 {
310   const PetscReal k = PetscRealPart(constants[0]);
311   PetscScalar     potential;
312 
313   PetscCallAbort(PETSC_COMM_SELF, sensor_u(dim, t, x, dim, &potential, NULL));
314   for (PetscInt d = 0; d < dim; ++d) f0[d * dim + d] = -k * potential * n[d];
315 }
316 
317 /* <v, \nabla\cdot q> */
318 static void g1_mixed_uq(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[])
319 {
320   for (PetscInt d = 0; d < dim; d++) g1[d * dim + d] = -1.0;
321 }
322 
323 /* < t, q> */
324 static void g0_mixed_qq(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[])
325 {
326   for (PetscInt d = 0; d < dim; d++) g0[d * dim + d] = 1.0;
327 }
328 
329 /* <\nabla\cdot t, u> = <\nabla t, Iu> */
330 static void g2_mixed_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 g2[])
331 {
332   const PetscReal k = PetscRealPart(constants[0]);
333 
334   for (PetscInt d = 0; d < dim; d++) g2[d * dim + d] = k;
335 }
336 
337 static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *user)
338 {
339   PetscFunctionBeginUser;
340   PetscOptionsBegin(comm, "", "Flux norm error in primal poisson problem Options", "DMPLEX");
341   user->byHand   = PETSC_TRUE;
342   user->numAdapt = 1;
343   user->solType  = SOL_QUADRATIC;
344 
345   PetscCall(PetscOptionsGetBool(NULL, NULL, "-by_hand", &user->byHand, NULL));
346   PetscCall(PetscOptionsGetInt(NULL, NULL, "-num_adapt", &user->numAdapt, NULL));
347   PetscCall(PetscOptionsEnum("-sol_type", "Type of exact solution", "ex27.c", SolTypeNames, (PetscEnum)user->solType, (PetscEnum *)&user->solType, NULL));
348   PetscOptionsEnd();
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 static PetscErrorCode SetupParameters(PetscBag bag, AppCtx *user)
353 {
354   Parameter *param;
355 
356   PetscFunctionBeginUser;
357   PetscCall(PetscBagGetData(bag, (void **)&param));
358   PetscCall(PetscBagSetName(bag, "par", "Poisson parameters"));
359   PetscCall(PetscBagRegisterReal(bag, &param->k, 1.0, "k", "Thermal conductivity"));
360   PetscCall(PetscBagSetFromOptions(bag));
361   PetscFunctionReturn(PETSC_SUCCESS);
362 }
363 
364 static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm)
365 {
366   PetscFunctionBeginUser;
367   PetscCall(DMCreate(comm, dm));
368   PetscCall(DMSetType(*dm, DMPLEX));
369   PetscCall(DMSetFromOptions(*dm));
370   PetscCall(DMSetApplicationContext(*dm, &user));
371   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
372   PetscFunctionReturn(PETSC_SUCCESS);
373 }
374 
375 static PetscErrorCode SetupPrimalProblem(DM dm, AppCtx *user)
376 {
377   PetscDS       ds;
378   DMLabel       label;
379   PetscInt      id, bd;
380   Parameter    *param;
381   PetscWeakForm wf;
382 
383   PetscFunctionBeginUser;
384   PetscCall(DMGetDS(dm, &ds));
385   PetscCall(DMGetLabel(dm, "marker", &label));
386 
387   PetscCall(PetscDSSetJacobian(ds, 0, 0, NULL, NULL, NULL, g3_primal_uu));
388 
389   switch (user->solType) {
390   case SOL_QUADRATIC:
391     PetscCall(PetscDSSetResidual(ds, 0, f0_quadratic_primal, f1_primal));
392 
393     id = 1;
394     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "bottom wall primal potential", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)quadratic_u, NULL, user, NULL));
395 
396     id = 2;
397     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "right wall flux", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
398     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
399     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_quadratic_bd_primal, 0, NULL));
400 
401     id = 3;
402     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "top wall primal potential", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)quadratic_u, NULL, user, NULL));
403 
404     id = 4;
405     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "left wall flux", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
406     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
407     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_quadratic_bd_primal, 0, NULL));
408 
409     PetscCall(PetscDSSetExactSolution(ds, 0, quadratic_u, user));
410     break;
411   case SOL_TRIG:
412     PetscCall(PetscDSSetResidual(ds, 0, f0_trig_primal, f1_primal));
413 
414     id = 1;
415     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "bottom wall primal potential", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)trig_u, NULL, user, NULL));
416 
417     id = 2;
418     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "right wall flux", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
419     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
420     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_trig_bd_primal, 0, NULL));
421 
422     id = 3;
423     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "top wall primal potential", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)trig_u, NULL, user, NULL));
424 
425     id = 4;
426     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "left wall flux", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
427     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
428     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_trig_bd_primal, 0, NULL));
429 
430     PetscCall(PetscDSSetExactSolution(ds, 0, trig_u, user));
431     break;
432   case SOL_SENSOR:
433     PetscCall(PetscDSSetResidual(ds, 0, f0_sensor_primal, f1_primal));
434 
435     id = 1;
436     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "bottom wall primal potential", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)sensor_u, NULL, user, NULL));
437 
438     id = 2;
439     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "right wall flux", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
440     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
441     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_sensor_bd_primal, 0, NULL));
442 
443     id = 3;
444     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "top wall primal potential", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)sensor_u, NULL, user, NULL));
445 
446     id = 4;
447     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "left wall flux", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
448     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
449     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_sensor_bd_primal, 0, NULL));
450 
451     PetscCall(PetscDSSetExactSolution(ds, 0, sensor_u, user));
452     break;
453   default:
454     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Invalid exact solution type %s", SolTypeNames[PetscMin(user->solType, SOL_UNKNOWN)]);
455   }
456 
457   /* Setup constants */
458   {
459     PetscCall(PetscBagGetData(user->param, (void **)&param));
460     PetscScalar constants[1];
461 
462     constants[0] = param->k;
463 
464     PetscCall(PetscDSSetConstants(ds, 1, constants));
465   }
466   PetscFunctionReturn(PETSC_SUCCESS);
467 }
468 
469 static PetscErrorCode SetupPrimalDiscretization(DM dm, AppCtx *user)
470 {
471   DM             cdm = dm;
472   PetscFE        fe[1];
473   DMPolytopeType ct;
474   PetscInt       dim, cStart;
475   MPI_Comm       comm;
476 
477   PetscFunctionBeginUser;
478   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
479   PetscCall(DMGetDimension(dm, &dim));
480 
481   /* Create finite element */
482   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
483   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
484   PetscCall(PetscFECreateByCell(comm, dim, 1, ct, "primal_potential_", PETSC_DEFAULT, &fe[0]));
485   PetscCall(PetscObjectSetName((PetscObject)fe[0], "primal potential"));
486 
487   /* Set discretization and boundary conditions for each mesh */
488   PetscCall(DMSetField(dm, 0, NULL, (PetscObject)fe[0]));
489   PetscCall(DMCreateDS(dm));
490   while (cdm) {
491     PetscCall(DMCopyDisc(dm, cdm));
492     PetscCall(DMGetCoarseDM(cdm, &cdm));
493   }
494 
495   PetscCall(PetscFEDestroy(&fe[0]));
496   PetscFunctionReturn(PETSC_SUCCESS);
497 }
498 
499 static PetscErrorCode SetupMixedProblem(DM dm, AppCtx *user)
500 {
501   PetscDS       ds;
502   DMLabel       label;
503   PetscInt      id, bd;
504   Parameter    *param;
505   PetscWeakForm wf;
506 
507   PetscFunctionBeginUser;
508   PetscCall(DMGetDS(dm, &ds));
509   PetscCall(DMGetLabel(dm, "marker", &label));
510 
511   /* Residual terms */
512   PetscCall(PetscDSSetResidual(ds, 0, f0_mixed_q, f1_mixed_q));
513 
514   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_mixed_qq, NULL, NULL, NULL));
515   PetscCall(PetscDSSetJacobian(ds, 0, 1, NULL, NULL, g2_mixed_qu, NULL));
516 
517   PetscCall(PetscDSSetJacobian(ds, 1, 0, NULL, g1_mixed_uq, NULL, NULL));
518 
519   switch (user->solType) {
520   case SOL_QUADRATIC:
521     PetscCall(PetscDSSetResidual(ds, 1, f0_mixed_quadratic_u, NULL));
522 
523     id = 1;
524     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "Dirichlet Bd Integral bottom wall", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
525     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
526     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_quadratic_bd_mixed_q, 0, NULL));
527 
528     id = 2;
529     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "right wall flux", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)quadratic_q, NULL, user, NULL));
530 
531     id = 4;
532     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "left wall flux", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)quadratic_q, NULL, user, NULL));
533 
534     id = 3;
535     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "Dirichlet Bd Integral top wall", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
536     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
537     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_quadratic_bd_mixed_q, 0, NULL));
538 
539     PetscCall(PetscDSSetExactSolution(ds, 0, quadratic_q, user));
540     PetscCall(PetscDSSetExactSolution(ds, 1, quadratic_u, user));
541     break;
542   case SOL_TRIG:
543     PetscCall(PetscDSSetResidual(ds, 1, f0_mixed_trig_u, NULL));
544 
545     id = 1;
546     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "Dirichlet Bd Integral bottom wall", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
547     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
548     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_trig_bd_mixed_q, 0, NULL));
549 
550     id = 2;
551     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "right wall flux", label, 1, &id, 1, 0, NULL, (PetscVoidFn *)trig_q, NULL, user, NULL));
552 
553     id = 4;
554     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "left wall flux", label, 1, &id, 1, 0, NULL, (PetscVoidFn *)trig_q, NULL, user, NULL));
555 
556     id = 3;
557     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "Dirichlet Bd Integral top wall", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
558     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
559     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_trig_bd_mixed_q, 0, NULL));
560 
561     PetscCall(PetscDSSetExactSolution(ds, 0, trig_q, user));
562     PetscCall(PetscDSSetExactSolution(ds, 1, trig_u, user));
563     break;
564   case SOL_SENSOR:
565     PetscCall(PetscDSSetResidual(ds, 1, f0_mixed_sensor_u, NULL));
566 
567     id = 1;
568     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "Dirichlet Bd Integral bottom wall", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
569     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
570     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_sensor_bd_mixed_q, 0, NULL));
571 
572     id = 2;
573     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "right wall flux", label, 1, &id, 1, 0, NULL, (PetscVoidFn *)sensor_q, NULL, user, NULL));
574 
575     id = 4;
576     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "left wall flux", label, 1, &id, 1, 0, NULL, (PetscVoidFn *)sensor_q, NULL, user, NULL));
577 
578     id = 3;
579     PetscCall(DMAddBoundary(dm, DM_BC_NATURAL, "Dirichlet Bd Integral top wall", label, 1, &id, 0, 0, NULL, NULL, NULL, user, &bd));
580     PetscCall(PetscDSGetBoundary(ds, bd, &wf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
581     PetscCall(PetscWeakFormSetIndexBdResidual(wf, label, id, 0, 0, 0, f0_sensor_bd_mixed_q, 0, NULL));
582 
583     PetscCall(PetscDSSetExactSolution(ds, 0, sensor_q, user));
584     PetscCall(PetscDSSetExactSolution(ds, 1, sensor_u, user));
585     break;
586   default:
587     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Invalid exact solution type %s", SolTypeNames[PetscMin(user->solType, SOL_UNKNOWN)]);
588   }
589 
590   /* Setup constants */
591   {
592     PetscCall(PetscBagGetData(user->param, (void **)&param));
593     PetscScalar constants[1];
594 
595     constants[0] = param->k;
596 
597     PetscCall(PetscDSSetConstants(ds, 1, constants));
598   }
599   PetscFunctionReturn(PETSC_SUCCESS);
600 }
601 
602 static PetscErrorCode SetupMixedDiscretization(DM dm, AppCtx *user)
603 {
604   DM             cdm = dm;
605   PetscFE        fe[2];
606   DMPolytopeType ct;
607   PetscInt       dim, cStart;
608   MPI_Comm       comm;
609 
610   PetscFunctionBeginUser;
611   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
612   PetscCall(DMGetDimension(dm, &dim));
613 
614   /* Create finite element */
615   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, NULL));
616   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
617   PetscCall(PetscFECreateByCell(comm, dim, dim, ct, "mixed_flux_", PETSC_DEFAULT, &fe[0]));
618   PetscCall(PetscObjectSetName((PetscObject)fe[0], "mixed flux"));
619   /* NOTE:  Set the same quadrature order as the primal problem here or use the command line option. */
620 
621   PetscCall(PetscFECreateByCell(comm, dim, 1, ct, "mixed_potential_", PETSC_DEFAULT, &fe[1]));
622   PetscCall(PetscFECopyQuadrature(fe[0], fe[1]));
623   PetscCall(PetscObjectSetName((PetscObject)fe[1], "mixed potential"));
624 
625   /* Set discretization and boundary conditions for each mesh */
626   PetscCall(DMSetField(dm, 0, NULL, (PetscObject)fe[0]));
627   PetscCall(DMSetField(dm, 1, NULL, (PetscObject)fe[1]));
628   PetscCall(DMCreateDS(dm));
629   while (cdm) {
630     PetscCall(DMCopyDisc(dm, cdm));
631     PetscCall(DMGetCoarseDM(cdm, &cdm));
632   }
633   PetscCall(PetscFEDestroy(&fe[0]));
634   PetscCall(PetscFEDestroy(&fe[1]));
635   PetscFunctionReturn(PETSC_SUCCESS);
636 }
637 
638 PetscErrorCode SetupMixed(DMAdaptor adaptor, DM mdm)
639 {
640   AppCtx *ctx;
641 
642   PetscFunctionBeginUser;
643   PetscCall(DMGetApplicationContext(mdm, (void **)&ctx));
644   PetscCall(SetupMixedDiscretization(mdm, ctx));
645   PetscCall(SetupMixedProblem(mdm, ctx));
646   PetscFunctionReturn(PETSC_SUCCESS);
647 }
648 
649 int main(int argc, char **argv)
650 {
651   DM        dm, mdm;               /* problem specification */
652   SNES      snes, msnes;           /* nonlinear solvers */
653   Vec       u, mu;                 /* solution vectors */
654   Vec       fluxError, exactError; /* Element wise error vector */
655   PetscReal fluxNorm, exactNorm;   /* Flux error norm */
656   AppCtx    user;                  /* user-defined work context */
657 
658   PetscFunctionBeginUser;
659   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
660   PetscCall(PetscBagCreate(PETSC_COMM_WORLD, sizeof(Parameter), &user.param));
661   PetscCall(SetupParameters(user.param, &user));
662   PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user));
663 
664   // Set up and solve primal problem
665   PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm));
666   PetscCall(DMSetApplicationContext(dm, &user));
667   PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
668   PetscCall(SNESSetDM(snes, dm));
669 
670   PetscCall(SetupPrimalDiscretization(dm, &user));
671   PetscCall(SetupPrimalProblem(dm, &user));
672   PetscCall(DMCreateGlobalVector(dm, &u));
673   PetscCall(VecSet(u, 0.0));
674   PetscCall(DMPlexSetSNESLocalFEM(dm, PETSC_FALSE, &user));
675   PetscCall(SNESSetFromOptions(snes));
676   PetscCall(DMSNESCheckFromOptions(snes, u));
677 
678   for (PetscInt a = 0; a < user.numAdapt; ++a) {
679     if (a > 0) {
680       char prefix[16];
681 
682       PetscCall(PetscSNPrintf(prefix, 16, "a%d_", (int)a));
683       PetscCall(SNESSetOptionsPrefix(snes, prefix));
684     }
685     PetscCall(SNESSolve(snes, NULL, u));
686 
687     // Needed if you allow SNES to refine
688     PetscCall(SNESGetSolution(snes, &u));
689     PetscCall(VecGetDM(u, &dm));
690   }
691 
692   PetscCall(PetscObjectSetName((PetscObject)u, "Primal Solution "));
693   PetscCall(VecViewFromOptions(u, NULL, "-primal_sol_vec_view"));
694 
695   if (user.byHand) {
696     // Set up and solve mixed problem
697     PetscCall(DMClone(dm, &mdm));
698     PetscCall(SNESCreate(PETSC_COMM_WORLD, &msnes));
699     PetscCall(SNESSetOptionsPrefix(msnes, "mixed_"));
700     PetscCall(SNESSetDM(msnes, mdm));
701 
702     PetscCall(SetupMixedDiscretization(mdm, &user));
703     PetscCall(SetupMixedProblem(mdm, &user));
704     PetscCall(DMCreateGlobalVector(mdm, &mu));
705     PetscCall(VecSet(mu, 0.0));
706     PetscCall(DMPlexSetSNESLocalFEM(mdm, PETSC_FALSE, &user));
707     PetscCall(SNESSetFromOptions(msnes));
708 
709     PetscCall(DMSNESCheckFromOptions(msnes, mu));
710     PetscCall(SNESSolve(msnes, NULL, mu));
711     PetscCall(PetscObjectSetName((PetscObject)mu, "Mixed Solution "));
712     PetscCall(VecViewFromOptions(mu, NULL, "-mixed_sol_vec_view"));
713 
714     // Create the error space of piecewise constants
715     DM             dmErr;
716     PetscFE        feErr;
717     DMPolytopeType ct;
718     PetscInt       dim, cStart;
719 
720     PetscCall(DMClone(dm, &dmErr));
721     PetscCall(DMGetDimension(dmErr, &dim));
722     PetscCall(DMPlexGetHeightStratum(dmErr, 0, &cStart, NULL));
723     PetscCall(DMPlexGetCellType(dmErr, cStart, &ct));
724     PetscCall(PetscFECreateLagrangeByCell(PETSC_COMM_SELF, dim, 1, ct, 0, PETSC_DEFAULT, &feErr));
725     PetscCall(PetscObjectSetName((PetscObject)feErr, "Error"));
726     PetscCall(DMSetField(dmErr, 0, NULL, (PetscObject)feErr));
727     PetscCall(PetscFEDestroy(&feErr));
728     PetscCall(DMCreateDS(dmErr));
729     PetscCall(DMViewFromOptions(dmErr, NULL, "-dmerr_view"));
730 
731     // Compute the flux norm
732     PetscCall(DMGetGlobalVector(dmErr, &fluxError));
733     PetscCall(PetscObjectSetName((PetscObject)fluxError, "Flux Error"));
734     PetscCall(DMGetGlobalVector(dmErr, &exactError));
735     PetscCall(PetscObjectSetName((PetscObject)exactError, "Analytical Error"));
736     PetscCall(DMPlexComputeL2FluxDiffVec(u, 0, mu, 0, fluxError));
737     {
738       PetscDS             ds;
739       PetscSimplePointFn *func[2] = {NULL, NULL};
740       void               *ctx[2]  = {NULL, NULL};
741 
742       PetscCall(DMGetDS(mdm, &ds));
743       PetscCall(PetscDSGetExactSolution(ds, 0, &func[0], &ctx[0]));
744       PetscCall(DMPlexComputeL2DiffVec(mdm, 0.0, func, ctx, mu, exactError));
745     }
746     PetscCall(VecNorm(fluxError, NORM_2, &fluxNorm));
747     PetscCall(VecNorm(exactError, NORM_2, &exactNorm));
748     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Flux error norm = %g\t Exact flux error norm = %g\n", (double)fluxNorm, (double)exactNorm));
749     PetscCall(VecViewFromOptions(fluxError, NULL, "-flux_error_vec_view"));
750     PetscCall(VecViewFromOptions(exactError, NULL, "-exact_error_vec_view"));
751 
752     // Adaptive refinement based on calculated error
753     DM        rdm;
754     VecTagger refineTag;
755     DMLabel   adaptLabel;
756     IS        refineIS;
757     Vec       ref;
758 
759     PetscCall(DMLabelCreate(PETSC_COMM_WORLD, "adapt", &adaptLabel));
760     PetscCall(DMLabelSetDefaultValue(adaptLabel, DM_ADAPT_COARSEN));
761     PetscCall(VecTaggerCreate(PETSC_COMM_WORLD, &refineTag));
762     PetscCall(VecTaggerSetFromOptions(refineTag));
763     PetscCall(VecTaggerSetUp(refineTag));
764     PetscCall(PetscObjectViewFromOptions((PetscObject)refineTag, NULL, "-tag_view"));
765 
766     PetscCall(VecTaggerComputeIS(refineTag, fluxError, &refineIS, NULL));
767     PetscCall(VecTaggerDestroy(&refineTag));
768     PetscCall(DMLabelSetStratumIS(adaptLabel, DM_ADAPT_REFINE, refineIS));
769     PetscCall(ISViewFromOptions(refineIS, NULL, "-refine_is_view"));
770     PetscCall(ISDestroy(&refineIS));
771 
772     PetscCall(DMPlexCreateLabelField(dm, adaptLabel, &ref));
773     PetscCall(VecViewFromOptions(ref, NULL, "-refine_vec_view"));
774     PetscCall(VecDestroy(&ref));
775 
776     // Mark adaptation phase with prefix ref_
777     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, "adapt_"));
778     PetscCall(DMAdaptLabel(dm, adaptLabel, &rdm));
779     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)dm, NULL));
780     PetscCall(PetscObjectSetName((PetscObject)rdm, "Adaptively Refined DM"));
781     PetscCall(DMViewFromOptions(rdm, NULL, "-adapt_dm_view"));
782     PetscCall(DMDestroy(&rdm));
783     PetscCall(DMLabelDestroy(&adaptLabel));
784 
785     // Destroy the error structures
786     PetscCall(DMRestoreGlobalVector(dmErr, &fluxError));
787     PetscCall(DMRestoreGlobalVector(dmErr, &exactError));
788     PetscCall(DMDestroy(&dmErr));
789 
790     // Destroy the mixed structures
791     PetscCall(VecDestroy(&mu));
792     PetscCall(DMDestroy(&mdm));
793     PetscCall(SNESDestroy(&msnes));
794   }
795 
796   // Destroy the primal structures
797   PetscCall(VecDestroy(&u));
798   PetscCall(DMDestroy(&dm));
799   PetscCall(SNESDestroy(&snes));
800   PetscCall(PetscBagDestroy(&user.param));
801   PetscCall(PetscFinalize());
802   return 0;
803 }
804 
805 /*TEST
806 
807   # Tests using the explicit code above
808   testset:
809     suffix: 2d_p2_rt0p0_byhand
810     requires: triangle
811     args: -dm_plex_box_faces 1,1 -dm_plex_box_lower 0,0 -dm_plex_box_upper 1,1 -dm_refine 3 \
812           -primal_potential_petscspace_degree 2 \
813           -mixed_potential_petscdualspace_lagrange_continuity 0 \
814           -mixed_flux_petscspace_type ptrimmed \
815           -mixed_flux_petscspace_components 2 \
816           -mixed_flux_petscspace_ptrimmed_form_degree -1 \
817           -mixed_flux_petscdualspace_order 1 \
818           -mixed_flux_petscdualspace_form_degree -1 \
819           -mixed_flux_petscdualspace_lagrange_trimmed true \
820           -mixed_flux_petscfe_default_quadrature_order 2 \
821           -vec_tagger_type cdf -vec_tagger_box 0.9,1.0 \
822             -tag_view \
823             -adapt_dm_adaptor cellrefiner -adapt_dm_plex_transform_type refine_sbr \
824           -dmsnes_check 0.001 -mixed_dmsnes_check 0.001 -pc_type jacobi -mixed_pc_type jacobi
825     test:
826       suffix: quadratic
827       args: -sol_type quadratic
828     test:
829       suffix: trig
830       args: -sol_type trig
831     test:
832       suffix: sensor
833       args: -sol_type sensor
834 
835   # Tests using the embedded adaptor in SNES
836   testset:
837     suffix: 2d_p2_rt0p0
838     requires: triangle defined(PETSC_HAVE_EXECUTABLE_EXPORT)
839     args: -dm_plex_box_faces 1,1 -dm_plex_box_lower 0,0 -dm_plex_box_upper 1,1 -dm_refine 3 \
840           -primal_potential_petscspace_degree 2 \
841           -mixed_potential_petscdualspace_lagrange_continuity 0 \
842           -mixed_flux_petscspace_type ptrimmed \
843           -mixed_flux_petscspace_components 2 \
844           -mixed_flux_petscspace_ptrimmed_form_degree -1 \
845           -mixed_flux_petscdualspace_order 1 \
846           -mixed_flux_petscdualspace_form_degree -1 \
847           -mixed_flux_petscdualspace_lagrange_trimmed true \
848           -mixed_flux_petscfe_default_quadrature_order 2 \
849           -by_hand 0 \
850           -refine_vec_tagger_type cdf -refine_vec_tagger_box 0.9,1.0 \
851             -snes_adapt_view \
852             -adapt_dm_adaptor cellrefiner -adapt_dm_plex_transform_type refine_sbr \
853             -adaptor_criterion label -adaptor_type flux -adaptor_mixed_setup_function SetupMixed \
854           -snes_adapt_sequence 1 -pc_type jacobi -mixed_pc_type jacobi
855     test:
856       suffix: quadratic
857       args: -sol_type quadratic -adaptor_monitor_error
858     test:
859       suffix: trig
860       args: -sol_type trig -adaptor_monitor_error
861     test:
862       suffix: sensor
863       args: -sol_type sensor
864 
865   # Tests using multiple adaptor loops
866   testset:
867     suffix: 2d_p2_rt0p0_a2
868     requires: triangle defined(PETSC_HAVE_EXECUTABLE_EXPORT)
869     args: -dm_plex_box_faces 1,1 -dm_plex_box_lower 0,0 -dm_plex_box_upper 1,1 -dm_refine 3 \
870           -primal_potential_petscspace_degree 2 \
871           -mixed_potential_petscdualspace_lagrange_continuity 0 \
872           -mixed_flux_petscspace_type ptrimmed \
873           -mixed_flux_petscspace_components 2 \
874           -mixed_flux_petscspace_ptrimmed_form_degree -1 \
875           -mixed_flux_petscdualspace_order 1 \
876           -mixed_flux_petscdualspace_form_degree -1 \
877           -mixed_flux_petscdualspace_lagrange_trimmed true \
878           -mixed_flux_petscfe_default_quadrature_order 2 \
879           -by_hand 0 \
880           -num_adapt 2 \
881           -refine_vec_tagger_type cdf -refine_vec_tagger_box 0.9,1.0 \
882             -snes_adapt_view \
883             -adapt_dm_adaptor cellrefiner -adapt_dm_plex_transform_type refine_sbr \
884             -adaptor_criterion label -adaptor_type gradient -adaptor_mixed_setup_function SetupMixed \
885           -snes_adapt_sequence 2 -pc_type jacobi \
886           -a1_refine_vec_tagger_type cdf -a1_refine_vec_tagger_box 0.9,1.0 \
887             -a1_snes_adapt_view \
888             -a1_adaptor_criterion label -a1_adaptor_type flux -a1_adaptor_mixed_setup_function SetupMixed \
889           -a1_snes_adapt_sequence 1 -a1_pc_type jacobi -a1_mixed_pc_type jacobi
890     test:
891       suffix: sensor
892       args: -sol_type sensor
893 
894 TEST*/
895