xref: /petsc/src/dm/impls/plex/plexmetric.c (revision a3f1d042deeee8d591d0e166df91c7782e45ac59)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petscblaslapack.h>
3 
4 PetscLogEvent DMPLEX_MetricEnforceSPD, DMPLEX_MetricNormalize, DMPLEX_MetricAverage, DMPLEX_MetricIntersection;
5 
6 PetscErrorCode DMPlexMetricSetFromOptions(DM dm)
7 {
8   DM_Plex  *plex = (DM_Plex *)dm->data;
9   MPI_Comm  comm;
10   PetscBool isotropic = PETSC_FALSE, uniform = PETSC_FALSE, restrictAnisotropyFirst = PETSC_FALSE;
11   PetscBool noInsert = PETSC_FALSE, noSwap = PETSC_FALSE, noMove = PETSC_FALSE, noSurf = PETSC_FALSE;
12   PetscInt  verbosity = -1, numIter = 3;
13   PetscReal h_min = 1.0e-30, h_max = 1.0e+30, a_max = 1.0e+05, p = 1.0, target = 1000.0, beta = 1.3, hausd = 0.01;
14 
15   PetscFunctionBegin;
16   if (!plex->metricCtx) PetscCall(PetscNew(&plex->metricCtx));
17   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
18   PetscOptionsBegin(comm, "", "Riemannian metric options", "DMPlexMetric");
19   PetscCall(PetscOptionsBool("-dm_plex_metric_isotropic", "Is the metric isotropic?", "DMPlexMetricCreateIsotropic", isotropic, &isotropic, NULL));
20   PetscCall(DMPlexMetricSetIsotropic(dm, isotropic));
21   PetscCall(PetscOptionsBool("-dm_plex_metric_uniform", "Is the metric uniform?", "DMPlexMetricCreateUniform", uniform, &uniform, NULL));
22   PetscCall(DMPlexMetricSetUniform(dm, uniform));
23   PetscCall(PetscOptionsBool("-dm_plex_metric_restrict_anisotropy_first", "Should anisotropy be restricted before normalization?", "DMPlexNormalize", restrictAnisotropyFirst, &restrictAnisotropyFirst, NULL));
24   PetscCall(DMPlexMetricSetRestrictAnisotropyFirst(dm, restrictAnisotropyFirst));
25   PetscCall(PetscOptionsBool("-dm_plex_metric_no_insert", "Turn off node insertion and deletion", "DMAdaptMetric", noInsert, &noInsert, NULL));
26   PetscCall(DMPlexMetricSetNoInsertion(dm, noInsert));
27   PetscCall(PetscOptionsBool("-dm_plex_metric_no_swap", "Turn off facet swapping", "DMAdaptMetric", noSwap, &noSwap, NULL));
28   PetscCall(DMPlexMetricSetNoSwapping(dm, noSwap));
29   PetscCall(PetscOptionsBool("-dm_plex_metric_no_move", "Turn off facet node movement", "DMAdaptMetric", noMove, &noMove, NULL));
30   PetscCall(DMPlexMetricSetNoMovement(dm, noMove));
31   PetscCall(PetscOptionsBool("-dm_plex_metric_no_surf", "Turn off surface modification", "DMAdaptMetric", noSurf, &noSurf, NULL));
32   PetscCall(DMPlexMetricSetNoSurf(dm, noSurf));
33   PetscCall(PetscOptionsBoundedInt("-dm_plex_metric_num_iterations", "Number of ParMmg adaptation iterations", "DMAdaptMetric", numIter, &numIter, NULL, 0));
34   PetscCall(DMPlexMetricSetNumIterations(dm, numIter));
35   PetscCall(PetscOptionsRangeInt("-dm_plex_metric_verbosity", "Verbosity of metric-based mesh adaptation package (-1 = silent, 10 = maximum)", "DMAdaptMetric", verbosity, &verbosity, NULL, -1, 10));
36   PetscCall(DMPlexMetricSetVerbosity(dm, verbosity));
37   PetscCall(PetscOptionsReal("-dm_plex_metric_h_min", "Minimum tolerated metric magnitude", "DMPlexMetricEnforceSPD", h_min, &h_min, NULL));
38   PetscCall(DMPlexMetricSetMinimumMagnitude(dm, h_min));
39   PetscCall(PetscOptionsReal("-dm_plex_metric_h_max", "Maximum tolerated metric magnitude", "DMPlexMetricEnforceSPD", h_max, &h_max, NULL));
40   PetscCall(DMPlexMetricSetMaximumMagnitude(dm, h_max));
41   PetscCall(PetscOptionsReal("-dm_plex_metric_a_max", "Maximum tolerated anisotropy", "DMPlexMetricEnforceSPD", a_max, &a_max, NULL));
42   PetscCall(DMPlexMetricSetMaximumAnisotropy(dm, a_max));
43   PetscCall(PetscOptionsReal("-dm_plex_metric_p", "L-p normalization order", "DMPlexMetricNormalize", p, &p, NULL));
44   PetscCall(DMPlexMetricSetNormalizationOrder(dm, p));
45   PetscCall(PetscOptionsReal("-dm_plex_metric_target_complexity", "Target metric complexity", "DMPlexMetricNormalize", target, &target, NULL));
46   PetscCall(DMPlexMetricSetTargetComplexity(dm, target));
47   PetscCall(PetscOptionsReal("-dm_plex_metric_gradation_factor", "Metric gradation factor", "DMAdaptMetric", beta, &beta, NULL));
48   PetscCall(DMPlexMetricSetGradationFactor(dm, beta));
49   PetscCall(PetscOptionsReal("-dm_plex_metric_hausdorff_number", "Metric Hausdorff number", "DMAdaptMetric", hausd, &hausd, NULL));
50   PetscCall(DMPlexMetricSetHausdorffNumber(dm, hausd));
51   PetscOptionsEnd();
52   PetscFunctionReturn(PETSC_SUCCESS);
53 }
54 
55 /*@
56   DMPlexMetricSetIsotropic - Record whether a metric is isotropic
57 
58   Input Parameters:
59 + dm        - The `DM`
60 - isotropic - Is the metric isotropic?
61 
62   Level: beginner
63 
64 .seealso: `DMPLEX`, `DMPlexMetricIsIsotropic()`, `DMPlexMetricSetUniform()`, `DMPlexMetricSetRestrictAnisotropyFirst()`
65 @*/
66 PetscErrorCode DMPlexMetricSetIsotropic(DM dm, PetscBool isotropic)
67 {
68   DM_Plex *plex = (DM_Plex *)dm->data;
69 
70   PetscFunctionBegin;
71   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
72   plex->metricCtx->isotropic = isotropic;
73   PetscFunctionReturn(PETSC_SUCCESS);
74 }
75 
76 /*@
77   DMPlexMetricIsIsotropic - Is a metric isotropic?
78 
79   Input Parameters:
80 . dm - The `DM`
81 
82   Output Parameters:
83 . isotropic - Is the metric isotropic?
84 
85   Level: beginner
86 
87 .seealso: `DMPLEX`, `DMPlexMetricSetIsotropic()`, `DMPlexMetricIsUniform()`, `DMPlexMetricRestrictAnisotropyFirst()`
88 @*/
89 PetscErrorCode DMPlexMetricIsIsotropic(DM dm, PetscBool *isotropic)
90 {
91   DM_Plex *plex = (DM_Plex *)dm->data;
92 
93   PetscFunctionBegin;
94   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
95   *isotropic = plex->metricCtx->isotropic;
96   PetscFunctionReturn(PETSC_SUCCESS);
97 }
98 
99 /*@
100   DMPlexMetricSetUniform - Record whether a metric is uniform
101 
102   Input Parameters:
103 + dm      - The `DM`
104 - uniform - Is the metric uniform?
105 
106   Level: beginner
107 
108   Note:
109   If the metric is specified as uniform then it is assumed to be isotropic, too.
110 
111 .seealso: `DMPLEX`, `DMPlexMetricIsUniform()`, `DMPlexMetricSetIsotropic()`, `DMPlexMetricSetRestrictAnisotropyFirst()`
112 @*/
113 PetscErrorCode DMPlexMetricSetUniform(DM dm, PetscBool uniform)
114 {
115   DM_Plex *plex = (DM_Plex *)dm->data;
116 
117   PetscFunctionBegin;
118   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
119   plex->metricCtx->uniform = uniform;
120   if (uniform) plex->metricCtx->isotropic = uniform;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 /*@
125   DMPlexMetricIsUniform - Is a metric uniform?
126 
127   Input Parameters:
128 . dm - The `DM`
129 
130   Output Parameters:
131 . uniform - Is the metric uniform?
132 
133   Level: beginner
134 
135 .seealso: `DMPLEX`, `DMPlexMetricSetUniform()`, `DMPlexMetricIsIsotropic()`, `DMPlexMetricRestrictAnisotropyFirst()`
136 @*/
137 PetscErrorCode DMPlexMetricIsUniform(DM dm, PetscBool *uniform)
138 {
139   DM_Plex *plex = (DM_Plex *)dm->data;
140 
141   PetscFunctionBegin;
142   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
143   *uniform = plex->metricCtx->uniform;
144   PetscFunctionReturn(PETSC_SUCCESS);
145 }
146 
147 /*@
148   DMPlexMetricSetRestrictAnisotropyFirst - Record whether anisotropy should be restricted before normalization
149 
150   Input Parameters:
151 + dm                      - The `DM`
152 - restrictAnisotropyFirst - Should anisotropy be normalized first?
153 
154   Level: beginner
155 
156 .seealso: `DMPLEX`, `DMPlexMetricSetIsotropic()`, `DMPlexMetricRestrictAnisotropyFirst()`
157 @*/
158 PetscErrorCode DMPlexMetricSetRestrictAnisotropyFirst(DM dm, PetscBool restrictAnisotropyFirst)
159 {
160   DM_Plex *plex = (DM_Plex *)dm->data;
161 
162   PetscFunctionBegin;
163   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
164   plex->metricCtx->restrictAnisotropyFirst = restrictAnisotropyFirst;
165   PetscFunctionReturn(PETSC_SUCCESS);
166 }
167 
168 /*@
169   DMPlexMetricRestrictAnisotropyFirst - Is anisotropy restricted before normalization or after?
170 
171   Input Parameters:
172 . dm - The `DM`
173 
174   Output Parameters:
175 . restrictAnisotropyFirst - Is anisotropy be normalized first?
176 
177   Level: beginner
178 
179 .seealso: `DMPLEX`, `DMPlexMetricIsIsotropic()`, `DMPlexMetricSetRestrictAnisotropyFirst()`
180 @*/
181 PetscErrorCode DMPlexMetricRestrictAnisotropyFirst(DM dm, PetscBool *restrictAnisotropyFirst)
182 {
183   DM_Plex *plex = (DM_Plex *)dm->data;
184 
185   PetscFunctionBegin;
186   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
187   *restrictAnisotropyFirst = plex->metricCtx->restrictAnisotropyFirst;
188   PetscFunctionReturn(PETSC_SUCCESS);
189 }
190 
191 /*@
192   DMPlexMetricSetNoInsertion - Should node insertion and deletion be turned off?
193 
194   Input Parameters:
195 + dm       - The `DM`
196 - noInsert - Should node insertion and deletion be turned off?
197 
198   Level: beginner
199 
200   Note:
201   This is only used by Mmg and ParMmg (not Pragmatic).
202 
203 .seealso: `DMPLEX`, `DMPlexMetricNoInsertion()`, `DMPlexMetricSetNoSwapping()`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricSetNoSurf()`
204 @*/
205 PetscErrorCode DMPlexMetricSetNoInsertion(DM dm, PetscBool noInsert)
206 {
207   DM_Plex *plex = (DM_Plex *)dm->data;
208 
209   PetscFunctionBegin;
210   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
211   plex->metricCtx->noInsert = noInsert;
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 /*@
216   DMPlexMetricNoInsertion - Are node insertion and deletion turned off?
217 
218   Input Parameters:
219 . dm - The `DM`
220 
221   Output Parameters:
222 . noInsert - Are node insertion and deletion turned off?
223 
224   Level: beginner
225 
226   Note:
227   This is only used by Mmg and ParMmg (not Pragmatic).
228 
229 .seealso: `DMPLEX`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricNoSwapping()`, `DMPlexMetricNoMovement()`, `DMPlexMetricNoSurf()`
230 @*/
231 PetscErrorCode DMPlexMetricNoInsertion(DM dm, PetscBool *noInsert)
232 {
233   DM_Plex *plex = (DM_Plex *)dm->data;
234 
235   PetscFunctionBegin;
236   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
237   *noInsert = plex->metricCtx->noInsert;
238   PetscFunctionReturn(PETSC_SUCCESS);
239 }
240 
241 /*@
242   DMPlexMetricSetNoSwapping - Should facet swapping be turned off?
243 
244   Input Parameters:
245 + dm     - The `DM`
246 - noSwap - Should facet swapping be turned off?
247 
248   Level: beginner
249 
250   Note:
251   This is only used by Mmg and ParMmg (not Pragmatic).
252 
253 .seealso: `DMPLEX`, `DMPlexMetricNoSwapping()`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricSetNoSurf()`
254 @*/
255 PetscErrorCode DMPlexMetricSetNoSwapping(DM dm, PetscBool noSwap)
256 {
257   DM_Plex *plex = (DM_Plex *)dm->data;
258 
259   PetscFunctionBegin;
260   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
261   plex->metricCtx->noSwap = noSwap;
262   PetscFunctionReturn(PETSC_SUCCESS);
263 }
264 
265 /*@
266   DMPlexMetricNoSwapping - Is facet swapping turned off?
267 
268   Input Parameters:
269 . dm - The `DM`
270 
271   Output Parameters:
272 . noSwap - Is facet swapping turned off?
273 
274   Level: beginner
275 
276   Note:
277   This is only used by Mmg and ParMmg (not Pragmatic).
278 
279 .seealso: `DMPLEX`, `DMPlexMetricSetNoSwapping()`, `DMPlexMetricNoInsertion()`, `DMPlexMetricNoMovement()`, `DMPlexMetricNoSurf()`
280 @*/
281 PetscErrorCode DMPlexMetricNoSwapping(DM dm, PetscBool *noSwap)
282 {
283   DM_Plex *plex = (DM_Plex *)dm->data;
284 
285   PetscFunctionBegin;
286   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
287   *noSwap = plex->metricCtx->noSwap;
288   PetscFunctionReturn(PETSC_SUCCESS);
289 }
290 
291 /*@
292   DMPlexMetricSetNoMovement - Should node movement be turned off?
293 
294   Input Parameters:
295 + dm     - The `DM`
296 - noMove - Should node movement be turned off?
297 
298   Level: beginner
299 
300   Note:
301   This is only used by Mmg and ParMmg (not Pragmatic).
302 
303 .seealso: `DMPLEX`, `DMPlexMetricNoMovement()`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricSetNoSwapping()`, `DMPlexMetricSetNoSurf()`
304 @*/
305 PetscErrorCode DMPlexMetricSetNoMovement(DM dm, PetscBool noMove)
306 {
307   DM_Plex *plex = (DM_Plex *)dm->data;
308 
309   PetscFunctionBegin;
310   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
311   plex->metricCtx->noMove = noMove;
312   PetscFunctionReturn(PETSC_SUCCESS);
313 }
314 
315 /*@
316   DMPlexMetricNoMovement - Is node movement turned off?
317 
318   Input Parameters:
319 . dm - The `DM`
320 
321   Output Parameters:
322 . noMove - Is node movement turned off?
323 
324   Level: beginner
325 
326   Note:
327   This is only used by Mmg and ParMmg (not Pragmatic).
328 
329 .seealso: `DMPLEX`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricNoInsertion()`, `DMPlexMetricNoSwapping()`, `DMPlexMetricNoSurf()`
330 @*/
331 PetscErrorCode DMPlexMetricNoMovement(DM dm, PetscBool *noMove)
332 {
333   DM_Plex *plex = (DM_Plex *)dm->data;
334 
335   PetscFunctionBegin;
336   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
337   *noMove = plex->metricCtx->noMove;
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 /*@
342   DMPlexMetricSetNoSurf - Should surface modification be turned off?
343 
344   Input Parameters:
345 + dm     - The `DM`
346 - noSurf - Should surface modification be turned off?
347 
348   Level: beginner
349 
350   Note:
351   This is only used by Mmg and ParMmg (not Pragmatic).
352 
353 .seealso: `DMPLEX`, `DMPlexMetricNoSurf()`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricSetNoSwapping()`
354 @*/
355 PetscErrorCode DMPlexMetricSetNoSurf(DM dm, PetscBool noSurf)
356 {
357   DM_Plex *plex = (DM_Plex *)dm->data;
358 
359   PetscFunctionBegin;
360   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
361   plex->metricCtx->noSurf = noSurf;
362   PetscFunctionReturn(PETSC_SUCCESS);
363 }
364 
365 /*@
366   DMPlexMetricNoSurf - Is surface modification turned off?
367 
368   Input Parameters:
369 . dm - The `DM`
370 
371   Output Parameters:
372 . noSurf - Is surface modification turned off?
373 
374   Level: beginner
375 
376   Note:
377   This is only used by Mmg and ParMmg (not Pragmatic).
378 
379 .seealso: `DMPLEX`, `DMPlexMetricSetNoSurf()`, `DMPlexMetricNoMovement()`, `DMPlexMetricNoInsertion()`, `DMPlexMetricNoSwapping()`
380 @*/
381 PetscErrorCode DMPlexMetricNoSurf(DM dm, PetscBool *noSurf)
382 {
383   DM_Plex *plex = (DM_Plex *)dm->data;
384 
385   PetscFunctionBegin;
386   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
387   *noSurf = plex->metricCtx->noSurf;
388   PetscFunctionReturn(PETSC_SUCCESS);
389 }
390 
391 /*@
392   DMPlexMetricSetMinimumMagnitude - Set the minimum tolerated metric magnitude
393 
394   Input Parameters:
395 + dm    - The `DM`
396 - h_min - The minimum tolerated metric magnitude
397 
398   Level: beginner
399 
400 .seealso: `DMPLEX`, `DMPlexMetricGetMinimumMagnitude()`, `DMPlexMetricSetMaximumMagnitude()`
401 @*/
402 PetscErrorCode DMPlexMetricSetMinimumMagnitude(DM dm, PetscReal h_min)
403 {
404   DM_Plex *plex = (DM_Plex *)dm->data;
405 
406   PetscFunctionBegin;
407   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
408   PetscCheck(h_min > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Metric magnitudes must be in (0, inf)");
409   plex->metricCtx->h_min = h_min;
410   PetscFunctionReturn(PETSC_SUCCESS);
411 }
412 
413 /*@
414   DMPlexMetricGetMinimumMagnitude - Get the minimum tolerated metric magnitude
415 
416   Input Parameters:
417 . dm - The `DM`
418 
419   Output Parameters:
420 . h_min - The minimum tolerated metric magnitude
421 
422   Level: beginner
423 
424 .seealso: `DMPLEX`, `DMPlexMetricSetMinimumMagnitude()`, `DMPlexMetricGetMaximumMagnitude()`
425 @*/
426 PetscErrorCode DMPlexMetricGetMinimumMagnitude(DM dm, PetscReal *h_min)
427 {
428   DM_Plex *plex = (DM_Plex *)dm->data;
429 
430   PetscFunctionBegin;
431   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
432   *h_min = plex->metricCtx->h_min;
433   PetscFunctionReturn(PETSC_SUCCESS);
434 }
435 
436 /*@
437   DMPlexMetricSetMaximumMagnitude - Set the maximum tolerated metric magnitude
438 
439   Input Parameters:
440 + dm    - The `DM`
441 - h_max - The maximum tolerated metric magnitude
442 
443   Level: beginner
444 
445 .seealso: `DMPLEX`, `DMPlexMetricGetMaximumMagnitude()`, `DMPlexMetricSetMinimumMagnitude()`
446 @*/
447 PetscErrorCode DMPlexMetricSetMaximumMagnitude(DM dm, PetscReal h_max)
448 {
449   DM_Plex *plex = (DM_Plex *)dm->data;
450 
451   PetscFunctionBegin;
452   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
453   PetscCheck(h_max > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Metric magnitudes must be in (0, inf)");
454   plex->metricCtx->h_max = h_max;
455   PetscFunctionReturn(PETSC_SUCCESS);
456 }
457 
458 /*@
459   DMPlexMetricGetMaximumMagnitude - Get the maximum tolerated metric magnitude
460 
461   Input Parameters:
462 . dm - The `DM`
463 
464   Output Parameters:
465 . h_max - The maximum tolerated metric magnitude
466 
467   Level: beginner
468 
469 .seealso: `DMPLEX`, `DMPlexMetricSetMaximumMagnitude()`, `DMPlexMetricGetMinimumMagnitude()`
470 @*/
471 PetscErrorCode DMPlexMetricGetMaximumMagnitude(DM dm, PetscReal *h_max)
472 {
473   DM_Plex *plex = (DM_Plex *)dm->data;
474 
475   PetscFunctionBegin;
476   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
477   *h_max = plex->metricCtx->h_max;
478   PetscFunctionReturn(PETSC_SUCCESS);
479 }
480 
481 /*@
482   DMPlexMetricSetMaximumAnisotropy - Set the maximum tolerated metric anisotropy
483 
484   Input Parameters:
485 + dm    - The `DM`
486 - a_max - The maximum tolerated metric anisotropy
487 
488   Level: beginner
489 
490   Note:
491   If the value zero is given then anisotropy will not be restricted. Otherwise, it should be at least one.
492 
493 .seealso: `DMPLEX`, `DMPlexMetricGetMaximumAnisotropy()`, `DMPlexMetricSetMaximumMagnitude()`
494 @*/
495 PetscErrorCode DMPlexMetricSetMaximumAnisotropy(DM dm, PetscReal a_max)
496 {
497   DM_Plex *plex = (DM_Plex *)dm->data;
498 
499   PetscFunctionBegin;
500   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
501   PetscCheck(a_max >= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Anisotropy must be in [1, inf)");
502   plex->metricCtx->a_max = a_max;
503   PetscFunctionReturn(PETSC_SUCCESS);
504 }
505 
506 /*@
507   DMPlexMetricGetMaximumAnisotropy - Get the maximum tolerated metric anisotropy
508 
509   Input Parameters:
510 . dm - The `DM`
511 
512   Output Parameters:
513 . a_max - The maximum tolerated metric anisotropy
514 
515   Level: beginner
516 
517 .seealso: `DMPLEX`, `DMPlexMetricSetMaximumAnisotropy()`, `DMPlexMetricGetMaximumMagnitude()`
518 @*/
519 PetscErrorCode DMPlexMetricGetMaximumAnisotropy(DM dm, PetscReal *a_max)
520 {
521   DM_Plex *plex = (DM_Plex *)dm->data;
522 
523   PetscFunctionBegin;
524   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
525   *a_max = plex->metricCtx->a_max;
526   PetscFunctionReturn(PETSC_SUCCESS);
527 }
528 
529 /*@
530   DMPlexMetricSetTargetComplexity - Set the target metric complexity
531 
532   Input Parameters:
533 + dm               - The `DM`
534 - targetComplexity - The target metric complexity
535 
536   Level: beginner
537 
538 .seealso: `DMPLEX`, `DMPlexMetricGetTargetComplexity()`, `DMPlexMetricSetNormalizationOrder()`
539 @*/
540 PetscErrorCode DMPlexMetricSetTargetComplexity(DM dm, PetscReal targetComplexity)
541 {
542   DM_Plex *plex = (DM_Plex *)dm->data;
543 
544   PetscFunctionBegin;
545   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
546   PetscCheck(targetComplexity > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Metric complexity must be in (0, inf)");
547   plex->metricCtx->targetComplexity = targetComplexity;
548   PetscFunctionReturn(PETSC_SUCCESS);
549 }
550 
551 /*@
552   DMPlexMetricGetTargetComplexity - Get the target metric complexity
553 
554   Input Parameters:
555 . dm - The `DM`
556 
557   Output Parameters:
558 . targetComplexity - The target metric complexity
559 
560   Level: beginner
561 
562 .seealso: `DMPLEX`, `DMPlexMetricSetTargetComplexity()`, `DMPlexMetricGetNormalizationOrder()`
563 @*/
564 PetscErrorCode DMPlexMetricGetTargetComplexity(DM dm, PetscReal *targetComplexity)
565 {
566   DM_Plex *plex = (DM_Plex *)dm->data;
567 
568   PetscFunctionBegin;
569   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
570   *targetComplexity = plex->metricCtx->targetComplexity;
571   PetscFunctionReturn(PETSC_SUCCESS);
572 }
573 
574 /*@
575   DMPlexMetricSetNormalizationOrder - Set the order p for L-p normalization
576 
577   Input Parameters:
578 + dm - The `DM`
579 - p  - The normalization order
580 
581   Level: beginner
582 
583 .seealso: `DMPLEX`, `DMPlexMetricGetNormalizationOrder()`, `DMPlexMetricSetTargetComplexity()`
584 @*/
585 PetscErrorCode DMPlexMetricSetNormalizationOrder(DM dm, PetscReal p)
586 {
587   DM_Plex *plex = (DM_Plex *)dm->data;
588 
589   PetscFunctionBegin;
590   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
591   PetscCheck(p >= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Normalization order must be in [1, inf)");
592   plex->metricCtx->p = p;
593   PetscFunctionReturn(PETSC_SUCCESS);
594 }
595 
596 /*@
597   DMPlexMetricGetNormalizationOrder - Get the order p for L-p normalization
598 
599   Input Parameters:
600 . dm - The `DM`
601 
602   Output Parameters:
603 . p - The normalization order
604 
605   Level: beginner
606 
607 .seealso: `DMPLEX`, `DMPlexMetricSetNormalizationOrder()`, `DMPlexMetricGetTargetComplexity()`
608 @*/
609 PetscErrorCode DMPlexMetricGetNormalizationOrder(DM dm, PetscReal *p)
610 {
611   DM_Plex *plex = (DM_Plex *)dm->data;
612 
613   PetscFunctionBegin;
614   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
615   *p = plex->metricCtx->p;
616   PetscFunctionReturn(PETSC_SUCCESS);
617 }
618 
619 /*@
620   DMPlexMetricSetGradationFactor - Set the metric gradation factor
621 
622   Input Parameters:
623 + dm   - The `DM`
624 - beta - The metric gradation factor
625 
626   Level: beginner
627 
628   Notes:
629   The gradation factor is the maximum tolerated length ratio between adjacent edges.
630 
631   Turn off gradation by passing the value -1. Otherwise, pass a positive value.
632 
633   This is only used by Mmg and ParMmg (not Pragmatic).
634 
635 .seealso: `DMPLEX`, `DMPlexMetricGetGradationFactor()`, `DMPlexMetricSetHausdorffNumber()`
636 @*/
637 PetscErrorCode DMPlexMetricSetGradationFactor(DM dm, PetscReal beta)
638 {
639   DM_Plex *plex = (DM_Plex *)dm->data;
640 
641   PetscFunctionBegin;
642   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
643   PetscCheck(beta > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Metric gradation factor must be in (0, inf)");
644   plex->metricCtx->gradationFactor = beta;
645   PetscFunctionReturn(PETSC_SUCCESS);
646 }
647 
648 /*@
649   DMPlexMetricGetGradationFactor - Get the metric gradation factor
650 
651   Input Parameters:
652 . dm - The `DM`
653 
654   Output Parameters:
655 . beta - The metric gradation factor
656 
657   Level: beginner
658 
659   Notes:
660   The gradation factor is the maximum tolerated length ratio between adjacent edges.
661 
662   The value -1 implies that gradation is turned off.
663 
664   This is only used by Mmg and ParMmg (not Pragmatic).
665 
666 .seealso: `DMPLEX`, `DMPlexMetricSetGradationFactor()`, `DMPlexMetricGetHausdorffNumber()`
667 @*/
668 PetscErrorCode DMPlexMetricGetGradationFactor(DM dm, PetscReal *beta)
669 {
670   DM_Plex *plex = (DM_Plex *)dm->data;
671 
672   PetscFunctionBegin;
673   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
674   *beta = plex->metricCtx->gradationFactor;
675   PetscFunctionReturn(PETSC_SUCCESS);
676 }
677 
678 /*@
679   DMPlexMetricSetHausdorffNumber - Set the metric Hausdorff number
680 
681   Input Parameters:
682 + dm    - The `DM`
683 - hausd - The metric Hausdorff number
684 
685   Level: beginner
686 
687   Notes:
688   The Hausdorff number imposes the maximal distance between the piecewise linear approximation of the
689   boundary and the reconstructed ideal boundary. Thus, a low Hausdorff parameter leads to refine the
690   high curvature areas. By default, the Hausdorff value is set to 0.01, which is a suitable value for
691   an object of size 1 in each direction. For smaller (resp. larger) objects, you may need to decrease
692   (resp. increase) the Hausdorff parameter. (Taken from
693   https://www.mmgtools.org/mmg-remesher-try-mmg/mmg-remesher-options/mmg-remesher-option-hausd).
694 
695   This is only used by Mmg and ParMmg (not Pragmatic).
696 
697 .seealso: `DMPLEX`, `DMPlexMetricSetGradationFactor()`, `DMPlexMetricGetHausdorffNumber()`
698 @*/
699 PetscErrorCode DMPlexMetricSetHausdorffNumber(DM dm, PetscReal hausd)
700 {
701   DM_Plex *plex = (DM_Plex *)dm->data;
702 
703   PetscFunctionBegin;
704   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
705   PetscCheck(hausd > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Metric Hausdorff number must be in (0, inf)");
706   plex->metricCtx->hausdorffNumber = hausd;
707   PetscFunctionReturn(PETSC_SUCCESS);
708 }
709 
710 /*@
711   DMPlexMetricGetHausdorffNumber - Get the metric Hausdorff number
712 
713   Input Parameters:
714 . dm - The `DM`
715 
716   Output Parameters:
717 . hausd - The metric Hausdorff number
718 
719   Level: beginner
720 
721   Notes:
722   The Hausdorff number imposes the maximal distance between the piecewise linear approximation of the
723   boundary and the reconstructed ideal boundary. Thus, a low Hausdorff parameter leads to refine the
724   high curvature areas. By default, the Hausdorff value is set to 0.01, which is a suitable value for
725   an object of size 1 in each direction. For smaller (resp. larger) objects, you may need to decrease
726   (resp. increase) the Hausdorff parameter. (Taken from
727   https://www.mmgtools.org/mmg-remesher-try-mmg/mmg-remesher-options/mmg-remesher-option-hausd).
728 
729   This is only used by Mmg and ParMmg (not Pragmatic).
730 
731 .seealso: `DMPLEX`, `DMPlexMetricGetGradationFactor()`, `DMPlexMetricSetHausdorffNumber()`
732 @*/
733 PetscErrorCode DMPlexMetricGetHausdorffNumber(DM dm, PetscReal *hausd)
734 {
735   DM_Plex *plex = (DM_Plex *)dm->data;
736 
737   PetscFunctionBegin;
738   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
739   *hausd = plex->metricCtx->hausdorffNumber;
740   PetscFunctionReturn(PETSC_SUCCESS);
741 }
742 
743 /*@
744   DMPlexMetricSetVerbosity - Set the verbosity of the mesh adaptation package
745 
746   Input Parameters:
747 + dm        - The `DM`
748 - verbosity - The verbosity, where -1 is silent and 10 is maximum
749 
750   Level: beginner
751 
752   Note:
753   This is only used by Mmg and ParMmg (not Pragmatic).
754 
755 .seealso: `DMPLEX`, `DMPlexMetricGetVerbosity()`, `DMPlexMetricSetNumIterations()`
756 @*/
757 PetscErrorCode DMPlexMetricSetVerbosity(DM dm, PetscInt verbosity)
758 {
759   DM_Plex *plex = (DM_Plex *)dm->data;
760 
761   PetscFunctionBegin;
762   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
763   plex->metricCtx->verbosity = verbosity;
764   PetscFunctionReturn(PETSC_SUCCESS);
765 }
766 
767 /*@
768   DMPlexMetricGetVerbosity - Get the verbosity of the mesh adaptation package
769 
770   Input Parameters:
771 . dm - The `DM`
772 
773   Output Parameters:
774 . verbosity - The verbosity, where -1 is silent and 10 is maximum
775 
776   Level: beginner
777 
778   Note:
779   This is only used by Mmg and ParMmg (not Pragmatic).
780 
781 .seealso: `DMPLEX`, `DMPlexMetricSetVerbosity()`, `DMPlexMetricGetNumIterations()`
782 @*/
783 PetscErrorCode DMPlexMetricGetVerbosity(DM dm, PetscInt *verbosity)
784 {
785   DM_Plex *plex = (DM_Plex *)dm->data;
786 
787   PetscFunctionBegin;
788   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
789   *verbosity = plex->metricCtx->verbosity;
790   PetscFunctionReturn(PETSC_SUCCESS);
791 }
792 
793 /*@
794   DMPlexMetricSetNumIterations - Set the number of parallel adaptation iterations
795 
796   Input Parameters:
797 + dm      - The `DM`
798 - numIter - the number of parallel adaptation iterations
799 
800   Level: beginner
801 
802   Note:
803   This is only used by ParMmg (not Pragmatic or Mmg).
804 
805 .seealso: `DMPLEX`, `DMPlexMetricSetVerbosity()`, `DMPlexMetricGetNumIterations()`
806 @*/
807 PetscErrorCode DMPlexMetricSetNumIterations(DM dm, PetscInt numIter)
808 {
809   DM_Plex *plex = (DM_Plex *)dm->data;
810 
811   PetscFunctionBegin;
812   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
813   plex->metricCtx->numIter = numIter;
814   PetscFunctionReturn(PETSC_SUCCESS);
815 }
816 
817 /*@
818   DMPlexMetricGetNumIterations - Get the number of parallel adaptation iterations
819 
820   Input Parameters:
821 . dm - The `DM`
822 
823   Output Parameters:
824 . numIter - the number of parallel adaptation iterations
825 
826   Level: beginner
827 
828   Note:
829   This is only used by Mmg and ParMmg (not Pragmatic or Mmg).
830 
831 .seealso: `DMPLEX`, `DMPlexMetricSetNumIterations()`, `DMPlexMetricGetVerbosity()`
832 @*/
833 PetscErrorCode DMPlexMetricGetNumIterations(DM dm, PetscInt *numIter)
834 {
835   DM_Plex *plex = (DM_Plex *)dm->data;
836 
837   PetscFunctionBegin;
838   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
839   *numIter = plex->metricCtx->numIter;
840   PetscFunctionReturn(PETSC_SUCCESS);
841 }
842 
843 static PetscErrorCode DMPlexP1FieldCreate_Private(DM dm, PetscInt f, PetscInt size, Vec *metric)
844 {
845   MPI_Comm comm;
846   PetscFE  fe;
847   PetscInt dim;
848 
849   PetscFunctionBegin;
850   /* Extract metadata from dm */
851   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
852   PetscCall(DMGetDimension(dm, &dim));
853 
854   /* Create a P1 field of the requested size */
855   PetscCall(PetscFECreateLagrange(comm, dim, size, PETSC_TRUE, 1, PETSC_DETERMINE, &fe));
856   PetscCall(DMSetField(dm, f, NULL, (PetscObject)fe));
857   PetscCall(DMCreateDS(dm));
858   PetscCall(PetscFEDestroy(&fe));
859   PetscCall(DMCreateLocalVector(dm, metric));
860   PetscFunctionReturn(PETSC_SUCCESS);
861 }
862 
863 /*@
864   DMPlexMetricCreate - Create a Riemannian metric field
865 
866   Input Parameters:
867 + dm - The `DM`
868 - f  - The field number to use
869 
870   Output Parameter:
871 . metric - The metric
872 
873   Options Database Key:
874 . -dm_adaptor <pragmatic/mmg/parmmg> - specify dm adaptor to use
875 
876   Options Database Keys for Mmg and ParMmg:
877 + -dm_plex_metric_gradation_factor - Maximum ratio by which edge lengths may grow during gradation
878 . -dm_plex_metric_num_iterations   - Number of parallel mesh adaptation iterations for ParMmg
879 . -dm_plex_metric_no_insert        - Should node insertion/deletion be turned off?
880 . -dm_plex_metric_no_swap          - Should facet swapping be turned off?
881 . -dm_plex_metric_no_move          - Should node movement be turned off?
882 - -dm_plex_metric_verbosity        - Choose a verbosity level from -1 (silent) to 10 (maximum).
883 
884   Options Database Keys for Riemannian metrics:
885 + -dm_plex_metric_isotropic                 - Is the metric isotropic?
886 . -dm_plex_metric_uniform                   - Is the metric uniform?
887 . -dm_plex_metric_restrict_anisotropy_first - Should anisotropy be restricted before normalization?
888 . -dm_plex_metric_h_min                     - Minimum tolerated metric magnitude
889 . -dm_plex_metric_h_max                     - Maximum tolerated metric magnitude
890 . -dm_plex_metric_a_max                     - Maximum tolerated anisotropy
891 . -dm_plex_metric_p                         - L-p normalization order
892 - -dm_plex_metric_target_complexity         - Target metric complexity
893 
894   Level: beginner
895 
896   Note:
897   It is assumed that the `DM` is comprised of simplices.
898 
899 .seealso: `DMPLEX`, `DMPlexMetricCreateUniform()`, `DMPlexMetricCreateIsotropic()`
900 @*/
901 PetscErrorCode DMPlexMetricCreate(DM dm, PetscInt f, Vec *metric)
902 {
903   PetscBool isotropic, uniform;
904   PetscInt  coordDim, Nd;
905 
906   PetscFunctionBegin;
907   PetscCall(DMGetCoordinateDim(dm, &coordDim));
908   Nd = coordDim * coordDim;
909   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
910   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
911   if (uniform) {
912     MPI_Comm comm;
913 
914     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
915     PetscCheck(isotropic, comm, PETSC_ERR_SUP, "Uniform anisotropic metrics not supported");
916     PetscCall(VecCreate(comm, metric));
917     PetscCall(VecSetSizes(*metric, 1, PETSC_DECIDE));
918     PetscCall(VecSetFromOptions(*metric));
919   } else if (isotropic) PetscCall(DMPlexP1FieldCreate_Private(dm, f, 1, metric));
920   else PetscCall(DMPlexP1FieldCreate_Private(dm, f, Nd, metric));
921   PetscFunctionReturn(PETSC_SUCCESS);
922 }
923 
924 /*@
925   DMPlexMetricCreateUniform - Construct a uniform isotropic metric
926 
927   Input Parameters:
928 + dm    - The `DM`
929 . f     - The field number to use
930 - alpha - Scaling parameter for the diagonal
931 
932   Output Parameter:
933 . metric - The uniform metric
934 
935   Level: beginner
936 
937   Note:
938   In this case, the metric is constant in space and so is only specified for a single vertex.
939 
940 .seealso: `DMPLEX`, `DMPlexMetricCreate()`, `DMPlexMetricCreateIsotropic()`
941 @*/
942 PetscErrorCode DMPlexMetricCreateUniform(DM dm, PetscInt f, PetscReal alpha, Vec *metric)
943 {
944   PetscFunctionBegin;
945   PetscCall(DMPlexMetricSetUniform(dm, PETSC_TRUE));
946   PetscCall(DMPlexMetricCreate(dm, f, metric));
947   PetscCheck(alpha, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform metric scaling is undefined");
948   PetscCheck(alpha > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Uniform metric scaling should be in (0, inf)");
949   PetscCall(VecSet(*metric, alpha));
950   PetscCall(VecAssemblyBegin(*metric));
951   PetscCall(VecAssemblyEnd(*metric));
952   PetscFunctionReturn(PETSC_SUCCESS);
953 }
954 
955 static void identity(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[])
956 {
957   f0[0] = u[0];
958 }
959 
960 /*@
961   DMPlexMetricCreateIsotropic - Construct an isotropic metric from an error indicator
962 
963   Input Parameters:
964 + dm        - The `DM`
965 . f         - The field number to use
966 - indicator - The error indicator
967 
968   Output Parameter:
969 . metric - The isotropic metric
970 
971   Level: beginner
972 
973   Notes:
974   It is assumed that the `DM` is comprised of simplices.
975 
976   The indicator needs to be a scalar field. If it is not defined vertex-wise, then it is projected appropriately.
977 
978 .seealso: `DMPLEX`, `DMPlexMetricCreate()`, `DMPlexMetricCreateUniform()`
979 @*/
980 PetscErrorCode DMPlexMetricCreateIsotropic(DM dm, PetscInt f, Vec indicator, Vec *metric)
981 {
982   PetscInt m, n;
983 
984   PetscFunctionBegin;
985   PetscCall(DMPlexMetricSetIsotropic(dm, PETSC_TRUE));
986   PetscCall(DMPlexMetricCreate(dm, f, metric));
987   PetscCall(VecGetSize(indicator, &m));
988   PetscCall(VecGetSize(*metric, &n));
989   if (m == n) PetscCall(VecCopy(indicator, *metric));
990   else {
991     DM dmIndi;
992     void (*funcs[1])(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
993 
994     PetscCall(VecGetDM(indicator, &dmIndi));
995     funcs[0] = identity;
996     PetscCall(DMProjectFieldLocal(dmIndi, 0.0, indicator, funcs, INSERT_VALUES, *metric));
997   }
998   PetscFunctionReturn(PETSC_SUCCESS);
999 }
1000 
1001 /*@
1002   DMPlexMetricDeterminantCreate - Create the determinant field for a Riemannian metric
1003 
1004   Input Parameters:
1005 + dm - The `DM` of the metric field
1006 - f  - The field number to use
1007 
1008   Output Parameters:
1009 + determinant - The determinant field
1010 - dmDet       - The corresponding `DM`
1011 
1012   Level: beginner
1013 
1014 .seealso: `DMPLEX`, `DMPlexMetricCreateUniform()`, `DMPlexMetricCreateIsotropic()`, `DMPlexMetricCreate()`
1015 @*/
1016 PetscErrorCode DMPlexMetricDeterminantCreate(DM dm, PetscInt f, Vec *determinant, DM *dmDet)
1017 {
1018   PetscBool uniform;
1019 
1020   PetscFunctionBegin;
1021   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1022   PetscCall(DMClone(dm, dmDet));
1023   if (uniform) {
1024     MPI_Comm comm;
1025 
1026     PetscCall(PetscObjectGetComm((PetscObject)*dmDet, &comm));
1027     PetscCall(VecCreate(comm, determinant));
1028     PetscCall(VecSetSizes(*determinant, 1, PETSC_DECIDE));
1029     PetscCall(VecSetFromOptions(*determinant));
1030   } else PetscCall(DMPlexP1FieldCreate_Private(*dmDet, f, 1, determinant));
1031   PetscFunctionReturn(PETSC_SUCCESS);
1032 }
1033 
1034 static PetscErrorCode LAPACKsyevFail(PetscInt dim, PetscScalar Mpos[])
1035 {
1036   PetscInt i, j;
1037 
1038   PetscFunctionBegin;
1039   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Failed to apply LAPACKsyev to the matrix\n"));
1040   for (i = 0; i < dim; ++i) {
1041     if (i == 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, "    [["));
1042     else PetscCall(PetscPrintf(PETSC_COMM_SELF, "     ["));
1043     for (j = 0; j < dim; ++j) {
1044       if (j < dim - 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%15.8e, ", (double)PetscAbsScalar(Mpos[i * dim + j])));
1045       else PetscCall(PetscPrintf(PETSC_COMM_SELF, "%15.8e", (double)PetscAbsScalar(Mpos[i * dim + j])));
1046     }
1047     if (i < dim - 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n"));
1048     else PetscCall(PetscPrintf(PETSC_COMM_SELF, "]]\n"));
1049   }
1050   PetscFunctionReturn(PETSC_SUCCESS);
1051 }
1052 
1053 static PetscErrorCode DMPlexMetricModify_Private(PetscInt dim, PetscReal h_min, PetscReal h_max, PetscReal a_max, PetscScalar Mp[], PetscScalar *detMp)
1054 {
1055   PetscInt     i, j, k;
1056   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);
1057   PetscScalar *Mpos;
1058 
1059   PetscFunctionBegin;
1060   PetscCall(PetscMalloc2(dim * dim, &Mpos, dim, &eigs));
1061 
1062   /* Symmetrize */
1063   for (i = 0; i < dim; ++i) {
1064     Mpos[i * dim + i] = Mp[i * dim + i];
1065     for (j = i + 1; j < dim; ++j) {
1066       Mpos[i * dim + j] = 0.5 * (Mp[i * dim + j] + Mp[j * dim + i]);
1067       Mpos[j * dim + i] = Mpos[i * dim + j];
1068     }
1069   }
1070 
1071   /* Compute eigendecomposition */
1072   if (dim == 1) {
1073     /* Isotropic case */
1074     eigs[0] = PetscRealPart(Mpos[0]);
1075     Mpos[0] = 1.0;
1076   } else {
1077     /* Anisotropic case */
1078     PetscScalar *work;
1079     PetscBLASInt lwork = (PetscBLASInt)(5 * dim);
1080 
1081     PetscCall(PetscMalloc1(5 * dim, &work));
1082     {
1083       PetscBLASInt lierr;
1084       PetscBLASInt nb;
1085 
1086       PetscCall(PetscBLASIntCast(dim, &nb));
1087       PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1088 #if defined(PETSC_USE_COMPLEX)
1089       {
1090         PetscReal *rwork;
1091         PetscCall(PetscMalloc1(3 * dim, &rwork));
1092         PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, Mpos, &nb, eigs, work, &lwork, rwork, &lierr));
1093         PetscCall(PetscFree(rwork));
1094       }
1095 #else
1096       PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, Mpos, &nb, eigs, work, &lwork, &lierr));
1097 #endif
1098       if (lierr) {
1099         for (i = 0; i < dim; ++i) {
1100           Mpos[i * dim + i] = Mp[i * dim + i];
1101           for (j = i + 1; j < dim; ++j) {
1102             Mpos[i * dim + j] = 0.5 * (Mp[i * dim + j] + Mp[j * dim + i]);
1103             Mpos[j * dim + i] = Mpos[i * dim + j];
1104           }
1105         }
1106         PetscCall(LAPACKsyevFail(dim, Mpos));
1107         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int)lierr);
1108       }
1109       PetscCall(PetscFPTrapPop());
1110     }
1111     PetscCall(PetscFree(work));
1112   }
1113 
1114   /* Reflect to positive orthant and enforce maximum and minimum size */
1115   max_eig = 0.0;
1116   for (i = 0; i < dim; ++i) {
1117     eigs[i] = PetscMin(l_max, PetscMax(l_min, PetscAbsReal(eigs[i])));
1118     max_eig = PetscMax(eigs[i], max_eig);
1119   }
1120 
1121   /* Enforce maximum anisotropy and compute determinant */
1122   *detMp = 1.0;
1123   for (i = 0; i < dim; ++i) {
1124     if (a_max >= 1.0) eigs[i] = PetscMax(eigs[i], max_eig * la_min);
1125     *detMp *= eigs[i];
1126   }
1127 
1128   /* Reconstruct Hessian */
1129   for (i = 0; i < dim; ++i) {
1130     for (j = 0; j < dim; ++j) {
1131       Mp[i * dim + j] = 0.0;
1132       for (k = 0; k < dim; ++k) Mp[i * dim + j] += Mpos[k * dim + i] * eigs[k] * Mpos[k * dim + j];
1133     }
1134   }
1135   PetscCall(PetscFree2(Mpos, eigs));
1136   PetscFunctionReturn(PETSC_SUCCESS);
1137 }
1138 
1139 /*@
1140   DMPlexMetricEnforceSPD - Enforce symmetric positive-definiteness of a metric
1141 
1142   Input Parameters:
1143 + dm                 - The `DM`
1144 . metricIn           - The metric
1145 . restrictSizes      - Should maximum/minimum metric magnitudes be enforced?
1146 - restrictAnisotropy - Should maximum anisotropy be enforced?
1147 
1148   Output Parameters:
1149 + metricOut   - The metric
1150 - determinant - Its determinant
1151 
1152   Options Database Keys:
1153 + -dm_plex_metric_isotropic - Is the metric isotropic?
1154 . -dm_plex_metric_uniform   - Is the metric uniform?
1155 . -dm_plex_metric_h_min     - Minimum tolerated metric magnitude
1156 . -dm_plex_metric_h_max     - Maximum tolerated metric magnitude
1157 - -dm_plex_metric_a_max     - Maximum tolerated anisotropy
1158 
1159   Level: beginner
1160 
1161 .seealso: `DMPLEX`, `DMPlexMetricNormalize()`, `DMPlexMetricIntersection()`
1162 @*/
1163 PetscErrorCode DMPlexMetricEnforceSPD(DM dm, Vec metricIn, PetscBool restrictSizes, PetscBool restrictAnisotropy, Vec metricOut, Vec determinant)
1164 {
1165   DM           dmDet;
1166   PetscBool    isotropic, uniform;
1167   PetscInt     dim, vStart, vEnd, v;
1168   PetscScalar *met, *det;
1169   PetscReal    h_min = 1.0e-30, h_max = 1.0e+30, a_max = 1.0e+15;
1170 
1171   PetscFunctionBegin;
1172   PetscCall(PetscLogEventBegin(DMPLEX_MetricEnforceSPD, 0, 0, 0, 0));
1173 
1174   /* Extract metadata from dm */
1175   PetscCall(DMGetDimension(dm, &dim));
1176   if (restrictSizes) {
1177     PetscCall(DMPlexMetricGetMinimumMagnitude(dm, &h_min));
1178     PetscCall(DMPlexMetricGetMaximumMagnitude(dm, &h_max));
1179     h_min = PetscMax(h_min, 1.0e-30);
1180     h_max = PetscMin(h_max, 1.0e+30);
1181     PetscCheck(h_min < h_max, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Minimum metric magnitude should be smaller than maximum metric magnitude");
1182   }
1183   if (restrictAnisotropy) {
1184     PetscCall(DMPlexMetricGetMaximumAnisotropy(dm, &a_max));
1185     a_max = PetscMin(a_max, 1.0e+30);
1186   }
1187 
1188   /* Setup output metric */
1189   PetscCall(VecCopy(metricIn, metricOut));
1190 
1191   /* Enforce SPD and extract determinant */
1192   PetscCall(VecGetArray(metricOut, &met));
1193   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1194   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
1195   if (uniform) {
1196     PetscCheck(isotropic, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform anisotropic metrics cannot exist");
1197 
1198     /* Uniform case */
1199     PetscCall(VecGetArray(determinant, &det));
1200     PetscCall(DMPlexMetricModify_Private(1, h_min, h_max, a_max, met, det));
1201     PetscCall(VecRestoreArray(determinant, &det));
1202   } else {
1203     /* Spatially varying case */
1204     PetscInt nrow;
1205 
1206     if (isotropic) nrow = 1;
1207     else nrow = dim;
1208     PetscCall(VecGetDM(determinant, &dmDet));
1209     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1210     PetscCall(VecGetArray(determinant, &det));
1211     for (v = vStart; v < vEnd; ++v) {
1212       PetscScalar *vmet, *vdet;
1213       PetscCall(DMPlexPointLocalRef(dm, v, met, &vmet));
1214       PetscCall(DMPlexPointLocalRef(dmDet, v, det, &vdet));
1215       PetscCall(DMPlexMetricModify_Private(nrow, h_min, h_max, a_max, vmet, vdet));
1216     }
1217     PetscCall(VecRestoreArray(determinant, &det));
1218   }
1219   PetscCall(VecRestoreArray(metricOut, &met));
1220 
1221   PetscCall(PetscLogEventEnd(DMPLEX_MetricEnforceSPD, 0, 0, 0, 0));
1222   PetscFunctionReturn(PETSC_SUCCESS);
1223 }
1224 
1225 static void detMFunc(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[])
1226 {
1227   const PetscScalar p = constants[0];
1228 
1229   f0[0] = PetscPowScalar(u[0], p / (2.0 * p + dim));
1230 }
1231 
1232 /*@
1233   DMPlexMetricNormalize - Apply L-p normalization to a metric
1234 
1235   Input Parameters:
1236 + dm                 - The `DM`
1237 . metricIn           - The unnormalized metric
1238 . restrictSizes      - Should maximum/minimum metric magnitudes be enforced?
1239 - restrictAnisotropy - Should maximum metric anisotropy be enforced?
1240 
1241   Output Parameters:
1242 + metricOut   - The normalized metric
1243 - determinant - computed determinant
1244 
1245   Options Database Keys:
1246 + -dm_plex_metric_isotropic                 - Is the metric isotropic?
1247 . -dm_plex_metric_uniform                   - Is the metric uniform?
1248 . -dm_plex_metric_restrict_anisotropy_first - Should anisotropy be restricted before normalization?
1249 . -dm_plex_metric_h_min                     - Minimum tolerated metric magnitude
1250 . -dm_plex_metric_h_max                     - Maximum tolerated metric magnitude
1251 . -dm_plex_metric_a_max                     - Maximum tolerated anisotropy
1252 . -dm_plex_metric_p                         - L-p normalization order
1253 - -dm_plex_metric_target_complexity         - Target metric complexity
1254 
1255   Level: beginner
1256 
1257 .seealso: `DMPLEX`, `DMPlexMetricEnforceSPD()`, `DMPlexMetricIntersection()`
1258 @*/
1259 PetscErrorCode DMPlexMetricNormalize(DM dm, Vec metricIn, PetscBool restrictSizes, PetscBool restrictAnisotropy, Vec metricOut, Vec determinant)
1260 {
1261   DM           dmDet;
1262   MPI_Comm     comm;
1263   PetscBool    restrictAnisotropyFirst, isotropic, uniform;
1264   PetscDS      ds;
1265   PetscInt     dim, Nd, vStart, vEnd, v, i;
1266   PetscScalar *met, *det, integral, constants[1];
1267   PetscReal    p, h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0, factGlob, fact, target, realIntegral;
1268 
1269   PetscFunctionBegin;
1270   PetscCall(PetscLogEventBegin(DMPLEX_MetricNormalize, 0, 0, 0, 0));
1271 
1272   /* Extract metadata from dm */
1273   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1274   PetscCall(DMGetDimension(dm, &dim));
1275   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1276   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
1277   if (isotropic) Nd = 1;
1278   else Nd = dim * dim;
1279 
1280   /* Set up metric and ensure it is SPD */
1281   PetscCall(DMPlexMetricRestrictAnisotropyFirst(dm, &restrictAnisotropyFirst));
1282   PetscCall(DMPlexMetricEnforceSPD(dm, metricIn, PETSC_FALSE, (PetscBool)(restrictAnisotropy && restrictAnisotropyFirst), metricOut, determinant));
1283 
1284   /* Compute global normalization factor */
1285   PetscCall(DMPlexMetricGetTargetComplexity(dm, &target));
1286   PetscCall(DMPlexMetricGetNormalizationOrder(dm, &p));
1287   constants[0] = p;
1288   if (uniform) {
1289     PetscCheck(isotropic, comm, PETSC_ERR_SUP, "Uniform anisotropic metrics not supported");
1290     DM  dmTmp;
1291     Vec tmp;
1292 
1293     PetscCall(DMClone(dm, &dmTmp));
1294     PetscCall(DMPlexP1FieldCreate_Private(dmTmp, 0, 1, &tmp));
1295     PetscCall(VecGetArray(determinant, &det));
1296     PetscCall(VecSet(tmp, det[0]));
1297     PetscCall(VecRestoreArray(determinant, &det));
1298     PetscCall(DMGetDS(dmTmp, &ds));
1299     PetscCall(PetscDSSetConstants(ds, 1, constants));
1300     PetscCall(PetscDSSetObjective(ds, 0, detMFunc));
1301     PetscCall(DMPlexComputeIntegralFEM(dmTmp, tmp, &integral, NULL));
1302     PetscCall(VecDestroy(&tmp));
1303     PetscCall(DMDestroy(&dmTmp));
1304   } else {
1305     PetscCall(VecGetDM(determinant, &dmDet));
1306     PetscCall(DMGetDS(dmDet, &ds));
1307     PetscCall(PetscDSSetConstants(ds, 1, constants));
1308     PetscCall(PetscDSSetObjective(ds, 0, detMFunc));
1309     PetscCall(DMPlexComputeIntegralFEM(dmDet, determinant, &integral, NULL));
1310   }
1311   realIntegral = PetscRealPart(integral);
1312   PetscCheck(realIntegral > 1.0e-30, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Global metric normalization factor must be in (0, inf). Is the input metric positive-definite?");
1313   factGlob = PetscPowReal(target / realIntegral, 2.0 / dim);
1314 
1315   /* Apply local scaling */
1316   if (restrictSizes) {
1317     PetscCall(DMPlexMetricGetMinimumMagnitude(dm, &h_min));
1318     PetscCall(DMPlexMetricGetMaximumMagnitude(dm, &h_max));
1319     h_min = PetscMax(h_min, 1.0e-30);
1320     h_max = PetscMin(h_max, 1.0e+30);
1321     PetscCheck(h_min < h_max, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Minimum metric magnitude should be smaller than maximum metric magnitude");
1322   }
1323   if (restrictAnisotropy && !restrictAnisotropyFirst) {
1324     PetscCall(DMPlexMetricGetMaximumAnisotropy(dm, &a_max));
1325     a_max = PetscMin(a_max, 1.0e+30);
1326   }
1327   PetscCall(VecGetArray(metricOut, &met));
1328   PetscCall(VecGetArray(determinant, &det));
1329   if (uniform) {
1330     /* Uniform case */
1331     met[0] *= factGlob * PetscPowReal(PetscAbsScalar(det[0]), -1.0 / (2 * p + dim));
1332     if (restrictSizes) PetscCall(DMPlexMetricModify_Private(1, h_min, h_max, a_max, met, det));
1333   } else {
1334     /* Spatially varying case */
1335     PetscInt nrow;
1336 
1337     if (isotropic) nrow = 1;
1338     else nrow = dim;
1339     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1340     PetscCall(VecGetDM(determinant, &dmDet));
1341     for (v = vStart; v < vEnd; ++v) {
1342       PetscScalar *Mp, *detM;
1343 
1344       PetscCall(DMPlexPointLocalRef(dm, v, met, &Mp));
1345       PetscCall(DMPlexPointLocalRef(dmDet, v, det, &detM));
1346       fact = factGlob * PetscPowReal(PetscAbsScalar(detM[0]), -1.0 / (2 * p + dim));
1347       for (i = 0; i < Nd; ++i) Mp[i] *= fact;
1348       if (restrictSizes) PetscCall(DMPlexMetricModify_Private(nrow, h_min, h_max, a_max, Mp, detM));
1349     }
1350   }
1351   PetscCall(VecRestoreArray(determinant, &det));
1352   PetscCall(VecRestoreArray(metricOut, &met));
1353 
1354   PetscCall(PetscLogEventEnd(DMPLEX_MetricNormalize, 0, 0, 0, 0));
1355   PetscFunctionReturn(PETSC_SUCCESS);
1356 }
1357 
1358 /*@
1359   DMPlexMetricAverage - Compute the average of a list of metrics
1360 
1361   Input Parameters:
1362 + dm         - The `DM`
1363 . numMetrics - The number of metrics to be averaged
1364 . weights    - Weights for the average
1365 - metrics    - The metrics to be averaged
1366 
1367   Output Parameter:
1368 . metricAvg - The averaged metric
1369 
1370   Level: beginner
1371 
1372   Notes:
1373   The weights should sum to unity.
1374 
1375   If weights are not provided then an unweighted average is used.
1376 
1377 .seealso: `DMPLEX`, `DMPlexMetricAverage2()`, `DMPlexMetricAverage3()`, `DMPlexMetricIntersection()`
1378 @*/
1379 PetscErrorCode DMPlexMetricAverage(DM dm, PetscInt numMetrics, PetscReal weights[], Vec metrics[], Vec metricAvg)
1380 {
1381   PetscBool haveWeights = PETSC_TRUE;
1382   PetscInt  i, m, n;
1383   PetscReal sum = 0.0, tol = 1.0e-10;
1384 
1385   PetscFunctionBegin;
1386   PetscCall(PetscLogEventBegin(DMPLEX_MetricAverage, 0, 0, 0, 0));
1387   PetscCheck(numMetrics >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot average %" PetscInt_FMT " < 1 metrics", numMetrics);
1388   PetscCall(VecSet(metricAvg, 0.0));
1389   PetscCall(VecGetSize(metricAvg, &m));
1390   for (i = 0; i < numMetrics; ++i) {
1391     PetscCall(VecGetSize(metrics[i], &n));
1392     PetscCheck(m == n, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Averaging different metric types not implemented");
1393   }
1394 
1395   /* Default to the unweighted case */
1396   if (!weights) {
1397     PetscCall(PetscMalloc1(numMetrics, &weights));
1398     haveWeights = PETSC_FALSE;
1399     for (i = 0; i < numMetrics; ++i) weights[i] = 1.0 / numMetrics;
1400   }
1401 
1402   /* Check weights sum to unity */
1403   for (i = 0; i < numMetrics; ++i) sum += weights[i];
1404   PetscCheck(PetscAbsReal(sum - 1) <= tol, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Weights do not sum to unity");
1405 
1406   /* Compute metric average */
1407   for (i = 0; i < numMetrics; ++i) PetscCall(VecAXPY(metricAvg, weights[i], metrics[i]));
1408   if (!haveWeights) PetscCall(PetscFree(weights));
1409 
1410   PetscCall(PetscLogEventEnd(DMPLEX_MetricAverage, 0, 0, 0, 0));
1411   PetscFunctionReturn(PETSC_SUCCESS);
1412 }
1413 
1414 /*@
1415   DMPlexMetricAverage2 - Compute the unweighted average of two metrics
1416 
1417   Input Parameters:
1418 + dm      - The `DM`
1419 . metric1 - The first metric to be averaged
1420 - metric2 - The second metric to be averaged
1421 
1422   Output Parameter:
1423 . metricAvg - The averaged metric
1424 
1425   Level: beginner
1426 
1427 .seealso: `DMPLEX`, `DMPlexMetricAverage()`, `DMPlexMetricAverage3()`
1428 @*/
1429 PetscErrorCode DMPlexMetricAverage2(DM dm, Vec metric1, Vec metric2, Vec metricAvg)
1430 {
1431   PetscReal weights[2] = {0.5, 0.5};
1432   Vec       metrics[2] = {metric1, metric2};
1433 
1434   PetscFunctionBegin;
1435   PetscCall(DMPlexMetricAverage(dm, 2, weights, metrics, metricAvg));
1436   PetscFunctionReturn(PETSC_SUCCESS);
1437 }
1438 
1439 /*@
1440   DMPlexMetricAverage3 - Compute the unweighted average of three metrics
1441 
1442   Input Parameters:
1443 + dm      - The `DM`
1444 . metric1 - The first metric to be averaged
1445 . metric2 - The second metric to be averaged
1446 - metric3 - The third metric to be averaged
1447 
1448   Output Parameter:
1449 . metricAvg - The averaged metric
1450 
1451   Level: beginner
1452 
1453 .seealso: `DMPLEX`, `DMPlexMetricAverage()`, `DMPlexMetricAverage2()`
1454 @*/
1455 PetscErrorCode DMPlexMetricAverage3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec metricAvg)
1456 {
1457   PetscReal weights[3] = {1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0};
1458   Vec       metrics[3] = {metric1, metric2, metric3};
1459 
1460   PetscFunctionBegin;
1461   PetscCall(DMPlexMetricAverage(dm, 3, weights, metrics, metricAvg));
1462   PetscFunctionReturn(PETSC_SUCCESS);
1463 }
1464 
1465 static PetscErrorCode DMPlexMetricIntersection_Private(PetscInt dim, PetscScalar M1[], PetscScalar M2[])
1466 {
1467   PetscInt     i, j, k, l, m;
1468   PetscReal   *evals;
1469   PetscScalar *evecs, *sqrtM1, *isqrtM1;
1470 
1471   PetscFunctionBegin;
1472   /* Isotropic case */
1473   if (dim == 1) {
1474     M2[0] = (PetscScalar)PetscMax(PetscRealPart(M1[0]), PetscRealPart(M2[0]));
1475     PetscFunctionReturn(PETSC_SUCCESS);
1476   }
1477 
1478   /* Anisotropic case */
1479   PetscCall(PetscMalloc4(dim * dim, &evecs, dim * dim, &sqrtM1, dim * dim, &isqrtM1, dim, &evals));
1480   for (i = 0; i < dim; ++i) {
1481     for (j = 0; j < dim; ++j) evecs[i * dim + j] = M1[i * dim + j];
1482   }
1483   {
1484     PetscScalar *work;
1485     PetscBLASInt lwork = (PetscBLASInt)(5 * dim);
1486     PetscCall(PetscMalloc1(5 * dim, &work));
1487     {
1488       PetscBLASInt lierr, nb;
1489       PetscReal    sqrtj;
1490 
1491       /* Compute eigendecomposition of M1 */
1492       PetscCall(PetscBLASIntCast(dim, &nb));
1493       PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1494 #if defined(PETSC_USE_COMPLEX)
1495       {
1496         PetscReal *rwork;
1497         PetscCall(PetscMalloc1(3 * dim, &rwork));
1498         PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
1499         PetscCall(PetscFree(rwork));
1500       }
1501 #else
1502       PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
1503 #endif
1504       if (lierr) {
1505         PetscCall(LAPACKsyevFail(dim, M1));
1506         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int)lierr);
1507       }
1508       PetscCall(PetscFPTrapPop());
1509 
1510       /* Compute square root and the reciprocal thereof */
1511       for (i = 0; i < dim; ++i) {
1512         for (k = 0; k < dim; ++k) {
1513           sqrtM1[i * dim + k]  = 0.0;
1514           isqrtM1[i * dim + k] = 0.0;
1515           for (j = 0; j < dim; ++j) {
1516             sqrtj = PetscSqrtReal(evals[j]);
1517             sqrtM1[i * dim + k] += evecs[j * dim + i] * sqrtj * evecs[j * dim + k];
1518             isqrtM1[i * dim + k] += evecs[j * dim + i] * (1.0 / sqrtj) * evecs[j * dim + k];
1519           }
1520         }
1521       }
1522 
1523       /* Map M2 into the space spanned by the eigenvectors of M1 */
1524       for (i = 0; i < dim; ++i) {
1525         for (l = 0; l < dim; ++l) {
1526           evecs[i * dim + l] = 0.0;
1527           for (j = 0; j < dim; ++j) {
1528             for (k = 0; k < dim; ++k) evecs[i * dim + l] += isqrtM1[j * dim + i] * M2[j * dim + k] * isqrtM1[k * dim + l];
1529           }
1530         }
1531       }
1532 
1533       /* Compute eigendecomposition */
1534       PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1535 #if defined(PETSC_USE_COMPLEX)
1536       {
1537         PetscReal *rwork;
1538         PetscCall(PetscMalloc1(3 * dim, &rwork));
1539         PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
1540         PetscCall(PetscFree(rwork));
1541       }
1542 #else
1543       PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
1544 #endif
1545       if (lierr) {
1546         for (i = 0; i < dim; ++i) {
1547           for (l = 0; l < dim; ++l) {
1548             evecs[i * dim + l] = 0.0;
1549             for (j = 0; j < dim; ++j) {
1550               for (k = 0; k < dim; ++k) evecs[i * dim + l] += isqrtM1[j * dim + i] * M2[j * dim + k] * isqrtM1[k * dim + l];
1551             }
1552           }
1553         }
1554         PetscCall(LAPACKsyevFail(dim, evecs));
1555         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int)lierr);
1556       }
1557       PetscCall(PetscFPTrapPop());
1558 
1559       /* Modify eigenvalues */
1560       for (i = 0; i < dim; ++i) evals[i] = PetscMax(evals[i], 1.0);
1561 
1562       /* Map back to get the intersection */
1563       for (i = 0; i < dim; ++i) {
1564         for (m = 0; m < dim; ++m) {
1565           M2[i * dim + m] = 0.0;
1566           for (j = 0; j < dim; ++j) {
1567             for (k = 0; k < dim; ++k) {
1568               for (l = 0; l < dim; ++l) M2[i * dim + m] += sqrtM1[j * dim + i] * evecs[j * dim + k] * evals[k] * evecs[l * dim + k] * sqrtM1[l * dim + m];
1569             }
1570           }
1571         }
1572       }
1573     }
1574     PetscCall(PetscFree(work));
1575   }
1576   PetscCall(PetscFree4(evecs, sqrtM1, isqrtM1, evals));
1577   PetscFunctionReturn(PETSC_SUCCESS);
1578 }
1579 
1580 /*@
1581   DMPlexMetricIntersection - Compute the intersection of a list of metrics
1582 
1583   Input Parameters:
1584 + dm         - The `DM`
1585 . numMetrics - The number of metrics to be intersected
1586 - metrics    - The metrics to be intersected
1587 
1588   Output Parameter:
1589 . metricInt - The intersected metric
1590 
1591   Level: beginner
1592 
1593   Notes:
1594   The intersection of a list of metrics has the minimal ellipsoid which fits within the ellipsoids of the component metrics.
1595 
1596   The implementation used here is only consistent with the minimal ellipsoid definition in the case numMetrics = 2.
1597 
1598 .seealso: `DMPLEX`, `DMPlexMetricIntersection2()`, `DMPlexMetricIntersection3()`, `DMPlexMetricAverage()`
1599 @*/
1600 PetscErrorCode DMPlexMetricIntersection(DM dm, PetscInt numMetrics, Vec metrics[], Vec metricInt)
1601 {
1602   PetscBool    isotropic, uniform;
1603   PetscInt     v, i, m, n;
1604   PetscScalar *met, *meti;
1605 
1606   PetscFunctionBegin;
1607   PetscCall(PetscLogEventBegin(DMPLEX_MetricIntersection, 0, 0, 0, 0));
1608   PetscCheck(numMetrics >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot intersect %" PetscInt_FMT " < 1 metrics", numMetrics);
1609 
1610   /* Copy over the first metric */
1611   PetscCall(VecCopy(metrics[0], metricInt));
1612   if (numMetrics == 1) PetscFunctionReturn(PETSC_SUCCESS);
1613   PetscCall(VecGetSize(metricInt, &m));
1614   for (i = 0; i < numMetrics; ++i) {
1615     PetscCall(VecGetSize(metrics[i], &n));
1616     PetscCheck(m == n, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Intersecting different metric types not implemented");
1617   }
1618 
1619   /* Intersect subsequent metrics in turn */
1620   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1621   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
1622   if (uniform) {
1623     /* Uniform case */
1624     PetscCall(VecGetArray(metricInt, &met));
1625     for (i = 1; i < numMetrics; ++i) {
1626       PetscCall(VecGetArray(metrics[i], &meti));
1627       PetscCall(DMPlexMetricIntersection_Private(1, meti, met));
1628       PetscCall(VecRestoreArray(metrics[i], &meti));
1629     }
1630     PetscCall(VecRestoreArray(metricInt, &met));
1631   } else {
1632     /* Spatially varying case */
1633     PetscInt     dim, vStart, vEnd, nrow;
1634     PetscScalar *M, *Mi;
1635 
1636     PetscCall(DMGetDimension(dm, &dim));
1637     if (isotropic) nrow = 1;
1638     else nrow = dim;
1639     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1640     PetscCall(VecGetArray(metricInt, &met));
1641     for (i = 1; i < numMetrics; ++i) {
1642       PetscCall(VecGetArray(metrics[i], &meti));
1643       for (v = vStart; v < vEnd; ++v) {
1644         PetscCall(DMPlexPointLocalRef(dm, v, met, &M));
1645         PetscCall(DMPlexPointLocalRef(dm, v, meti, &Mi));
1646         PetscCall(DMPlexMetricIntersection_Private(nrow, Mi, M));
1647       }
1648       PetscCall(VecRestoreArray(metrics[i], &meti));
1649     }
1650     PetscCall(VecRestoreArray(metricInt, &met));
1651   }
1652 
1653   PetscCall(PetscLogEventEnd(DMPLEX_MetricIntersection, 0, 0, 0, 0));
1654   PetscFunctionReturn(PETSC_SUCCESS);
1655 }
1656 
1657 /*@
1658   DMPlexMetricIntersection2 - Compute the intersection of two metrics
1659 
1660   Input Parameters:
1661 + dm      - The `DM`
1662 . metric1 - The first metric to be intersected
1663 - metric2 - The second metric to be intersected
1664 
1665   Output Parameter:
1666 . metricInt - The intersected metric
1667 
1668   Level: beginner
1669 
1670 .seealso: `DMPLEX`, `DMPlexMetricIntersection()`, `DMPlexMetricIntersection3()`
1671 @*/
1672 PetscErrorCode DMPlexMetricIntersection2(DM dm, Vec metric1, Vec metric2, Vec metricInt)
1673 {
1674   Vec metrics[2] = {metric1, metric2};
1675 
1676   PetscFunctionBegin;
1677   PetscCall(DMPlexMetricIntersection(dm, 2, metrics, metricInt));
1678   PetscFunctionReturn(PETSC_SUCCESS);
1679 }
1680 
1681 /*@
1682   DMPlexMetricIntersection3 - Compute the intersection of three metrics
1683 
1684   Input Parameters:
1685 + dm      - The `DM`
1686 . metric1 - The first metric to be intersected
1687 . metric2 - The second metric to be intersected
1688 - metric3 - The third metric to be intersected
1689 
1690   Output Parameter:
1691 . metricInt - The intersected metric
1692 
1693   Level: beginner
1694 
1695 .seealso: `DMPLEX`, `DMPlexMetricIntersection()`, `DMPlexMetricIntersection2()`
1696 @*/
1697 PetscErrorCode DMPlexMetricIntersection3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec metricInt)
1698 {
1699   Vec metrics[3] = {metric1, metric2, metric3};
1700 
1701   PetscFunctionBegin;
1702   PetscCall(DMPlexMetricIntersection(dm, 3, metrics, metricInt));
1703   PetscFunctionReturn(PETSC_SUCCESS);
1704 }
1705