Actual source code: plexmetric.c

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

  4: PetscErrorCode DMPlexP1FieldCreate_Private(DM dm, PetscInt f, PetscInt size, Vec *metric)
  5: {
  6:   MPI_Comm       comm;
  8:   PetscFE        fe;
  9:   PetscInt       dim;


 13:   /* Extract metadata from dm */
 14:   PetscObjectGetComm((PetscObject) dm, &comm);
 15:   DMGetDimension(dm, &dim);

 17:   /* Create a P1 field of the requested size */
 18:   PetscFECreateLagrange(comm, dim, size, PETSC_TRUE, 1, PETSC_DETERMINE, &fe);
 19:   DMSetField(dm, f, NULL, (PetscObject)fe);
 20:   DMCreateDS(dm);
 21:   PetscFEDestroy(&fe);
 22:   DMCreateLocalVector(dm, metric);

 24:   return(0);
 25: }

 27: /*
 28:   DMPlexMetricCreate - Create a Riemannian metric field

 30:   Input parameters:
 31: + dm     - The DM
 32: - f      - The field number to use

 34:   Output parameter:
 35: . metric - The metric

 37:   Level: beginner

 39:   Note: It is assumed that the DM is comprised of simplices.

 41: .seealso: DMPlexMetricCreateUniform(), DMPlexMetricCreateIsotropic()
 42: */
 43: PetscErrorCode DMPlexMetricCreate(DM dm, PetscInt f, Vec *metric)
 44: {
 46:   PetscInt       coordDim, Nd;

 49:   DMGetCoordinateDim(dm, &coordDim);
 50:   Nd = coordDim*coordDim;
 51:   DMPlexP1FieldCreate_Private(dm, f, Nd, metric);
 52:   return(0);
 53: }

 55: typedef struct {
 56:   PetscReal scaling;  /* Scaling for uniform metric diagonal */
 57: } DMPlexMetricUniformCtx;

 59: static PetscErrorCode diagonal(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
 60: {
 61:   DMPlexMetricUniformCtx *user = (DMPlexMetricUniformCtx*)ctx;
 62:   PetscInt                i, j;

 64:   for (i = 0; i < dim; ++i) {
 65:     for (j = 0; j < dim; ++j) {
 66:       if (i == j) u[i+dim*j] = user->scaling;
 67:       else u[i+dim*j] = 0.0;
 68:     }
 69:   }
 70:   return 0;
 71: }

 73: /*
 74:   DMPlexMetricCreateUniform - Construct a uniform isotropic metric

 76:   Input parameters:
 77: + dm     - The DM
 78: . f      - The field number to use
 79: - alpha  - Scaling parameter for the diagonal

 81:   Output parameter:
 82: . metric - The uniform metric

 84:   Level: beginner

 86:   Note: It is assumed that the DM is comprised of simplices.

 88: .seealso: DMPlexMetricCreate(), DMPlexMetricCreateIsotropic()
 89: */
 90: PetscErrorCode DMPlexMetricCreateUniform(DM dm, PetscInt f, PetscReal alpha, Vec *metric)
 91: {
 92:   DMPlexMetricUniformCtx user;
 93:   PetscErrorCode       (*funcs[1])(PetscInt, PetscReal, const PetscReal [], PetscInt, PetscScalar *, void *);
 94:   PetscErrorCode         ierr;
 95:   void                  *ctxs[1];

 98:   DMPlexMetricCreate(dm, f, metric);
 99:   if (!alpha) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform metric scaling is undefined");
100:   if (alpha < 1.0e-30) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Uniform metric scaling %e should be positive", alpha);
101:   else user.scaling = alpha;
102:   funcs[0] = diagonal;
103:   ctxs[0] = &user;
104:   DMProjectFunctionLocal(dm, 0.0, funcs, ctxs, INSERT_ALL_VALUES, *metric);
105:   return(0);
106: }

108: /*
109:   DMPlexMetricCreateIsotropic - Construct an isotropic metric from an error indicator

111:   Input parameters:
112: + dm        - The DM
113: . f         - The field number to use
114: - indicator - The error indicator

116:   Output parameter:
117: . metric    - The isotropic metric

119:   Level: beginner

121:   Notes:

123:   It is assumed that the DM is comprised of simplices.

125:   The indicator needs to be a scalar field defined at *vertices*.

127: .seealso: DMPlexMetricCreate(), DMPlexMetricCreateUniform()
128: */
129: PetscErrorCode DMPlexMetricCreateIsotropic(DM dm, PetscInt f, Vec indicator, Vec *metric)
130: {
131:   DM                 dmIndi;
132:   PetscErrorCode     ierr;
133:   PetscInt           dim, d, vStart, vEnd, v;
134:   const PetscScalar *indi;
135:   PetscScalar       *met;

138:   DMGetDimension(dm, &dim);
139:   DMPlexMetricCreate(dm, f, metric);
140:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);
141:   VecGetArrayRead(indicator, &indi);
142:   VecGetArrayWrite(*metric, &met);
143:   VecGetDM(indicator, &dmIndi);
144:   for (v = vStart; v < vEnd; ++v) {
145:     PetscScalar *vindi, *vmet;
146:     DMPlexPointLocalRead(dmIndi, v, indi, &vindi);
147:     DMPlexPointLocalRef(dm, v, met, &vmet);
148:     for (d = 0; d < dim; ++d) vmet[d*(dim+1)] = vindi[0];
149:   }
150:   VecRestoreArrayWrite(*metric, &met);
151:   VecRestoreArrayRead(indicator, &indi);
152:   return(0);
153: }

