Actual source code: plexmetric.c

  1: #include <petsc/private/dmpleximpl.h>
  2: #include <petscblaslapack.h>

  4: PetscLogEvent DMPLEX_MetricEnforceSPD, DMPLEX_MetricNormalize, DMPLEX_MetricAverage, DMPLEX_MetricIntersection;

  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;

 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: }

 55: /*@
 56:   DMPlexMetricSetIsotropic - Record whether a metric is isotropic

 58:   Input Parameters:
 59: + dm        - The `DM`
 60: - isotropic - Is the metric isotropic?

 62:   Level: beginner

 64: .seealso: `DMPLEX`, `DMPlexMetricIsIsotropic()`, `DMPlexMetricSetUniform()`, `DMPlexMetricSetRestrictAnisotropyFirst()`
 65: @*/
 66: PetscErrorCode DMPlexMetricSetIsotropic(DM dm, PetscBool isotropic)
 67: {
 68:   DM_Plex *plex = (DM_Plex *)dm->data;

 70:   PetscFunctionBegin;
 71:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
 72:   plex->metricCtx->isotropic = isotropic;
 73:   PetscFunctionReturn(PETSC_SUCCESS);
 74: }

 76: /*@
 77:   DMPlexMetricIsIsotropic - Is a metric isotropic?

 79:   Input Parameters:
 80: . dm - The `DM`

 82:   Output Parameters:
 83: . isotropic - Is the metric isotropic?

 85:   Level: beginner

 87: .seealso: `DMPLEX`, `DMPlexMetricSetIsotropic()`, `DMPlexMetricIsUniform()`, `DMPlexMetricRestrictAnisotropyFirst()`
 88: @*/
 89: PetscErrorCode DMPlexMetricIsIsotropic(DM dm, PetscBool *isotropic)
 90: {
 91:   DM_Plex *plex = (DM_Plex *)dm->data;

 93:   PetscFunctionBegin;
 94:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
 95:   *isotropic = plex->metricCtx->isotropic;
 96:   PetscFunctionReturn(PETSC_SUCCESS);
 97: }

 99: /*@
100:   DMPlexMetricSetUniform - Record whether a metric is uniform

102:   Input Parameters:
103: + dm      - The `DM`
104: - uniform - Is the metric uniform?

106:   Level: beginner

108:   Note:
109:   If the metric is specified as uniform then it is assumed to be isotropic, too.

111: .seealso: `DMPLEX`, `DMPlexMetricIsUniform()`, `DMPlexMetricSetIsotropic()`, `DMPlexMetricSetRestrictAnisotropyFirst()`
112: @*/
113: PetscErrorCode DMPlexMetricSetUniform(DM dm, PetscBool uniform)
114: {
115:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

124: /*@
125:   DMPlexMetricIsUniform - Is a metric uniform?

127:   Input Parameters:
128: . dm - The `DM`

130:   Output Parameters:
131: . uniform - Is the metric uniform?

133:   Level: beginner

135: .seealso: `DMPLEX`, `DMPlexMetricSetUniform()`, `DMPlexMetricIsIsotropic()`, `DMPlexMetricRestrictAnisotropyFirst()`
136: @*/
137: PetscErrorCode DMPlexMetricIsUniform(DM dm, PetscBool *uniform)
138: {
139:   DM_Plex *plex = (DM_Plex *)dm->data;

141:   PetscFunctionBegin;
142:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
143:   *uniform = plex->metricCtx->uniform;
144:   PetscFunctionReturn(PETSC_SUCCESS);
145: }

147: /*@
148:   DMPlexMetricSetRestrictAnisotropyFirst - Record whether anisotropy should be restricted before normalization

150:   Input Parameters:
151: + dm                      - The `DM`
152: - restrictAnisotropyFirst - Should anisotropy be normalized first?

154:   Level: beginner

156: .seealso: `DMPLEX`, `DMPlexMetricSetIsotropic()`, `DMPlexMetricRestrictAnisotropyFirst()`
157: @*/
158: PetscErrorCode DMPlexMetricSetRestrictAnisotropyFirst(DM dm, PetscBool restrictAnisotropyFirst)
159: {
160:   DM_Plex *plex = (DM_Plex *)dm->data;

162:   PetscFunctionBegin;
163:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
164:   plex->metricCtx->restrictAnisotropyFirst = restrictAnisotropyFirst;
165:   PetscFunctionReturn(PETSC_SUCCESS);
166: }

168: /*@
169:   DMPlexMetricRestrictAnisotropyFirst - Is anisotropy restricted before normalization or after?

171:   Input Parameters:
172: . dm - The `DM`

174:   Output Parameters:
175: . restrictAnisotropyFirst - Is anisotropy be normalized first?

177:   Level: beginner

179: .seealso: `DMPLEX`, `DMPlexMetricIsIsotropic()`, `DMPlexMetricSetRestrictAnisotropyFirst()`
180: @*/
181: PetscErrorCode DMPlexMetricRestrictAnisotropyFirst(DM dm, PetscBool *restrictAnisotropyFirst)
182: {
183:   DM_Plex *plex = (DM_Plex *)dm->data;

185:   PetscFunctionBegin;
186:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
187:   *restrictAnisotropyFirst = plex->metricCtx->restrictAnisotropyFirst;
188:   PetscFunctionReturn(PETSC_SUCCESS);
189: }

191: /*@
192:   DMPlexMetricSetNoInsertion - Should node insertion and deletion be turned off?

194:   Input Parameters:
195: + dm       - The `DM`
196: - noInsert - Should node insertion and deletion be turned off?

198:   Level: beginner

200:   Note:
201:   This is only used by Mmg and ParMmg (not Pragmatic).

203: .seealso: `DMPLEX`, `DMPlexMetricNoInsertion()`, `DMPlexMetricSetNoSwapping()`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricSetNoSurf()`
204: @*/
205: PetscErrorCode DMPlexMetricSetNoInsertion(DM dm, PetscBool noInsert)
206: {
207:   DM_Plex *plex = (DM_Plex *)dm->data;

209:   PetscFunctionBegin;
210:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
211:   plex->metricCtx->noInsert = noInsert;
212:   PetscFunctionReturn(PETSC_SUCCESS);
213: }

215: /*@
216:   DMPlexMetricNoInsertion - Are node insertion and deletion turned off?

218:   Input Parameters:
219: . dm - The `DM`

221:   Output Parameters:
222: . noInsert - Are node insertion and deletion turned off?

224:   Level: beginner

226:   Note:
227:   This is only used by Mmg and ParMmg (not Pragmatic).

229: .seealso: `DMPLEX`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricNoSwapping()`, `DMPlexMetricNoMovement()`, `DMPlexMetricNoSurf()`
230: @*/
231: PetscErrorCode DMPlexMetricNoInsertion(DM dm, PetscBool *noInsert)
232: {
233:   DM_Plex *plex = (DM_Plex *)dm->data;

235:   PetscFunctionBegin;
236:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
237:   *noInsert = plex->metricCtx->noInsert;
238:   PetscFunctionReturn(PETSC_SUCCESS);
239: }

241: /*@
242:   DMPlexMetricSetNoSwapping - Should facet swapping be turned off?

244:   Input Parameters:
245: + dm     - The `DM`
246: - noSwap - Should facet swapping be turned off?

248:   Level: beginner

250:   Note:
251:   This is only used by Mmg and ParMmg (not Pragmatic).

253: .seealso: `DMPLEX`, `DMPlexMetricNoSwapping()`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricSetNoSurf()`
254: @*/
255: PetscErrorCode DMPlexMetricSetNoSwapping(DM dm, PetscBool noSwap)
256: {
257:   DM_Plex *plex = (DM_Plex *)dm->data;

259:   PetscFunctionBegin;
260:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
261:   plex->metricCtx->noSwap = noSwap;
262:   PetscFunctionReturn(PETSC_SUCCESS);
263: }

265: /*@
266:   DMPlexMetricNoSwapping - Is facet swapping turned off?

268:   Input Parameters:
269: . dm - The `DM`

271:   Output Parameters:
272: . noSwap - Is facet swapping turned off?

274:   Level: beginner

276:   Note:
277:   This is only used by Mmg and ParMmg (not Pragmatic).

279: .seealso: `DMPLEX`, `DMPlexMetricSetNoSwapping()`, `DMPlexMetricNoInsertion()`, `DMPlexMetricNoMovement()`, `DMPlexMetricNoSurf()`
280: @*/
281: PetscErrorCode DMPlexMetricNoSwapping(DM dm, PetscBool *noSwap)
282: {
283:   DM_Plex *plex = (DM_Plex *)dm->data;

285:   PetscFunctionBegin;
286:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
287:   *noSwap = plex->metricCtx->noSwap;
288:   PetscFunctionReturn(PETSC_SUCCESS);
289: }

291: /*@
292:   DMPlexMetricSetNoMovement - Should node movement be turned off?

294:   Input Parameters:
295: + dm     - The `DM`
296: - noMove - Should node movement be turned off?

298:   Level: beginner

300:   Note:
301:   This is only used by Mmg and ParMmg (not Pragmatic).

303: .seealso: `DMPLEX`, `DMPlexMetricNoMovement()`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricSetNoSwapping()`, `DMPlexMetricSetNoSurf()`
304: @*/
305: PetscErrorCode DMPlexMetricSetNoMovement(DM dm, PetscBool noMove)
306: {
307:   DM_Plex *plex = (DM_Plex *)dm->data;

309:   PetscFunctionBegin;
310:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
311:   plex->metricCtx->noMove = noMove;
312:   PetscFunctionReturn(PETSC_SUCCESS);
313: }

315: /*@
316:   DMPlexMetricNoMovement - Is node movement turned off?

318:   Input Parameters:
319: . dm - The `DM`

321:   Output Parameters:
322: . noMove - Is node movement turned off?

324:   Level: beginner

326:   Note:
327:   This is only used by Mmg and ParMmg (not Pragmatic).

329: .seealso: `DMPLEX`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricNoInsertion()`, `DMPlexMetricNoSwapping()`, `DMPlexMetricNoSurf()`
330: @*/
331: PetscErrorCode DMPlexMetricNoMovement(DM dm, PetscBool *noMove)
332: {
333:   DM_Plex *plex = (DM_Plex *)dm->data;

335:   PetscFunctionBegin;
336:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
337:   *noMove = plex->metricCtx->noMove;
338:   PetscFunctionReturn(PETSC_SUCCESS);
339: }

341: /*@
342:   DMPlexMetricSetNoSurf - Should surface modification be turned off?

344:   Input Parameters:
345: + dm     - The `DM`
346: - noSurf - Should surface modification be turned off?

348:   Level: beginner

350:   Note:
351:   This is only used by Mmg and ParMmg (not Pragmatic).

353: .seealso: `DMPLEX`, `DMPlexMetricNoSurf()`, `DMPlexMetricSetNoMovement()`, `DMPlexMetricSetNoInsertion()`, `DMPlexMetricSetNoSwapping()`
354: @*/
355: PetscErrorCode DMPlexMetricSetNoSurf(DM dm, PetscBool noSurf)
356: {
357:   DM_Plex *plex = (DM_Plex *)dm->data;

359:   PetscFunctionBegin;
360:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
361:   plex->metricCtx->noSurf = noSurf;
362:   PetscFunctionReturn(PETSC_SUCCESS);
363: }

365: /*@
366:   DMPlexMetricNoSurf - Is surface modification turned off?

368:   Input Parameters:
369: . dm - The `DM`

371:   Output Parameters:
372: . noSurf - Is surface modification turned off?

374:   Level: beginner

376:   Note:
377:   This is only used by Mmg and ParMmg (not Pragmatic).

379: .seealso: `DMPLEX`, `DMPlexMetricSetNoSurf()`, `DMPlexMetricNoMovement()`, `DMPlexMetricNoInsertion()`, `DMPlexMetricNoSwapping()`
380: @*/
381: PetscErrorCode DMPlexMetricNoSurf(DM dm, PetscBool *noSurf)
382: {
383:   DM_Plex *plex = (DM_Plex *)dm->data;

385:   PetscFunctionBegin;
386:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
387:   *noSurf = plex->metricCtx->noSurf;
388:   PetscFunctionReturn(PETSC_SUCCESS);
389: }

391: /*@
392:   DMPlexMetricSetMinimumMagnitude - Set the minimum tolerated metric magnitude

394:   Input Parameters:
395: + dm    - The `DM`
396: - h_min - The minimum tolerated metric magnitude

398:   Level: beginner

400: .seealso: `DMPLEX`, `DMPlexMetricGetMinimumMagnitude()`, `DMPlexMetricSetMaximumMagnitude()`
401: @*/
402: PetscErrorCode DMPlexMetricSetMinimumMagnitude(DM dm, PetscReal h_min)
403: {
404:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

413: /*@
414:   DMPlexMetricGetMinimumMagnitude - Get the minimum tolerated metric magnitude

416:   Input Parameters:
417: . dm - The `DM`

419:   Output Parameters:
420: . h_min - The minimum tolerated metric magnitude

422:   Level: beginner

424: .seealso: `DMPLEX`, `DMPlexMetricSetMinimumMagnitude()`, `DMPlexMetricGetMaximumMagnitude()`
425: @*/
426: PetscErrorCode DMPlexMetricGetMinimumMagnitude(DM dm, PetscReal *h_min)
427: {
428:   DM_Plex *plex = (DM_Plex *)dm->data;

430:   PetscFunctionBegin;
431:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
432:   *h_min = plex->metricCtx->h_min;
433:   PetscFunctionReturn(PETSC_SUCCESS);
434: }

436: /*@
437:   DMPlexMetricSetMaximumMagnitude - Set the maximum tolerated metric magnitude

439:   Input Parameters:
440: + dm    - The `DM`
441: - h_max - The maximum tolerated metric magnitude

443:   Level: beginner

445: .seealso: `DMPLEX`, `DMPlexMetricGetMaximumMagnitude()`, `DMPlexMetricSetMinimumMagnitude()`
446: @*/
447: PetscErrorCode DMPlexMetricSetMaximumMagnitude(DM dm, PetscReal h_max)
448: {
449:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

458: /*@
459:   DMPlexMetricGetMaximumMagnitude - Get the maximum tolerated metric magnitude

461:   Input Parameters:
462: . dm - The `DM`

464:   Output Parameters:
465: . h_max - The maximum tolerated metric magnitude

467:   Level: beginner

469: .seealso: `DMPLEX`, `DMPlexMetricSetMaximumMagnitude()`, `DMPlexMetricGetMinimumMagnitude()`
470: @*/
471: PetscErrorCode DMPlexMetricGetMaximumMagnitude(DM dm, PetscReal *h_max)
472: {
473:   DM_Plex *plex = (DM_Plex *)dm->data;

475:   PetscFunctionBegin;
476:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
477:   *h_max = plex->metricCtx->h_max;
478:   PetscFunctionReturn(PETSC_SUCCESS);
479: }

481: /*@
482:   DMPlexMetricSetMaximumAnisotropy - Set the maximum tolerated metric anisotropy

484:   Input Parameters:
485: + dm    - The `DM`
486: - a_max - The maximum tolerated metric anisotropy

488:   Level: beginner

490:   Note:
491:   If the value zero is given then anisotropy will not be restricted. Otherwise, it should be at least one.

493: .seealso: `DMPLEX`, `DMPlexMetricGetMaximumAnisotropy()`, `DMPlexMetricSetMaximumMagnitude()`
494: @*/
495: PetscErrorCode DMPlexMetricSetMaximumAnisotropy(DM dm, PetscReal a_max)
496: {
497:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

506: /*@
507:   DMPlexMetricGetMaximumAnisotropy - Get the maximum tolerated metric anisotropy

509:   Input Parameters:
510: . dm - The `DM`

512:   Output Parameters:
513: . a_max - The maximum tolerated metric anisotropy

515:   Level: beginner

517: .seealso: `DMPLEX`, `DMPlexMetricSetMaximumAnisotropy()`, `DMPlexMetricGetMaximumMagnitude()`
518: @*/
519: PetscErrorCode DMPlexMetricGetMaximumAnisotropy(DM dm, PetscReal *a_max)
520: {
521:   DM_Plex *plex = (DM_Plex *)dm->data;

523:   PetscFunctionBegin;
524:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
525:   *a_max = plex->metricCtx->a_max;
526:   PetscFunctionReturn(PETSC_SUCCESS);
527: }

529: /*@
530:   DMPlexMetricSetTargetComplexity - Set the target metric complexity

532:   Input Parameters:
533: + dm               - The `DM`
534: - targetComplexity - The target metric complexity

536:   Level: beginner

538: .seealso: `DMPLEX`, `DMPlexMetricGetTargetComplexity()`, `DMPlexMetricSetNormalizationOrder()`
539: @*/
540: PetscErrorCode DMPlexMetricSetTargetComplexity(DM dm, PetscReal targetComplexity)
541: {
542:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

551: /*@
552:   DMPlexMetricGetTargetComplexity - Get the target metric complexity

554:   Input Parameters:
555: . dm - The `DM`

557:   Output Parameters:
558: . targetComplexity - The target metric complexity

560:   Level: beginner

562: .seealso: `DMPLEX`, `DMPlexMetricSetTargetComplexity()`, `DMPlexMetricGetNormalizationOrder()`
563: @*/
564: PetscErrorCode DMPlexMetricGetTargetComplexity(DM dm, PetscReal *targetComplexity)
565: {
566:   DM_Plex *plex = (DM_Plex *)dm->data;

568:   PetscFunctionBegin;
569:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
570:   *targetComplexity = plex->metricCtx->targetComplexity;
571:   PetscFunctionReturn(PETSC_SUCCESS);
572: }

574: /*@
575:   DMPlexMetricSetNormalizationOrder - Set the order p for L-p normalization

577:   Input Parameters:
578: + dm - The `DM`
579: - p  - The normalization order

581:   Level: beginner

583: .seealso: `DMPLEX`, `DMPlexMetricGetNormalizationOrder()`, `DMPlexMetricSetTargetComplexity()`
584: @*/
585: PetscErrorCode DMPlexMetricSetNormalizationOrder(DM dm, PetscReal p)
586: {
587:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

596: /*@
597:   DMPlexMetricGetNormalizationOrder - Get the order p for L-p normalization

599:   Input Parameters:
600: . dm - The `DM`

602:   Output Parameters:
603: . p - The normalization order

605:   Level: beginner

607: .seealso: `DMPLEX`, `DMPlexMetricSetNormalizationOrder()`, `DMPlexMetricGetTargetComplexity()`
608: @*/
609: PetscErrorCode DMPlexMetricGetNormalizationOrder(DM dm, PetscReal *p)
610: {
611:   DM_Plex *plex = (DM_Plex *)dm->data;

613:   PetscFunctionBegin;
614:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
615:   *p = plex->metricCtx->p;
616:   PetscFunctionReturn(PETSC_SUCCESS);
617: }

619: /*@
620:   DMPlexMetricSetGradationFactor - Set the metric gradation factor

622:   Input Parameters:
623: + dm   - The `DM`
624: - beta - The metric gradation factor

626:   Level: beginner

628:   Notes:
629:   The gradation factor is the maximum tolerated length ratio between adjacent edges.

631:   Turn off gradation by passing the value -1. Otherwise, pass a positive value.

633:   This is only used by Mmg and ParMmg (not Pragmatic).

635: .seealso: `DMPLEX`, `DMPlexMetricGetGradationFactor()`, `DMPlexMetricSetHausdorffNumber()`
636: @*/
637: PetscErrorCode DMPlexMetricSetGradationFactor(DM dm, PetscReal beta)
638: {
639:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

648: /*@
649:   DMPlexMetricGetGradationFactor - Get the metric gradation factor

651:   Input Parameters:
652: . dm - The `DM`

654:   Output Parameters:
655: . beta - The metric gradation factor

657:   Level: beginner

659:   Notes:
660:   The gradation factor is the maximum tolerated length ratio between adjacent edges.

662:   The value -1 implies that gradation is turned off.

664:   This is only used by Mmg and ParMmg (not Pragmatic).

666: .seealso: `DMPLEX`, `DMPlexMetricSetGradationFactor()`, `DMPlexMetricGetHausdorffNumber()`
667: @*/
668: PetscErrorCode DMPlexMetricGetGradationFactor(DM dm, PetscReal *beta)
669: {
670:   DM_Plex *plex = (DM_Plex *)dm->data;

672:   PetscFunctionBegin;
673:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
674:   *beta = plex->metricCtx->gradationFactor;
675:   PetscFunctionReturn(PETSC_SUCCESS);
676: }

678: /*@
679:   DMPlexMetricSetHausdorffNumber - Set the metric Hausdorff number

681:   Input Parameters:
682: + dm    - The `DM`
683: - hausd - The metric Hausdorff number

685:   Level: beginner

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).

695:   This is only used by Mmg and ParMmg (not Pragmatic).

697: .seealso: `DMPLEX`, `DMPlexMetricSetGradationFactor()`, `DMPlexMetricGetHausdorffNumber()`
698: @*/
699: PetscErrorCode DMPlexMetricSetHausdorffNumber(DM dm, PetscReal hausd)
700: {
701:   DM_Plex *plex = (DM_Plex *)dm->data;

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: }

710: /*@
711:   DMPlexMetricGetHausdorffNumber - Get the metric Hausdorff number

713:   Input Parameters:
714: . dm - The `DM`

716:   Output Parameters:
717: . hausd - The metric Hausdorff number

719:   Level: beginner

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).

729:   This is only used by Mmg and ParMmg (not Pragmatic).

731: .seealso: `DMPLEX`, `DMPlexMetricGetGradationFactor()`, `DMPlexMetricSetHausdorffNumber()`
732: @*/
733: PetscErrorCode DMPlexMetricGetHausdorffNumber(DM dm, PetscReal *hausd)
734: {
735:   DM_Plex *plex = (DM_Plex *)dm->data;

737:   PetscFunctionBegin;
738:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
739:   *hausd = plex->metricCtx->hausdorffNumber;
740:   PetscFunctionReturn(PETSC_SUCCESS);
741: }

743: /*@
744:   DMPlexMetricSetVerbosity - Set the verbosity of the mesh adaptation package

746:   Input Parameters:
747: + dm        - The `DM`
748: - verbosity - The verbosity, where -1 is silent and 10 is maximum

750:   Level: beginner

752:   Note:
753:   This is only used by Mmg and ParMmg (not Pragmatic).

755: .seealso: `DMPLEX`, `DMPlexMetricGetVerbosity()`, `DMPlexMetricSetNumIterations()`
756: @*/
757: PetscErrorCode DMPlexMetricSetVerbosity(DM dm, PetscInt verbosity)
758: {
759:   DM_Plex *plex = (DM_Plex *)dm->data;

761:   PetscFunctionBegin;
762:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
763:   plex->metricCtx->verbosity = verbosity;
764:   PetscFunctionReturn(PETSC_SUCCESS);
765: }

767: /*@
768:   DMPlexMetricGetVerbosity - Get the verbosity of the mesh adaptation package

770:   Input Parameters:
771: . dm - The `DM`

773:   Output Parameters:
774: . verbosity - The verbosity, where -1 is silent and 10 is maximum

776:   Level: beginner

778:   Note:
779:   This is only used by Mmg and ParMmg (not Pragmatic).

781: .seealso: `DMPLEX`, `DMPlexMetricSetVerbosity()`, `DMPlexMetricGetNumIterations()`
782: @*/
783: PetscErrorCode DMPlexMetricGetVerbosity(DM dm, PetscInt *verbosity)
784: {
785:   DM_Plex *plex = (DM_Plex *)dm->data;

787:   PetscFunctionBegin;
788:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
789:   *verbosity = plex->metricCtx->verbosity;
790:   PetscFunctionReturn(PETSC_SUCCESS);
791: }

793: /*@
794:   DMPlexMetricSetNumIterations - Set the number of parallel adaptation iterations

796:   Input Parameters:
797: + dm      - The `DM`
798: - numIter - the number of parallel adaptation iterations

800:   Level: beginner

802:   Note:
803:   This is only used by ParMmg (not Pragmatic or Mmg).

805: .seealso: `DMPLEX`, `DMPlexMetricSetVerbosity()`, `DMPlexMetricGetNumIterations()`
806: @*/
807: PetscErrorCode DMPlexMetricSetNumIterations(DM dm, PetscInt numIter)
808: {
809:   DM_Plex *plex = (DM_Plex *)dm->data;

811:   PetscFunctionBegin;
812:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
813:   plex->metricCtx->numIter = numIter;
814:   PetscFunctionReturn(PETSC_SUCCESS);
815: }

817: /*@
818:   DMPlexMetricGetNumIterations - Get the number of parallel adaptation iterations

820:   Input Parameters:
821: . dm - The `DM`

823:   Output Parameters:
824: . numIter - the number of parallel adaptation iterations

826:   Level: beginner

828:   Note:
829:   This is only used by Mmg and ParMmg (not Pragmatic or Mmg).

831: .seealso: `DMPLEX`, `DMPlexMetricSetNumIterations()`, `DMPlexMetricGetVerbosity()`
832: @*/
833: PetscErrorCode DMPlexMetricGetNumIterations(DM dm, PetscInt *numIter)
834: {
835:   DM_Plex *plex = (DM_Plex *)dm->data;

837:   PetscFunctionBegin;
838:   if (!plex->metricCtx) PetscCall(DMPlexMetricSetFromOptions(dm));
839:   *numIter = plex->metricCtx->numIter;
840:   PetscFunctionReturn(PETSC_SUCCESS);
841: }

843: static PetscErrorCode DMPlexP1FieldCreate_Private(DM dm, PetscInt f, PetscInt size, Vec *metric)
844: {
845:   MPI_Comm comm;
846:   PetscFE  fe;
847:   PetscInt dim;

849:   PetscFunctionBegin;
850:   /* Extract metadata from dm */
851:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
852:   PetscCall(DMGetDimension(dm, &dim));

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: }

863: /*@
864:   DMPlexMetricCreate - Create a Riemannian metric field

866:   Input Parameters:
867: + dm - The `DM`
868: - f  - The field number to use

870:   Output Parameter:
871: . metric - The metric

873:   Options Database Key:
874: . -dm_adaptor <pragmatic/mmg/parmmg> - specify dm adaptor to use

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).

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

894:   Level: beginner

896:   Note:
897:   It is assumed that the `DM` is comprised of simplices.

899: .seealso: `DMPLEX`, `DMPlexMetricCreateUniform()`, `DMPlexMetricCreateIsotropic()`
900: @*/
901: PetscErrorCode DMPlexMetricCreate(DM dm, PetscInt f, Vec *metric)
902: {
903:   PetscBool isotropic, uniform;
904:   PetscInt  coordDim, Nd;

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;

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: }

924: /*@
925:   DMPlexMetricCreateUniform - Construct a uniform isotropic metric

927:   Input Parameters:
928: + dm    - The `DM`
929: . f     - The field number to use
930: - alpha - Scaling parameter for the diagonal

932:   Output Parameter:
933: . metric - The uniform metric

935:   Level: beginner

937:   Note:
938:   In this case, the metric is constant in space and so is only specified for a single vertex.

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: }

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: }

960: /*@
961:   DMPlexMetricCreateIsotropic - Construct an isotropic metric from an error indicator

963:   Input Parameters:
964: + dm        - The `DM`
965: . f         - The field number to use
966: - indicator - The error indicator

968:   Output Parameter:
969: . metric - The isotropic metric

971:   Level: beginner

973:   Notes:
974:   It is assumed that the `DM` is comprised of simplices.

976:   The indicator needs to be a scalar field. If it is not defined vertex-wise, then it is projected appropriately.

978: .seealso: `DMPLEX`, `DMPlexMetricCreate()`, `DMPlexMetricCreateUniform()`
979: @*/
980: PetscErrorCode DMPlexMetricCreateIsotropic(DM dm, PetscInt f, Vec indicator, Vec *metric)
981: {
982:   PetscInt m, n;

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[]);

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: }

1001: /*@
1002:   DMPlexMetricDeterminantCreate - Create the determinant field for a Riemannian metric

1004:   Input Parameters:
1005: + dm - The `DM` of the metric field
1006: - f  - The field number to use

1008:   Output Parameters:
1009: + determinant - The determinant field
1010: - dmDet       - The corresponding `DM`

1012:   Level: beginner

1014: .seealso: `DMPLEX`, `DMPlexMetricCreateUniform()`, `DMPlexMetricCreateIsotropic()`, `DMPlexMetricCreate()`
1015: @*/
1016: PetscErrorCode DMPlexMetricDeterminantCreate(DM dm, PetscInt f, Vec *determinant, DM *dmDet)
1017: {
1018:   PetscBool uniform;

1020:   PetscFunctionBegin;
1021:   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1022:   PetscCall(DMClone(dm, dmDet));
1023:   if (uniform) {
1024:     MPI_Comm comm;

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: }

1034: static PetscErrorCode LAPACKsyevFail(PetscInt dim, PetscScalar Mpos[])
1035: {
1036:   PetscInt i, j;

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: }

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;

1059:   PetscFunctionBegin;
1060:   PetscCall(PetscMalloc2(dim * dim, &Mpos, dim, &eigs));

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:   }

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;

1081:     PetscCall(PetscBLASIntCast(5 * dim, &lwork));
1082:     PetscCall(PetscMalloc1(5 * dim, &work));
1083:     {
1084:       PetscBLASInt lierr;
1085:       PetscBLASInt nb;

1087:       PetscCall(PetscBLASIntCast(dim, &nb));
1088:       PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1089: #if defined(PETSC_USE_COMPLEX)
1090:       {
1091:         PetscReal *rwork;
1092:         PetscCall(PetscMalloc1(3 * dim, &rwork));
1093:         PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, Mpos, &nb, eigs, work, &lwork, rwork, &lierr));
1094:         PetscCall(PetscFree(rwork));
1095:       }
1096: #else
1097:       PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, Mpos, &nb, eigs, work, &lwork, &lierr));
1098: #endif
1099:       if (lierr) {
1100:         for (i = 0; i < dim; ++i) {
1101:           Mpos[i * dim + i] = Mp[i * dim + i];
1102:           for (j = i + 1; j < dim; ++j) {
1103:             Mpos[i * dim + j] = 0.5 * (Mp[i * dim + j] + Mp[j * dim + i]);
1104:             Mpos[j * dim + i] = Mpos[i * dim + j];
1105:           }
1106:         }
1107:         PetscCall(LAPACKsyevFail(dim, Mpos));
1108:         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %" PetscBLASInt_FMT, lierr);
1109:       }
1110:       PetscCall(PetscFPTrapPop());
1111:     }
1112:     PetscCall(PetscFree(work));
1113:   }

1115:   /* Reflect to positive orthant and enforce maximum and minimum size */
1116:   max_eig = 0.0;
1117:   for (i = 0; i < dim; ++i) {
1118:     eigs[i] = PetscMin(l_max, PetscMax(l_min, PetscAbsReal(eigs[i])));
1119:     max_eig = PetscMax(eigs[i], max_eig);
1120:   }

1122:   /* Enforce maximum anisotropy and compute determinant */
1123:   *detMp = 1.0;
1124:   for (i = 0; i < dim; ++i) {
1125:     if (a_max >= 1.0) eigs[i] = PetscMax(eigs[i], max_eig * la_min);
1126:     *detMp *= eigs[i];
1127:   }

1129:   /* Reconstruct Hessian */
1130:   for (i = 0; i < dim; ++i) {
1131:     for (j = 0; j < dim; ++j) {
1132:       Mp[i * dim + j] = 0.0;
1133:       for (k = 0; k < dim; ++k) Mp[i * dim + j] += Mpos[k * dim + i] * eigs[k] * Mpos[k * dim + j];
1134:     }
1135:   }
1136:   PetscCall(PetscFree2(Mpos, eigs));
1137:   PetscFunctionReturn(PETSC_SUCCESS);
1138: }

