Date: 2026-03-01T05:25:19+00:00

Actual source code: ex11_sa.c

      1: static char help[] = "Second Order TVD Finite Volume Example.\n";
      2: /*F

      4: We use a second order TVD finite volume method to evolve a system of PDEs. Our simple upwinded residual evaluation loops
      5: over all mesh faces and uses a Riemann solver to produce the flux given the face geometry and cell values,
      6: \begin{equation}
      7:   f_i = \mathrm{riemann}(\mathrm{phys}, p_\mathrm{centroid}, \hat n, x^L, x^R)
      8: \end{equation}
      9: and then update the cell values given the cell volume.
     10: \begin{eqnarray}
     11:     f^L_i &-=& \frac{f_i}{vol^L} \\
     12:     f^R_i &+=& \frac{f_i}{vol^R}
     13: \end{eqnarray}

     15: As an example, we can consider the shallow water wave equation,
     16: \begin{eqnarray}
     17:      h_t + \nabla\cdot \left( uh                              \right) &=& 0 \\
     18:   (uh)_t + \nabla\cdot \left( u\otimes uh + \frac{g h^2}{2} I \right) &=& 0
     19: \end{eqnarray}
     20: where $h$ is wave height, $u$ is wave velocity, and $g$ is the acceleration due to gravity.

     22: A representative Riemann solver for the shallow water equations is given in the PhysicsRiemann_SW() function,
     23: \begin{eqnarray}
     24:   f^{L,R}_h    &=& uh^{L,R} \cdot \hat n \\
     25:   f^{L,R}_{uh} &=& \frac{f^{L,R}_h}{h^{L,R}} uh^{L,R} + g (h^{L,R})^2 \hat n \\
     26:   c^{L,R}      &=& \sqrt{g h^{L,R}} \\
     27:   s            &=& \max\left( \left|\frac{uh^L \cdot \hat n}{h^L}\right| + c^L, \left|\frac{uh^R \cdot \hat n}{h^R}\right| + c^R \right) \\
     28:   f_i          &=& \frac{A_\mathrm{face}}{2} \left( f^L_i + f^R_i + s \left( x^L_i - x^R_i \right) \right)
     29: \end{eqnarray}
     30: where $c$ is the local gravity wave speed and $f_i$ is a Rusanov flux.

     32: The more sophisticated residual evaluation in RHSFunctionLocal_LS() uses a least-squares fit to a quadratic polynomial
     33: over a neighborhood of the given element.

     35: The mesh is read in from an ExodusII file, usually generated by Cubit.
     36: F*/
     37: #include <petscts.h>
     38: #include <petscfv.h>
     39: #include <petscdmplex.h>
     40: #include <petscsf.h>
     41: #include <petscblaslapack.h>

     43: #define DIM 2 /* Geometric dimension */

     45: static PetscFunctionList PhysicsList;

     47: /* Represents continuum physical equations. */
     48: typedef struct _n_Physics *Physics;

     50: /* Physical model includes boundary conditions, initial conditions, and functionals of interest. It is
     51:  * discretization-independent, but its members depend on the scenario being solved. */
     52: typedef struct _n_Model *Model;

     54: /* 'User' implements a discretization of a continuous model. */
     55: typedef struct _n_User *User;

     57: typedef PetscErrorCode (*RiemannFunction)(const PetscReal *, const PetscReal *, const PetscScalar *, const PetscScalar *, PetscScalar *, void *);
     58: typedef PetscErrorCode (*SolutionFunction)(Model, PetscReal, const PetscReal *, PetscScalar *, void *);
     59: typedef PetscErrorCode (*FunctionalFunction)(Model, PetscReal, const PetscReal *, const PetscScalar *, PetscReal *, void *);
     60: typedef PetscErrorCode (*SetupFields)(Physics, PetscSection);
     61: static PetscErrorCode ModelSolutionSetDefault(Model, SolutionFunction, void *);
     62: static PetscErrorCode ModelFunctionalRegister(Model, const char *, PetscInt *, FunctionalFunction, void *);
     63: static PetscErrorCode OutputVTK(DM, const char *, PetscViewer *);

     65: struct FieldDescription {
     66:   const char *name;
     67:   PetscInt    dof;
     68: };

     70: typedef struct _n_FunctionalLink *FunctionalLink;
     71: struct _n_FunctionalLink {
     72:   char              *name;
     73:   FunctionalFunction func;
     74:   void              *ctx;
     75:   PetscInt           offset;
     76:   FunctionalLink     next;
     77: };

     79: struct _n_Physics {
     80:   RiemannFunction                riemann;
     81:   PetscInt                       dof;      /* number of degrees of freedom per cell */
     82:   PetscReal                      maxspeed; /* kludge to pick initial time step, need to add monitoring and step control */
     83:   void                          *data;
     84:   PetscInt                       nfields;
     85:   const struct FieldDescription *field_desc;
     86: };

     88: struct _n_Model {
     89:   MPI_Comm         comm; /* Does not do collective communication, but some error conditions can be collective */
     90:   Physics          physics;
     91:   FunctionalLink   functionalRegistry;
     92:   PetscInt         maxComputed;
     93:   PetscInt         numMonitored;
     94:   FunctionalLink  *functionalMonitored;
     95:   PetscInt         numCall;
     96:   FunctionalLink  *functionalCall;
     97:   SolutionFunction solution;
     98:   void            *solutionctx;
     99:   PetscReal        maxspeed; /* estimate of global maximum speed (for CFL calculation) */
    100: };

    102: struct _n_User {
    103:   PetscInt numSplitFaces;
    104:   PetscInt vtkInterval; /* For monitor */
    105:   Model    model;
    106: };

    108: static inline PetscScalar DotDIM(const PetscScalar *x, const PetscScalar *y)
    109: {
    110:   PetscInt    i;
    111:   PetscScalar prod = 0.0;

    113:   for (i = 0; i < DIM; i++) prod += x[i] * y[i];
    114:   return prod;
    115: }
    116: static inline PetscReal NormDIM(const PetscScalar *x)
    117: {
    118:   return PetscSqrtReal(PetscAbsScalar(DotDIM(x, x)));
    119: }
    120: static inline void axDIM(const PetscScalar a, PetscScalar *x)
    121: {
    122:   PetscInt i;
    123:   for (i = 0; i < DIM; i++) x[i] *= a;
    124: }
    125: static inline void waxDIM(const PetscScalar a, const PetscScalar *x, PetscScalar *w)
    126: {
    127:   PetscInt i;
    128:   for (i = 0; i < DIM; i++) w[i] = x[i] * a;
    129: }
    130: static inline void NormalSplitDIM(const PetscReal *n, const PetscScalar *x, PetscScalar *xn, PetscScalar *xt)
    131: { /* Split x into normal and tangential components */
    132:   PetscInt    i;
    133:   PetscScalar c;
    134:   c = DotDIM(x, n) / DotDIM(n, n);
    135:   for (i = 0; i < DIM; i++) {
    136:     xn[i] = c * n[i];
    137:     xt[i] = x[i] - xn[i];
    138:   }
    139: }

    141: static inline PetscScalar Dot2(const PetscScalar *x, const PetscScalar *y)
    142: {
    143:   return x[0] * y[0] + x[1] * y[1];
    144: }
    145: static inline PetscReal Norm2(const PetscScalar *x)
    146: {
    147:   return PetscSqrtReal(PetscAbsScalar(Dot2(x, x)));
    148: }
    149: static inline void Normalize2(PetscScalar *x)
    150: {
    151:   PetscReal a = 1. / Norm2(x);
    152:   x[0] *= a;
    153:   x[1] *= a;
    154: }
    155: static inline void Waxpy2(PetscScalar a, const PetscScalar *x, const PetscScalar *y, PetscScalar *w)
    156: {
    157:   w[0] = a * x[0] + y[0];
    158:   w[1] = a * x[1] + y[1];
    159: }
    160: static inline void Scale2(PetscScalar a, const PetscScalar *x, PetscScalar *y)
    161: {
    162:   y[0] = a * x[0];
    163:   y[1] = a * x[1];
    164: }

    166: static inline void WaxpyD(PetscInt dim, PetscScalar a, const PetscScalar *x, const PetscScalar *y, PetscScalar *w)
    167: {
    168:   PetscInt d;
    169:   for (d = 0; d < dim; ++d) w[d] = a * x[d] + y[d];
    170: }
    171: static inline PetscScalar DotD(PetscInt dim, const PetscScalar *x, const PetscScalar *y)
    172: {
    173:   PetscScalar sum = 0.0;
    174:   PetscInt    d;
    175:   for (d = 0; d < dim; ++d) sum += x[d] * y[d];
    176:   return sum;
    177: }
    178: static inline PetscReal NormD(PetscInt dim, const PetscScalar *x)
    179: {
    180:   return PetscSqrtReal(PetscAbsScalar(DotD(dim, x, x)));
    181: }

    183: static inline void NormalSplit(const PetscReal *n, const PetscScalar *x, PetscScalar *xn, PetscScalar *xt)
    184: { /* Split x into normal and tangential components */
    185:   Scale2(Dot2(x, n) / Dot2(n, n), n, xn);
    186:   Waxpy2(-1, xn, x, xt);
    187: }

    189: /******************* Advect ********************/
    190: typedef enum {
    191:   ADVECT_SOL_TILTED,
    192:   ADVECT_SOL_BUMP
    193: } AdvectSolType;
    194: static const char *const AdvectSolTypes[] = {"TILTED", "BUMP", "AdvectSolType", "ADVECT_SOL_", 0};
    195: typedef enum {
    196:   ADVECT_SOL_BUMP_CONE,
    197:   ADVECT_SOL_BUMP_COS
    198: } AdvectSolBumpType;
    199: static const char *const AdvectSolBumpTypes[] = {"CONE", "COS", "AdvectSolBumpType", "ADVECT_SOL_BUMP_", 0};

    201: typedef struct {
    202:   PetscReal wind[DIM];
    203: } Physics_Advect_Tilted;
    204: typedef struct {
    205:   PetscReal         center[DIM];
    206:   PetscReal         radius;
    207:   AdvectSolBumpType type;
    208: } Physics_Advect_Bump;

    210: typedef struct {
    211:   PetscReal     inflowState;
    212:   AdvectSolType soltype;
    213:   union
    214:   {
    215:     Physics_Advect_Tilted tilted;
    216:     Physics_Advect_Bump   bump;
    217:   } sol;
    218:   struct {
    219:     PetscInt Error;
    220:   } functional;
    221: } Physics_Advect;

    223: static const struct FieldDescription PhysicsFields_Advect[] = {
    224:   {"U",  1},
    225:   {NULL, 0}
    226: };

    228: static PetscErrorCode PhysicsBoundary_Advect_Inflow(PetscReal time, const PetscReal *c, const PetscReal *n, const PetscScalar *xI, PetscScalar *xG, void *ctx)
    229: {
    230:   Physics         phys   = (Physics)ctx;
    231:   Physics_Advect *advect = (Physics_Advect *)phys->data;

    233:   PetscFunctionBeginUser;
    234:   xG[0] = advect->inflowState;
    235:   PetscFunctionReturn(PETSC_SUCCESS);
    236: }

    238: static PetscErrorCode PhysicsBoundary_Advect_Outflow(PetscReal time, const PetscReal *c, const PetscReal *n, const PetscScalar *xI, PetscScalar *xG, void *ctx)
    239: {
    240:   PetscFunctionBeginUser;
    241:   xG[0] = xI[0];
    242:   PetscFunctionReturn(PETSC_SUCCESS);
    243: }

    245: static PetscErrorCode PhysicsRiemann_Advect(const PetscReal *qp, const PetscReal *n, const PetscScalar *xL, const PetscScalar *xR, PetscScalar *flux, Physics phys)
    246: {
    247:   Physics_Advect *advect = (Physics_Advect *)phys->data;
    248:   PetscReal       wind[DIM], wn;

    250:   PetscFunctionBeginUser;
    251:   switch (advect->soltype) {
    252:   case ADVECT_SOL_TILTED: {
    253:     Physics_Advect_Tilted *tilted = &advect->sol.tilted;
    254:     wind[0]                       = tilted->wind[0];
    255:     wind[1]                       = tilted->wind[1];
    256:   } break;
    257:   case ADVECT_SOL_BUMP:
    258:     wind[0] = -qp[1];
    259:     wind[1] = qp[0];
    260:     break;
    261:   default:
    262:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "No support for solution type %s", AdvectSolBumpTypes[advect->soltype]);
    263:   }
    264:   wn      = Dot2(wind, n);
    265:   flux[0] = (wn > 0 ? xL[0] : xR[0]) * wn;
    266:   PetscFunctionReturn(PETSC_SUCCESS);
    267: }

    269: static PetscErrorCode PhysicsSolution_Advect(Model mod, PetscReal time, const PetscReal *x, PetscScalar *u, void *ctx)
    270: {
    271:   Physics         phys   = (Physics)ctx;
    272:   Physics_Advect *advect = (Physics_Advect *)phys->data;

    274:   PetscFunctionBeginUser;
    275:   switch (advect->soltype) {
    276:   case ADVECT_SOL_TILTED: {
    277:     PetscReal              x0[DIM];
    278:     Physics_Advect_Tilted *tilted = &advect->sol.tilted;
    279:     Waxpy2(-time, tilted->wind, x, x0);
    280:     if (x0[1] > 0) u[0] = 1. * x[0] + 3. * x[1];
    281:     else u[0] = advect->inflowState;
    282:   } break;
    283:   case ADVECT_SOL_BUMP: {
    284:     Physics_Advect_Bump *bump = &advect->sol.bump;
    285:     PetscReal            x0[DIM], v[DIM], r, cost, sint;
    286:     cost  = PetscCosReal(time);
    287:     sint  = PetscSinReal(time);
    288:     x0[0] = cost * x[0] + sint * x[1];
    289:     x0[1] = -sint * x[0] + cost * x[1];
    290:     Waxpy2(-1, bump->center, x0, v);
    291:     r = Norm2(v);
    292:     switch (bump->type) {
    293:     case ADVECT_SOL_BUMP_CONE:
    294:       u[0] = PetscMax(1 - r / bump->radius, 0);
    295:       break;
    296:     case ADVECT_SOL_BUMP_COS:
    297:       u[0] = 0.5 + 0.5 * PetscCosReal(PetscMin(r / bump->radius, 1) * PETSC_PI);
    298:       break;
    299:     }
    300:   } break;
    301:   default:
    302:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unknown solution type");
    303:   }
    304:   PetscFunctionReturn(PETSC_SUCCESS);
    305: }

    307: static PetscErrorCode PhysicsFunctional_Advect(Model mod, PetscReal time, const PetscScalar *x, const PetscScalar *y, PetscReal *f, void *ctx)
    308: {
    309:   Physics         phys   = (Physics)ctx;
    310:   Physics_Advect *advect = (Physics_Advect *)phys->data;
    311:   PetscScalar     yexact[1];

    313:   PetscFunctionBeginUser;
    314:   PetscCall(PhysicsSolution_Advect(mod, time, x, yexact, phys));
    315:   f[advect->functional.Error] = PetscAbsScalar(y[0] - yexact[0]);
    316:   PetscFunctionReturn(PETSC_SUCCESS);
    317: }

    319: static PetscErrorCode PhysicsCreate_Advect(PetscDS prob, Model mod, Physics phys, PetscOptions *PetscOptionsObject)
    320: {
    321:   Physics_Advect *advect;

    323:   PetscFunctionBeginUser;
    324:   phys->field_desc = PhysicsFields_Advect;
    325:   phys->riemann    = (RiemannFunction)PhysicsRiemann_Advect;
    326:   PetscCall(PetscNew(&advect));
    327:   phys->data = advect;
    328:   PetscOptionsHeadBegin(PetscOptionsObject, "Advect options");
    329:   {
    330:     PetscInt two = 2, dof = 1;
    331:     advect->soltype = ADVECT_SOL_TILTED;
    332:     PetscCall(PetscOptionsEnum("-advect_sol_type", "solution type", "", AdvectSolTypes, (PetscEnum)advect->soltype, (PetscEnum *)&advect->soltype, NULL));
    333:     switch (advect->soltype) {
    334:     case ADVECT_SOL_TILTED: {
    335:       Physics_Advect_Tilted *tilted = &advect->sol.tilted;
    336:       two                           = 2;
    337:       tilted->wind[0]               = 0.0;
    338:       tilted->wind[1]               = 1.0;
    339:       PetscCall(PetscOptionsRealArray("-advect_tilted_wind", "background wind vx,vy", "", tilted->wind, &two, NULL));
    340:       advect->inflowState = -2.0;
    341:       PetscCall(PetscOptionsRealArray("-advect_tilted_inflow", "Inflow state", "", &advect->inflowState, &dof, NULL));
    342:       phys->maxspeed = Norm2(tilted->wind);
    343:     } break;
    344:     case ADVECT_SOL_BUMP: {
    345:       Physics_Advect_Bump *bump = &advect->sol.bump;
    346:       two                       = 2;
    347:       bump->center[0]           = 2.;
    348:       bump->center[1]           = 0.;
    349:       PetscCall(PetscOptionsRealArray("-advect_bump_center", "location of center of bump x,y", "", bump->center, &two, NULL));
    350:       bump->radius = 0.9;
    351:       PetscCall(PetscOptionsReal("-advect_bump_radius", "radius of bump", "", bump->radius, &bump->radius, NULL));
    352:       bump->type = ADVECT_SOL_BUMP_CONE;
    353:       PetscCall(PetscOptionsEnum("-advect_bump_type", "type of bump", "", AdvectSolBumpTypes, (PetscEnum)bump->type, (PetscEnum *)&bump->type, NULL));
    354:       phys->maxspeed = 3.; /* radius of mesh, kludge */
    355:     } break;
    356:     }
    357:   }
    358:   PetscOptionsHeadEnd();
    359:   {
    360:     const PetscInt inflowids[] = {100, 200, 300}, outflowids[] = {101};
    361:     DMLabel        label;

    363:     PetscCall(DMGetLabel(dm, "Face Sets", &label));
    364:     /* Register "canned" boundary conditions and defaults for where to apply. */
    365:     PetscCall(PetscDSAddBoundary(prob, PETSC_TRUE, "inflow", label, PETSC_STATIC_ARRAY_LENGTH(inflowids), inflowids, 0, 0, NULL, (void (*)())PhysicsBoundary_Advect_Inflow, NULL, phys, NULL));
    366:     PetscCall(PetscDSAddBoundary(prob, PETSC_TRUE, "outflow", label, PETSC_STATIC_ARRAY_LENGTH(outflowids), outflowids, 0, 0, NULL, (void (*)())PhysicsBoundary_Advect_Outflow, NULL, phys, NULL));
    367:     /* Initial/transient solution with default boundary conditions */
    368:     PetscCall(ModelSolutionSetDefault(mod, PhysicsSolution_Advect, phys));
    369:     /* Register "canned" functionals */
    370:     PetscCall(ModelFunctionalRegister(mod, "Error", &advect->functional.Error, PhysicsFunctional_Advect, phys));
    371:   }
    372:   PetscFunctionReturn(PETSC_SUCCESS);
    373: }

    375: /******************* Shallow Water ********************/
    376: typedef struct {
    377:   PetscReal gravity;
    378:   PetscReal boundaryHeight;
    379:   struct {
    380:     PetscInt Height;
    381:     PetscInt Speed;
    382:     PetscInt Energy;
    383:   } functional;
    384: } Physics_SW;
    385: typedef struct {
    386:   PetscScalar vals[0];
    387:   PetscScalar h;
    388:   PetscScalar uh[DIM];
    389: } SWNode;

    391: static const struct FieldDescription PhysicsFields_SW[] = {
    392:   {"Height",   1  },
    393:   {"Momentum", DIM},
    394:   {NULL,       0  }
    395: };

    397: /*
    398:  * h_t + div(uh) = 0
    399:  * (uh)_t + div (u\otimes uh + g h^2 / 2 I) = 0
    400:  *
    401:  * */
    402: static PetscErrorCode SWFlux(Physics phys, const PetscReal *n, const SWNode *x, SWNode *f)
    403: {
    404:   Physics_SW *sw = (Physics_SW *)phys->data;
    405:   PetscScalar uhn, u[DIM];
    406:   PetscInt    i;

    408:   PetscFunctionBeginUser;
    409:   Scale2(1. / x->h, x->uh, u);
    410:   uhn  = Dot2(x->uh, n);
    411:   f->h = uhn;
    412:   for (i = 0; i < DIM; i++) f->uh[i] = u[i] * uhn + sw->gravity * PetscSqr(x->h) * n[i];
    413:   PetscFunctionReturn(PETSC_SUCCESS);
    414: }

    416: static PetscErrorCode PhysicsBoundary_SW_Wall(PetscReal time, const PetscReal *c, const PetscReal *n, const PetscScalar *xI, PetscScalar *xG, void *ctx)
    417: {
    418:   PetscFunctionBeginUser;
    419:   xG[0] = xI[0];
    420:   xG[1] = -xI[1];
    421:   xG[2] = -xI[2];
    422:   PetscFunctionReturn(PETSC_SUCCESS);
    423: }

    425: static PetscErrorCode PhysicsRiemann_SW(const PetscReal *qp, const PetscReal *n, const PetscScalar *xL, const PetscScalar *xR, PetscScalar *flux, Physics phys)
    426: {
    427:   Physics_SW   *sw = (Physics_SW *)phys->data;
    428:   PetscReal     cL, cR, speed, nn[DIM];
    429:   const SWNode *uL = (const SWNode *)xL, *uR = (const SWNode *)xR;
    430:   SWNode        fL, fR;
    431:   PetscInt      i;

    433:   PetscFunctionBeginUser;
    434:   PetscCheck(uL->h >= 0 && uR->h >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Reconstructed thickness is negative");
    435:   nn[0] = n[0];
    436:   nn[1] = n[1];
    437:   Normalize2(nn);
    438:   SWFlux(phys, nn, uL, &fL);
    439:   SWFlux(phys, nn, uR, &fR);
    440:   cL    = PetscSqrtReal(sw->gravity * PetscRealPart(uL->h));
    441:   cR    = PetscSqrtReal(sw->gravity * PetscRealPart(uR->h)); /* gravity wave speed */
    442:   speed = PetscMax(PetscAbsScalar(Dot2(uL->uh, nn) / uL->h) + cL, PetscAbsScalar(Dot2(uR->uh, nn) / uR->h) + cR);
    443:   for (i = 0; i < 1 + DIM; i++) flux[i] = (0.5 * (fL.vals[i] + fR.vals[i]) + 0.5 * speed * (xL[i] - xR[i])) * Norm2(n);
    444:   PetscFunctionReturn(PETSC_SUCCESS);
    445: }

    447: static PetscErrorCode PhysicsSolution_SW(Model mod, PetscReal time, const PetscReal *x, PetscScalar *u, void *ctx)
    448: {
    449:   PetscReal dx[2], r, sigma;

    451:   PetscFunctionBeginUser;
    452:   PetscCheck(time == 0.0, mod->comm, PETSC_ERR_SUP, "No solution known for time %g", (double)time);
    453:   dx[0] = x[0] - 1.5;
    454:   dx[1] = x[1] - 1.0;
    455:   r     = Norm2(dx);
    456:   sigma = 0.5;
    457:   u[0]  = 1 + 2 * PetscExpScalar(-PetscSqr(r) / (2 * PetscSqr(sigma)));
    458:   u[1]  = 0.0;
    459:   u[2]  = 0.0;
    460:   PetscFunctionReturn(PETSC_SUCCESS);
    461: }

    463: static PetscErrorCode PhysicsFunctional_SW(Model mod, PetscReal time, const PetscReal *coord, const PetscScalar *xx, PetscReal *f, void *ctx)
    464: {
    465:   Physics       phys = (Physics)ctx;
    466:   Physics_SW   *sw   = (Physics_SW *)phys->data;
    467:   const SWNode *x    = (const SWNode *)xx;
    468:   PetscScalar   u[2];
    469:   PetscReal     h;

    471:   PetscFunctionBeginUser;
    472:   h = PetscRealPart(x->h);
    473:   Scale2(1. / x->h, x->uh, u);
    474:   f[sw->functional.Height] = h;
    475:   f[sw->functional.Speed]  = Norm2(u) + PetscSqrtReal(sw->gravity * h);
    476:   f[sw->functional.Energy] = 0.5 * (Dot2(x->uh, u) + sw->gravity * PetscSqr(h));
    477:   PetscFunctionReturn(PETSC_SUCCESS);
    478: }

    480: static PetscErrorCode PhysicsCreate_SW(PetscDS prob, Model mod, Physics phys, PetscOptions *PetscOptionsObject)
    481: {
    482:   Physics_SW *sw;

    484:   PetscFunctionBeginUser;
    485:   phys->field_desc = PhysicsFields_SW;
    486:   phys->riemann    = (RiemannFunction)PhysicsRiemann_SW;
    487:   PetscCall(PetscNew(&sw));
    488:   phys->data = sw;
    489:   PetscOptionsHeadBegin(PetscOptionsObject, "SW options");
    490:   {
    491:     sw->gravity = 1.0;
    492:     PetscCall(PetscOptionsReal("-sw_gravity", "Gravitational constant", "", sw->gravity, &sw->gravity, NULL));
    493:   }
    494:   PetscOptionsHeadEnd();
    495:   phys->maxspeed = PetscSqrtReal(2.0 * sw->gravity); /* Mach 1 for depth of 2 */

    497:   {
    498:     const PetscInt wallids[] = {100, 101, 200, 300};
    499:     DMLabel        label;

    501:     PetscCall(DMGetLabel(dm, "Face Sets", &label));
    502:     PetscCall(PetscDSAddBoundary(prob, PETSC_TRUE, "wall", label, PETSC_STATIC_ARRAY_LENGTH(wallids), wallids, 0, 0, NULL, (void (*)())PhysicsBoundary_SW_Wall, NULL, phys, NULL));
    503:     PetscCall(ModelSolutionSetDefault(mod, PhysicsSolution_SW, phys));
    504:     PetscCall(ModelFunctionalRegister(mod, "Height", &sw->functional.Height, PhysicsFunctional_SW, phys));
    505:     PetscCall(ModelFunctionalRegister(mod, "Speed", &sw->functional.Speed, PhysicsFunctional_SW, phys));
    506:     PetscCall(ModelFunctionalRegister(mod, "Energy", &sw->functional.Energy, PhysicsFunctional_SW, phys));
    507:   }
    508:   PetscFunctionReturn(PETSC_SUCCESS);
    509: }

    511: /******************* Euler ********************/
    512: typedef struct {
    513:   PetscScalar vals[0];
    514:   PetscScalar r;
    515:   PetscScalar ru[DIM];
    516:   PetscScalar e;
    517: } EulerNode;
    518: typedef PetscErrorCode (*EquationOfState)(const PetscReal *, const EulerNode *, PetscScalar *);
    519: typedef struct {
    520:   PetscInt        npars;
    521:   PetscReal       pars[DIM];
    522:   EquationOfState pressure;
    523:   EquationOfState sound;
    524:   struct {
    525:     PetscInt Density;
    526:     PetscInt Momentum;
    527:     PetscInt Energy;
    528:     PetscInt Pressure;
    529:     PetscInt Speed;
    530:   } monitor;
    531: } Physics_Euler;

    533: static const struct FieldDescription PhysicsFields_Euler[] = {
    534:   {"Density",  1  },
    535:   {"Momentum", DIM},
    536:   {"Energy",   1  },
    537:   {NULL,       0  }
    538: };

    540: static PetscErrorCode Pressure_PG(const PetscReal *pars, const EulerNode *x, PetscScalar *p)
    541: {
    542:   PetscScalar ru2;

    544:   PetscFunctionBeginUser;
    545:   ru2 = DotDIM(x->ru, x->ru);
    546:   ru2 /= x->r;
    547:   /* kinematic dof = params[0] */
    548:   (*p) = 2.0 * (x->e - 0.5 * ru2) / pars[0];
    549:   PetscFunctionReturn(PETSC_SUCCESS);
    550: }

    552: static PetscErrorCode SpeedOfSound_PG(const PetscReal *pars, const EulerNode *x, PetscScalar *c)
    553: {
    554:   PetscScalar p;

    556:   PetscFunctionBeginUser;
    557:   /* TODO remove direct usage of Pressure_PG */
    558:   Pressure_PG(pars, x, &p);
    559:   /* TODO check the sign of p */
    560:   /* pars[1] = heat capacity ratio */
    561:   (*c) = PetscSqrtScalar(pars[1] * p / x->r);
    562:   PetscFunctionReturn(PETSC_SUCCESS);
    563: }

    565: /*
    566:  * x = (rho,rho*(u_1),...,rho*e)^T
    567:  * x_t+div(f_1(x))+...+div(f_DIM(x)) = 0
    568:  *
    569:  * f_i(x) = u_i*x+(0,0,...,p,...,p*u_i)^T
    570:  *
    571:  * */
    572: static PetscErrorCode EulerFlux(Physics phys, const PetscReal *n, const EulerNode *x, EulerNode *f)
    573: {
    574:   Physics_Euler *eu = (Physics_Euler *)phys->data;
    575:   PetscScalar    u, nu, p;
    576:   PetscInt       i;

    578:   PetscFunctionBeginUser;
    579:   u = DotDIM(x->ru, x->ru);
    580:   u /= (x->r * x->r);
    581:   nu = DotDIM(x->ru, n);
    582:   /* TODO check the sign of p */
    583:   eu->pressure(eu->pars, x, &p);
    584:   f->r = nu * x->r;
    585:   for (i = 0; i < DIM; i++) f->ru[i] = nu * x->ru[i] + n[i] * p;
    586:   f->e = nu * (x->e + p);
    587:   PetscFunctionReturn(PETSC_SUCCESS);
    588: }

    590: /* PetscReal* => EulerNode* conversion */
    591: static PetscErrorCode PhysicsBoundary_Euler_Wall(PetscReal time, const PetscReal *c, const PetscReal *n, const PetscScalar *xI, PetscScalar *xG, void *ctx)
    592: {
    593:   PetscInt    i;
    594:   PetscScalar xn[DIM], xt[DIM];

    596:   PetscFunctionBeginUser;
    597:   xG[0] = xI[0];
    598:   NormalSplitDIM(n, xI + 1, xn, xt);
    599:   for (i = 0; i < DIM; i++) xG[i + 1] = -xn[i] + xt[i];
    600:   xG[DIM + 1] = xI[DIM + 1];
    601:   PetscFunctionReturn(PETSC_SUCCESS);
    602: }

    604: /* PetscReal* => EulerNode* conversion */
    605: static PetscErrorCode PhysicsRiemann_Euler_Rusanov(const PetscReal *qp, const PetscReal *n, const PetscScalar *xL, const PetscScalar *xR, PetscScalar *flux, Physics phys)
    606: {
    607:   Physics_Euler   *eu = (Physics_Euler *)phys->data;
    608:   PetscScalar      cL, cR, speed;
    609:   const EulerNode *uL = (const EulerNode *)xL, *uR = (const EulerNode *)xR;
    610:   EulerNode        fL, fR;
    611:   PetscInt         i;

    613:   PetscFunctionBeginUser;
    614:   PetscCheck(uL->r >= 0 && uR->r >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Reconstructed density is negative");
    615:   EulerFlux(phys, n, uL, &fL);
    616:   EulerFlux(phys, n, uR, &fR);
    617:   eu->sound(eu->pars, uL, &cL);
    618:   eu->sound(eu->pars, uR, &cR);
    619:   speed = PetscMax(cL, cR) + PetscMax(PetscAbsScalar(DotDIM(uL->ru, n) / NormDIM(n)), PetscAbsScalar(DotDIM(uR->ru, n) / NormDIM(n)));
    620:   for (i = 0; i < 2 + DIM; i++) flux[i] = 0.5 * (fL.vals[i] + fR.vals[i]) + 0.5 * speed * (xL[i] - xR[i]);
    621:   PetscFunctionReturn(PETSC_SUCCESS);
    622: }

    624: static PetscErrorCode PhysicsSolution_Euler(Model mod, PetscReal time, const PetscReal *x, PetscScalar *u, void *ctx)
    625: {
    626:   PetscInt i;

    628:   PetscFunctionBeginUser;
    629:   PetscCheck(time == 0.0, mod->comm, PETSC_ERR_SUP, "No solution known for time %g", (double)time);
    630:   u[0]       = 1.0;
    631:   u[DIM + 1] = 1.0 + PetscAbsReal(x[0]);
    632:   for (i = 1; i < DIM + 1; i++) u[i] = 0.0;
    633:   PetscFunctionReturn(PETSC_SUCCESS);
    634: }

    636: static PetscErrorCode PhysicsFunctional_Euler(Model mod, PetscReal time, const PetscReal *coord, const PetscScalar *xx, PetscReal *f, void *ctx)
    637: {
    638:   Physics          phys = (Physics)ctx;
    639:   Physics_Euler   *eu   = (Physics_Euler *)phys->data;
    640:   const EulerNode *x    = (const EulerNode *)xx;
    641:   PetscScalar      p;

    643:   PetscFunctionBeginUser;
    644:   f[eu->monitor.Density]  = x->r;
    645:   f[eu->monitor.Momentum] = NormDIM(x->ru);
    646:   f[eu->monitor.Energy]   = x->e;
    647:   f[eu->monitor.Speed]    = NormDIM(x->ru) / x->r;
    648:   eu->pressure(eu->pars, x, &p);
    649:   f[eu->monitor.Pressure] = p;
    650:   PetscFunctionReturn(PETSC_SUCCESS);
    651: }

    653: static PetscErrorCode PhysicsCreate_Euler(PetscDS prob, Model mod, Physics phys, PetscOptions *PetscOptionsObject)
    654: {
    655:   Physics_Euler *eu;

    657:   PetscFunctionBeginUser;
    658:   phys->field_desc = PhysicsFields_Euler;
    659:   phys->riemann    = (RiemannFunction)PhysicsRiemann_Euler_Rusanov;
    660:   PetscCall(PetscNew(&eu));
    661:   phys->data = eu;
    662:   PetscOptionsHeadBegin(PetscOptionsObject, "Euler options");
    663:   {
    664:     eu->pars[0] = 3.0;
    665:     eu->pars[1] = 1.67;
    666:     PetscCall(PetscOptionsReal("-eu_f", "Degrees of freedom", "", eu->pars[0], &eu->pars[0], NULL));
    667:     PetscCall(PetscOptionsReal("-eu_gamma", "Heat capacity ratio", "", eu->pars[1], &eu->pars[1], NULL));
    668:   }
    669:   PetscOptionsHeadEnd();
    670:   eu->pressure   = Pressure_PG;
    671:   eu->sound      = SpeedOfSound_PG;
    672:   phys->maxspeed = 1.0;
    673:   {
    674:     const PetscInt wallids[] = {100, 101, 200, 300};
    675:     DMLabel        label;

    677:     PetscCall(DMGetLabel(dm, "Face Sets", &label));
    678:     PetscCall(PetscDSAddBoundary(prob, PETSC_TRUE, "wall", label, PETSC_STATIC_ARRAY_LENGTH(wallids), wallids, 0, 0, NULL, (void (*)())PhysicsBoundary_Euler_Wall, NULL, phys, NULL));
    679:     PetscCall(ModelSolutionSetDefault(mod, PhysicsSolution_Euler, phys));
    680:     PetscCall(ModelFunctionalRegister(mod, "Speed", &eu->monitor.Speed, PhysicsFunctional_Euler, phys));
    681:     PetscCall(ModelFunctionalRegister(mod, "Energy", &eu->monitor.Energy, PhysicsFunctional_Euler, phys));
    682:     PetscCall(ModelFunctionalRegister(mod, "Density", &eu->monitor.Density, PhysicsFunctional_Euler, phys));
    683:     PetscCall(ModelFunctionalRegister(mod, "Momentum", &eu->monitor.Momentum, PhysicsFunctional_Euler, phys));
    684:     PetscCall(ModelFunctionalRegister(mod, "Pressure", &eu->monitor.Pressure, PhysicsFunctional_Euler, phys));
    685:   }
    686:   PetscFunctionReturn(PETSC_SUCCESS);
    687: }

    689: PetscErrorCode ConstructCellBoundary(DM dm, User user)
    690: {
    691:   const char     *name   = "Cell Sets";
    692:   const char     *bdname = "split faces";
    693:   IS              regionIS, innerIS;
    694:   const PetscInt *regions, *cells;
    695:   PetscInt        numRegions, innerRegion, numCells, c;
    696:   PetscInt        cStart, cEnd, cEndInterior, fStart, fEnd;
    697:   PetscBool       hasLabel;

    699:   PetscFunctionBeginUser;
    700:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
    701:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    702:   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));

    704:   PetscCall(DMHasLabel(dm, name, &hasLabel));
    705:   if (!hasLabel) PetscFunctionReturn(PETSC_SUCCESS);
    706:   PetscCall(DMGetLabelSize(dm, name, &numRegions));
    707:   if (numRegions != 2) PetscFunctionReturn(PETSC_SUCCESS);
    708:   /* Get the inner id */
    709:   PetscCall(DMGetLabelIdIS(dm, name, &regionIS));
    710:   PetscCall(ISGetIndices(regionIS, &regions));
    711:   innerRegion = regions[0];
    712:   PetscCall(ISRestoreIndices(regionIS, &regions));
    713:   PetscCall(ISDestroy(&regionIS));
    714:   /* Find the faces between cells in different regions, could call DMPlexCreateNeighborCSR() */
    715:   PetscCall(DMGetStratumIS(dm, name, innerRegion, &innerIS));
    716:   PetscCall(ISGetLocalSize(innerIS, &numCells));
    717:   PetscCall(ISGetIndices(innerIS, &cells));
    718:   PetscCall(DMCreateLabel(dm, bdname));
    719:   for (c = 0; c < numCells; ++c) {
    720:     const PetscInt  cell = cells[c];
    721:     const PetscInt *faces;
    722:     PetscInt        numFaces, f;

    724:     PetscCheck((cell >= cStart) && (cell < cEnd), PETSC_COMM_SELF, PETSC_ERR_LIB, "Got invalid point %d which is not a cell", cell);
    725:     PetscCall(DMPlexGetConeSize(dm, cell, &numFaces));
    726:     PetscCall(DMPlexGetCone(dm, cell, &faces));
    727:     for (f = 0; f < numFaces; ++f) {
    728:       const PetscInt  face = faces[f];
    729:       const PetscInt *neighbors;
    730:       PetscInt        nC, regionA, regionB;

    732:       PetscCheck((face >= fStart) && (face < fEnd), PETSC_COMM_SELF, PETSC_ERR_LIB, "Got invalid point %d which is not a face", face);
    733:       PetscCall(DMPlexGetSupportSize(dm, face, &nC));
    734:       if (nC != 2) continue;
    735:       PetscCall(DMPlexGetSupport(dm, face, &neighbors));
    736:       if ((neighbors[0] >= cEndInterior) || (neighbors[1] >= cEndInterior)) continue;
    737:       PetscCheck((neighbors[0] >= cStart) && (neighbors[0] < cEnd), PETSC_COMM_SELF, PETSC_ERR_LIB, "Got invalid point %d which is not a cell", neighbors[0]);
    738:       PetscCheck((neighbors[1] >= cStart) && (neighbors[1] < cEnd), PETSC_COMM_SELF, PETSC_ERR_LIB, "Got invalid point %d which is not a cell", neighbors[1]);
    739:       PetscCall(DMGetLabelValue(dm, name, neighbors[0], &regionA));
    740:       PetscCall(DMGetLabelValue(dm, name, neighbors[1], &regionB));
    741:       PetscCheck(regionA >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Invalid label %s: Cell %d has no value", name, neighbors[0]);
    742:       PetscCheck(regionB >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Invalid label %s: Cell %d has no value", name, neighbors[1]);
    743:       if (regionA != regionB) PetscCall(DMSetLabelValue(dm, bdname, faces[f], 1));
    744:     }
    745:   }
    746:   PetscCall(ISRestoreIndices(innerIS, &cells));
    747:   PetscCall(ISDestroy(&innerIS));
    748:   {
    749:     DMLabel label;

    751:     PetscCall(DMGetLabel(dm, bdname, &label));
    752:     PetscCall(DMLabelView(label, PETSC_VIEWER_STDOUT_WORLD));
    753:   }
    754:   PetscFunctionReturn(PETSC_SUCCESS);
    755: }

    757: /* Right now, I have just added duplicate faces, which see both cells. We can
    758: - Add duplicate vertices and decouple the face cones
    759: - Disconnect faces from cells across the rotation gap
    760: */
    761: PetscErrorCode SplitFaces(DM *dmSplit, const char labelName[], User user)
    762: {
    763:   DM              dm = *dmSplit, sdm;
    764:   PetscSF         sfPoint, gsfPoint;
    765:   PetscSection    coordSection, newCoordSection;
    766:   Vec             coordinates;
    767:   IS              idIS;
    768:   const PetscInt *ids;
    769:   PetscInt       *newpoints;
    770:   PetscInt        dim, depth, maxConeSize, maxSupportSize, numLabels, numGhostCells;
    771:   PetscInt        numFS, fs, pStart, pEnd, p, cEnd, cEndInterior, vStart, vEnd, v, fStart, fEnd, newf, d, l;
    772:   PetscBool       hasLabel;

    774:   PetscFunctionBeginUser;
    775:   PetscCall(DMHasLabel(dm, labelName, &hasLabel));
    776:   if (!hasLabel) PetscFunctionReturn(PETSC_SUCCESS);
    777:   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), &sdm));
    778:   PetscCall(DMSetType(sdm, DMPLEX));
    779:   PetscCall(DMGetDimension(dm, &dim));
    780:   PetscCall(DMSetDimension(sdm, dim));

    782:   PetscCall(DMGetLabelIdIS(dm, labelName, &idIS));
    783:   PetscCall(ISGetLocalSize(idIS, &numFS));
    784:   PetscCall(ISGetIndices(idIS, &ids));

    786:   user->numSplitFaces = 0;
    787:   for (fs = 0; fs < numFS; ++fs) {
    788:     PetscInt numBdFaces;

    790:     PetscCall(DMGetStratumSize(dm, labelName, ids[fs], &numBdFaces));
    791:     user->numSplitFaces += numBdFaces;
    792:   }
    793:   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
    794:   pEnd += user->numSplitFaces;
    795:   PetscCall(DMPlexSetChart(sdm, pStart, pEnd));
    796:   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
    797:   PetscCall(DMPlexGetHeightStratum(dm, 0, NULL, &cEnd));
    798:   numGhostCells = cEnd - cEndInterior;
    799:   /* Set cone and support sizes */
    800:   PetscCall(DMPlexGetDepth(dm, &depth));
    801:   for (d = 0; d <= depth; ++d) {
    802:     PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
    803:     for (p = pStart; p < pEnd; ++p) {
    804:       PetscInt newp = p;
    805:       PetscInt size;

    807:       PetscCall(DMPlexGetConeSize(dm, p, &size));
    808:       PetscCall(DMPlexSetConeSize(sdm, newp, size));
    809:       PetscCall(DMPlexGetSupportSize(dm, p, &size));
    810:       PetscCall(DMPlexSetSupportSize(sdm, newp, size));
    811:     }
    812:   }
    813:   PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
    814:   for (fs = 0, newf = fEnd; fs < numFS; ++fs) {
    815:     IS              faceIS;
    816:     const PetscInt *faces;
    817:     PetscInt        numFaces, f;

    819:     PetscCall(DMGetStratumIS(dm, labelName, ids[fs], &faceIS));
    820:     PetscCall(ISGetLocalSize(faceIS, &numFaces));
    821:     PetscCall(ISGetIndices(faceIS, &faces));
    822:     for (f = 0; f < numFaces; ++f, ++newf) {
    823:       PetscInt size;

    825:       /* Right now I think that both faces should see both cells */
    826:       PetscCall(DMPlexGetConeSize(dm, faces[f], &size));
    827:       PetscCall(DMPlexSetConeSize(sdm, newf, size));
    828:       PetscCall(DMPlexGetSupportSize(dm, faces[f], &size));
    829:       PetscCall(DMPlexSetSupportSize(sdm, newf, size));
    830:     }
    831:     PetscCall(ISRestoreIndices(faceIS, &faces));
    832:     PetscCall(ISDestroy(&faceIS));
    833:   }
    834:   PetscCall(DMSetUp(sdm));
    835:   /* Set cones and supports */
    836:   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
    837:   PetscCall(PetscMalloc1(PetscMax(maxConeSize, maxSupportSize), &newpoints));
    838:   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
    839:   for (p = pStart; p < pEnd; ++p) {
    840:     const PetscInt *points, *orientations;
    841:     PetscInt        size, i, newp = p;

    843:     PetscCall(DMPlexGetConeSize(dm, p, &size));
    844:     PetscCall(DMPlexGetCone(dm, p, &points));
    845:     PetscCall(DMPlexGetConeOrientation(dm, p, &orientations));
    846:     for (i = 0; i < size; ++i) newpoints[i] = points[i];
    847:     PetscCall(DMPlexSetCone(sdm, newp, newpoints));
    848:     PetscCall(DMPlexSetConeOrientation(sdm, newp, orientations));
    849:     PetscCall(DMPlexGetSupportSize(dm, p, &size));
    850:     PetscCall(DMPlexGetSupport(dm, p, &points));
    851:     for (i = 0; i < size; ++i) newpoints[i] = points[i];
    852:     PetscCall(DMPlexSetSupport(sdm, newp, newpoints));
    853:   }
    854:   PetscCall(PetscFree(newpoints));
    855:   for (fs = 0, newf = fEnd; fs < numFS; ++fs) {
    856:     IS              faceIS;
    857:     const PetscInt *faces;
    858:     PetscInt        numFaces, f;

    860:     PetscCall(DMGetStratumIS(dm, labelName, ids[fs], &faceIS));
    861:     PetscCall(ISGetLocalSize(faceIS, &numFaces));
    862:     PetscCall(ISGetIndices(faceIS, &faces));
    863:     for (f = 0; f < numFaces; ++f, ++newf) {
    864:       const PetscInt *points;

    866:       PetscCall(DMPlexGetCone(dm, faces[f], &points));
    867:       PetscCall(DMPlexSetCone(sdm, newf, points));
    868:       PetscCall(DMPlexGetSupport(dm, faces[f], &points));
    869:       PetscCall(DMPlexSetSupport(sdm, newf, points));
    870:     }
    871:     PetscCall(ISRestoreIndices(faceIS, &faces));
    872:     PetscCall(ISDestroy(&faceIS));
    873:   }
    874:   PetscCall(ISRestoreIndices(idIS, &ids));
    875:   PetscCall(ISDestroy(&idIS));
    876:   PetscCall(DMPlexStratify(sdm));
    877:   /* Convert coordinates */
    878:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    879:   PetscCall(DMGetCoordinateSection(dm, &coordSection));
    880:   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &newCoordSection));
    881:   PetscCall(PetscSectionSetNumFields(newCoordSection, 1));
    882:   PetscCall(PetscSectionSetFieldComponents(newCoordSection, 0, dim));
    883:   PetscCall(PetscSectionSetChart(newCoordSection, vStart, vEnd));
    884:   for (v = vStart; v < vEnd; ++v) {
    885:     PetscCall(PetscSectionSetDof(newCoordSection, v, dim));
    886:     PetscCall(PetscSectionSetFieldDof(newCoordSection, v, 0, dim));
    887:   }
    888:   PetscCall(PetscSectionSetUp(newCoordSection));
    889:   PetscCall(DMSetCoordinateSection(sdm, PETSC_DETERMINE, newCoordSection));
    890:   PetscCall(PetscSectionDestroy(&newCoordSection)); /* relinquish our reference */
    891:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    892:   PetscCall(DMSetCoordinatesLocal(sdm, coordinates));
    893:   /* Convert labels */
    894:   PetscCall(DMGetNumLabels(dm, &numLabels));
    895:   for (l = 0; l < numLabels; ++l) {
    896:     const char *lname;
    897:     PetscBool   isDepth;

    899:     PetscCall(DMGetLabelName(dm, l, &lname));
    900:     PetscCall(PetscStrcmp(lname, "depth", &isDepth));
    901:     if (isDepth) continue;
    902:     PetscCall(DMCreateLabel(sdm, lname));
    903:     PetscCall(DMGetLabelIdIS(dm, lname, &idIS));
    904:     PetscCall(ISGetLocalSize(idIS, &numFS));
    905:     PetscCall(ISGetIndices(idIS, &ids));
    906:     for (fs = 0; fs < numFS; ++fs) {
    907:       IS              pointIS;
    908:       const PetscInt *points;
    909:       PetscInt        numPoints;

    911:       PetscCall(DMGetStratumIS(dm, lname, ids[fs], &pointIS));
    912:       PetscCall(ISGetLocalSize(pointIS, &numPoints));
    913:       PetscCall(ISGetIndices(pointIS, &points));
    914:       for (p = 0; p < numPoints; ++p) {
    915:         PetscInt newpoint = points[p];

    917:         PetscCall(DMSetLabelValue(sdm, lname, newpoint, ids[fs]));
    918:       }
    919:       PetscCall(ISRestoreIndices(pointIS, &points));
    920:       PetscCall(ISDestroy(&pointIS));
    921:     }
    922:     PetscCall(ISRestoreIndices(idIS, &ids));
    923:     PetscCall(ISDestroy(&idIS));
    924:   }
    925:   /* Convert pointSF */
    926:   const PetscSFNode *remotePoints;
    927:   PetscSFNode       *gremotePoints;
    928:   const PetscInt    *localPoints;
    929:   PetscInt          *glocalPoints, *newLocation, *newRemoteLocation;
    930:   PetscInt           numRoots, numLeaves;
    931:   PetscMPIInt        size;

    933:   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
    934:   PetscCall(DMGetPointSF(dm, &sfPoint));
    935:   PetscCall(DMGetPointSF(sdm, &gsfPoint));
    936:   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
    937:   PetscCall(PetscSFGetGraph(sfPoint, &numRoots, &numLeaves, &localPoints, &remotePoints));
    938:   if (numRoots >= 0) {
    939:     PetscCall(PetscMalloc2(numRoots, &newLocation, pEnd - pStart, &newRemoteLocation));
    940:     for (l = 0; l < numRoots; l++) newLocation[l] = l; /* + (l >= cEnd ? numGhostCells : 0); */
    941:     PetscCall(PetscSFBcastBegin(sfPoint, MPIU_INT, newLocation, newRemoteLocation, MPI_REPLACE));
    942:     PetscCall(PetscSFBcastEnd(sfPoint, MPIU_INT, newLocation, newRemoteLocation, MPI_REPLACE));
    943:     PetscCall(PetscMalloc1(numLeaves, &glocalPoints));
    944:     PetscCall(PetscMalloc1(numLeaves, &gremotePoints));
    945:     for (l = 0; l < numLeaves; ++l) {
    946:       glocalPoints[l]        = localPoints[l]; /* localPoints[l] >= cEnd ? localPoints[l] + numGhostCells : localPoints[l]; */
    947:       gremotePoints[l].rank  = remotePoints[l].rank;
    948:       gremotePoints[l].index = newRemoteLocation[localPoints[l]];
    949:     }
    950:     PetscCall(PetscFree2(newLocation, newRemoteLocation));
    951:     PetscCall(PetscSFSetGraph(gsfPoint, numRoots + numGhostCells, numLeaves, glocalPoints, PETSC_OWN_POINTER, gremotePoints, PETSC_OWN_POINTER));
    952:   }
    953:   PetscCall(DMDestroy(dmSplit));
    954:   *dmSplit = sdm;
    955:   PetscFunctionReturn(PETSC_SUCCESS);
    956: }

    958: PetscErrorCode CreatePartitionVec(DM dm, DM *dmCell, Vec *partition)
    959: {
    960:   PetscSF      sfPoint;
    961:   PetscSection coordSection;
    962:   Vec          coordinates;
    963:   PetscSection sectionCell;
    964:   PetscScalar *part;
    965:   PetscInt     cStart, cEnd, c;
    966:   PetscMPIInt  rank;

    968:   PetscFunctionBeginUser;
    969:   PetscCall(DMGetCoordinateSection(dm, &coordSection));
    970:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    971:   PetscCall(DMClone(dm, dmCell));
    972:   PetscCall(DMGetPointSF(dm, &sfPoint));
    973:   PetscCall(DMSetPointSF(*dmCell, sfPoint));
    974:   PetscCall(DMSetCoordinateSection(*dmCell, PETSC_DETERMINE, coordSection));
    975:   PetscCall(DMSetCoordinatesLocal(*dmCell, coordinates));
    976:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
    977:   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionCell));
    978:   PetscCall(DMPlexGetHeightStratum(*dmCell, 0, &cStart, &cEnd));
    979:   PetscCall(PetscSectionSetChart(sectionCell, cStart, cEnd));
    980:   for (c = cStart; c < cEnd; ++c) PetscCall(PetscSectionSetDof(sectionCell, c, 1));
    981:   PetscCall(PetscSectionSetUp(sectionCell));
    982:   PetscCall(DMSetLocalSection(*dmCell, sectionCell));
    983:   PetscCall(PetscSectionDestroy(&sectionCell));
    984:   PetscCall(DMCreateLocalVector(*dmCell, partition));
    985:   PetscCall(PetscObjectSetName((PetscObject)*partition, "partition"));
    986:   PetscCall(VecGetArray(*partition, &part));
    987:   for (c = cStart; c < cEnd; ++c) {
    988:     PetscScalar *p;

    990:     PetscCall(DMPlexPointLocalRef(*dmCell, c, part, &p));
    991:     p[0] = rank;
    992:   }
    993:   PetscCall(VecRestoreArray(*partition, &part));
    994:   PetscFunctionReturn(PETSC_SUCCESS);
    995: }

    997: PetscErrorCode CreateMassMatrix(DM dm, Vec *massMatrix, User user)
    998: {
    999:   DM                 plex, dmMass, dmFace, dmCell, dmCoord;
    1000:   PetscSection       coordSection;
    1001:   Vec                coordinates, facegeom, cellgeom;
    1002:   PetscSection       sectionMass;
    1003:   PetscScalar       *m;
    1004:   const PetscScalar *fgeom, *cgeom, *coords;
    1005:   PetscInt           vStart, vEnd, v;

    1007:   PetscFunctionBeginUser;
    1008:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    1009:   PetscCall(DMGetCoordinateSection(dm, &coordSection));
    1010:   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
    1011:   PetscCall(DMClone(dm, &dmMass));
    1012:   PetscCall(DMSetCoordinateSection(dmMass, PETSC_DETERMINE, coordSection));
    1013:   PetscCall(DMSetCoordinatesLocal(dmMass, coordinates));
    1014:   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &sectionMass));
    1015:   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
    1016:   PetscCall(PetscSectionSetChart(sectionMass, vStart, vEnd));
    1017:   for (v = vStart; v < vEnd; ++v) {
    1018:     PetscInt numFaces;

    1020:     PetscCall(DMPlexGetSupportSize(dmMass, v, &numFaces));
    1021:     PetscCall(PetscSectionSetDof(sectionMass, v, numFaces * numFaces));
    1022:   }
    1023:   PetscCall(PetscSectionSetUp(sectionMass));
    1024:   PetscCall(DMSetLocalSection(dmMass, sectionMass));
    1025:   PetscCall(PetscSectionDestroy(&sectionMass));
    1026:   PetscCall(DMGetLocalVector(dmMass, massMatrix));
    1027:   PetscCall(VecGetArray(*massMatrix, &m));
    1028:   PetscCall(DMPlexGetGeometryFVM(plex, &facegeom, &cellgeom, NULL));
    1029:   PetscCall(VecGetDM(facegeom, &dmFace));
    1030:   PetscCall(VecGetArrayRead(facegeom, &fgeom));
    1031:   PetscCall(VecGetDM(cellgeom, &dmCell));
    1032:   PetscCall(VecGetArrayRead(cellgeom, &cgeom));
    1033:   PetscCall(DMGetCoordinateDM(dm, &dmCoord));
    1034:   PetscCall(VecGetArrayRead(coordinates, &coords));
    1035:   for (v = vStart; v < vEnd; ++v) {
    1036:     const PetscInt        *faces;
    1037:     const PetscFVFaceGeom *fgA, *fgB, *cg;
    1038:     const PetscScalar     *vertex;
    1039:     PetscInt               numFaces, sides[2], f, g;

    1041:     PetscCall(DMPlexPointLocalRead(dmCoord, v, coords, &vertex));
    1042:     PetscCall(DMPlexGetSupportSize(dmMass, v, &numFaces));
    1043:     PetscCall(DMPlexGetSupport(dmMass, v, &faces));
    1044:     for (f = 0; f < numFaces; ++f) {
    1045:       sides[0] = faces[f];
    1046:       PetscCall(DMPlexPointLocalRead(dmFace, faces[f], fgeom, &fgA));
    1047:       for (g = 0; g < numFaces; ++g) {
    1048:         const PetscInt *cells = NULL;
    1049:         PetscReal       area  = 0.0;
    1050:         PetscInt        numCells;

    1052:         sides[1] = faces[g];
    1053:         PetscCall(DMPlexPointLocalRead(dmFace, faces[g], fgeom, &fgB));
    1054:         PetscCall(DMPlexGetJoin(dmMass, 2, sides, &numCells, &cells));
    1055:         PetscCheck(numCells == 1, PETSC_COMM_SELF, PETSC_ERR_LIB, "Invalid join for faces");
    1056:         PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cgeom, &cg));
    1057:         area += PetscAbsScalar((vertex[0] - cg->centroid[0]) * (fgA->centroid[1] - cg->centroid[1]) - (vertex[1] - cg->centroid[1]) * (fgA->centroid[0] - cg->centroid[0]));
    1058:         area += PetscAbsScalar((vertex[0] - cg->centroid[0]) * (fgB->centroid[1] - cg->centroid[1]) - (vertex[1] - cg->centroid[1]) * (fgB->centroid[0] - cg->centroid[0]));
    1059:         m[f * numFaces + g] = Dot2(fgA->normal, fgB->normal) * area * 0.5;
    1060:         PetscCall(DMPlexRestoreJoin(dmMass, 2, sides, &numCells, &cells));
    1061:       }
    1062:     }
    1063:   }
    1064:   PetscCall(VecRestoreArrayRead(facegeom, &fgeom));
    1065:   PetscCall(VecRestoreArrayRead(cellgeom, &cgeom));
    1066:   PetscCall(VecRestoreArrayRead(coordinates, &coords));
    1067:   PetscCall(VecRestoreArray(*massMatrix, &m));
    1068:   PetscCall(DMDestroy(&dmMass));
    1069:   PetscCall(DMDestroy(&plex));
    1070:   PetscFunctionReturn(PETSC_SUCCESS);
    1071: }

    1073: PetscErrorCode SetUpLocalSpace(DM dm, User user)
    1074: {
    1075:   PetscSection stateSection;
    1076:   Physics      phys;
    1077:   PetscInt     dof = user->model->physics->dof, *cind, d, stateSize, cStart, cEnd, cEndInterior, c, i;

    1079:   PetscFunctionBeginUser;
    1080:   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
    1081:   PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &cEndInterior, NULL));
    1082:   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &stateSection));
    1083:   phys = user->model->physics;
    1084:   PetscCall(PetscSectionSetNumFields(stateSection, phys->nfields));
    1085:   for (i = 0; i < phys->nfields; i++) {
    1086:     PetscCall(PetscSectionSetFieldName(stateSection, i, phys->field_desc[i].name));
    1087:     PetscCall(PetscSectionSetFieldComponents(stateSection, i, phys->field_desc[i].dof));
    1088:   }
    1089:   PetscCall(PetscSectionSetChart(stateSection, cStart, cEnd));
    1090:   for (c = cStart; c < cEnd; ++c) {
    1091:     for (i = 0; i < phys->nfields; i++) PetscCall(PetscSectionSetFieldDof(stateSection, c, i, phys->field_desc[i].dof));
    1092:     PetscCall(PetscSectionSetDof(stateSection, c, dof));
    1093:   }
    1094:   for (c = cEndInterior; c < cEnd; ++c) PetscCall(PetscSectionSetConstraintDof(stateSection, c, dof));
    1095:   PetscCall(PetscSectionSetUp(stateSection));
    1096:   PetscCall(PetscMalloc1(dof, &cind));
    1097:   for (d = 0; d < dof; ++d) cind[d] = d;
    1098: #if 0
    1099:   for (c = cStart; c < cEnd; ++c) {
    1100:     PetscInt val;

    1102:     PetscCall(DMGetLabelValue(dm, "vtk", c, &val));
    1103:     if (val < 0) PetscCall(PetscSectionSetConstraintIndices(stateSection, c, cind));
    1104:   }
    1105: #endif
    1106:   for (c = cEndInterior; c < cEnd; ++c) PetscCall(PetscSectionSetConstraintIndices(stateSection, c, cind));
    1107:   PetscCall(PetscFree(cind));
    1108:   PetscCall(PetscSectionGetStorageSize(stateSection, &stateSize));
    1109:   PetscCall(DMSetLocalSection(dm, stateSection));
    1110:   PetscCall(PetscSectionDestroy(&stateSection));
    1111:   PetscFunctionReturn(PETSC_SUCCESS);
    1112: }

    1114: #if 0
    1115: PetscErrorCode SetUpBoundaries(DM dm, User user)
    1116: {
    1117:   Model          mod = user->model;
    1118:   BoundaryLink   b;

    1120:   PetscFunctionBeginUser;
    1121:   PetscOptionsBegin(PetscObjectComm((PetscObject)dm),NULL,"Boundary condition options","");
    1122:   for (b = mod->boundary; b; b=b->next) {
    1123:     char      optname[512];
    1124:     PetscInt  ids[512],len = 512;
    1125:     PetscBool flg;
    1126:     PetscCall(PetscSNPrintf(optname,sizeof optname,"-bc_%s",b->name));
    1127:     PetscCall(PetscMemzero(ids,sizeof(ids)));
    1128:     PetscCall(PetscOptionsIntArray(optname,"List of boundary IDs","",ids,&len,&flg));
    1129:     if (flg) {
    1130:       /* TODO: check all IDs to make sure they exist in the mesh */
    1131:       PetscCall(PetscFree(b->ids));
    1132:       b->numids = len;
    1133:       PetscCall(PetscMalloc1(len,&b->ids));
    1134:       PetscCall(PetscArraycpy(b->ids,ids,len));
    1135:     }
    1136:   }
    1137:   PetscOptionsEnd();
    1138:   PetscFunctionReturn(PETSC_SUCCESS);
    1139: }
    1140: #endif

    1142: /* Behavior will be different for multi-physics or when using non-default boundary conditions */
    1143: static PetscErrorCode ModelSolutionSetDefault(Model mod, SolutionFunction func, void *ctx)
    1144: {
    1145:   PetscFunctionBeginUser;
    1146:   mod->solution    = func;
    1147:   mod->solutionctx = ctx;
    1148:   PetscFunctionReturn(PETSC_SUCCESS);
    1149: }

    1151: static PetscErrorCode ModelFunctionalRegister(Model mod, const char *name, PetscInt *offset, FunctionalFunction func, void *ctx)
    1152: {
    1153:   FunctionalLink link, *ptr;
    1154:   PetscInt       lastoffset = -1;

    1156:   PetscFunctionBeginUser;
    1157:   for (ptr = &mod->functionalRegistry; *ptr; ptr = &(*ptr)->next) lastoffset = (*ptr)->offset;
    1158:   PetscCall(PetscNew(&link));
    1159:   PetscCall(PetscStrallocpy(name, &link->name));
    1160:   link->offset = lastoffset + 1;
    1161:   link->func   = func;
    1162:   link->ctx    = ctx;
    1163:   link->next   = NULL;
    1164:   *ptr         = link;
    1165:   *offset      = link->offset;
    1166:   PetscFunctionReturn(PETSC_SUCCESS);
    1167: }

    1169: static PetscErrorCode ModelFunctionalSetFromOptions(Model mod, PetscOptions *PetscOptionsObject)
    1170: {
    1171:   PetscInt       i, j;
    1172:   FunctionalLink link;
    1173:   char          *names[256];

    1175:   PetscFunctionBeginUser;
    1176:   mod->numMonitored = PETSC_STATIC_ARRAY_LENGTH(names);
    1177:   PetscCall(PetscOptionsStringArray("-monitor", "list of functionals to monitor", "", names, &mod->numMonitored, NULL));
    1178:   /* Create list of functionals that will be computed somehow */
    1179:   PetscCall(PetscMalloc1(mod->numMonitored, &mod->functionalMonitored));
    1180:   /* Create index of calls that we will have to make to compute these functionals (over-allocation in general). */
    1181:   PetscCall(PetscMalloc1(mod->numMonitored, &mod->functionalCall));
    1182:   mod->numCall = 0;
    1183:   for (i = 0; i < mod->numMonitored; i++) {
    1184:     for (link = mod->functionalRegistry; link; link = link->next) {
    1185:       PetscBool match;
    1186:       PetscCall(PetscStrcasecmp(names[i], link->name, &match));
    1187:       if (match) break;
    1188:     }
    1189:     PetscCheck(link, mod->comm, PETSC_ERR_USER, "No known functional '%s'", names[i]);
    1190:     mod->functionalMonitored[i] = link;
    1191:     for (j = 0; j < i; j++) {
    1192:       if (mod->functionalCall[j]->func == link->func && mod->functionalCall[j]->ctx == link->ctx) goto next_name;
    1193:     }
    1194:     mod->functionalCall[mod->numCall++] = link; /* Just points to the first link using the result. There may be more results. */
    1195:   next_name:
    1196:     PetscCall(PetscFree(names[i]));
    1197:   }

    1199:   /* Find out the maximum index of any functional computed by a function we will be calling (even if we are not using it) */
    1200:   mod->maxComputed = -1;
    1201:   for (link = mod->functionalRegistry; link; link = link->next) {
    1202:     for (i = 0; i < mod->numCall; i++) {
    1203:       FunctionalLink call = mod->functionalCall[i];
    1204:       if (link->func == call->func && link->ctx == call->ctx) mod->maxComputed = PetscMax(mod->maxComputed, link->offset);
    1205:     }
    1206:   }
    1207:   PetscFunctionReturn(PETSC_SUCCESS);
    1208: }

    1210: static PetscErrorCode FunctionalLinkDestroy(FunctionalLink *link)
    1211: {
    1212:   FunctionalLink l, next;

    1214:   PetscFunctionBeginUser;
    1215:   if (!link) PetscFunctionReturn(PETSC_SUCCESS);
    1216:   l     = *link;
    1217:   *link = NULL;
    1218:   for (; l; l = next) {
    1219:     next = l->next;
    1220:     PetscCall(PetscFree(l->name));
    1221:     PetscCall(PetscFree(l));
    1222:   }
    1223:   PetscFunctionReturn(PETSC_SUCCESS);
    1224: }

    1226: PetscErrorCode SetInitialCondition(DM dm, Vec X, User user)
    1227: {
    1228:   DM                 plex, dmCell;
    1229:   Model              mod = user->model;
    1230:   Vec                cellgeom;
    1231:   const PetscScalar *cgeom;
    1232:   PetscScalar       *x;
    1233:   PetscInt           cStart, cEnd, c;

    1235:   PetscFunctionBeginUser;
    1236:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    1237:   PetscCall(DMPlexGetGeometryFVM(plex, NULL, &cellgeom, NULL));
    1238:   PetscCall(VecGetDM(cellgeom, &dmCell));
    1239:   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1240:   PetscCall(VecGetArrayRead(cellgeom, &cgeom));
    1241:   PetscCall(VecGetArray(X, &x));
    1242:   for (c = cStart; c < cEnd; ++c) {
    1243:     const PetscFVCellGeom *cg;
    1244:     PetscScalar           *xc;

    1246:     PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
    1247:     PetscCall(DMPlexPointGlobalRef(dm, c, x, &xc));
    1248:     if (xc) PetscCall((*mod->solution)(mod, 0.0, cg->centroid, xc, mod->solutionctx));
    1249:   }
    1250:   PetscCall(VecRestoreArrayRead(cellgeom, &cgeom));
    1251:   PetscCall(VecRestoreArray(X, &x));
    1252:   PetscCall(DMDestroy(&plex));
    1253:   PetscFunctionReturn(PETSC_SUCCESS);
    1254: }

    1256: static PetscErrorCode OutputVTK(DM dm, const char *filename, PetscViewer *viewer)
    1257: {
    1258:   PetscFunctionBeginUser;
    1259:   PetscCall(PetscViewerCreate(PetscObjectComm((PetscObject)dm), viewer));
    1260:   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERVTK));
    1261:   PetscCall(PetscViewerFileSetName(*viewer, filename));
    1262:   PetscFunctionReturn(PETSC_SUCCESS);
    1263: }

    1265: static PetscErrorCode MonitorVTK(TS ts, PetscInt stepnum, PetscReal time, Vec X, void *ctx)
    1266: {
    1267:   User        user = (User)ctx;
    1268:   DM          dm, plex;
    1269:   PetscViewer viewer;
    1270:   char        filename[PETSC_MAX_PATH_LEN], *ftable = NULL;
    1271:   PetscReal   xnorm;

    1273:   PetscFunctionBeginUser;
    1274:   PetscCall(PetscObjectSetName((PetscObject)X, "solution"));
    1275:   PetscCall(VecGetDM(X, &dm));
    1276:   PetscCall(VecNorm(X, NORM_INFINITY, &xnorm));
    1277:   if (stepnum >= 0) { /* No summary for final time */
    1278:     Model              mod = user->model;
    1279:     Vec                cellgeom;
    1280:     PetscInt           c, cStart, cEnd, fcount, i;
    1281:     size_t             ftableused, ftablealloc;
    1282:     const PetscScalar *cgeom, *x;
    1283:     DM                 dmCell;
    1284:     PetscReal         *fmin, *fmax, *fintegral, *ftmp;

    1286:     PetscCall(DMConvert(dm, DMPLEX, &plex));
    1287:     PetscCall(DMPlexGetGeometryFVM(plex, NULL, &cellgeom, NULL));
    1288:     fcount = mod->maxComputed + 1;
    1289:     PetscCall(PetscMalloc4(fcount, &fmin, fcount, &fmax, fcount, &fintegral, fcount, &ftmp));
    1290:     for (i = 0; i < fcount; i++) {
    1291:       fmin[i]      = PETSC_MAX_REAL;
    1292:       fmax[i]      = PETSC_MIN_REAL;
    1293:       fintegral[i] = 0;
    1294:     }
    1295:     PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
    1296:     PetscCall(VecGetDM(cellgeom, &dmCell));
    1297:     PetscCall(VecGetArrayRead(cellgeom, &cgeom));
    1298:     PetscCall(VecGetArrayRead(X, &x));
    1299:     for (c = cStart; c < cEnd; ++c) {
    1300:       const PetscFVCellGeom *cg;
    1301:       const PetscScalar     *cx;
    1302:       PetscCall(DMPlexPointLocalRead(dmCell, c, cgeom, &cg));
    1303:       PetscCall(DMPlexPointGlobalRead(dm, c, x, &cx));
    1304:       if (!cx) continue; /* not a global cell */
    1305:       for (i = 0; i < mod->numCall; i++) {
    1306:         FunctionalLink flink = mod->functionalCall[i];
    1307:         PetscCall((*flink->func)(mod, time, cg->centroid, cx, ftmp, flink->ctx));
    1308:       }
    1309:       for (i = 0; i < fcount; i++) {
    1310:         fmin[i] = PetscMin(fmin[i], ftmp[i]);
    1311:         fmax[i] = PetscMax(fmax[i], ftmp[i]);
    1312:         fintegral[i] += cg->volume * ftmp[i];
    1313:       }
    1314:     }
    1315:     PetscCall(VecRestoreArrayRead(cellgeom, &cgeom));
    1316:     PetscCall(VecRestoreArrayRead(X, &x));
    1317:     PetscCall(DMDestroy(&plex));
    1318:     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, fmin, fcount, MPIU_REAL, MPIU_MIN, PetscObjectComm((PetscObject)ts)));
    1319:     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, fmax, fcount, MPIU_REAL, MPIU_MAX, PetscObjectComm((PetscObject)ts)));
    1320:     PetscCallMPI(MPIU_Allreduce(MPI_IN_PLACE, fintegral, fcount, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)ts)));

    1322:     ftablealloc = fcount * 100;
    1323:     ftableused  = 0;
    1324:     PetscCall(PetscMalloc1(ftablealloc, &ftable));
    1325:     for (i = 0; i < mod->numMonitored; i++) {
    1326:       size_t         countused;
    1327:       char           buffer[256], *p;
    1328:       FunctionalLink flink = mod->functionalMonitored[i];
    1329:       PetscInt       id    = flink->offset;
    1330:       if (i % 3) {
    1331:         PetscCall(PetscArraycpy(buffer, "  ", 2));
    1332:         p = buffer + 2;
    1333:       } else if (i) {
    1334:         char newline[] = "\n";
    1335:         PetscCall(PetscArraycpy(buffer, newline, sizeof(newline) - 1));
    1336:         p = buffer + sizeof(newline) - 1;
    1337:       } else {
    1338:         p = buffer;
    1339:       }
    1340:       PetscCall(PetscSNPrintfCount(p, sizeof buffer - (p - buffer), "%12s [%10.7g,%10.7g] int %10.7g", &countused, flink->name, (double)fmin[id], (double)fmax[id], (double)fintegral[id]));
    1341:       countused += p - buffer;
    1342:       if (countused > ftablealloc - ftableused - 1) { /* reallocate */
    1343:         char *ftablenew;
    1344:         ftablealloc = 2 * ftablealloc + countused;
    1345:         PetscCall(PetscMalloc(ftablealloc, &ftablenew));
    1346:         PetscCall(PetscArraycpy(ftablenew, ftable, ftableused));
    1347:         PetscCall(PetscFree(ftable));
    1348:         ftable = ftablenew;
    1349:       }
    1350:       PetscCall(PetscArraycpy(ftable + ftableused, buffer, countused));
    1351:       ftableused += countused;
    1352:       ftable[ftableused] = 0;
    1353:     }
    1354:     PetscCall(PetscFree4(fmin, fmax, fintegral, ftmp));

    1356:     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)ts), "% 3" PetscInt_FMT "  time %8.4g  |x| %8.4g  %s\n", stepnum, (double)time, (double)xnorm, ftable ? ftable : ""));
    1357:     PetscCall(PetscFree(ftable));
    1358:   }
    1359:   if (user->vtkInterval < 1) PetscFunctionReturn(PETSC_SUCCESS);
    1360:   if ((stepnum == -1) ^ (stepnum % user->vtkInterval == 0)) {
    1361:     if (stepnum == -1) { /* Final time is not multiple of normal time interval, write it anyway */
    1362:       PetscCall(TSGetStepNumber(ts, &stepnum));
    1363:     }
    1364:     PetscCall(PetscSNPrintf(filename, sizeof filename, "ex11-%03" PetscInt_FMT ".vtu", stepnum));
    1365:     PetscCall(OutputVTK(dm, filename, &viewer));
    1366:     PetscCall(VecView(X, viewer));
    1367:     PetscCall(PetscViewerDestroy(&viewer));
    1368:   }
    1369:   PetscFunctionReturn(PETSC_SUCCESS);
    1370: }

    1372: static PetscErrorCode OutputBIN(DM dm, const char *filename, PetscViewer *viewer)
    1373: {
    1374:   PetscFunctionBeginUser;
    1375:   PetscCall(PetscViewerCreate(PetscObjectComm((PetscObject)dm), viewer));
    1376:   PetscCall(PetscViewerSetType(*viewer, PETSCVIEWERBINARY));
    1377:   PetscCall(PetscViewerFileSetMode(*viewer, FILE_MODE_WRITE));
    1378:   PetscCall(PetscViewerFileSetName(*viewer, filename));
    1379:   PetscFunctionReturn(PETSC_SUCCESS);
    1380: }

    1382: static PetscErrorCode TestMonitor(DM dm, const char *filename, Vec X, PetscReal time)
    1383: {
    1384:   Vec         odesolution;
    1385:   PetscInt    Nr;
    1386:   PetscBool   equal;
    1387:   PetscReal   timeread;
    1388:   PetscViewer viewer;

    1390:   PetscFunctionBeginUser;
    1391:   PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, filename, FILE_MODE_READ, &viewer));
    1392:   PetscCall(VecCreate(PETSC_COMM_WORLD, &odesolution));
    1393:   PetscCall(VecLoad(odesolution, viewer));
    1394:   VecEqual(X, odesolution, &equal);
    1395:   PetscCheck(equal, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED, "Error in reading the vec data from file");
    1396:   else
    1397:   {
    1398:     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "IO test OK for Vec\n"));
    1399:   }
    1400:   /*Nr   = 1;
    1401:    PetscCall(PetscRealLoad(Nr,&Nr,&timeread,viewer));*/
    1402:   PetscCall(PetscViewerBinaryRead(viewer, &timeread, 1, NULL, PETSC_REAL));

    1404:   PetscCheck(timeread == time, PETSC_COMM_WORLD, PETSC_ERR_FILE_UNEXPECTED, "Error in reading the scalar data from file");
    1405:   else
    1406:   {
    1407:     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "IO test OK for PetscReal\n"));
    1408:   }

    1410:   PetscCall(PetscViewerDestroy(&viewer));
    1411:   PetscCall(VecDestroy(&odesolution));
    1412:   PetscFunctionReturn(PETSC_SUCCESS);
    1413: }

    1415: static PetscErrorCode MonitorBIN(TS ts, PetscInt stepnum, PetscReal time, Vec X, void *ctx)
    1416: {
    1417:   User        user = (User)ctx;
    1418:   DM          dm;
    1419:   PetscViewer viewer;
    1420:   char        filename[PETSC_MAX_PATH_LEN];

    1422:   PetscFunctionBeginUser;
    1423:   PetscCall(VecGetDM(X, &dm));
    1424:   PetscCall(PetscSNPrintf(filename, sizeof filename, "ex11-SA-%06d.bin", stepnum));
    1425:   PetscCall(OutputBIN(dm, filename, &viewer));
    1426:   PetscCall(VecView(X, viewer));
    1427:   PetscCall(PetscRealView(1, &time, viewer));
    1428:   /* PetscCall(PetscViewerBinaryWrite(viewer,&time,1,PETSC_REAL));*/
    1429:   PetscCall(PetscViewerDestroy(&viewer));
    1430:   PetscCall(TestMonitor(dm, filename, X, time));
    1431:   PetscFunctionReturn(PETSC_SUCCESS);
    1432: }

    1434: int main(int argc, char **argv)
    1435: {
    1436:   MPI_Comm          comm;
    1437:   PetscFV           fvm;
    1438:   User              user;
    1439:   Model             mod;
    1440:   Physics           phys;
    1441:   DM                dm, plex;
    1442:   PetscReal         ftime, cfl, dt, minRadius;
    1443:   PetscInt          dim, nsteps;
    1444:   TS                ts;
    1445:   TSConvergedReason reason;
    1446:   Vec               X;
    1447:   PetscViewer       viewer;
    1448:   PetscMPIInt       rank;
    1449:   PetscBool         vtkCellGeom, splitFaces;
    1450:   PetscInt          overlap, f;
    1451:   char              filename[PETSC_MAX_PATH_LEN] = "sevenside.exo";

    1453:   PetscFunctionBeginUser;
    1454:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
    1455:   comm = PETSC_COMM_WORLD;
    1456:   PetscCallMPI(MPI_Comm_rank(comm, &rank));

    1458:   PetscCall(PetscNew(&user));
    1459:   PetscCall(PetscNew(&user->model));
    1460:   PetscCall(PetscNew(&user->model->physics));
    1461:   mod       = user->model;
    1462:   phys      = mod->physics;
    1463:   mod->comm = comm;

    1465:   /* Register physical models to be available on the command line */
    1466:   PetscCall(PetscFunctionListAdd(&PhysicsList, "advect", PhysicsCreate_Advect));
    1467:   PetscCall(PetscFunctionListAdd(&PhysicsList, "sw", PhysicsCreate_SW));
    1468:   PetscCall(PetscFunctionListAdd(&PhysicsList, "euler", PhysicsCreate_Euler));

    1470:   PetscOptionsBegin(comm, NULL, "Unstructured Finite Volume Mesh Options", "");
    1471:   {
    1472:     cfl = 0.9 * 4; /* default SSPRKS2 with s=5 stages is stable for CFL number s-1 */
    1473:     PetscCall(PetscOptionsReal("-ufv_cfl", "CFL number per step", "", cfl, &cfl, NULL));
    1474:     PetscCall(PetscOptionsString("-f", "Exodus.II filename to read", "", filename, filename, sizeof(filename), NULL));
    1475:     splitFaces = PETSC_FALSE;
    1476:     PetscCall(PetscOptionsBool("-ufv_split_faces", "Split faces between cell sets", "", splitFaces, &splitFaces, NULL));
    1477:     overlap = 1;
    1478:     PetscCall(PetscOptionsInt("-ufv_mesh_overlap", "Number of cells to overlap partitions", "", overlap, &overlap, NULL));
    1479:     user->vtkInterval = 1;
    1480:     PetscCall(PetscOptionsInt("-ufv_vtk_interval", "VTK output interval (0 to disable)", "", user->vtkInterval, &user->vtkInterval, NULL));
    1481:     vtkCellGeom = PETSC_FALSE;
    1482:     PetscCall(PetscOptionsBool("-ufv_vtk_cellgeom", "Write cell geometry (for debugging)", "", vtkCellGeom, &vtkCellGeom, NULL));
    1483:   }
    1484:   PetscOptionsEnd();
    1485:   PetscCall(DMPlexCreateExodusFromFile(comm, filename, PETSC_TRUE, &dm));
    1486:   PetscCall(DMViewFromOptions(dm, NULL, "-dm_view"));
    1487:   PetscCall(DMGetDimension(dm, &dim));

    1489:   PetscOptionsBegin(comm, NULL, "Unstructured Finite Volume Physics Options", "");
    1490:   {
    1491:     PetscDS prob;
    1492:     PetscErrorCode (*physcreate)(PetscDS, Model, Physics);
    1493:     char physname[256] = "advect";

    1495:     PetscCall(DMCreateLabel(dm, "Face Sets"));
    1496:     PetscCall(PetscOptionsFList("-physics", "Physics module to solve", "", PhysicsList, physname, physname, sizeof physname, NULL));
    1497:     PetscCall(PetscFunctionListFind(PhysicsList, physname, &physcreate));
    1498:     PetscCall(PetscMemzero(phys, sizeof(struct _n_Physics)));
    1499:     PetscCall(DMGetDS(dm, &prob));
    1500:     PetscCall((*physcreate)(prob, mod, phys));
    1501:     mod->maxspeed = phys->maxspeed;
    1502:     /* Count number of fields and dofs */
    1503:     for (phys->nfields = 0, phys->dof = 0; phys->field_desc[phys->nfields].name; phys->nfields++) phys->dof += phys->field_desc[phys->nfields].dof;

    1505:     PetscCheck(mod->maxspeed > 0, comm, PETSC_ERR_ARG_WRONGSTATE, "Physics '%s' did not set maxspeed", physname);
    1506:     PetscCheck(phys->dof > 0, comm, PETSC_ERR_ARG_WRONGSTATE, "Physics '%s' did not set dof", physname);
    1507:     PetscCall(ModelFunctionalSetFromOptions(mod, PetscOptionsObject));
    1508:   }
    1509:   PetscOptionsEnd();
    1510:   {
    1511:     DM dmDist;

    1513:     PetscCall(DMSetBasicAdjacency(dm, PETSC_TRUE, PETSC_FALSE));
    1514:     PetscCall(DMPlexDistribute(dm, overlap, NULL, &dmDist));
    1515:     if (dmDist) {
    1516:       PetscCall(DMDestroy(&dm));
    1517:       dm = dmDist;
    1518:     }
    1519:   }
    1520:   PetscCall(DMSetFromOptions(dm));
    1521:   {
    1522:     DM gdm;

    1524:     PetscCall(DMPlexConstructGhostCells(dm, NULL, NULL, &gdm));
    1525:     PetscCall(DMDestroy(&dm));
    1526:     dm = gdm;
    1527:     PetscCall(DMViewFromOptions(dm, NULL, "-dm_view"));
    1528:   }
    1529:   if (splitFaces) PetscCall(ConstructCellBoundary(dm, user));
    1530:   PetscCall(SplitFaces(&dm, "split faces", user));

    1532:   PetscCall(PetscFVCreate(comm, &fvm));
    1533:   PetscCall(PetscFVSetFromOptions(fvm));
    1534:   PetscCall(DMSetNumFields(dm, phys->nfields));
    1535:   for (f = 0; f < phys->nfields; ++f) PetscCall(DMSetField(dm, f, (PetscObject)fvm));
    1536:   PetscCall(PetscFVSetNumComponents(fvm, phys->dof));
    1537:   PetscCall(PetscFVSetSpatialDimension(fvm, dim));

    1539:   /* Set up DM with section describing local vector and configure local vector. */
    1540:   PetscCall(SetUpLocalSpace(dm, user));

    1542:   PetscCall(TSCreate(comm, &ts));
    1543:   PetscCall(TSSetType(ts, TSSSP));
    1544:   /* PetscCall(TSSetType(ts, TSRK)); */
    1545:   PetscCall(TSSetDM(ts, dm));
    1546:   /* PetscCall(TSMonitorSet(ts,MonitorVTK,user,NULL)); */
    1547:   PetscCall(TSMonitorSet(ts, MonitorBIN, user, NULL));
    1548:   PetscCall(DMTSSetRHSFunctionLocal(dm, DMPlexTSComputeRHSFunctionFVM, user));
    1549:   PetscCall(DMCreateGlobalVector(dm, &X));
    1550:   PetscCall(PetscObjectSetName((PetscObject)X, "solution"));
    1551:   PetscCall(SetInitialCondition(dm, X, user));
    1552:   PetscCall(DMConvert(dm, DMPLEX, &plex));
    1553:   if (vtkCellGeom) {
    1554:     DM  dmCell;
    1555:     Vec cellgeom, partition;

    1557:     PetscCall(DMPlexGetGeometryFVM(plex, NULL, &cellgeom, NULL));
    1558:     PetscCall(OutputVTK(dm, "ex11-cellgeom.vtk", &viewer));
    1559:     PetscCall(VecView(cellgeom, viewer));
    1560:     PetscCall(PetscViewerDestroy(&viewer));
    1561:     PetscCall(CreatePartitionVec(dm, &dmCell, &partition));
    1562:     PetscCall(OutputVTK(dmCell, "ex11-partition.vtk", &viewer));
    1563:     PetscCall(VecView(partition, viewer));
    1564:     PetscCall(PetscViewerDestroy(&viewer));
    1565:     PetscCall(VecDestroy(&partition));
    1566:     PetscCall(DMDestroy(&dmCell));
    1567:   }

    1569:   PetscCall(DMPlexGetGeometryFVM(plex, NULL, NULL, &minRadius));
    1570:   PetscCall(DMDestroy(&plex));
    1571:   PetscCall(TSSetMaxTime(ts, 2.0));
    1572:   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER));
    1573:   dt = cfl * minRadius / user->model->maxspeed;
    1574:   PetscCall(TSSetTimeStep(ts, dt));
    1575:   PetscCall(TSSetFromOptions(ts));
    1576:   PetscCall(TSSolve(ts, X));
    1577:   PetscCall(TSGetSolveTime(ts, &ftime));
    1578:   PetscCall(TSGetStepNumber(ts, &nsteps));
    1579:   PetscCall(TSGetConvergedReason(ts, &reason));
    1580:   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%s at time %g after %" PetscInt_FMT " steps\n", TSConvergedReasons[reason], (double)ftime, nsteps));
    1581:   PetscCall(TSDestroy(&ts));

    1583:   PetscCall(PetscFunctionListDestroy(&PhysicsList));
    1584:   PetscCall(FunctionalLinkDestroy(&user->model->functionalRegistry));
    1585:   PetscCall(PetscFree(user->model->functionalMonitored));
    1586:   PetscCall(PetscFree(user->model->functionalCall));
    1587:   PetscCall(PetscFree(user->model->physics->data));
    1588:   PetscCall(PetscFree(user->model->physics));
    1589:   PetscCall(PetscFree(user->model));
    1590:   PetscCall(PetscFree(user));
    1591:   PetscCall(VecDestroy(&X));
    1592:   PetscCall(PetscFVDestroy(&fvm));
    1593:   PetscCall(DMDestroy(&dm));
    1594:   PetscCall(PetscFinalize());
    1595:   return 0;
    1596: }