155: static PetscErrorCode DMPlexMetricModify_Private(PetscInt dim, PetscReal h_min, PetscReal h_max, PetscReal a_max, PetscScalar Mp[])
156: {
158:   PetscInt       i, j, k;
159:   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);
160:   PetscScalar   *Mpos;

163:   PetscMalloc2(dim*dim, &Mpos, dim, &eigs);

165:   /* Symmetrize */
166:   for (i = 0; i < dim; ++i) {
167:     Mpos[i*dim+i] = Mp[i*dim+i];
168:     for (j = i+1; j < dim; ++j) {
169:       Mpos[i*dim+j] = 0.5*(Mp[i*dim+j] + Mp[j*dim+i]);
170:       Mpos[j*dim+i] = Mpos[i*dim+j];
171:     }
172:   }

174:   /* Compute eigendecomposition */
175:   {
176:     PetscScalar  *work;
177:     PetscBLASInt lwork;

179:     lwork = 5*dim;
180:     PetscMalloc1(5*dim, &work);
181:     {
182:       PetscBLASInt lierr;
183:       PetscBLASInt nb;

185:       PetscBLASIntCast(dim, &nb);
186:       PetscFPTrapPush(PETSC_FP_TRAP_OFF);
187: #if defined(PETSC_USE_COMPLEX)
188:       {
189:         PetscReal *rwork;
190:         PetscMalloc1(3*dim, &rwork);
191:         PetscStackCallBLAS("LAPACKsyev",LAPACKsyev_("V","U",&nb,Mpos,&nb,eigs,work,&lwork,rwork,&lierr));
192:         PetscFree(rwork);
193:       }
194: #else
195:       PetscStackCallBLAS("LAPACKsyev",LAPACKsyev_("V","U",&nb,Mpos,&nb,eigs,work,&lwork,&lierr));
196: #endif
197:       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
198:       PetscFPTrapPop();
199:     }
200:     PetscFree(work);
201:   }

203:   /* Reflect to positive orthant and enforce maximum and minimum size */
204:   max_eig = 0.0;
205:   for (i = 0; i < dim; ++i) {
206:     eigs[i] = PetscMin(l_max, PetscMax(l_min, PetscAbsReal(eigs[i])));
207:     max_eig = PetscMax(eigs[i], max_eig);
208:   }

210:   /* Enforce maximum anisotropy */
211:   for (i = 0; i < dim; ++i) {
212:     if (a_max > 1.0) eigs[i] = PetscMax(eigs[i], max_eig*la_min);
213:   }

215:   /* Reconstruct Hessian */
216:   for (i = 0; i < dim; ++i) {
217:     for (j = 0; j < dim; ++j) {
218:       Mp[i*dim+j] = 0.0;
219:       for (k = 0; k < dim; ++k) {
220:         Mp[i*dim+j] += Mpos[k*dim+i] * eigs[k] * Mpos[k*dim+j];
221:       }
222:     }
223:   }
224:   PetscFree2(Mpos, eigs);

226:   return(0);
227: }

