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