xref: /petsc/src/dm/impls/plex/plexmetric.c (revision 99abec2b429b33f09a86a719839caa30dbc4af3f)
1 #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
2 #include <petscblaslapack.h>
3 
4 PetscErrorCode DMPlexMetricSetFromOptions(DM dm)
5 {
6   MPI_Comm       comm;
7   PetscBool      isotropic = PETSC_FALSE, restrictAnisotropyFirst = PETSC_FALSE;
8   PetscErrorCode ierr;
9   PetscReal      h_min = 1.0e-30, h_max = 1.0e+30, a_max = 1.0e+05, p = 1.0, target = 1000.0;
10 
11   PetscFunctionBegin;
12   ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
13   ierr = PetscOptionsBegin(comm, "", "Riemannian metric options", "DMPlexMetric");CHKERRQ(ierr);
14   ierr = PetscOptionsBool("-dm_plex_metric_isotropic", "Is the metric isotropic?", "DMPlexMetricCreateIsotropic", isotropic, &isotropic, NULL);CHKERRQ(ierr);
15   ierr = DMPlexMetricSetIsotropic(dm, isotropic);
16   ierr = PetscOptionsBool("-dm_plex_metric_restrict_anisotropy_first", "Should anisotropy be restricted before normalization?", "DMPlexNormalize", restrictAnisotropyFirst, &restrictAnisotropyFirst, NULL);CHKERRQ(ierr);
17   ierr = DMPlexMetricSetRestrictAnisotropyFirst(dm, restrictAnisotropyFirst);
18   ierr = PetscOptionsReal("-dm_plex_metric_h_min", "Minimum tolerated metric magnitude", "DMPlexMetricEnforceSPD", h_min, &h_min, NULL);CHKERRQ(ierr);
19   ierr = DMPlexMetricSetMinimumMagnitude(dm, h_min);CHKERRQ(ierr);
20   ierr = PetscOptionsReal("-dm_plex_metric_h_max", "Maximum tolerated metric magnitude", "DMPlexMetricEnforceSPD", h_max, &h_max, NULL);CHKERRQ(ierr);
21   ierr = DMPlexMetricSetMaximumMagnitude(dm, h_max);CHKERRQ(ierr);
22   ierr = PetscOptionsReal("-dm_plex_metric_a_max", "Maximum tolerated anisotropy", "DMPlexMetricEnforceSPD", a_max, &a_max, NULL);CHKERRQ(ierr);
23   ierr = DMPlexMetricSetMaximumAnisotropy(dm, a_max);CHKERRQ(ierr);
24   ierr = PetscOptionsReal("-dm_plex_metric_p", "L-p normalization order", "DMPlexMetricNormalize", p, &p, NULL);CHKERRQ(ierr);
25   ierr = DMPlexMetricSetNormalizationOrder(dm, p);CHKERRQ(ierr);
26   ierr = PetscOptionsReal("-dm_plex_metric_target_complexity", "Target metric complexity", "DMPlexMetricNormalize", target, &target, NULL);CHKERRQ(ierr);
27   ierr = DMPlexMetricSetTargetComplexity(dm, target);CHKERRQ(ierr);
28   ierr = PetscOptionsEnd();CHKERRQ(ierr);
29   PetscFunctionReturn(0);
30 }
31 
32 /*
33   DMPlexMetricSetIsotropic - Record whether a metric is isotropic
34 
35   Input parameters:
36 + dm        - The DM
37 - isotropic - Is the metric isotropic?
38 
39   Level: beginner
40 
41 .seealso: DMPlexMetricIsIsotropic(), DMPlexMetricSetRestrictAnisotropyFirst()
42 */
43 PetscErrorCode DMPlexMetricSetIsotropic(DM dm, PetscBool isotropic)
44 {
45   DM_Plex       *plex = (DM_Plex *) dm->data;
46   PetscErrorCode ierr;
47 
48   PetscFunctionBegin;
49   if (!plex->metricCtx) {
50     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
51     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
52   }
53   plex->metricCtx->isotropic = isotropic;
54   PetscFunctionReturn(0);
55 }
56 
57 /*
58   DMPlexMetricIsIsotropic - Is a metric is isotropic?
59 
60   Input parameters:
61 . dm        - The DM
62 
63   Output parameters:
64 . isotropic - Is the metric isotropic?
65 
66   Level: beginner
67 
68 .seealso: DMPlexMetricSetIsotropic(), DMPlexMetricRestrictAnisotropyFirst()
69 */
70 PetscErrorCode DMPlexMetricIsIsotropic(DM dm, PetscBool *isotropic)
71 {
72   DM_Plex       *plex = (DM_Plex *) dm->data;
73   PetscErrorCode ierr;
74 
75   PetscFunctionBegin;
76   if (!plex->metricCtx) {
77     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
78     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
79   }
80   *isotropic = plex->metricCtx->isotropic;
81   PetscFunctionReturn(0);
82 }
83 
84 /*
85   DMPlexMetricSetRestrictAnisotropyFirst - Record whether anisotropy should be restricted before normalization
86 
87   Input parameters:
88 + dm                      - The DM
89 - restrictAnisotropyFirst - Should anisotropy be normalized first?
90 
91   Level: beginner
92 
93 .seealso: DMPlexMetricSetIsotropic(), DMPlexMetricRestrictAnisotropyFirst()
94 */
95 PetscErrorCode DMPlexMetricSetRestrictAnisotropyFirst(DM dm, PetscBool restrictAnisotropyFirst)
96 {
97   DM_Plex       *plex = (DM_Plex *) dm->data;
98   PetscErrorCode ierr;
99 
100   PetscFunctionBegin;
101   if (!plex->metricCtx) {
102     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
103     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
104   }
105   plex->metricCtx->restrictAnisotropyFirst = restrictAnisotropyFirst;
106   PetscFunctionReturn(0);
107 }
108 
109 /*
110   DMPlexMetricRestrictAnisotropyFirst - Is anisotropy restricted before normalization or after?
111 
112   Input parameters:
113 . dm                      - The DM
114 
115   Output parameters:
116 . restrictAnisotropyFirst - Is anisotropy be normalized first?
117 
118   Level: beginner
119 
120 .seealso: DMPlexMetricIsIsotropic(), DMPlexMetricSetRestrictAnisotropyFirst()
121 */
122 PetscErrorCode DMPlexMetricRestrictAnisotropyFirst(DM dm, PetscBool *restrictAnisotropyFirst)
123 {
124   DM_Plex       *plex = (DM_Plex *) dm->data;
125   PetscErrorCode ierr;
126 
127   PetscFunctionBegin;
128   if (!plex->metricCtx) {
129     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
130     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
131   }
132   *restrictAnisotropyFirst = plex->metricCtx->restrictAnisotropyFirst;
133   PetscFunctionReturn(0);
134 }
135 
136 /*
137   DMPlexMetricSetMinimumMagnitude - Set the minimum tolerated metric magnitude
138 
139   Input parameters:
140 + dm    - The DM
141 - h_min - The minimum tolerated metric magnitude
142 
143   Level: beginner
144 
145 .seealso: DMPlexMetricGetMinimumMagnitude(), DMPlexMetricSetMaximumMagnitude()
146 */
147 PetscErrorCode DMPlexMetricSetMinimumMagnitude(DM dm, PetscReal h_min)
148 {
149   DM_Plex       *plex = (DM_Plex *) dm->data;
150   PetscErrorCode ierr;
151 
152   PetscFunctionBegin;
153   if (!plex->metricCtx) {
154     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
155     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
156   }
157   if (h_min <= 0.0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Metric magnitudes must be positive, not %.4e", h_min);
158   plex->metricCtx->h_min = h_min;
159   PetscFunctionReturn(0);
160 }
161 
162 /*
163   DMPlexMetricGetMinimumMagnitude - Get the minimum tolerated metric magnitude
164 
165   Input parameters:
166 . dm    - The DM
167 
168   Input parameters:
169 . h_min - The minimum tolerated metric magnitude
170 
171   Level: beginner
172 
173 .seealso: DMPlexMetricSetMinimumMagnitude(), DMPlexMetricGetMaximumMagnitude()
174 */
175 PetscErrorCode DMPlexMetricGetMinimumMagnitude(DM dm, PetscReal *h_min)
176 {
177   DM_Plex       *plex = (DM_Plex *) dm->data;
178   PetscErrorCode ierr;
179 
180   PetscFunctionBegin;
181   if (!plex->metricCtx) {
182     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
183     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
184   }
185   *h_min = plex->metricCtx->h_min;
186   PetscFunctionReturn(0);
187 }
188 
189 /*
190   DMPlexMetricSetMaximumMagnitude - Set the maximum tolerated metric magnitude
191 
192   Input parameters:
193 + dm    - The DM
194 - h_max - The maximum tolerated metric magnitude
195 
196   Level: beginner
197 
198 .seealso: DMPlexMetricGetMaximumMagnitude(), DMPlexMetricSetMinimumMagnitude()
199 */
200 PetscErrorCode DMPlexMetricSetMaximumMagnitude(DM dm, PetscReal h_max)
201 {
202   DM_Plex       *plex = (DM_Plex *) dm->data;
203   PetscErrorCode ierr;
204 
205   PetscFunctionBegin;
206   if (!plex->metricCtx) {
207     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
208     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
209   }
210   if (h_max <= 0.0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Metric magnitudes must be positive, not %.4e", h_max);
211   plex->metricCtx->h_max = h_max;
212   PetscFunctionReturn(0);
213 }
214 
215 /*
216   DMPlexMetricGetMaximumMagnitude - Get the maximum tolerated metric magnitude
217 
218   Input parameters:
219 . dm    - The DM
220 
221   Input parameters:
222 . h_max - The maximum tolerated metric magnitude
223 
224   Level: beginner
225 
226 .seealso: DMPlexMetricSetMaximumMagnitude(), DMPlexMetricGetMinimumMagnitude()
227 */
228 PetscErrorCode DMPlexMetricGetMaximumMagnitude(DM dm, PetscReal *h_max)
229 {
230   DM_Plex       *plex = (DM_Plex *) dm->data;
231   PetscErrorCode ierr;
232 
233   PetscFunctionBegin;
234   if (!plex->metricCtx) {
235     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
236     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
237   }
238   *h_max = plex->metricCtx->h_max;
239   PetscFunctionReturn(0);
240 }
241 
242 /*
243   DMPlexMetricSetMaximumAnisotropy - Set the maximum tolerated metric anisotropy
244 
245   Input parameters:
246 + dm    - The DM
247 - a_max - The maximum tolerated metric anisotropy
248 
249   Level: beginner
250 
251   Note: If the value zero is given then anisotropy will not be restricted. Otherwise, it should be at least one.
252 
253 .seealso: DMPlexMetricGetMaximumAnisotropy(), DMPlexMetricSetMaximumMagnitude()
254 */
255 PetscErrorCode DMPlexMetricSetMaximumAnisotropy(DM dm, PetscReal a_max)
256 {
257   DM_Plex       *plex = (DM_Plex *) dm->data;
258   PetscErrorCode ierr;
259 
260   PetscFunctionBegin;
261   if (!plex->metricCtx) {
262     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
263     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
264   }
265   if (a_max < 1.0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Anisotropy must be at least one, not %.4e", a_max);
266   plex->metricCtx->a_max = a_max;
267   PetscFunctionReturn(0);
268 }
269 
270 /*
271   DMPlexMetricGetMaximumAnisotropy - Get the maximum tolerated metric anisotropy
272 
273   Input parameters:
274 . dm    - The DM
275 
276   Input parameters:
277 . a_max - The maximum tolerated metric anisotropy
278 
279   Level: beginner
280 
281 .seealso: DMPlexMetricSetMaximumAnisotropy(), DMPlexMetricGetMaximumMagnitude()
282 */
283 PetscErrorCode DMPlexMetricGetMaximumAnisotropy(DM dm, PetscReal *a_max)
284 {
285   DM_Plex       *plex = (DM_Plex *) dm->data;
286   PetscErrorCode ierr;
287 
288   PetscFunctionBegin;
289   if (!plex->metricCtx) {
290     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
291     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
292   }
293   *a_max = plex->metricCtx->a_max;
294   PetscFunctionReturn(0);
295 }
296 
297 /*
298   DMPlexMetricSetTargetComplexity - Set the target metric complexity
299 
300   Input parameters:
301 + dm               - The DM
302 - targetComplexity - The target metric complexity
303 
304   Level: beginner
305 
306 .seealso: DMPlexMetricGetTargetComplexity(), DMPlexMetricSetNormalizationOrder()
307 */
308 PetscErrorCode DMPlexMetricSetTargetComplexity(DM dm, PetscReal targetComplexity)
309 {
310   DM_Plex       *plex = (DM_Plex *) dm->data;
311   PetscErrorCode ierr;
312 
313   PetscFunctionBegin;
314   if (!plex->metricCtx) {
315     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
316     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
317   }
318   if (targetComplexity <= 0.0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Metric complexity must be positive");
319   plex->metricCtx->targetComplexity = targetComplexity;
320   PetscFunctionReturn(0);
321 }
322 
323 /*
324   DMPlexMetricGetTargetComplexity - Get the target metric complexity
325 
326   Input parameters:
327 . dm               - The DM
328 
329   Input parameters:
330 . targetComplexity - The target metric complexity
331 
332   Level: beginner
333 
334 .seealso: DMPlexMetricSetTargetComplexity(), DMPlexMetricGetNormalizationOrder()
335 */
336 PetscErrorCode DMPlexMetricGetTargetComplexity(DM dm, PetscReal *targetComplexity)
337 {
338   DM_Plex       *plex = (DM_Plex *) dm->data;
339   PetscErrorCode ierr;
340 
341   PetscFunctionBegin;
342   if (!plex->metricCtx) {
343     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
344     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
345   }
346   *targetComplexity = plex->metricCtx->targetComplexity;
347   PetscFunctionReturn(0);
348 }
349 
350 /*
351   DMPlexMetricSetNormalizationOrder - Set the order p for L-p normalization
352 
353   Input parameters:
354 + dm - The DM
355 - p  - The normalization order
356 
357   Level: beginner
358 
359 .seealso: DMPlexMetricGetNormalizationOrder(), DMPlexMetricSetTargetComplexity()
360 */
361 PetscErrorCode DMPlexMetricSetNormalizationOrder(DM dm, PetscReal p)
362 {
363   DM_Plex       *plex = (DM_Plex *) dm->data;
364   PetscErrorCode ierr;
365 
366   PetscFunctionBegin;
367   if (!plex->metricCtx) {
368     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
369     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
370   }
371   if (p < 1.0) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Normalization order must be one or greater");
372   plex->metricCtx->p = p;
373   PetscFunctionReturn(0);
374 }
375 
376 /*
377   DMPlexMetricGetNormalizationOrder - Get the order p for L-p normalization
378 
379   Input parameters:
380 . dm - The DM
381 
382   Input parameters:
383 . p - The normalization order
384 
385   Level: beginner
386 
387 .seealso: DMPlexMetricSetNormalizationOrder(), DMPlexMetricGetTargetComplexity()
388 */
389 PetscErrorCode DMPlexMetricGetNormalizationOrder(DM dm, PetscReal *p)
390 {
391   DM_Plex       *plex = (DM_Plex *) dm->data;
392   PetscErrorCode ierr;
393 
394   PetscFunctionBegin;
395   if (!plex->metricCtx) {
396     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
397     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
398   }
399   *p = plex->metricCtx->p;
400   PetscFunctionReturn(0);
401 }
402 
403 PetscErrorCode DMPlexP1FieldCreate_Private(DM dm, PetscInt f, PetscInt size, Vec *metric)
404 {
405   MPI_Comm       comm;
406   PetscErrorCode ierr;
407   PetscFE        fe;
408   PetscInt       dim;
409 
410   PetscFunctionBegin;
411 
412   /* Extract metadata from dm */
413   ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
414   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
415 
416   /* Create a P1 field of the requested size */
417   ierr = PetscFECreateLagrange(comm, dim, size, PETSC_TRUE, 1, PETSC_DETERMINE, &fe);CHKERRQ(ierr);
418   ierr = DMSetField(dm, f, NULL, (PetscObject)fe);CHKERRQ(ierr);
419   ierr = DMCreateDS(dm);CHKERRQ(ierr);
420   ierr = PetscFEDestroy(&fe);CHKERRQ(ierr);
421   ierr = DMCreateLocalVector(dm, metric);CHKERRQ(ierr);
422 
423   PetscFunctionReturn(0);
424 }
425 
426 /*
427   DMPlexMetricCreate - Create a Riemannian metric field
428 
429   Input parameters:
430 + dm     - The DM
431 - f      - The field number to use
432 
433   Output parameter:
434 . metric - The metric
435 
436   Level: beginner
437 
438   Notes:
439 
440   It is assumed that the DM is comprised of simplices.
441 
442   Command line options for Riemannian metrics:
443 
444   -dm_plex_metric_isotropic                 - Is the metric isotropic?
445   -dm_plex_metric_restrict_anisotropy_first - Should anisotropy be restricted before normalization?
446   -dm_plex_metric_h_min                     - Minimum tolerated metric magnitude
447   -dm_plex_metric_h_max                     - Maximum tolerated metric magnitude
448   -dm_plex_metric_a_max                     - Maximum tolerated anisotropy
449   -dm_plex_metric_p                         - L-p normalization order
450   -dm_plex_metric_target_complexity         - Target metric complexity
451 
452 .seealso: DMPlexMetricCreateUniform(), DMPlexMetricCreateIsotropic()
453 */
454 PetscErrorCode DMPlexMetricCreate(DM dm, PetscInt f, Vec *metric)
455 {
456   DM_Plex       *plex = (DM_Plex *) dm->data;
457   PetscErrorCode ierr;
458   PetscInt       coordDim, Nd;
459 
460   PetscFunctionBegin;
461   if (!plex->metricCtx) {
462     ierr = PetscNew(&plex->metricCtx);CHKERRQ(ierr);
463     ierr = DMPlexMetricSetFromOptions(dm);CHKERRQ(ierr);
464   }
465   ierr = DMGetCoordinateDim(dm, &coordDim);CHKERRQ(ierr);
466   Nd = coordDim*coordDim;
467   ierr = DMPlexP1FieldCreate_Private(dm, f, Nd, metric);CHKERRQ(ierr);
468   PetscFunctionReturn(0);
469 }
470 
471 typedef struct {
472   PetscReal scaling;  /* Scaling for uniform metric diagonal */
473 } DMPlexMetricUniformCtx;
474 
475 static PetscErrorCode diagonal(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
476 {
477   DMPlexMetricUniformCtx *user = (DMPlexMetricUniformCtx*)ctx;
478   PetscInt                i, j;
479 
480   for (i = 0; i < dim; ++i) {
481     for (j = 0; j < dim; ++j) {
482       if (i == j) u[i+dim*j] = user->scaling;
483       else u[i+dim*j] = 0.0;
484     }
485   }
486   return 0;
487 }
488 
489 /*
490   DMPlexMetricCreateUniform - Construct a uniform isotropic metric
491 
492   Input parameters:
493 + dm     - The DM
494 . f      - The field number to use
495 - alpha  - Scaling parameter for the diagonal
496 
497   Output parameter:
498 . metric - The uniform metric
499 
500   Level: beginner
501 
502   Note: It is assumed that the DM is comprised of simplices.
503 
504 .seealso: DMPlexMetricCreate(), DMPlexMetricCreateIsotropic()
505 */
506 PetscErrorCode DMPlexMetricCreateUniform(DM dm, PetscInt f, PetscReal alpha, Vec *metric)
507 {
508   DMPlexMetricUniformCtx user;
509   PetscErrorCode       (*funcs[1])(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *);
510   PetscErrorCode         ierr;
511   void                  *ctxs[1];
512 
513   PetscFunctionBegin;
514   ierr = DMPlexMetricCreate(dm, f, metric);CHKERRQ(ierr);
515   if (!alpha) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform metric scaling is undefined");
516   if (alpha < 1.0e-30) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform metric scaling %e should be positive", alpha);
517   else user.scaling = alpha;
518   funcs[0] = diagonal;
519   ctxs[0] = &user;
520   ierr = DMProjectFunctionLocal(dm, 0.0, funcs, ctxs, INSERT_ALL_VALUES, *metric);CHKERRQ(ierr);
521   PetscFunctionReturn(0);
522 }
523 
524 /*
525   DMPlexMetricCreateIsotropic - Construct an isotropic metric from an error indicator
526 
527   Input parameters:
528 + dm        - The DM
529 . f         - The field number to use
530 - indicator - The error indicator
531 
532   Output parameter:
533 . metric    - The isotropic metric
534 
535   Level: beginner
536 
537   Notes:
538 
539   It is assumed that the DM is comprised of simplices.
540 
541   The indicator needs to be a scalar field defined at *vertices*.
542 
543 .seealso: DMPlexMetricCreate(), DMPlexMetricCreateUniform()
544 */
545 PetscErrorCode DMPlexMetricCreateIsotropic(DM dm, PetscInt f, Vec indicator, Vec *metric)
546 {
547   DM                 dmIndi;
548   PetscErrorCode     ierr;
549   PetscInt           dim, d, vStart, vEnd, v;
550   const PetscScalar *indi;
551   PetscScalar       *met;
552 
553   PetscFunctionBegin;
554   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
555   ierr = DMPlexMetricCreate(dm, f, metric);CHKERRQ(ierr);
556   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
557   ierr = VecGetArrayRead(indicator, &indi);CHKERRQ(ierr);
558   ierr = VecGetArrayWrite(*metric, &met);CHKERRQ(ierr);
559   ierr = VecGetDM(indicator, &dmIndi);CHKERRQ(ierr);
560   for (v = vStart; v < vEnd; ++v) {
561     PetscScalar *vindi, *vmet;
562     ierr = DMPlexPointLocalRead(dmIndi, v, indi, &vindi);CHKERRQ(ierr);
563     ierr = DMPlexPointLocalRef(dm, v, met, &vmet);CHKERRQ(ierr);
564     for (d = 0; d < dim; ++d) vmet[d*(dim+1)] = vindi[0];
565   }
566   ierr = VecRestoreArrayWrite(*metric, &met);CHKERRQ(ierr);
567   ierr = VecRestoreArrayRead(indicator, &indi);CHKERRQ(ierr);
568   PetscFunctionReturn(0);
569 }
570 
571 static PetscErrorCode DMPlexMetricModify_Private(PetscInt dim, PetscReal h_min, PetscReal h_max, PetscReal a_max, PetscScalar Mp[])
572 {
573   PetscErrorCode ierr;
574   PetscInt       i, j, k;
575   PetscReal     *eigs, max_eig, l_min = 1.0/(h_max*h_max), l_max = 1.0/(h_min*h_min), la_min = 1.0/(a_max*a_max);
576   PetscScalar   *Mpos;
577 
578   PetscFunctionBegin;
579   ierr = PetscMalloc2(dim*dim, &Mpos, dim, &eigs);CHKERRQ(ierr);
580 
581   /* Symmetrize */
582   for (i = 0; i < dim; ++i) {
583     Mpos[i*dim+i] = Mp[i*dim+i];
584     for (j = i+1; j < dim; ++j) {
585       Mpos[i*dim+j] = 0.5*(Mp[i*dim+j] + Mp[j*dim+i]);
586       Mpos[j*dim+i] = Mpos[i*dim+j];
587     }
588   }
589 
590   /* Compute eigendecomposition */
591   {
592     PetscScalar  *work;
593     PetscBLASInt lwork;
594 
595     lwork = 5*dim;
596     ierr = PetscMalloc1(5*dim, &work);CHKERRQ(ierr);
597     {
598       PetscBLASInt lierr;
599       PetscBLASInt nb;
600 
601       ierr = PetscBLASIntCast(dim, &nb);CHKERRQ(ierr);
602       ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
603 #if defined(PETSC_USE_COMPLEX)
604       {
605         PetscReal *rwork;
606         ierr = PetscMalloc1(3*dim, &rwork);CHKERRQ(ierr);
607         PetscStackCallBLAS("LAPACKsyev",LAPACKsyev_("V","U",&nb,Mpos,&nb,eigs,work,&lwork,rwork,&lierr));
608         ierr = PetscFree(rwork);CHKERRQ(ierr);
609       }
610 #else
611       PetscStackCallBLAS("LAPACKsyev",LAPACKsyev_("V","U",&nb,Mpos,&nb,eigs,work,&lwork,&lierr));
612 #endif
613       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
614       ierr = PetscFPTrapPop();CHKERRQ(ierr);
615     }
616     ierr = PetscFree(work);CHKERRQ(ierr);
617   }
618 
619   /* Reflect to positive orthant and enforce maximum and minimum size */
620   max_eig = 0.0;
621   for (i = 0; i < dim; ++i) {
622     eigs[i] = PetscMin(l_max, PetscMax(l_min, PetscAbsReal(eigs[i])));
623     max_eig = PetscMax(eigs[i], max_eig);
624   }
625 
626   /* Enforce maximum anisotropy */
627   for (i = 0; i < dim; ++i) {
628     if (a_max > 1.0) eigs[i] = PetscMax(eigs[i], max_eig*la_min);
629   }
630 
631   /* Reconstruct Hessian */
632   for (i = 0; i < dim; ++i) {
633     for (j = 0; j < dim; ++j) {
634       Mp[i*dim+j] = 0.0;
635       for (k = 0; k < dim; ++k) {
636         Mp[i*dim+j] += Mpos[k*dim+i] * eigs[k] * Mpos[k*dim+j];
637       }
638     }
639   }
640   ierr = PetscFree2(Mpos, eigs);CHKERRQ(ierr);
641 
642   PetscFunctionReturn(0);
643 }
644 
645 /*
646   DMPlexMetricEnforceSPD - Enforce symmetric positive-definiteness of a metric
647 
648   Input parameters:
649 + dm                 - The DM
650 . restrictSizes      - Should maximum/minimum metric magnitudes be enforced?
651 . restrictAnisotropy - Should maximum anisotropy be enforced?
652 - metric             - The metric
653 
654   Output parameter:
655 . metric             - The metric
656 
657   Level: beginner
658 
659 .seealso: DMPlexMetricNormalize(), DMPlexMetricIntersection()
660 */
661 PetscErrorCode DMPlexMetricEnforceSPD(DM dm, PetscBool restrictSizes, PetscBool restrictAnisotropy, Vec metric)
662 {
663   PetscErrorCode ierr;
664   PetscInt       dim, vStart, vEnd, v;
665   PetscScalar   *met;
666   PetscReal      h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0;
667 
668   PetscFunctionBegin;
669 
670   /* Extract metadata from dm */
671   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
672   if (restrictSizes) {
673     ierr = DMPlexMetricGetMinimumMagnitude(dm, &h_min);CHKERRQ(ierr);
674     ierr = DMPlexMetricGetMaximumMagnitude(dm, &h_max);CHKERRQ(ierr);
675     h_min = PetscMax(h_min, 1.0e-30);
676     h_max = PetscMin(h_max, 1.0e+30);
677     if (h_min >= h_max) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Incompatible min/max metric magnitudes (%.4e not smaller than %.4e)", h_min, h_max);
678   }
679   if (restrictAnisotropy) {
680     ierr = DMPlexMetricGetMaximumAnisotropy(dm, &a_max);CHKERRQ(ierr);
681     a_max = PetscMin(a_max, 1.0e+30);
682   }
683 
684   /* Enforce SPD */
685   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
686   ierr = VecGetArray(metric, &met);CHKERRQ(ierr);
687   for (v = vStart; v < vEnd; ++v) {
688     PetscScalar *vmet;
689     ierr = DMPlexPointLocalRef(dm, v, met, &vmet);CHKERRQ(ierr);
690     ierr = DMPlexMetricModify_Private(dim, h_min, h_max, a_max, vmet);CHKERRQ(ierr);
691   }
692   ierr = VecRestoreArray(metric, &met);CHKERRQ(ierr);
693 
694   PetscFunctionReturn(0);
695 }
696 
697 static void detMFunc(PetscInt dim, PetscInt Nf, PetscInt NfAux,
698                      const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
699                      const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
700                      PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
701 {
702   const PetscScalar p = constants[0];
703   PetscReal         detH = 0.0;
704 
705   if      (dim == 2) DMPlex_Det2D_Scalar_Internal(&detH, u);
706   else if (dim == 3) DMPlex_Det3D_Scalar_Internal(&detH, u);
707   f0[0] = PetscPowReal(detH, p/(2.0*p + dim));
708 }
709 
710 /*
711   DMPlexMetricNormalize - Apply L-p normalization to a metric
712 
713   Input parameters:
714 + dm            - The DM
715 . metricIn      - The unnormalized metric
716 - restrictSizes - Should maximum/minimum metric magnitudes and anisotropy be enforced?
717 
718   Output parameter:
719 . metricOut     - The normalized metric
720 
721   Level: beginner
722 
723 .seealso: DMPlexMetricEnforceSPD(), DMPlexMetricIntersection()
724 */
725 PetscErrorCode DMPlexMetricNormalize(DM dm, Vec metricIn, PetscBool restrictSizes, Vec *metricOut)
726 {
727   DMPlexMetricCtx *user;
728   MPI_Comm         comm;
729   PetscDS          ds;
730   PetscErrorCode   ierr;
731   PetscInt         dim, Nd, vStart, vEnd, v, i;
732   PetscScalar     *met, integral, constants[1];
733   PetscReal        p, h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0, factGlob, target;
734 
735   PetscFunctionBegin;
736 
737   /* Extract metadata from dm */
738   ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
739   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
740   Nd = dim*dim;
741   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
742   ierr = DMGetApplicationContext(dm, (void**)&user);CHKERRQ(ierr);
743   if (restrictSizes && user->restrictAnisotropyFirst && user->a_max > 1.0) a_max = user->a_max;
744   if (PetscAbsReal(user->p) >= 1.0) p = user->p;
745   else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Metric normalization order %f should be greater than one.", user->p);
746   constants[0] = p;
747   if (user->targetComplexity > 0.0) target = user->targetComplexity;
748   else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Target metric complexity %f should be positive.", user->targetComplexity);
749 
750   /* Set up metric and ensure it is SPD */
751   ierr = DMPlexMetricCreate(dm, 0, metricOut);CHKERRQ(ierr);
752   ierr = VecCopy(metricIn, *metricOut);CHKERRQ(ierr);
753   ierr = DMPlexMetricEnforceSPD(dm, PETSC_FALSE, *metricOut);CHKERRQ(ierr);
754 
755   /* Compute global normalization factor */
756   ierr = DMGetDS(dm, &ds);CHKERRQ(ierr);
757   ierr = PetscDSSetConstants(ds, 1, constants);CHKERRQ(ierr);
758   ierr = PetscDSSetObjective(ds, 0, detMFunc);CHKERRQ(ierr);
759   ierr = DMPlexComputeIntegralFEM(dm, *metricOut, &integral, NULL);CHKERRQ(ierr);
760   factGlob = PetscPowReal(target/PetscRealPart(integral), 2.0/dim);
761 
762   /* Apply local scaling */
763   a_max = 0.0;
764   if (restrictSizes) {
765     if (user->h_max > h_min) h_max = PetscMin(h_max, user->h_max);
766     if (user->h_min > 0.0) h_min = PetscMax(h_min, user->h_min);
767     if (!user->restrictAnisotropyFirst && user->a_max > 1.0) a_max = user->a_max;
768   }
769   ierr = VecGetArray(*metricOut, &met);CHKERRQ(ierr);
770   for (v = vStart; v < vEnd; ++v) {
771     PetscScalar       *Mp;
772     PetscReal          detM, fact;
773 
774     ierr = DMPlexPointLocalRef(dm, v, met, &Mp);CHKERRQ(ierr);
775     if      (dim == 2) DMPlex_Det2D_Scalar_Internal(&detM, Mp);
776     else if (dim == 3) DMPlex_Det3D_Scalar_Internal(&detM, Mp);
777     else SETERRQ1(comm, PETSC_ERR_SUP, "Dimension %d not supported", dim);
778     fact = factGlob * PetscPowReal(PetscAbsReal(detM), -1.0/(2*p+dim));
779     for (i = 0; i < Nd; ++i) Mp[i] *= fact;
780     if (restrictSizes) { ierr = DMPlexMetricModify_Private(dim, h_min, h_max, a_max, Mp);CHKERRQ(ierr); }
781   }
782   ierr = VecRestoreArray(*metricOut, &met);CHKERRQ(ierr);
783 
784   PetscFunctionReturn(0);
785 }
786 
787 /*
788   DMPlexMetricAverage - Compute the average of a list of metrics
789 
790   Input Parameter:
791 + dm         - The DM
792 . numMetrics - The number of metrics to be averaged
793 . weights    - Weights for the average
794 - metrics    - The metrics to be averaged
795 
796   Output Parameter:
797 . metricAvg  - The averaged metric
798 
799   Level: beginner
800 
801   Notes:
802   The weights should sum to unity.
803 
804   If weights are not provided then an unweighted average is used.
805 
806 .seealso: DMPlexMetricAverage2(), DMPlexMetricAverage3(), DMPlexMetricIntersection()
807 */
808 PetscErrorCode DMPlexMetricAverage(DM dm, PetscInt numMetrics, PetscReal weights[], Vec metrics[], Vec *metricAvg)
809 {
810   PetscBool      haveWeights = PETSC_TRUE;
811   PetscErrorCode ierr;
812   PetscInt       i;
813   PetscReal      sum = 0.0, tol = 1.0e-10;
814 
815   PetscFunctionBegin;
816   if (numMetrics < 1) { SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot average %d < 1 metrics", numMetrics); }
817   ierr = DMPlexMetricCreate(dm, 0, metricAvg);CHKERRQ(ierr);
818   ierr = VecSet(*metricAvg, 0.0);CHKERRQ(ierr);
819 
820   /* Default to the unweighted case */
821   if (!weights) {
822     ierr = PetscMalloc1(numMetrics, &weights);CHKERRQ(ierr);
823     haveWeights = PETSC_FALSE;
824     for (i = 0; i < numMetrics; ++i) {weights[i] = 1.0/numMetrics; }
825   }
826 
827   /* Check weights sum to unity */
828   for (i = 0; i < numMetrics; ++i) { sum += weights[i]; }
829   if (PetscAbsReal(sum - 1) > tol) { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Weights do not sum to unity"); }
830 
831   /* Compute metric average */
832   for (i = 0; i < numMetrics; ++i) { ierr = VecAXPY(*metricAvg, weights[i], metrics[i]);CHKERRQ(ierr); }
833   if (!haveWeights) {ierr = PetscFree(weights); }
834   PetscFunctionReturn(0);
835 }
836 
837 /*
838   DMPlexMetricAverage2 - Compute the unweighted average of two metrics
839 
840   Input Parameter:
841 + dm         - The DM
842 . metric1    - The first metric to be averaged
843 - metric2    - The second metric to be averaged
844 
845   Output Parameter:
846 . metricAvg  - The averaged metric
847 
848   Level: beginner
849 
850 .seealso: DMPlexMetricAverage(), DMPlexMetricAverage3()
851 */
852 PetscErrorCode DMPlexMetricAverage2(DM dm, Vec metric1, Vec metric2, Vec *metricAvg)
853 {
854   PetscErrorCode ierr;
855   PetscReal      weights[2] = {0.5, 0.5};
856   Vec            metrics[2] = {metric1, metric2};
857 
858   PetscFunctionBegin;
859   ierr = DMPlexMetricAverage(dm, 2, weights, metrics, metricAvg);CHKERRQ(ierr);
860   PetscFunctionReturn(0);
861 }
862 
863 /*
864   DMPlexMetricAverage3 - Compute the unweighted average of three metrics
865 
866   Input Parameter:
867 + dm         - The DM
868 . metric1    - The first metric to be averaged
869 . metric2    - The second metric to be averaged
870 - metric3    - The third metric to be averaged
871 
872   Output Parameter:
873 . metricAvg  - The averaged metric
874 
875   Level: beginner
876 
877 .seealso: DMPlexMetricAverage(), DMPlexMetricAverage2()
878 */
879 PetscErrorCode DMPlexMetricAverage3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec *metricAvg)
880 {
881   PetscErrorCode ierr;
882   PetscReal      weights[3] = {1.0/3.0, 1.0/3.0, 1.0/3.0};
883   Vec            metrics[3] = {metric1, metric2, metric3};
884 
885   PetscFunctionBegin;
886   ierr = DMPlexMetricAverage(dm, 3, weights, metrics, metricAvg);CHKERRQ(ierr);
887   PetscFunctionReturn(0);
888 }
889 
890 static PetscErrorCode DMPlexMetricIntersection_Private(PetscInt dim, PetscScalar M1[], PetscScalar M2[])
891 {
892   PetscErrorCode ierr;
893   PetscInt       i, j, k, l, m;
894   PetscReal     *evals, *evals1;
895   PetscScalar   *evecs, *sqrtM1, *isqrtM1;
896 
897   PetscFunctionBegin;
898   ierr = PetscMalloc5(dim*dim, &evecs, dim*dim, &sqrtM1, dim*dim, &isqrtM1, dim, &evals, dim, &evals1);CHKERRQ(ierr);
899   for (i = 0; i < dim; ++i) {
900     for (j = 0; j < dim; ++j) {
901       evecs[i*dim+j] = M1[i*dim+j];
902     }
903   }
904   {
905     PetscScalar *work;
906     PetscBLASInt lwork;
907 
908     lwork = 5*dim;
909     ierr = PetscMalloc1(5*dim, &work);CHKERRQ(ierr);
910     {
911       PetscBLASInt lierr, nb;
912       PetscReal    sqrtk;
913 
914       /* Compute eigendecomposition of M1 */
915       ierr = PetscBLASIntCast(dim, &nb);CHKERRQ(ierr);
916       ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
917 #if defined(PETSC_USE_COMPLEX)
918       {
919         PetscReal *rwork;
920         ierr = PetscMalloc1(3*dim, &rwork);CHKERRQ(ierr);
921         PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals1, work, &lwork, rwork, &lierr));
922         ierr = PetscFree(rwork);CHKERRQ(ierr);
923       }
924 #else
925       PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals1, work, &lwork, &lierr));
926 #endif
927       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
928       ierr = PetscFPTrapPop();
929 
930       /* Compute square root and reciprocal */
931       for (i = 0; i < dim; ++i) {
932         for (j = 0; j < dim; ++j) {
933           sqrtM1[i*dim+j] = 0.0;
934           isqrtM1[i*dim+j] = 0.0;
935           for (k = 0; k < dim; ++k) {
936             sqrtk = PetscSqrtReal(evals1[k]);
937             sqrtM1[i*dim+j] += evecs[k*dim+i] * sqrtk * evecs[k*dim+j];
938             isqrtM1[i*dim+j] += evecs[k*dim+i] * (1.0/sqrtk) * evecs[k*dim+j];
939           }
940         }
941       }
942 
943       /* Map into the space spanned by the eigenvectors of M1 */
944       for (i = 0; i < dim; ++i) {
945         for (j = 0; j < dim; ++j) {
946           evecs[i*dim+j] = 0.0;
947           for (k = 0; k < dim; ++k) {
948             for (l = 0; l < dim; ++l) {
949               evecs[i*dim+j] += isqrtM1[i*dim+k] * M2[l*dim+k] * isqrtM1[j*dim+l];
950             }
951           }
952         }
953       }
954 
955       /* Compute eigendecomposition */
956       ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
957 #if defined(PETSC_USE_COMPLEX)
958       {
959         PetscReal *rwork;
960         ierr = PetscMalloc1(3*dim, &rwork);CHKERRQ(ierr);
961         PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
962         ierr = PetscFree(rwork);CHKERRQ(ierr);
963       }
964 #else
965       PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
966 #endif
967       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
968       ierr = PetscFPTrapPop();
969 
970       /* Modify eigenvalues */
971       for (i = 0; i < dim; ++i) evals[i] = PetscMin(evals[i], evals1[i]);
972 
973       /* Map back to get the intersection */
974       for (i = 0; i < dim; ++i) {
975         for (j = 0; j < dim; ++j) {
976           M2[i*dim+j] = 0.0;
977           for (k = 0; k < dim; ++k) {
978             for (l = 0; l < dim; ++l) {
979               for (m = 0; m < dim; ++m) {
980                 M2[i*dim+j] += sqrtM1[i*dim+k] * evecs[l*dim+k] * evals[l] * evecs[l*dim+m] * sqrtM1[j*dim+m];
981               }
982             }
983           }
984         }
985       }
986     }
987     ierr = PetscFree(work);CHKERRQ(ierr);
988   }
989   ierr = PetscFree5(evecs, sqrtM1, isqrtM1, evals, evals1);CHKERRQ(ierr);
990   PetscFunctionReturn(0);
991 }
992 
993 /*
994   DMPlexMetricIntersection - Compute the intersection of a list of metrics
995 
996   Input Parameter:
997 + dm         - The DM
998 . numMetrics - The number of metrics to be intersected
999 - metrics    - The metrics to be intersected
1000 
1001   Output Parameter:
1002 . metricInt  - The intersected metric
1003 
1004   Level: beginner
1005 
1006   Notes:
1007 
1008   The intersection of a list of metrics has the maximal ellipsoid which fits within the ellipsoids of the component metrics.
1009 
1010   The implementation used here is only consistent with the maximal ellipsoid definition in the case numMetrics = 2.
1011 
1012 .seealso: DMPlexMetricIntersection2(), DMPlexMetricIntersection3(), DMPlexMetricAverage()
1013 */
1014 PetscErrorCode DMPlexMetricIntersection(DM dm, PetscInt numMetrics, Vec metrics[], Vec *metricInt)
1015 {
1016   PetscErrorCode ierr;
1017   PetscInt       dim, vStart, vEnd, v, i;
1018   PetscScalar   *met, *meti, *M, *Mi;
1019 
1020   PetscFunctionBegin;
1021   if (numMetrics < 1) { SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot intersect %d < 1 metrics", numMetrics); }
1022 
1023   /* Extract metadata from dm */
1024   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1025   ierr = DMPlexMetricCreate(dm, 0, metricInt);CHKERRQ(ierr);
1026   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
1027 
1028   /* Copy over the first metric */
1029   ierr = VecCopy(metrics[0], *metricInt);CHKERRQ(ierr);
1030 
1031   /* Intersect subsequent metrics in turn */
1032   if (numMetrics > 1) {
1033     ierr = VecGetArray(*metricInt, &met);CHKERRQ(ierr);
1034     for (i = 1; i < numMetrics; ++i) {
1035       ierr = VecGetArray(metrics[i], &meti);CHKERRQ(ierr);
1036       for (v = vStart; v < vEnd; ++v) {
1037         ierr = DMPlexPointLocalRef(dm, v, met, &M);CHKERRQ(ierr);
1038         ierr = DMPlexPointLocalRef(dm, v, meti, &Mi);CHKERRQ(ierr);
1039         ierr = DMPlexMetricIntersection_Private(dim, Mi, M);CHKERRQ(ierr);
1040       }
1041       ierr = VecRestoreArray(metrics[i], &meti);CHKERRQ(ierr);
1042     }
1043     ierr = VecRestoreArray(*metricInt, &met);CHKERRQ(ierr);
1044   }
1045 
1046   PetscFunctionReturn(0);
1047 }
1048 
1049 /*
1050   DMPlexMetricIntersection2 - Compute the intersection of two metrics
1051 
1052   Input Parameter:
1053 + dm        - The DM
1054 . metric1   - The first metric to be intersected
1055 - metric2   - The second metric to be intersected
1056 
1057   Output Parameter:
1058 . metricInt - The intersected metric
1059 
1060   Level: beginner
1061 
1062 .seealso: DMPlexMetricIntersection(), DMPlexMetricIntersection3()
1063 */
1064 PetscErrorCode DMPlexMetricIntersection2(DM dm, Vec metric1, Vec metric2, Vec *metricInt)
1065 {
1066   PetscErrorCode ierr;
1067   Vec            metrics[2] = {metric1, metric2};
1068 
1069   PetscFunctionBegin;
1070   ierr = DMPlexMetricIntersection(dm, 2, metrics, metricInt);CHKERRQ(ierr);
1071   PetscFunctionReturn(0);
1072 }
1073 
1074 /*
1075   DMPlexMetricIntersection3 - Compute the intersection of three metrics
1076 
1077   Input Parameter:
1078 + dm        - The DM
1079 . metric1   - The first metric to be intersected
1080 . metric2   - The second metric to be intersected
1081 - metric3   - The third metric to be intersected
1082 
1083   Output Parameter:
1084 . metricInt - The intersected metric
1085 
1086   Level: beginner
1087 
1088 .seealso: DMPlexMetricIntersection(), DMPlexMetricIntersection2()
1089 */
1090 PetscErrorCode DMPlexMetricIntersection3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec *metricInt)
1091 {
1092   PetscErrorCode ierr;
1093   Vec            metrics[3] = {metric1, metric2, metric3};
1094 
1095   PetscFunctionBegin;
1096   ierr = DMPlexMetricIntersection(dm, 3, metrics, metricInt);CHKERRQ(ierr);
1097   PetscFunctionReturn(0);
1098 }
1099