229: /*
230:   DMPlexMetricEnforceSPD - Enforce symmetric positive-definiteness of a metric

232:   Input parameters:
233: + dm            - The DM
234: . restrictSizes - Should maximum/minimum metric magnitudes and anisotropy be enforced?
235: - metric        - The metric

237:   Output parameter:
238: . metric        - The metric

240:   Level: beginner

242: .seealso: DMPlexMetricNormalize(), DMPlexMetricIntersection()
243: */
244: PetscErrorCode DMPlexMetricEnforceSPD(DM dm, PetscBool restrictSizes, Vec metric)
245: {
246:   DMPlexMetricCtx *user;
247:   PetscErrorCode   ierr;
248:   PetscInt         dim, vStart, vEnd, v;
249:   PetscScalar     *met;
250:   PetscReal        h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0;


254:   /* Extract metadata from dm */
255:   DMGetDimension(dm, &dim);
256:   DMGetApplicationContext(dm, (void**)&user);
257:   if (restrictSizes) {
258:     if (user->h_max > h_min) h_max = PetscMin(h_max, user->h_max);
259:     if (user->h_min > 0.0) h_min = PetscMax(h_min, user->h_min);
260:     if (user->a_max > 1.0) a_max = user->a_max;
261:   }

263:   /* Enforce SPD */
264:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);
265:   VecGetArray(metric, &met);
266:   for (v = vStart; v < vEnd; ++v) {
267:     PetscScalar *vmet;
268:     DMPlexPointLocalRef(dm, v, met, &vmet);
269:     DMPlexMetricModify_Private(dim, h_min, h_max, a_max, vmet);
270:   }
271:   VecRestoreArray(metric, &met);

273:   return(0);
274: }

276: static void detMFunc(PetscInt dim, PetscInt Nf, PetscInt NfAux,
277:                      const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[],
278:                      const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[],
279:                      PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[])
280: {
281:   const PetscScalar p = constants[0];
282:   PetscReal         detH = 0.0;

284:   if      (dim == 2) DMPlex_Det2D_Scalar_Internal(&detH, u);
285:   else if (dim == 3) DMPlex_Det3D_Scalar_Internal(&detH, u);
286:   f0[0] = PetscPowReal(detH, p/(2.0*p + dim));
287: }

289: /*
290:   DMPlexMetricNormalize - Apply L-p normalization to a metric

292:   Input parameters:
293: + dm            - The DM
294: . metricIn      - The unnormalized metric
295: - restrictSizes - Should maximum/minimum metric magnitudes and anisotropy be enforced?

297:   Output parameter:
298: . metricOut     - The normalized metric

300:   Level: beginner

302: .seealso: DMPlexMetricEnforceSPD(), DMPlexMetricIntersection()
303: */
304: PetscErrorCode DMPlexMetricNormalize(DM dm, Vec metricIn, PetscBool restrictSizes, Vec *metricOut)
305: {
306:   DMPlexMetricCtx *user;
307:   MPI_Comm         comm;
308:   PetscDS          ds;
309:   PetscErrorCode   ierr;
310:   PetscInt         dim, Nd, vStart, vEnd, v, i;
311:   PetscScalar     *met, integral, constants[1];
312:   PetscReal        p, h_min = 1.0e-30, h_max = 1.0e+30, a_max = 0.0, factGlob, target;


316:   /* Extract metadata from dm */
317:   PetscObjectGetComm((PetscObject) dm, &comm);
318:   DMGetDimension(dm, &dim);
319:   Nd = dim*dim;
320:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);
321:   DMGetApplicationContext(dm, (void**)&user);
322:   if (restrictSizes && user->restrictAnisotropyFirst && user->a_max > 1.0) a_max = user->a_max;
323:   if (PetscAbsReal(user->p) >= 1.0) p = user->p;
324:   else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Metric normalization order %f should be greater than one.", user->p);
325:   constants[0] = p;
326:   if (user->targetComplexity > 0.0) target = user->targetComplexity;
327:   else SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Target metric complexity %f should be positive.", user->targetComplexity);

329:   /* Set up metric and ensure it is SPD */
330:   DMPlexMetricCreate(dm, 0, metricOut);
331:   VecCopy(metricIn, *metricOut);
332:   DMPlexMetricEnforceSPD(dm, PETSC_FALSE, *metricOut);

