17f296bb3SBarry Smith(ch_fe)= 27f296bb3SBarry Smith 37f296bb3SBarry Smith# PetscFE: Finite Element Infrastructure in PETSc 47f296bb3SBarry Smith 57f296bb3SBarry SmithThis chapter introduces the `PetscFE` class, and related subclasses `PetscSpace` and `PetscDualSpace`, which are used to represent finite element discretizations. It details there interaction with the `DMPLEX` class to assemble functions and operators over computational meshes, and produce optimal solvers by constructing multilevel iterations, for example using `PCPATCH`. The idea behind these classes is not to encompass all of computational finite elements, but rather to establish an interface and infrastructure that will allow PETSc to leverage the excellent work done in packages such as Firedrake, FEniCS, LibMesh, and Deal.II. 67f296bb3SBarry Smith 77f296bb3SBarry Smith## Using Pointwise Functions to Specify Finite Element Problems 87f296bb3SBarry Smith 97f296bb3SBarry SmithSee the paper about [Unified Residual Evaluation](https://arxiv.org/abs/1309.1204), which explains the use of pointwise evaluation functions to describe weak forms. 107f296bb3SBarry Smith 117f296bb3SBarry Smith## Describing a particular finite element problem to PETSc 127f296bb3SBarry Smith 137f296bb3SBarry SmithA finite element problem is presented to PETSc in a series of steps. This is both to facilitate automation, and to allow multiple entry points for user code and external packages since so much finite element software already exists. First, we tell the `DM`, usually a `DMPLEX` or `DMFOREST`, that we have a set of finite element fields which we intended to solve for in our problem, using 147f296bb3SBarry Smith 157f296bb3SBarry Smith``` 167f296bb3SBarry SmithDMAddField(dm, NULL, presDisc); 177f296bb3SBarry SmithDMAddField(dm, channelLabel, velDisc); 187f296bb3SBarry Smith``` 197f296bb3SBarry Smith 207f296bb3SBarry SmithThe second argument is a `DMLabel` object indicating the support of the field on the mesh, with `NULL` indicating the entire domain. Once we have a set of fields, we calls 217f296bb3SBarry Smith 222192575eSBarry SmithA `PetscDS` (Discrete System) encodes a set of equations posed in a discrete space, which represents a set of nonlinear continuum equations. 232192575eSBarry SmithThe equations can have multiple fields, each field having a different discretization. In addition, different pieces of the domain can have different field combinations and equations. 242192575eSBarry Smith 252192575eSBarry SmithThe DS provides the user a description of the approximation space on any given cell. It also gives pointwise functions representing the equations. 262192575eSBarry Smith 272192575eSBarry SmithEach field is associated with a `DMLabel`, marking the cells on which it is supported. Note that a field can be 282192575eSBarry Smithsupported on the closure of a cell not in the label due to overlap of the boundary of neighboring cells. The `DM` 292192575eSBarry Smiththen creates a `PetscDS` for each set of cells with identical approximation spaces. When assembling, the user asks for 302192575eSBarry Smiththe space associated with a given cell. `DMPLEX` uses the labels associated with each `PetscDS` in the default integration loop. 312192575eSBarry Smith 327f296bb3SBarry Smith``` 337f296bb3SBarry SmithDMCreateDS(dm); 347f296bb3SBarry Smith``` 357f296bb3SBarry Smith 367f296bb3SBarry SmithThis divides the computational domain into subdomains, called *regions* in PETSc, each with a unique set of fields supported on it. These subdomain are identified by labels, and each one has a `PetscDS` object describing the discrete system on that subdomain. There are query functions to get the set of `PetscDS` objects for the `DM`, but it is usually easiest to get the proper `PetscDS` for a given cell using 377f296bb3SBarry Smith 387f296bb3SBarry Smith``` 397f296bb3SBarry SmithDMGetCellDS(dm, cell, &ds, NULL); 407f296bb3SBarry Smith``` 417f296bb3SBarry Smith 427f296bb3SBarry SmithEach `PetscDS` object has a set of fields, each with a `PetscFE` or `PetscFV` discretization. This allows it to calculate the size of the local discrete approximation, as well as allocate scratch space for all the associated computations. The final thing needed is to specify the actual equations to be enforced on each region. The `PetscDS` contains a `PetscWeakForm` object that holds callback function pointers that define the equations. A simplified, top-level interface through `PetscDS` allows users to quickly define problems for a single region. For example, in 437f296bb3SBarry Smith<a href="PETSC_DOC_OUT_ROOT_PLACEHOLDER/src/snes/tutorials/ex13.c.html">SNES Tutorial ex13</a>, we define the Poisson problem using 447f296bb3SBarry Smith 457f296bb3SBarry Smith``` 467f296bb3SBarry SmithDMLabel label; 477f296bb3SBarry SmithPetscInt f = 0, id = 1; 487f296bb3SBarry Smith 497f296bb3SBarry SmithPetscDSSetResidual(ds, f, f0_trig_inhomogeneous_u, f1_u); 507f296bb3SBarry SmithPetscDSSetJacobian(ds, f, f, NULL, NULL, NULL, g3_uu); 517f296bb3SBarry SmithPetscDSSetExactSolution(ds, f, trig_inhomogeneous_u, user); 527f296bb3SBarry SmithDMGetLabel(dm, "marker", &label); 5357d50842SBarry SmithDMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, &id, f, 0, NULL, (PetscVoidFn *) ex, NULL, user, NULL); 547f296bb3SBarry Smith``` 557f296bb3SBarry Smith 567f296bb3SBarry Smithwhere the pointwise functions are 577f296bb3SBarry Smith 587f296bb3SBarry Smith``` 59*2a8381b2SBarry Smithstatic PetscErrorCode trig_inhomogeneous_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx) 607f296bb3SBarry Smith{ 617f296bb3SBarry Smith PetscInt d; 627f296bb3SBarry Smith *u = 0.0; 637f296bb3SBarry Smith for (d = 0; d < dim; ++d) *u += PetscSinReal(2.0*PETSC_PI*x[d]); 647f296bb3SBarry Smith return 0; 657f296bb3SBarry Smith} 667f296bb3SBarry Smith 677f296bb3SBarry Smithstatic void f0_trig_inhomogeneous_u(PetscInt dim, PetscInt Nf, PetscInt NfAux, 687f296bb3SBarry Smith const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], 697f296bb3SBarry Smith const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], 707f296bb3SBarry Smith PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) 717f296bb3SBarry Smith{ 727f296bb3SBarry Smith PetscInt d; 737f296bb3SBarry Smith for (d = 0; d < dim; ++d) f0[0] += -4.0*PetscSqr(PETSC_PI)*PetscSinReal(2.0*PETSC_PI*x[d]); 747f296bb3SBarry Smith} 757f296bb3SBarry Smith 767f296bb3SBarry Smithstatic void f1_u(PetscInt dim, PetscInt Nf, PetscInt NfAux, 777f296bb3SBarry Smith const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], 787f296bb3SBarry Smith const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], 797f296bb3SBarry Smith PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f1[]) 807f296bb3SBarry Smith{ 817f296bb3SBarry Smith PetscInt d; 827f296bb3SBarry Smith for (d = 0; d < dim; ++d) f1[d] = u_x[d]; 837f296bb3SBarry Smith} 847f296bb3SBarry Smith 857f296bb3SBarry Smithstatic void g3_uu(PetscInt dim, PetscInt Nf, PetscInt NfAux, 867f296bb3SBarry Smith const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], 877f296bb3SBarry Smith const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], 887f296bb3SBarry Smith PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g3[]) 897f296bb3SBarry Smith{ 907f296bb3SBarry Smith PetscInt d; 917f296bb3SBarry Smith for (d = 0; d < dim; ++d) g3[d*dim+d] = 1.0; 927f296bb3SBarry Smith} 937f296bb3SBarry Smith``` 947f296bb3SBarry Smith 957f296bb3SBarry SmithNotice that we set boundary conditions using `DMAddBoundary`, which will be described later in this chapter. Also we set an exact solution for the field. This can be used to automatically calculate mesh convergence using the `PetscConvEst` object described later in this chapter. 967f296bb3SBarry Smith 977f296bb3SBarry SmithFor more complex cases with multiple regions, we need to use the `PetscWeakForm` interface directly. The weak form object allows you to set any number of functions for a given field, and also allows functions to be associated with particular subsets of the mesh using labels and label values. We can reproduce the above problem using the *SetIndex* variants which only set a single function at the specified index, rather than a list of functions. We use a `NULL` label and value, meaning that the entire domain is used. 987f296bb3SBarry Smith 997f296bb3SBarry Smith``` 1007f296bb3SBarry SmithPetscInt f = 0, val = 0; 1017f296bb3SBarry Smith 1027f296bb3SBarry SmithPetscDSGetWeakForm(ds, &wf); 1037f296bb3SBarry SmithPetscWeakFormSetIndexResidual(ds, NULL, val, f, 0, 0, f0_trig_inhomogeneous_u, 0, f1_u); 1047f296bb3SBarry SmithPetscWeakFormSetIndexJacobian(ds, NULL, val, f, f, 0, 0, NULL, 0, NULL, 0, NULL, 0, g3_uu); 1057f296bb3SBarry Smith``` 1067f296bb3SBarry Smith 1077f296bb3SBarry SmithIn <a href="PETSC_DOC_OUT_ROOT_PLACEHOLDER/src/snes/tutorials/ex23.c.html">SNES Tutorial ex23</a>, we define the Poisson problem over the entire domain, but in the top half we also define a pressure. The entire problem can be specified as follows 1087f296bb3SBarry Smith 1097f296bb3SBarry Smith``` 1107f296bb3SBarry SmithDMGetRegionNumDS(dm, 0, &label, NULL, &ds, NULL); 1117f296bb3SBarry SmithPetscDSGetWeakForm(ds, &wf); 1127f296bb3SBarry SmithPetscWeakFormSetIndexResidual(wf, label, 1, 0, 0, 0, f0_quad_u, 0, f1_u); 1137f296bb3SBarry SmithPetscWeakFormSetIndexJacobian(wf, label, 1, 0, 0, 0, 0, NULL, 0, NULL, 0, NULL, 0, g3_uu); 1147f296bb3SBarry SmithPetscDSSetExactSolution(ds, 0, quad_u, user); 1157f296bb3SBarry SmithDMGetRegionNumDS(dm, 1, &label, NULL, &ds, NULL); 1167f296bb3SBarry SmithPetscDSGetWeakForm(ds, &wf); 1177f296bb3SBarry SmithPetscWeakFormSetIndexResidual(wf, label, 1, 0, 0, 0, f0_quad_u, 0, f1_u); 1187f296bb3SBarry SmithPetscWeakFormSetIndexJacobian(wf, label, 1, 0, 0, 0, 0, NULL, 0, NULL, 0, NULL, 0, g3_uu); 1197f296bb3SBarry SmithPetscWeakFormSetIndexResidual(wf, label, 1, 1, 0, 0, f0_quad_p, 0, NULL); 1207f296bb3SBarry SmithPetscWeakFormSetIndexJacobian(wf, label, 1, 1, 1, 0, 0, g0_pp, 0, NULL, 0, NULL, 0, NULL); 1217f296bb3SBarry SmithPetscDSSetExactSolution(ds, 0, quad_u, user); 1227f296bb3SBarry SmithPetscDSSetExactSolution(ds, 1, quad_p, user); 1237f296bb3SBarry SmithDMGetLabel(dm, "marker", &label); 12457d50842SBarry SmithDMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, &id, 0, 0, NULL, (PetscVoidFn *) quad_u, NULL, user, NULL); 1257f296bb3SBarry Smith``` 1267f296bb3SBarry Smith 1277f296bb3SBarry SmithIn the [PyLith software](https://geodynamics.org/cig/software/pylith/) we use this capability to combine bulk elasticity with a fault constitutive model integrated over the embedded manifolds corresponding to earthquake faults. 1287f296bb3SBarry Smith 1297f296bb3SBarry Smith## Assembling finite element residuals and Jacobians 1307f296bb3SBarry Smith 1317f296bb3SBarry SmithOnce the pointwise functions are set in each `PetscDS`, mesh traversals can be automatically determined from the `DMLabel` and value specifications in the keys. This default traversal strategy can be activated by attaching the `DM` and default callbacks to a solver 1327f296bb3SBarry Smith 1337f296bb3SBarry Smith``` 1347f296bb3SBarry SmithSNESSetDM(snes, dm); 1357f296bb3SBarry SmithDMPlexSetSNESLocalFEM(dm, &user, &user, &user); 1367f296bb3SBarry Smith 1377f296bb3SBarry SmithTSSetDM(ts, dm); 1387f296bb3SBarry SmithDMTSSetBoundaryLocal(dm, DMPlexTSComputeBoundary, &user); 1397f296bb3SBarry SmithDMTSSetIFunctionLocal(dm, DMPlexTSComputeIFunctionFEM, &user); 1407f296bb3SBarry SmithDMTSSetIJacobianLocal(dm, DMPlexTSComputeIJacobianFEM, &user); 1417f296bb3SBarry Smith``` 142