1140: /*@
1141:   DMPlexMetricEnforceSPD - Enforce symmetric positive-definiteness of a metric

1143:   Input Parameters:
1144: + dm                 - The `DM`
1145: . metricIn           - The metric
1146: . restrictSizes      - Should maximum/minimum metric magnitudes be enforced?
1147: - restrictAnisotropy - Should maximum anisotropy be enforced?

1149:   Output Parameters:
1150: + metricOut   - The metric
1151: - determinant - Its determinant

1153:   Options Database Keys:
1154: + -dm_plex_metric_isotropic - Is the metric isotropic?
1155: . -dm_plex_metric_uniform   - Is the metric uniform?
1156: . -dm_plex_metric_h_min     - Minimum tolerated metric magnitude
1157: . -dm_plex_metric_h_max     - Maximum tolerated metric magnitude
1158: - -dm_plex_metric_a_max     - Maximum tolerated anisotropy

1160:   Level: beginner

1162: .seealso: `DMPLEX`, `DMPlexMetricNormalize()`, `DMPlexMetricIntersection()`
1163: @*/
1164: PetscErrorCode DMPlexMetricEnforceSPD(DM dm, Vec metricIn, PetscBool restrictSizes, PetscBool restrictAnisotropy, Vec metricOut, Vec determinant)
1165: {
1166:   DM           dmDet;
1167:   PetscBool    isotropic, uniform;
1168:   PetscInt     dim, vStart, vEnd, v;
1169:   PetscScalar *met, *det;
1170:   PetscReal    h_min = 1.0e-30, h_max = 1.0e+30, a_max = 1.0e+15;

1172:   PetscFunctionBegin;
1173:   PetscCall(PetscLogEventBegin(DMPLEX_MetricEnforceSPD, 0, 0, 0, 0));

1175:   /* Extract metadata from dm */
1176:   PetscCall(DMGetDimension(dm, &dim));
1177:   if (restrictSizes) {
1178:     PetscCall(DMPlexMetricGetMinimumMagnitude(dm, &h_min));
1179:     PetscCall(DMPlexMetricGetMaximumMagnitude(dm, &h_max));
1180:     h_min = PetscMax(h_min, 1.0e-30);
1181:     h_max = PetscMin(h_max, 1.0e+30);
1182:     PetscCheck(h_min < h_max, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Minimum metric magnitude should be smaller than maximum metric magnitude");
1183:   }
1184:   if (restrictAnisotropy) {
1185:     PetscCall(DMPlexMetricGetMaximumAnisotropy(dm, &a_max));
1186:     a_max = PetscMin(a_max, 1.0e+30);
1187:   }

1189:   /* Setup output metric */
1190:   PetscCall(VecCopy(metricIn, metricOut));

1192:   /* Enforce SPD and extract determinant */
1193:   PetscCall(VecGetArray(metricOut, &met));
1194:   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1195:   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
1196:   if (uniform) {
1197:     PetscCheck(isotropic, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform anisotropic metrics cannot exist");

1199:     /* Uniform case */
1200:     PetscCall(VecGetArray(determinant, &det));
1201:     PetscCall(DMPlexMetricModify_Private(1, h_min, h_max, a_max, met, det));
1202:     PetscCall(VecRestoreArray(determinant, &det));
1203:   } else {
1204:     /* Spatially varying case */
1205:     PetscInt nrow;

1207:     if (isotropic) nrow = 1;
1208:     else nrow = dim;
1209:     PetscCall(VecGetDM(determinant, &dmDet));
1210:     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1211:     PetscCall(VecGetArray(determinant, &det));
1212:     for (v = vStart; v < vEnd; ++v) {
1213:       PetscScalar *vmet, *vdet;
1214:       PetscCall(DMPlexPointLocalRef(dm, v, met, &vmet));
1215:       PetscCall(DMPlexPointLocalRef(dmDet, v, det, &vdet));
1216:       PetscCall(DMPlexMetricModify_Private(nrow, h_min, h_max, a_max, vmet, vdet));
1217:     }
1218:     PetscCall(VecRestoreArray(determinant, &det));
1219:   }
1220:   PetscCall(VecRestoreArray(metricOut, &met));

1222:   PetscCall(PetscLogEventEnd(DMPLEX_MetricEnforceSPD, 0, 0, 0, 0));
1223:   PetscFunctionReturn(PETSC_SUCCESS);
1224: }

1226: 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[])
1227: {
1228:   const PetscScalar p = constants[0];

1230:   f0[0] = PetscPowScalar(u[0], p / (2.0 * p + dim));
1231: }

1233: /*@
1234:   DMPlexMetricNormalize - Apply L-p normalization to a metric

1236:   Input Parameters:
1237: + dm                 - The `DM`
1238: . metricIn           - The unnormalized metric
1239: . restrictSizes      - Should maximum/minimum metric magnitudes be enforced?
1240: - restrictAnisotropy - Should maximum metric anisotropy be enforced?

1242:   Output Parameters:
1243: + metricOut   - The normalized metric
1244: - determinant - computed determinant

1246:   Options Database Keys:
1247: + -dm_plex_metric_isotropic                 - Is the metric isotropic?
1248: . -dm_plex_metric_uniform                   - Is the metric uniform?
1249: . -dm_plex_metric_restrict_anisotropy_first - Should anisotropy be restricted before normalization?
1250: . -dm_plex_metric_h_min                     - Minimum tolerated metric magnitude
1251: . -dm_plex_metric_h_max                     - Maximum tolerated metric magnitude
1252: . -dm_plex_metric_a_max                     - Maximum tolerated anisotropy
1253: . -dm_plex_metric_p                         - L-p normalization order
1254: - -dm_plex_metric_target_complexity         - Target metric complexity

1256:   Level: beginner

1258: .seealso: `DMPLEX`, `DMPlexMetricEnforceSPD()`, `DMPlexMetricIntersection()`
1259: @*/
1260: PetscErrorCode DMPlexMetricNormalize(DM dm, Vec metricIn, PetscBool restrictSizes, PetscBool restrictAnisotropy, Vec metricOut, Vec determinant)
1261: {
1262:   DM           dmDet;
1263:   MPI_Comm     comm;
1264:   PetscBool    restrictAnisotropyFirst, isotropic, uniform;
1265:   PetscDS      ds;
1266:   PetscInt     dim, Nd, vStart, vEnd, v, i;
1267:   PetscScalar *met, *det, integral, constants[1];
1268:   PetscReal    p, h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0, factGlob, fact, target, realIntegral;

1270:   PetscFunctionBegin;
1271:   PetscCall(PetscLogEventBegin(DMPLEX_MetricNormalize, 0, 0, 0, 0));

1273:   /* Extract metadata from dm */
1274:   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1275:   PetscCall(DMGetDimension(dm, &dim));
1276:   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1277:   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
1278:   if (isotropic) Nd = 1;
1279:   else Nd = dim * dim;

1281:   /* Set up metric and ensure it is SPD */
1282:   PetscCall(DMPlexMetricRestrictAnisotropyFirst(dm, &restrictAnisotropyFirst));
1283:   PetscCall(DMPlexMetricEnforceSPD(dm, metricIn, PETSC_FALSE, (PetscBool)(restrictAnisotropy && restrictAnisotropyFirst), metricOut, determinant));

1285:   /* Compute global normalization factor */
1286:   PetscCall(DMPlexMetricGetTargetComplexity(dm, &target));
1287:   PetscCall(DMPlexMetricGetNormalizationOrder(dm, &p));
1288:   constants[0] = p;
1289:   if (uniform) {
1290:     PetscCheck(isotropic, comm, PETSC_ERR_SUP, "Uniform anisotropic metrics not supported");
1291:     DM  dmTmp;
1292:     Vec tmp;

1294:     PetscCall(DMClone(dm, &dmTmp));
1295:     PetscCall(DMPlexP1FieldCreate_Private(dmTmp, 0, 1, &tmp));
1296:     PetscCall(VecGetArray(determinant, &det));
1297:     PetscCall(VecSet(tmp, det[0]));
1298:     PetscCall(VecRestoreArray(determinant, &det));
1299:     PetscCall(DMGetDS(dmTmp, &ds));
1300:     PetscCall(PetscDSSetConstants(ds, 1, constants));
1301:     PetscCall(PetscDSSetObjective(ds, 0, detMFunc));
1302:     PetscCall(DMPlexComputeIntegralFEM(dmTmp, tmp, &integral, NULL));
1303:     PetscCall(VecDestroy(&tmp));
1304:     PetscCall(DMDestroy(&dmTmp));
1305:   } else {
1306:     PetscCall(VecGetDM(determinant, &dmDet));
1307:     PetscCall(DMGetDS(dmDet, &ds));
1308:     PetscCall(PetscDSSetConstants(ds, 1, constants));
1309:     PetscCall(PetscDSSetObjective(ds, 0, detMFunc));
1310:     PetscCall(DMPlexComputeIntegralFEM(dmDet, determinant, &integral, NULL));
1311:   }
1312:   realIntegral = PetscRealPart(integral);
1313:   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?");
1314:   factGlob = PetscPowReal(target / realIntegral, 2.0 / dim);

1316:   /* Apply local scaling */
1317:   if (restrictSizes) {
1318:     PetscCall(DMPlexMetricGetMinimumMagnitude(dm, &h_min));
1319:     PetscCall(DMPlexMetricGetMaximumMagnitude(dm, &h_max));
1320:     h_min = PetscMax(h_min, 1.0e-30);
1321:     h_max = PetscMin(h_max, 1.0e+30);
1322:     PetscCheck(h_min < h_max, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Minimum metric magnitude should be smaller than maximum metric magnitude");
1323:   }
1324:   if (restrictAnisotropy && !restrictAnisotropyFirst) {
1325:     PetscCall(DMPlexMetricGetMaximumAnisotropy(dm, &a_max));
1326:     a_max = PetscMin(a_max, 1.0e+30);
1327:   }
1328:   PetscCall(VecGetArray(metricOut, &met));
1329:   PetscCall(VecGetArray(determinant, &det));
1330:   if (uniform) {
1331:     /* Uniform case */
1332:     met[0] *= factGlob * PetscPowReal(PetscAbsScalar(det[0]), -1.0 / (2 * p + dim));
1333:     if (restrictSizes) PetscCall(DMPlexMetricModify_Private(1, h_min, h_max, a_max, met, det));
1334:   } else {
1335:     /* Spatially varying case */
1336:     PetscInt nrow;

1338:     if (isotropic) nrow = 1;
1339:     else nrow = dim;
1340:     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1341:     PetscCall(VecGetDM(determinant, &dmDet));
1342:     for (v = vStart; v < vEnd; ++v) {
1343:       PetscScalar *Mp, *detM;

1345:       PetscCall(DMPlexPointLocalRef(dm, v, met, &Mp));
1346:       PetscCall(DMPlexPointLocalRef(dmDet, v, det, &detM));
1347:       fact = factGlob * PetscPowReal(PetscAbsScalar(detM[0]), -1.0 / (2 * p + dim));
1348:       for (i = 0; i < Nd; ++i) Mp[i] *= fact;
1349:       if (restrictSizes) PetscCall(DMPlexMetricModify_Private(nrow, h_min, h_max, a_max, Mp, detM));
1350:     }
1351:   }
1352:   PetscCall(VecRestoreArray(determinant, &det));
1353:   PetscCall(VecRestoreArray(metricOut, &met));

1355:   PetscCall(PetscLogEventEnd(DMPLEX_MetricNormalize, 0, 0, 0, 0));
1356:   PetscFunctionReturn(PETSC_SUCCESS);
1357: }

1359: /*@
1360:   DMPlexMetricAverage - Compute the average of a list of metrics

1362:   Input Parameters:
1363: + dm         - The `DM`
1364: . numMetrics - The number of metrics to be averaged
1365: . weights    - Weights for the average
1366: - metrics    - The metrics to be averaged

1368:   Output Parameter:
1369: . metricAvg - The averaged metric

1371:   Level: beginner

1373:   Notes:
1374:   The weights should sum to unity.

1376:   If weights are not provided then an unweighted average is used.

1378: .seealso: `DMPLEX`, `DMPlexMetricAverage2()`, `DMPlexMetricAverage3()`, `DMPlexMetricIntersection()`
1379: @*/
1380: PetscErrorCode DMPlexMetricAverage(DM dm, PetscInt numMetrics, PetscReal weights[], Vec metrics[], Vec metricAvg)
1381: {
1382:   PetscBool haveWeights = PETSC_TRUE;
1383:   PetscInt  i, m, n;
1384:   PetscReal sum = 0.0, tol = 1.0e-10;

1386:   PetscFunctionBegin;
1387:   PetscCall(PetscLogEventBegin(DMPLEX_MetricAverage, 0, 0, 0, 0));
1388:   PetscCheck(numMetrics >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot average %" PetscInt_FMT " < 1 metrics", numMetrics);
1389:   PetscCall(VecSet(metricAvg, 0.0));
1390:   PetscCall(VecGetSize(metricAvg, &m));
1391:   for (i = 0; i < numMetrics; ++i) {
1392:     PetscCall(VecGetSize(metrics[i], &n));
1393:     PetscCheck(m == n, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Averaging different metric types not implemented");
1394:   }

1396:   /* Default to the unweighted case */
1397:   if (!weights) {
1398:     PetscCall(PetscMalloc1(numMetrics, &weights));
1399:     haveWeights = PETSC_FALSE;
1400:     for (i = 0; i < numMetrics; ++i) weights[i] = 1.0 / numMetrics;
1401:   }

1403:   /* Check weights sum to unity */
1404:   for (i = 0; i < numMetrics; ++i) sum += weights[i];
1405:   PetscCheck(PetscAbsReal(sum - 1) <= tol, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Weights do not sum to unity");

1407:   /* Compute metric average */
1408:   for (i = 0; i < numMetrics; ++i) PetscCall(VecAXPY(metricAvg, weights[i], metrics[i]));
1409:   if (!haveWeights) PetscCall(PetscFree(weights));

1411:   PetscCall(PetscLogEventEnd(DMPLEX_MetricAverage, 0, 0, 0, 0));
1412:   PetscFunctionReturn(PETSC_SUCCESS);
1413: }

1415: /*@
1416:   DMPlexMetricAverage2 - Compute the unweighted average of two metrics

1418:   Input Parameters:
1419: + dm      - The `DM`
1420: . metric1 - The first metric to be averaged
1421: - metric2 - The second metric to be averaged

1423:   Output Parameter:
1424: . metricAvg - The averaged metric

1426:   Level: beginner

1428: .seealso: `DMPLEX`, `DMPlexMetricAverage()`, `DMPlexMetricAverage3()`
1429: @*/
1430: PetscErrorCode DMPlexMetricAverage2(DM dm, Vec metric1, Vec metric2, Vec metricAvg)
1431: {
1432:   PetscReal weights[2] = {0.5, 0.5};
1433:   Vec       metrics[2] = {metric1, metric2};

1435:   PetscFunctionBegin;
1436:   PetscCall(DMPlexMetricAverage(dm, 2, weights, metrics, metricAvg));
1437:   PetscFunctionReturn(PETSC_SUCCESS);
1438: }

1440: /*@
1441:   DMPlexMetricAverage3 - Compute the unweighted average of three metrics

1443:   Input Parameters:
1444: + dm      - The `DM`
1445: . metric1 - The first metric to be averaged
1446: . metric2 - The second metric to be averaged
1447: - metric3 - The third metric to be averaged

1449:   Output Parameter:
1450: . metricAvg - The averaged metric

1452:   Level: beginner

1454: .seealso: `DMPLEX`, `DMPlexMetricAverage()`, `DMPlexMetricAverage2()`
1455: @*/
1456: PetscErrorCode DMPlexMetricAverage3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec metricAvg)
1457: {
1458:   PetscReal weights[3] = {1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0};
1459:   Vec       metrics[3] = {metric1, metric2, metric3};

1461:   PetscFunctionBegin;
1462:   PetscCall(DMPlexMetricAverage(dm, 3, weights, metrics, metricAvg));
1463:   PetscFunctionReturn(PETSC_SUCCESS);
1464: }

1466: static PetscErrorCode DMPlexMetricIntersection_Private(PetscInt dim, PetscScalar M1[], PetscScalar M2[])
1467: {
1468:   PetscInt     i, j, k, l, m;
1469:   PetscReal   *evals;
1470:   PetscScalar *evecs, *sqrtM1, *isqrtM1;

1472:   PetscFunctionBegin;
1473:   /* Isotropic case */
1474:   if (dim == 1) {
1475:     M2[0] = PetscMax(PetscRealPart(M1[0]), PetscRealPart(M2[0]));
1476:     PetscFunctionReturn(PETSC_SUCCESS);
1477:   }

1479:   /* Anisotropic case */
1480:   PetscCall(PetscMalloc4(dim * dim, &evecs, dim * dim, &sqrtM1, dim * dim, &isqrtM1, dim, &evals));
1481:   for (i = 0; i < dim; ++i) {
1482:     for (j = 0; j < dim; ++j) evecs[i * dim + j] = M1[i * dim + j];
1483:   }
1484:   {
1485:     PetscScalar *work;
1486:     PetscBLASInt lwork;

1488:     PetscCall(PetscBLASIntCast(5 * dim, &lwork));
1489:     PetscCall(PetscMalloc1(5 * dim, &work));
1490:     {
1491:       PetscBLASInt lierr, nb;
1492:       PetscReal    sqrtj;

1494:       /* Compute eigendecomposition of M1 */
1495:       PetscCall(PetscBLASIntCast(dim, &nb));
1496:       PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1497: #if defined(PETSC_USE_COMPLEX)
1498:       {
1499:         PetscReal *rwork;
1500:         PetscCall(PetscMalloc1(3 * dim, &rwork));
1501:         PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
1502:         PetscCall(PetscFree(rwork));
1503:       }
1504: #else
1505:       PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
1506: #endif
1507:       if (lierr) {
1508:         PetscCall(LAPACKsyevFail(dim, M1));
1509:         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %" PetscBLASInt_FMT, lierr);
1510:       }
1511:       PetscCall(PetscFPTrapPop());

1513:       /* Compute square root and the reciprocal thereof */
1514:       for (i = 0; i < dim; ++i) {
1515:         for (k = 0; k < dim; ++k) {
1516:           sqrtM1[i * dim + k]  = 0.0;
1517:           isqrtM1[i * dim + k] = 0.0;
1518:           for (j = 0; j < dim; ++j) {
1519:             sqrtj = PetscSqrtReal(evals[j]);
1520:             sqrtM1[i * dim + k] += evecs[j * dim + i] * sqrtj * evecs[j * dim + k];
1521:             isqrtM1[i * dim + k] += evecs[j * dim + i] * (1.0 / sqrtj) * evecs[j * dim + k];
1522:           }
1523:         }
1524:       }

1526:       /* Map M2 into the space spanned by the eigenvectors of M1 */
1527:       for (i = 0; i < dim; ++i) {
1528:         for (l = 0; l < dim; ++l) {
1529:           evecs[i * dim + l] = 0.0;
1530:           for (j = 0; j < dim; ++j) {
1531:             for (k = 0; k < dim; ++k) evecs[i * dim + l] += isqrtM1[j * dim + i] * M2[j * dim + k] * isqrtM1[k * dim + l];
1532:           }
1533:         }
1534:       }

1536:       /* Compute eigendecomposition */
1537:       PetscCall(PetscFPTrapPush(PETSC_FP_TRAP_OFF));
1538: #if defined(PETSC_USE_COMPLEX)
1539:       {
1540:         PetscReal *rwork;
1541:         PetscCall(PetscMalloc1(3 * dim, &rwork));
1542:         PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
1543:         PetscCall(PetscFree(rwork));
1544:       }
1545: #else
1546:       PetscCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
1547: #endif
1548:       if (lierr) {
1549:         for (i = 0; i < dim; ++i) {
1550:           for (l = 0; l < dim; ++l) {
1551:             evecs[i * dim + l] = 0.0;
1552:             for (j = 0; j < dim; ++j) {
1553:               for (k = 0; k < dim; ++k) evecs[i * dim + l] += isqrtM1[j * dim + i] * M2[j * dim + k] * isqrtM1[k * dim + l];
1554:             }
1555:           }
1556:         }
1557:         PetscCall(LAPACKsyevFail(dim, evecs));
1558:         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %" PetscBLASInt_FMT, lierr);
1559:       }
1560:       PetscCall(PetscFPTrapPop());

1562:       /* Modify eigenvalues */
1563:       for (i = 0; i < dim; ++i) evals[i] = PetscMax(evals[i], 1.0);

1565:       /* Map back to get the intersection */
1566:       for (i = 0; i < dim; ++i) {
1567:         for (m = 0; m < dim; ++m) {
1568:           M2[i * dim + m] = 0.0;
1569:           for (j = 0; j < dim; ++j) {
1570:             for (k = 0; k < dim; ++k) {
1571:               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];
1572:             }
1573:           }
1574:         }
1575:       }
1576:     }
1577:     PetscCall(PetscFree(work));
1578:   }
1579:   PetscCall(PetscFree4(evecs, sqrtM1, isqrtM1, evals));
1580:   PetscFunctionReturn(PETSC_SUCCESS);
1581: }

1583: /*@
1584:   DMPlexMetricIntersection - Compute the intersection of a list of metrics

1586:   Input Parameters:
1587: + dm         - The `DM`
1588: . numMetrics - The number of metrics to be intersected
1589: - metrics    - The metrics to be intersected

1591:   Output Parameter:
1592: . metricInt - The intersected metric

1594:   Level: beginner

1596:   Notes:
1597:   The intersection of a list of metrics has the minimal ellipsoid which fits within the ellipsoids of the component metrics.

1599:   The implementation used here is only consistent with the minimal ellipsoid definition in the case numMetrics = 2.

1601: .seealso: `DMPLEX`, `DMPlexMetricIntersection2()`, `DMPlexMetricIntersection3()`, `DMPlexMetricAverage()`
1602: @*/
1603: PetscErrorCode DMPlexMetricIntersection(DM dm, PetscInt numMetrics, Vec metrics[], Vec metricInt)
1604: {
1605:   PetscBool    isotropic, uniform;
1606:   PetscInt     v, i, m, n;
1607:   PetscScalar *met, *meti;

1609:   PetscFunctionBegin;
1610:   PetscCall(PetscLogEventBegin(DMPLEX_MetricIntersection, 0, 0, 0, 0));
1611:   PetscCheck(numMetrics >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot intersect %" PetscInt_FMT " < 1 metrics", numMetrics);

1613:   /* Copy over the first metric */
1614:   PetscCall(VecCopy(metrics[0], metricInt));
1615:   if (numMetrics == 1) PetscFunctionReturn(PETSC_SUCCESS);
1616:   PetscCall(VecGetSize(metricInt, &m));
1617:   for (i = 0; i < numMetrics; ++i) {
1618:     PetscCall(VecGetSize(metrics[i], &n));
1619:     PetscCheck(m == n, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Intersecting different metric types not implemented");
1620:   }

1622:   /* Intersect subsequent metrics in turn */
1623:   PetscCall(DMPlexMetricIsUniform(dm, &uniform));
1624:   PetscCall(DMPlexMetricIsIsotropic(dm, &isotropic));
1625:   if (uniform) {
1626:     /* Uniform case */
1627:     PetscCall(VecGetArray(metricInt, &met));
1628:     for (i = 1; i < numMetrics; ++i) {
1629:       PetscCall(VecGetArray(metrics[i], &meti));
1630:       PetscCall(DMPlexMetricIntersection_Private(1, meti, met));
1631:       PetscCall(VecRestoreArray(metrics[i], &meti));
1632:     }
1633:     PetscCall(VecRestoreArray(metricInt, &met));
1634:   } else {
1635:     /* Spatially varying case */
1636:     PetscInt     dim, vStart, vEnd, nrow;
1637:     PetscScalar *M, *Mi;

1639:     PetscCall(DMGetDimension(dm, &dim));
1640:     if (isotropic) nrow = 1;
1641:     else nrow = dim;
1642:     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1643:     PetscCall(VecGetArray(metricInt, &met));
1644:     for (i = 1; i < numMetrics; ++i) {
1645:       PetscCall(VecGetArray(metrics[i], &meti));
1646:       for (v = vStart; v < vEnd; ++v) {
1647:         PetscCall(DMPlexPointLocalRef(dm, v, met, &M));
1648:         PetscCall(DMPlexPointLocalRef(dm, v, meti, &Mi));
1649:         PetscCall(DMPlexMetricIntersection_Private(nrow, Mi, M));
1650:       }
1651:       PetscCall(VecRestoreArray(metrics[i], &meti));
1652:     }
1653:     PetscCall(VecRestoreArray(metricInt, &met));
1654:   }

1656:   PetscCall(PetscLogEventEnd(DMPLEX_MetricIntersection, 0, 0, 0, 0));
1657:   PetscFunctionReturn(PETSC_SUCCESS);
1658: }

1660: /*@
1661:   DMPlexMetricIntersection2 - Compute the intersection of two metrics

1663:   Input Parameters:
1664: + dm      - The `DM`
1665: . metric1 - The first metric to be intersected
1666: - metric2 - The second metric to be intersected

1668:   Output Parameter:
1669: . metricInt - The intersected metric

1671:   Level: beginner

1673: .seealso: `DMPLEX`, `DMPlexMetricIntersection()`, `DMPlexMetricIntersection3()`
1674: @*/
1675: PetscErrorCode DMPlexMetricIntersection2(DM dm, Vec metric1, Vec metric2, Vec metricInt)
1676: {
1677:   Vec metrics[2] = {metric1, metric2};

1679:   PetscFunctionBegin;
1680:   PetscCall(DMPlexMetricIntersection(dm, 2, metrics, metricInt));
1681:   PetscFunctionReturn(PETSC_SUCCESS);
1682: }

1684: /*@
1685:   DMPlexMetricIntersection3 - Compute the intersection of three metrics

1687:   Input Parameters:
1688: + dm      - The `DM`
1689: . metric1 - The first metric to be intersected
1690: . metric2 - The second metric to be intersected
1691: - metric3 - The third metric to be intersected

1693:   Output Parameter:
1694: . metricInt - The intersected metric

1696:   Level: beginner

1698: .seealso: `DMPLEX`, `DMPlexMetricIntersection()`, `DMPlexMetricIntersection2()`
1699: @*/
1700: PetscErrorCode DMPlexMetricIntersection3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec metricInt)
1701: {
1702:   Vec metrics[3] = {metric1, metric2, metric3};

1704:   PetscFunctionBegin;
1705:   PetscCall(DMPlexMetricIntersection(dm, 3, metrics, metricInt));
1706:   PetscFunctionReturn(PETSC_SUCCESS);
1707: }