334:   /* Compute global normalization factor */
335:   DMGetDS(dm, &ds);
336:   PetscDSSetConstants(ds, 1, constants);
337:   PetscDSSetObjective(ds, 0, detMFunc);
338:   DMPlexComputeIntegralFEM(dm, *metricOut, &integral, NULL);
339:   factGlob = PetscPowReal(target/PetscRealPart(integral), 2.0/dim);

341:   /* Apply local scaling */
342:   a_max = 0.0;
343:   if (restrictSizes) {
344:     if (user->h_max > h_min) h_max = PetscMin(h_max, user->h_max);
345:     if (user->h_min > 0.0) h_min = PetscMax(h_min, user->h_min);
346:     if (!user->restrictAnisotropyFirst && user->a_max > 1.0) a_max = user->a_max;
347:   }
348:   VecGetArray(*metricOut, &met);
349:   for (v = vStart; v < vEnd; ++v) {
350:     PetscScalar       *Mp;
351:     PetscReal          detM, fact;

353:     DMPlexPointLocalRef(dm, v, met, &Mp);
354:     if      (dim == 2) DMPlex_Det2D_Scalar_Internal(&detM, Mp);
355:     else if (dim == 3) DMPlex_Det3D_Scalar_Internal(&detM, Mp);
356:     else SETERRQ1(comm, PETSC_ERR_SUP, "Dimension %d not supported", dim);
357:     fact = factGlob * PetscPowReal(PetscAbsReal(detM), -1.0/(2*p+dim));
358:     for (i = 0; i < Nd; ++i) Mp[i] *= fact;
359:     if (restrictSizes) { DMPlexMetricModify_Private(dim, h_min, h_max, a_max, Mp); }
360:   }
361:   VecRestoreArray(*metricOut, &met);

363:   return(0);
364: }

366: /*
367:   DMPlexMetricAverage - Compute the average of a list of metrics

369:   Input Parameter:
370: + dm         - The DM
371: . numMetrics - The number of metrics to be averaged
372: . weights    - Weights for the average
373: - metrics    - The metrics to be averaged

375:   Output Parameter:
376: . metricAvg  - The averaged metric

378:   Level: beginner

380:   Notes:
381:   The weights should sum to unity.

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

385: .seealso: DMPlexMetricAverage2(), DMPlexMetricAverage3(), DMPlexMetricIntersection()
386: */
387: PetscErrorCode DMPlexMetricAverage(DM dm, PetscInt numMetrics, PetscReal weights[], Vec metrics[], Vec *metricAvg)
388: {
389:   PetscBool      haveWeights = PETSC_TRUE;
391:   PetscInt       i;
392:   PetscReal      sum = 0.0, tol = 1.0e-10;

395:   if (numMetrics < 1) { SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot average %d < 1 metrics", numMetrics); }
396:   DMPlexMetricCreate(dm, 0, metricAvg);
397:   VecSet(*metricAvg, 0.0);

399:   /* Default to the unweighted case */
400:   if (!weights) {
401:     PetscMalloc1(numMetrics, &weights);
402:     haveWeights = PETSC_FALSE;
403:     for (i = 0; i < numMetrics; ++i) {weights[i] = 1.0/numMetrics; }
404:   }

406:   /* Check weights sum to unity */
407:   for (i = 0; i < numMetrics; ++i) { sum += weights[i]; }
408:   if (PetscAbsReal(sum - 1) > tol) { SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Weights do not sum to unity"); }

410:   /* Compute metric average */
411:   for (i = 0; i < numMetrics; ++i) { VecAXPY(*metricAvg, weights[i], metrics[i]); }
412:   if (!haveWeights) {PetscFree(weights); }
413:   return(0);
414: }

416: /*
417:   DMPlexMetricAverage2 - Compute the unweighted average of two metrics

419:   Input Parameter:
420: + dm         - The DM
421: . metric1    - The first metric to be averaged
422: - metric2    - The second metric to be averaged

424:   Output Parameter:
425: . metricAvg  - The averaged metric

427:   Level: beginner

429: .seealso: DMPlexMetricAverage(), DMPlexMetricAverage3()
430: */
431: PetscErrorCode DMPlexMetricAverage2(DM dm, Vec metric1, Vec metric2, Vec *metricAvg)
432: {
434:   PetscReal      weights[2] = {0.5, 0.5};
435:   Vec            metrics[2] = {metric1, metric2};

438:   DMPlexMetricAverage(dm, 2, weights, metrics, metricAvg);
439:   return(0);
440: }

442: /*
443:   DMPlexMetricAverage3 - Compute the unweighted average of three metrics

445:   Input Parameter:
446: + dm         - The DM
447: . metric1    - The first metric to be averaged
448: . metric2    - The second metric to be averaged
449: - metric3    - The third metric to be averaged

451:   Output Parameter:
452: . metricAvg  - The averaged metric

454:   Level: beginner

456: .seealso: DMPlexMetricAverage(), DMPlexMetricAverage2()
457: */
458: PetscErrorCode DMPlexMetricAverage3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec *metricAvg)
459: {
461:   PetscReal      weights[3] = {1.0/3.0, 1.0/3.0, 1.0/3.0};
462:   Vec            metrics[3] = {metric1, metric2, metric3};

465:   DMPlexMetricAverage(dm, 3, weights, metrics, metricAvg);
466:   return(0);
467: }

469: static PetscErrorCode DMPlexMetricIntersection_Private(PetscInt dim, PetscScalar M1[], PetscScalar M2[])
470: {
472:   PetscInt       i, j, k, l, m;
473:   PetscReal     *evals, *evals1;
474:   PetscScalar   *evecs, *sqrtM1, *isqrtM1;

477:   PetscMalloc5(dim*dim, &evecs, dim*dim, &sqrtM1, dim*dim, &isqrtM1, dim, &evals, dim, &evals1);
478:   for (i = 0; i < dim; ++i) {
479:     for (j = 0; j < dim; ++j) {
480:       evecs[i*dim+j] = M1[i*dim+j];
481:     }
482:   }
483:   {
484:     PetscScalar *work;
485:     PetscBLASInt lwork;

487:     lwork = 5*dim;
488:     PetscMalloc1(5*dim, &work);
489:     {
490:       PetscBLASInt lierr, nb;
491:       PetscReal    sqrtk;

493:       /* Compute eigendecomposition of M1 */
494:       PetscBLASIntCast(dim, &nb);
495:       PetscFPTrapPush(PETSC_FP_TRAP_OFF);
496: #if defined(PETSC_USE_COMPLEX)
497:       {
498:         PetscReal *rwork;
499:         PetscMalloc1(3*dim, &rwork);
500:         PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals1, work, &lwork, rwork, &lierr));
501:         PetscFree(rwork);
502:       }
503: #else
504:       PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals1, work, &lwork, &lierr));
505: #endif
506:       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
507:       PetscFPTrapPop();

