xref: /petsc/src/dm/impls/plex/plexmetric.c (revision 2f613bf53f46f9356e00a2ca2bd69453be72fc31)
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 be enforced?
717 - restrictAnisotropy - Should maximum metric anisotropy be enforced?
718 
719   Output parameter:
720 . metricOut          - The normalized metric
721 
722   Level: beginner
723 
724 .seealso: DMPlexMetricEnforceSPD(), DMPlexMetricIntersection()
725 */
726 PetscErrorCode DMPlexMetricNormalize(DM dm, Vec metricIn, PetscBool restrictSizes, PetscBool restrictAnisotropy, Vec *metricOut)
727 {
728   MPI_Comm         comm;
729   PetscBool        restrictAnisotropyFirst;
730   PetscDS          ds;
731   PetscErrorCode   ierr;
732   PetscInt         dim, Nd, vStart, vEnd, v, i;
733   PetscScalar     *met, integral, constants[1];
734   PetscReal        p, h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0, factGlob, target;
735 
736   PetscFunctionBegin;
737 
738   /* Extract metadata from dm */
739   ierr = PetscObjectGetComm((PetscObject) dm, &comm);CHKERRQ(ierr);
740   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
741   Nd = dim*dim;
742 
743   /* Set up metric and ensure it is SPD */
744   ierr = DMPlexMetricCreate(dm, 0, metricOut);CHKERRQ(ierr);
745   ierr = VecCopy(metricIn, *metricOut);CHKERRQ(ierr);
746   ierr = DMPlexMetricRestrictAnisotropyFirst(dm, &restrictAnisotropyFirst);CHKERRQ(ierr);
747   ierr = DMPlexMetricEnforceSPD(dm, PETSC_FALSE, (PetscBool)(restrictAnisotropy && restrictAnisotropyFirst), *metricOut);CHKERRQ(ierr);
748 
749   /* Compute global normalization factor */
750   ierr = DMPlexMetricGetTargetComplexity(dm, &target);CHKERRQ(ierr);
751   ierr = DMPlexMetricGetNormalizationOrder(dm, &p);CHKERRQ(ierr);
752   constants[0] = p;
753   ierr = DMGetDS(dm, &ds);CHKERRQ(ierr);
754   ierr = PetscDSSetConstants(ds, 1, constants);CHKERRQ(ierr);
755   ierr = PetscDSSetObjective(ds, 0, detMFunc);CHKERRQ(ierr);
756   ierr = DMPlexComputeIntegralFEM(dm, *metricOut, &integral, NULL);CHKERRQ(ierr);
757   factGlob = PetscPowReal(target/PetscRealPart(integral), 2.0/dim);
758 
759   /* Apply local scaling */
760   if (restrictSizes) {
761     ierr = DMPlexMetricGetMinimumMagnitude(dm, &h_min);CHKERRQ(ierr);
762     ierr = DMPlexMetricGetMaximumMagnitude(dm, &h_max);CHKERRQ(ierr);
763     h_min = PetscMax(h_min, 1.0e-30);
764     h_max = PetscMin(h_max, 1.0e+30);
765     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);
766   }
767   if (restrictAnisotropy && !restrictAnisotropyFirst) {
768     ierr = DMPlexMetricGetMaximumAnisotropy(dm, &a_max);CHKERRQ(ierr);
769     a_max = PetscMin(a_max, 1.0e+30);
770   }
771   ierr = VecGetArray(*metricOut, &met);CHKERRQ(ierr);
772   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
773   for (v = vStart; v < vEnd; ++v) {
774     PetscScalar *Mp;
775     PetscReal    detM, fact;
776 
777     ierr = DMPlexPointLocalRef(dm, v, met, &Mp);CHKERRQ(ierr);
778     if      (dim == 2) DMPlex_Det2D_Scalar_Internal(&detM, Mp);
779     else if (dim == 3) DMPlex_Det3D_Scalar_Internal(&detM, Mp);
780     else SETERRQ1(comm, PETSC_ERR_SUP, "Dimension %d not supported", dim);
781     fact = factGlob * PetscPowReal(PetscAbsReal(detM), -1.0/(2*p+dim));
782     for (i = 0; i < Nd; ++i) Mp[i] *= fact;
783     if (restrictSizes) { ierr = DMPlexMetricModify_Private(dim, h_min, h_max, a_max, Mp);CHKERRQ(ierr); }
784   }
785   ierr = VecRestoreArray(*metricOut, &met);CHKERRQ(ierr);
786 
787   PetscFunctionReturn(0);
788 }
789 
790 /*
791   DMPlexMetricAverage - Compute the average of a list of metrics
792 
793   Input Parameter:
794 + dm         - The DM
795 . numMetrics - The number of metrics to be averaged
796 . weights    - Weights for the average
797 - metrics    - The metrics to be averaged
798 
799   Output Parameter:
800 . metricAvg  - The averaged metric
801 
802   Level: beginner
803 
804   Notes:
805   The weights should sum to unity.
806 
807   If weights are not provided then an unweighted average is used.
808 
809 .seealso: DMPlexMetricAverage2(), DMPlexMetricAverage3(), DMPlexMetricIntersection()
810 */
811 PetscErrorCode DMPlexMetricAverage(DM dm, PetscInt numMetrics, PetscReal weights[], Vec metrics[], Vec *metricAvg)
812 {
813   PetscBool      haveWeights = PETSC_TRUE;
814   PetscErrorCode ierr;
815   PetscInt       i;
816   PetscReal      sum = 0.0, tol = 1.0e-10;
817 
818   PetscFunctionBegin;
819   if (numMetrics < 1) { SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot average %d < 1 metrics", numMetrics); }
820   ierr = DMPlexMetricCreate(dm, 0, metricAvg);CHKERRQ(ierr);
821   ierr = VecSet(*metricAvg, 0.0);CHKERRQ(ierr);
822 
823   /* Default to the unweighted case */
824   if (!weights) {
825     ierr = PetscMalloc1(numMetrics, &weights);CHKERRQ(ierr);
826     haveWeights = PETSC_FALSE;
827     for (i = 0; i < numMetrics; ++i) {weights[i] = 1.0/numMetrics; }
828   }
829 
830   /* Check weights sum to unity */
831   for (i = 0; i < numMetrics; ++i) { sum += weights[i]; }
832   if (PetscAbsReal(sum - 1) > tol) { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Weights do not sum to unity"); }
833 
834   /* Compute metric average */
835   for (i = 0; i < numMetrics; ++i) { ierr = VecAXPY(*metricAvg, weights[i], metrics[i]);CHKERRQ(ierr); }
836   if (!haveWeights) {ierr = PetscFree(weights); }
837   PetscFunctionReturn(0);
838 }
839 
840 /*
841   DMPlexMetricAverage2 - Compute the unweighted average of two metrics
842 
843   Input Parameter:
844 + dm         - The DM
845 . metric1    - The first metric to be averaged
846 - metric2    - The second metric to be averaged
847 
848   Output Parameter:
849 . metricAvg  - The averaged metric
850 
851   Level: beginner
852 
853 .seealso: DMPlexMetricAverage(), DMPlexMetricAverage3()
854 */
855 PetscErrorCode DMPlexMetricAverage2(DM dm, Vec metric1, Vec metric2, Vec *metricAvg)
856 {
857   PetscErrorCode ierr;
858   PetscReal      weights[2] = {0.5, 0.5};
859   Vec            metrics[2] = {metric1, metric2};
860 
861   PetscFunctionBegin;
862   ierr = DMPlexMetricAverage(dm, 2, weights, metrics, metricAvg);CHKERRQ(ierr);
863   PetscFunctionReturn(0);
864 }
865 
866 /*
867   DMPlexMetricAverage3 - Compute the unweighted average of three metrics
868 
869   Input Parameter:
870 + dm         - The DM
871 . metric1    - The first metric to be averaged
872 . metric2    - The second metric to be averaged
873 - metric3    - The third metric to be averaged
874 
875   Output Parameter:
876 . metricAvg  - The averaged metric
877 
878   Level: beginner
879 
880 .seealso: DMPlexMetricAverage(), DMPlexMetricAverage2()
881 */
882 PetscErrorCode DMPlexMetricAverage3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec *metricAvg)
883 {
884   PetscErrorCode ierr;
885   PetscReal      weights[3] = {1.0/3.0, 1.0/3.0, 1.0/3.0};
886   Vec            metrics[3] = {metric1, metric2, metric3};
887 
888   PetscFunctionBegin;
889   ierr = DMPlexMetricAverage(dm, 3, weights, metrics, metricAvg);CHKERRQ(ierr);
890   PetscFunctionReturn(0);
891 }
892 
893 static PetscErrorCode DMPlexMetricIntersection_Private(PetscInt dim, PetscScalar M1[], PetscScalar M2[])
894 {
895   PetscErrorCode ierr;
896   PetscInt       i, j, k, l, m;
897   PetscReal     *evals, *evals1;
898   PetscScalar   *evecs, *sqrtM1, *isqrtM1;
899 
900   PetscFunctionBegin;
901   ierr = PetscMalloc5(dim*dim, &evecs, dim*dim, &sqrtM1, dim*dim, &isqrtM1, dim, &evals, dim, &evals1);CHKERRQ(ierr);
902   for (i = 0; i < dim; ++i) {
903     for (j = 0; j < dim; ++j) {
904       evecs[i*dim+j] = M1[i*dim+j];
905     }
906   }
907   {
908     PetscScalar *work;
909     PetscBLASInt lwork;
910 
911     lwork = 5*dim;
912     ierr = PetscMalloc1(5*dim, &work);CHKERRQ(ierr);
913     {
914       PetscBLASInt lierr, nb;
915       PetscReal    sqrtk;
916 
917       /* Compute eigendecomposition of M1 */
918       ierr = PetscBLASIntCast(dim, &nb);CHKERRQ(ierr);
919       ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
920 #if defined(PETSC_USE_COMPLEX)
921       {
922         PetscReal *rwork;
923         ierr = PetscMalloc1(3*dim, &rwork);CHKERRQ(ierr);
924         PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals1, work, &lwork, rwork, &lierr));
925         ierr = PetscFree(rwork);CHKERRQ(ierr);
926       }
927 #else
928       PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals1, work, &lwork, &lierr));
929 #endif
930       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
931       ierr = PetscFPTrapPop();
932 
933       /* Compute square root and reciprocal */
934       for (i = 0; i < dim; ++i) {
935         for (j = 0; j < dim; ++j) {
936           sqrtM1[i*dim+j] = 0.0;
937           isqrtM1[i*dim+j] = 0.0;
938           for (k = 0; k < dim; ++k) {
939             sqrtk = PetscSqrtReal(evals1[k]);
940             sqrtM1[i*dim+j] += evecs[k*dim+i] * sqrtk * evecs[k*dim+j];
941             isqrtM1[i*dim+j] += evecs[k*dim+i] * (1.0/sqrtk) * evecs[k*dim+j];
942           }
943         }
944       }
945 
946       /* Map into the space spanned by the eigenvectors of M1 */
947       for (i = 0; i < dim; ++i) {
948         for (j = 0; j < dim; ++j) {
949           evecs[i*dim+j] = 0.0;
950           for (k = 0; k < dim; ++k) {
951             for (l = 0; l < dim; ++l) {
952               evecs[i*dim+j] += isqrtM1[i*dim+k] * M2[l*dim+k] * isqrtM1[j*dim+l];
953             }
954           }
955         }
956       }
957 
958       /* Compute eigendecomposition */
959       ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
960 #if defined(PETSC_USE_COMPLEX)
961       {
962         PetscReal *rwork;
963         ierr = PetscMalloc1(3*dim, &rwork);CHKERRQ(ierr);
964         PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
965         ierr = PetscFree(rwork);CHKERRQ(ierr);
966       }
967 #else
968       PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
969 #endif
970       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
971       ierr = PetscFPTrapPop();
972 
973       /* Modify eigenvalues */
974       for (i = 0; i < dim; ++i) evals[i] = PetscMin(evals[i], evals1[i]);
975 
976       /* Map back to get the intersection */
977       for (i = 0; i < dim; ++i) {
978         for (j = 0; j < dim; ++j) {
979           M2[i*dim+j] = 0.0;
980           for (k = 0; k < dim; ++k) {
981             for (l = 0; l < dim; ++l) {
982               for (m = 0; m < dim; ++m) {
983                 M2[i*dim+j] += sqrtM1[i*dim+k] * evecs[l*dim+k] * evals[l] * evecs[l*dim+m] * sqrtM1[j*dim+m];
984               }
985             }
986           }
987         }
988       }
989     }
990     ierr = PetscFree(work);CHKERRQ(ierr);
991   }
992   ierr = PetscFree5(evecs, sqrtM1, isqrtM1, evals, evals1);CHKERRQ(ierr);
993   PetscFunctionReturn(0);
994 }
995 
996 /*
997   DMPlexMetricIntersection - Compute the intersection of a list of metrics
998 
999   Input Parameter:
1000 + dm         - The DM
1001 . numMetrics - The number of metrics to be intersected
1002 - metrics    - The metrics to be intersected
1003 
1004   Output Parameter:
1005 . metricInt  - The intersected metric
1006 
1007   Level: beginner
1008 
1009   Notes:
1010 
1011   The intersection of a list of metrics has the maximal ellipsoid which fits within the ellipsoids of the component metrics.
1012 
1013   The implementation used here is only consistent with the maximal ellipsoid definition in the case numMetrics = 2.
1014 
1015 .seealso: DMPlexMetricIntersection2(), DMPlexMetricIntersection3(), DMPlexMetricAverage()
1016 */
1017 PetscErrorCode DMPlexMetricIntersection(DM dm, PetscInt numMetrics, Vec metrics[], Vec *metricInt)
1018 {
1019   PetscErrorCode ierr;
1020   PetscInt       dim, vStart, vEnd, v, i;
1021   PetscScalar   *met, *meti, *M, *Mi;
1022 
1023   PetscFunctionBegin;
1024   if (numMetrics < 1) { SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot intersect %d < 1 metrics", numMetrics); }
1025 
1026   /* Extract metadata from dm */
1027   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
1028   ierr = DMPlexMetricCreate(dm, 0, metricInt);CHKERRQ(ierr);
1029   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
1030 
1031   /* Copy over the first metric */
1032   ierr = VecCopy(metrics[0], *metricInt);CHKERRQ(ierr);
1033 
1034   /* Intersect subsequent metrics in turn */
1035   if (numMetrics > 1) {
1036     ierr = VecGetArray(*metricInt, &met);CHKERRQ(ierr);
1037     for (i = 1; i < numMetrics; ++i) {
1038       ierr = VecGetArray(metrics[i], &meti);CHKERRQ(ierr);
1039       for (v = vStart; v < vEnd; ++v) {
1040         ierr = DMPlexPointLocalRef(dm, v, met, &M);CHKERRQ(ierr);
1041         ierr = DMPlexPointLocalRef(dm, v, meti, &Mi);CHKERRQ(ierr);
1042         ierr = DMPlexMetricIntersection_Private(dim, Mi, M);CHKERRQ(ierr);
1043       }
1044       ierr = VecRestoreArray(metrics[i], &meti);CHKERRQ(ierr);
1045     }
1046     ierr = VecRestoreArray(*metricInt, &met);CHKERRQ(ierr);
1047   }
1048 
1049   PetscFunctionReturn(0);
1050 }
1051 
1052 /*
1053   DMPlexMetricIntersection2 - Compute the intersection of two metrics
1054 
1055   Input Parameter:
1056 + dm        - The DM
1057 . metric1   - The first metric to be intersected
1058 - metric2   - The second metric to be intersected
1059 
1060   Output Parameter:
1061 . metricInt - The intersected metric
1062 
1063   Level: beginner
1064 
1065 .seealso: DMPlexMetricIntersection(), DMPlexMetricIntersection3()
1066 */
1067 PetscErrorCode DMPlexMetricIntersection2(DM dm, Vec metric1, Vec metric2, Vec *metricInt)
1068 {
1069   PetscErrorCode ierr;
1070   Vec            metrics[2] = {metric1, metric2};
1071 
1072   PetscFunctionBegin;
1073   ierr = DMPlexMetricIntersection(dm, 2, metrics, metricInt);CHKERRQ(ierr);
1074   PetscFunctionReturn(0);
1075 }
1076 
1077 /*
1078   DMPlexMetricIntersection3 - Compute the intersection of three metrics
1079 
1080   Input Parameter:
1081 + dm        - The DM
1082 . metric1   - The first metric to be intersected
1083 . metric2   - The second metric to be intersected
1084 - metric3   - The third metric to be intersected
1085 
1086   Output Parameter:
1087 . metricInt - The intersected metric
1088 
1089   Level: beginner
1090 
1091 .seealso: DMPlexMetricIntersection(), DMPlexMetricIntersection2()
1092 */
1093 PetscErrorCode DMPlexMetricIntersection3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec *metricInt)
1094 {
1095   PetscErrorCode ierr;
1096   Vec            metrics[3] = {metric1, metric2, metric3};
1097 
1098   PetscFunctionBegin;
1099   ierr = DMPlexMetricIntersection(dm, 3, metrics, metricInt);CHKERRQ(ierr);
1100   PetscFunctionReturn(0);
1101 }
1102