509:       /* Compute square root and reciprocal */
510:       for (i = 0; i < dim; ++i) {
511:         for (j = 0; j < dim; ++j) {
512:           sqrtM1[i*dim+j] = 0.0;
513:           isqrtM1[i*dim+j] = 0.0;
514:           for (k = 0; k < dim; ++k) {
515:             sqrtk = PetscSqrtReal(evals1[k]);
516:             sqrtM1[i*dim+j] += evecs[k*dim+i] * sqrtk * evecs[k*dim+j];
517:             isqrtM1[i*dim+j] += evecs[k*dim+i] * (1.0/sqrtk) * evecs[k*dim+j];
518:           }
519:         }
520:       }

522:       /* Map into the space spanned by the eigenvectors of M1 */
523:       for (i = 0; i < dim; ++i) {
524:         for (j = 0; j < dim; ++j) {
525:           evecs[i*dim+j] = 0.0;
526:           for (k = 0; k < dim; ++k) {
527:             for (l = 0; l < dim; ++l) {
528:               evecs[i*dim+j] += isqrtM1[i*dim+k] * M2[l*dim+k] * isqrtM1[j*dim+l];
529:             }
530:           }
531:         }
532:       }

534:       /* Compute eigendecomposition */
535:       PetscFPTrapPush(PETSC_FP_TRAP_OFF);
536: #if defined(PETSC_USE_COMPLEX)
537:       {
538:         PetscReal *rwork;
539:         PetscMalloc1(3*dim, &rwork);
540:         PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, rwork, &lierr));
541:         PetscFree(rwork);
542:       }
543: #else
544:       PetscStackCallBLAS("LAPACKsyev", LAPACKsyev_("V", "U", &nb, evecs, &nb, evals, work, &lwork, &lierr));
545: #endif
546:       if (lierr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in LAPACK routine %d", (int) lierr);
547:       PetscFPTrapPop();

549:       /* Modify eigenvalues */
550:       for (i = 0; i < dim; ++i) evals[i] = PetscMin(evals[i], evals1[i]);

552:       /* Map back to get the intersection */
553:       for (i = 0; i < dim; ++i) {
554:         for (j = 0; j < dim; ++j) {
555:           M2[i*dim+j] = 0.0;
556:           for (k = 0; k < dim; ++k) {
557:             for (l = 0; l < dim; ++l) {
558:               for (m = 0; m < dim; ++m) {
559:                 M2[i*dim+j] += sqrtM1[i*dim+k] * evecs[l*dim+k] * evals[l] * evecs[l*dim+m] * sqrtM1[j*dim+m];
560:               }
561:             }
562:           }
563:         }
564:       }
565:     }
566:     PetscFree(work);
567:   }
568:   PetscFree5(evecs, sqrtM1, isqrtM1, evals, evals1);
569:   return(0);
570: }

572: /*
573:   DMPlexMetricIntersection - Compute the intersection of a list of metrics

575:   Input Parameter:
576: + dm         - The DM
577: . numMetrics - The number of metrics to be intersected
578: - metrics    - The metrics to be intersected

580:   Output Parameter:
581: . metricInt  - The intersected metric

583:   Level: beginner

585:   Notes:

587:   The intersection of a list of metrics has the maximal ellipsoid which fits within the ellipsoids of the component metrics.

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

591: .seealso: DMPlexMetricIntersection2(), DMPlexMetricIntersection3(), DMPlexMetricAverage()
592: */
593: PetscErrorCode DMPlexMetricIntersection(DM dm, PetscInt numMetrics, Vec metrics[], Vec *metricInt)
594: {
596:   PetscInt       dim, vStart, vEnd, v, i;
597:   PetscScalar   *met, *meti, *M, *Mi;

600:   if (numMetrics < 1) { SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot intersect %d < 1 metrics", numMetrics); }

602:   /* Extract metadata from dm */
603:   DMGetDimension(dm, &dim);
604:   DMPlexMetricCreate(dm, 0, metricInt);
605:   DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);

607:   /* Copy over the first metric */
608:   VecCopy(metrics[0], *metricInt);

610:   /* Intersect subsequent metrics in turn */
611:   if (numMetrics > 1) {
612:     VecGetArray(*metricInt, &met);
613:     for (i = 1; i < numMetrics; ++i) {
614:       VecGetArray(metrics[i], &meti);
615:       for (v = vStart; v < vEnd; ++v) {
616:         DMPlexPointLocalRef(dm, v, met, &M);
617:         DMPlexPointLocalRef(dm, v, meti, &Mi);
618:         DMPlexMetricIntersection_Private(dim, Mi, M);
619:       }
620:       VecRestoreArray(metrics[i], &meti);
621:     }
622:     VecRestoreArray(*metricInt, &met);
623:   }

625:   return(0);
626: }

628: /*
629:   DMPlexMetricIntersection2 - Compute the intersection of two metrics

631:   Input Parameter:
632: + dm        - The DM
633: . metric1   - The first metric to be intersected
634: - metric2   - The second metric to be intersected

636:   Output Parameter:
637: . metricInt - The intersected metric

639:   Level: beginner

641: .seealso: DMPlexMetricIntersection(), DMPlexMetricIntersection3()
642: */
643: PetscErrorCode DMPlexMetricIntersection2(DM dm, Vec metric1, Vec metric2, Vec *metricInt)
644: {
646:   Vec            metrics[2] = {metric1, metric2};

649:   DMPlexMetricIntersection(dm, 2, metrics, metricInt);
650:   return(0);
651: }

653: /*
654:   DMPlexMetricIntersection3 - Compute the intersection of three metrics

656:   Input Parameter:
657: + dm        - The DM
658: . metric1   - The first metric to be intersected
659: . metric2   - The second metric to be intersected
660: - metric3   - The third metric to be intersected

662:   Output Parameter:
663: . metricInt - The intersected metric

665:   Level: beginner

667: .seealso: DMPlexMetricIntersection(), DMPlexMetricIntersection2()
668: */
669: PetscErrorCode DMPlexMetricIntersection3(DM dm, Vec metric1, Vec metric2, Vec metric3, Vec *metricInt)
670: {
672:   Vec            metrics[3] = {metric1, metric2, metric3};

675:   DMPlexMetricIntersection(dm, 3, metrics, metricInt);
676:   return(0);
677: }