const char help[] = "A test of H-div conforming discretizations on different cell types.\n"; #include #include #include #include #include #include /* We are using the system \vec{u} = \vec{\hat{u}} p = \div{\vec{u}} in low degree approximation space d = \div{\vec{u}} - p == 0 in higher degree approximation space That is, we are using the field d to compute the error between \div{\vec{u}} computed in a space 1 degree higher than p and the value of p which is \div{u} computed in the low degree space. If H-div elements are implemented correctly then this should be identically zero since the divergence of a function in H(div) should be exactly representable in L_2 by definition. */ static PetscErrorCode zero_func(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx) { PetscInt c; for (c = 0; c < Nc; ++c) u[c] = 0; return PETSC_SUCCESS; } /* Linear Exact Functions \vec{u} = \vec{x}; p = dim; */ static PetscErrorCode linear_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx) { PetscInt c; for (c = 0; c < Nc; ++c) u[c] = x[c]; return PETSC_SUCCESS; } static PetscErrorCode linear_p(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx) { u[0] = dim; return PETSC_SUCCESS; } /* Sinusoidal Exact Functions * u_i = \sin{2*\pi*x_i} * p = \Sum_{i=1}^{dim} 2*\pi*cos{2*\pi*x_i} * */ static PetscErrorCode sinusoid_u(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx) { PetscInt c; for (c = 0; c < Nc; ++c) u[c] = PetscSinReal(2 * PETSC_PI * x[c]); return PETSC_SUCCESS; } static PetscErrorCode sinusoid_p(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, PetscCtx ctx) { PetscInt d; u[0] = 0; for (d = 0; d < dim; ++d) u[0] += 2 * PETSC_PI * PetscCosReal(2 * PETSC_PI * x[d]); return PETSC_SUCCESS; } /* Pointwise residual for u = u*. Need one of these for each possible u* */ static void f0_v_linear(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) { PetscInt i; PetscScalar *u_rhs; PetscCallAbort(PETSC_COMM_SELF, PetscCalloc1(dim, &u_rhs)); PetscCallAbort(PETSC_COMM_SELF, linear_u(dim, t, x, dim, u_rhs, NULL)); for (i = 0; i < dim; ++i) f0[i] = u[uOff[0] + i] - u_rhs[i]; PetscCallAbort(PETSC_COMM_SELF, PetscFree(u_rhs)); } static void f0_v_sinusoid(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) { PetscInt i; PetscScalar *u_rhs; PetscCallAbort(PETSC_COMM_SELF, PetscCalloc1(dim, &u_rhs)); PetscCallAbort(PETSC_COMM_SELF, sinusoid_u(dim, t, x, dim, u_rhs, NULL)); for (i = 0; i < dim; ++i) f0[i] = u[uOff[0] + i] - u_rhs[i]; PetscCallAbort(PETSC_COMM_SELF, PetscFree(u_rhs)); } /* Residual function for enforcing p = \div{u}. */ static void f0_q(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) { PetscInt i; PetscScalar divu; divu = 0.; for (i = 0; i < dim; ++i) divu += u_x[uOff_x[0] + i * dim + i]; f0[0] = u[uOff[1]] - divu; } /* Residual function for p_err = \div{u} - p. */ static void f0_w(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) { PetscInt i; PetscScalar divu; divu = 0.; for (i = 0; i < dim; ++i) divu += u_x[uOff_x[0] + i * dim + i]; f0[0] = u[uOff[2]] - u[uOff[1]] + divu; } /* Boundary residual for the embedding system. Need one for each form of * solution. These enforce u = \hat{u} at the boundary. */ static void f0_bd_u_sinusoid(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], const PetscReal n[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) { PetscInt d; PetscScalar *u_rhs; PetscCallAbort(PETSC_COMM_SELF, PetscCalloc1(dim, &u_rhs)); PetscCallAbort(PETSC_COMM_SELF, sinusoid_u(dim, t, x, dim, u_rhs, NULL)); for (d = 0; d < dim; ++d) f0[d] = u_rhs[d]; PetscCallAbort(PETSC_COMM_SELF, PetscFree(u_rhs)); } static void f0_bd_u_linear(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], const PetscReal n[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) { PetscInt d; PetscScalar *u_rhs; PetscCallAbort(PETSC_COMM_SELF, PetscCalloc1(dim, &u_rhs)); PetscCallAbort(PETSC_COMM_SELF, linear_u(dim, t, x, dim, u_rhs, NULL)); for (d = 0; d < dim; ++d) f0[d] = u_rhs[d]; PetscCallAbort(PETSC_COMM_SELF, PetscFree(u_rhs)); } /* Jacobian functions. For the following, v is the test function associated with * u, q the test function associated with p, and w the test function associated * with d. */ /* */ static void g0_vu(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[]) { PetscInt c; for (c = 0; c < dim; ++c) g0[c * dim + c] = 1.0; } /* */ static void g0_qp(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[]) { PetscInt d; for (d = 0; d < dim; ++d) g0[d * dim + d] = 1.0; } /* - For the embedded system. This is different from the method of * manufactured solution because instead of computing - we * need - This is only used by the embedded system. Where we need to compute * - + */ static void g0_wp(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[]) { PetscInt d; for (d = 0; d < dim; ++d) g0[d * dim + d] = -1.0; } /* */ static void g0_wd(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[]) { PetscInt c; for (c = 0; c < dim; ++c) g0[c * dim + c] = 1.0; } /* for the embedded system. */ static void g1_wu(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g1[]) { PetscInt d; for (d = 0; d < dim; ++d) g1[d * dim + d] = 1.0; } /* Enum and string array for selecting mesh perturbation options */ typedef enum { NONE = 0, PERTURB = 1, SKEW = 2, SKEW_PERTURB = 3 } Transform; const char *const TransformTypes[] = {"none", "perturb", "skew", "skew_perturb", "Perturbation", "", NULL}; /* Enum and string array for selecting the form of the exact solution*/ typedef enum { LINEAR = 0, SINUSOIDAL = 1 } Solution; const char *const SolutionTypes[] = {"linear", "sinusoidal", "Solution", "", NULL}; typedef struct { Transform mesh_transform; Solution sol_form; } UserCtx; /* Process command line options and initialize the UserCtx struct */ static PetscErrorCode ProcessOptions(MPI_Comm comm, UserCtx *user) { PetscFunctionBegin; /* Default to 2D, unperturbed triangle mesh and Linear solution.*/ user->mesh_transform = NONE; user->sol_form = LINEAR; PetscOptionsBegin(comm, "", "H-div Test Options", "DMPLEX"); PetscCall(PetscOptionsEnum("-mesh_transform", "Method used to perturb the mesh vertices. Options are skew, perturb, skew_perturb,or none", "ex39.c", TransformTypes, (PetscEnum)user->mesh_transform, (PetscEnum *)&user->mesh_transform, NULL)); PetscCall(PetscOptionsEnum("-sol_form", "Form of the exact solution. Options are Linear or Sinusoidal", "ex39.c", SolutionTypes, (PetscEnum)user->sol_form, (PetscEnum *)&user->sol_form, NULL)); PetscOptionsEnd(); PetscFunctionReturn(PETSC_SUCCESS); } /* Perturb the position of each mesh vertex by a small amount.*/ static PetscErrorCode PerturbMesh(DM *mesh, PetscScalar *coordVals, PetscInt npoints, PetscInt dim) { PetscInt i, j, k; PetscReal minCoords[3], maxCoords[3], maxPert[3], randVal, amp; PetscRandom ran; PetscFunctionBegin; PetscCall(DMGetCoordinateDim(*mesh, &dim)); PetscCall(DMGetLocalBoundingBox(*mesh, minCoords, maxCoords)); PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &ran)); /* Compute something approximately equal to half an edge length. This is the * most we can perturb points and guarantee that there won't be any topology * issues. */ for (k = 0; k < dim; ++k) maxPert[k] = 0.025 * (maxCoords[k] - minCoords[k]) / (PetscPowReal(npoints, 1. / dim) - 1); /* For each mesh vertex */ for (i = 0; i < npoints; ++i) { /* For each coordinate of the vertex */ for (j = 0; j < dim; ++j) { /* Generate a random amplitude in [-0.5*maxPert, 0.5*maxPert] */ PetscCall(PetscRandomGetValueReal(ran, &randVal)); amp = maxPert[j] * (randVal - 0.5); /* Add the perturbation to the vertex*/ coordVals[dim * i + j] += amp; } } PetscCall(PetscRandomDestroy(&ran)); PetscFunctionReturn(PETSC_SUCCESS); } /* Apply a global skew transformation to the mesh. */ static PetscErrorCode SkewMesh(DM *mesh, PetscScalar *coordVals, PetscInt npoints, PetscInt dim) { PetscInt i, j, k, l; PetscScalar *transMat; PetscScalar tmpcoord[3]; PetscRandom ran; PetscReal randVal; PetscFunctionBegin; PetscCall(PetscCalloc1(dim * dim, &transMat)); PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &ran)); /* Make a matrix representing a skew transformation */ for (i = 0; i < dim; ++i) { for (j = 0; j < dim; ++j) { PetscCall(PetscRandomGetValueReal(ran, &randVal)); if (i == j) transMat[i * dim + j] = 1.; else if (j < i) transMat[i * dim + j] = 2 * (j + i) * randVal; else transMat[i * dim + j] = 0; } } /* Multiply each coordinate vector by our transformation.*/ for (i = 0; i < npoints; ++i) { for (j = 0; j < dim; ++j) { tmpcoord[j] = 0; for (k = 0; k < dim; ++k) tmpcoord[j] += coordVals[dim * i + k] * transMat[dim * k + j]; } for (l = 0; l < dim; ++l) coordVals[dim * i + l] = tmpcoord[l]; } PetscCall(PetscFree(transMat)); PetscCall(PetscRandomDestroy(&ran)); PetscFunctionReturn(PETSC_SUCCESS); } /* Accesses the mesh coordinate array and performs the transformation operations * specified by the user options */ static PetscErrorCode TransformMesh(UserCtx *user, DM *mesh) { PetscInt dim, npoints; PetscScalar *coordVals; Vec coords; PetscFunctionBegin; PetscCall(DMGetCoordinates(*mesh, &coords)); PetscCall(VecGetArray(coords, &coordVals)); PetscCall(VecGetLocalSize(coords, &npoints)); PetscCall(DMGetCoordinateDim(*mesh, &dim)); npoints = npoints / dim; switch (user->mesh_transform) { case PERTURB: PetscCall(PerturbMesh(mesh, coordVals, npoints, dim)); break; case SKEW: PetscCall(SkewMesh(mesh, coordVals, npoints, dim)); break; case SKEW_PERTURB: PetscCall(SkewMesh(mesh, coordVals, npoints, dim)); PetscCall(PerturbMesh(mesh, coordVals, npoints, dim)); break; default: SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "invalid mesh transformation"); } PetscCall(VecRestoreArray(coords, &coordVals)); PetscCall(DMSetCoordinates(*mesh, coords)); PetscFunctionReturn(PETSC_SUCCESS); } static PetscErrorCode CreateMesh(MPI_Comm comm, UserCtx *user, DM *mesh) { PetscFunctionBegin; PetscCall(DMCreate(comm, mesh)); PetscCall(DMSetType(*mesh, DMPLEX)); PetscCall(DMSetFromOptions(*mesh)); /* Perform any mesh transformations if specified by user */ if (user->mesh_transform != NONE) PetscCall(TransformMesh(user, mesh)); /* Get any other mesh options from the command line */ PetscCall(DMSetApplicationContext(*mesh, user)); PetscCall(DMViewFromOptions(*mesh, NULL, "-dm_view")); PetscFunctionReturn(PETSC_SUCCESS); } /* Setup the system of equations that we wish to solve */ static PetscErrorCode SetupProblem(DM dm, UserCtx *user) { PetscDS prob; DMLabel label; const PetscInt id = 1; PetscFunctionBegin; PetscCall(DMGetDS(dm, &prob)); /* All of these are independent of the user's choice of solution */ PetscCall(PetscDSSetResidual(prob, 1, f0_q, NULL)); PetscCall(PetscDSSetResidual(prob, 2, f0_w, NULL)); PetscCall(PetscDSSetJacobian(prob, 0, 0, g0_vu, NULL, NULL, NULL)); PetscCall(PetscDSSetJacobian(prob, 1, 0, NULL, g1_qu, NULL, NULL)); PetscCall(PetscDSSetJacobian(prob, 1, 1, g0_qp, NULL, NULL, NULL)); PetscCall(PetscDSSetJacobian(prob, 2, 0, NULL, g1_wu, NULL, NULL)); PetscCall(PetscDSSetJacobian(prob, 2, 1, g0_wp, NULL, NULL, NULL)); PetscCall(PetscDSSetJacobian(prob, 2, 2, g0_wd, NULL, NULL, NULL)); /* Field 2 is the error between \div{u} and pressure in a higher dimensional * space. If all is right this should be machine zero. */ PetscCall(PetscDSSetExactSolution(prob, 2, zero_func, NULL)); switch (user->sol_form) { case LINEAR: PetscCall(PetscDSSetResidual(prob, 0, f0_v_linear, NULL)); PetscCall(PetscDSSetBdResidual(prob, 0, f0_bd_u_linear, NULL)); PetscCall(PetscDSSetExactSolution(prob, 0, linear_u, NULL)); PetscCall(PetscDSSetExactSolution(prob, 1, linear_p, NULL)); break; case SINUSOIDAL: PetscCall(PetscDSSetResidual(prob, 0, f0_v_sinusoid, NULL)); PetscCall(PetscDSSetBdResidual(prob, 0, f0_bd_u_sinusoid, NULL)); PetscCall(PetscDSSetExactSolution(prob, 0, sinusoid_u, NULL)); PetscCall(PetscDSSetExactSolution(prob, 1, sinusoid_p, NULL)); break; default: SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG, "invalid solution form"); } PetscCall(DMGetLabel(dm, "marker", &label)); PetscCall(PetscDSAddBoundary(prob, DM_BC_NATURAL, "Boundary Integral", label, 1, &id, 0, 0, NULL, (PetscVoidFn *)NULL, NULL, user, NULL)); PetscFunctionReturn(PETSC_SUCCESS); } /* Create the finite element spaces we will use for this system */ static PetscErrorCode SetupDiscretization(DM mesh, PetscErrorCode (*setup)(DM, UserCtx *), UserCtx *user) { DM cdm = mesh; PetscFE fevel, fepres, fedivErr; PetscInt dim; PetscBool simplex; PetscFunctionBegin; PetscCall(DMGetDimension(mesh, &dim)); PetscCall(DMPlexIsSimplex(mesh, &simplex)); /* Create FE objects and give them names so that options can be set from * command line */ PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh), dim, dim, simplex, "velocity_", -1, &fevel)); PetscCall(PetscObjectSetName((PetscObject)fevel, "velocity")); PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh), dim, 1, simplex, "pressure_", -1, &fepres)); PetscCall(PetscObjectSetName((PetscObject)fepres, "pressure")); PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)mesh), dim, 1, simplex, "divErr_", -1, &fedivErr)); PetscCall(PetscObjectSetName((PetscObject)fedivErr, "divErr")); PetscCall(PetscFECopyQuadrature(fevel, fepres)); PetscCall(PetscFECopyQuadrature(fevel, fedivErr)); /* Associate the FE objects with the mesh and setup the system */ PetscCall(DMSetField(mesh, 0, NULL, (PetscObject)fevel)); PetscCall(DMSetField(mesh, 1, NULL, (PetscObject)fepres)); PetscCall(DMSetField(mesh, 2, NULL, (PetscObject)fedivErr)); PetscCall(DMCreateDS(mesh)); PetscCall((*setup)(mesh, user)); while (cdm) { PetscCall(DMCopyDisc(mesh, cdm)); PetscCall(DMGetCoarseDM(cdm, &cdm)); } /* The Mesh now owns the fields, so we can destroy the FEs created in this * function */ PetscCall(PetscFEDestroy(&fevel)); PetscCall(PetscFEDestroy(&fepres)); PetscCall(PetscFEDestroy(&fedivErr)); PetscCall(DMDestroy(&cdm)); PetscFunctionReturn(PETSC_SUCCESS); } int main(int argc, char **argv) { PetscInt i; UserCtx user; DM mesh; SNES snes; Vec computed, divErr; PetscReal divErrNorm; IS *fieldIS; PetscBool exampleSuccess = PETSC_FALSE; const PetscReal errTol = 10. * PETSC_SMALL; char stdFormat[] = "L2 Norm of the Divergence Error is: %g\n H(div) elements working correctly: %s\n"; /* Initialize PETSc */ PetscFunctionBeginUser; PetscCall(PetscInitialize(&argc, &argv, NULL, help)); PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user)); /* Set up the system, we need to create a solver and a mesh and then assign * the correct spaces into the mesh*/ PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes)); PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &mesh)); PetscCall(SNESSetDM(snes, mesh)); PetscCall(SetupDiscretization(mesh, SetupProblem, &user)); PetscCall(DMPlexSetSNESLocalFEM(mesh, PETSC_FALSE, &user)); PetscCall(SNESSetFromOptions(snes)); /* Grab field IS so that we can view the solution by field */ PetscCall(DMCreateFieldIS(mesh, NULL, NULL, &fieldIS)); /* Create a vector to store the SNES solution, solve the system and grab the * solution from SNES */ PetscCall(DMCreateGlobalVector(mesh, &computed)); PetscCall(PetscObjectSetName((PetscObject)computed, "computedSolution")); PetscCall(VecSet(computed, 0.0)); PetscCall(SNESSolve(snes, NULL, computed)); PetscCall(SNESGetSolution(snes, &computed)); PetscCall(VecViewFromOptions(computed, NULL, "-computedSolution_view")); /* Now we pull out the portion of the vector corresponding to the 3rd field * which is the error between \div{u} computed in a higher dimensional space * and p = \div{u} computed in a low dimension space. We report the L2 norm of * this vector which should be zero if the H(div) spaces are implemented * correctly. */ PetscCall(VecGetSubVector(computed, fieldIS[2], &divErr)); PetscCall(VecNorm(divErr, NORM_2, &divErrNorm)); PetscCall(VecRestoreSubVector(computed, fieldIS[2], &divErr)); exampleSuccess = (PetscBool)(divErrNorm <= errTol); PetscCall(PetscPrintf(PETSC_COMM_WORLD, stdFormat, divErrNorm, exampleSuccess ? "true" : "false")); /* Tear down */ PetscCall(VecDestroy(&divErr)); PetscCall(VecDestroy(&computed)); for (i = 0; i < 3; ++i) PetscCall(ISDestroy(&fieldIS[i])); PetscCall(PetscFree(fieldIS)); PetscCall(SNESDestroy(&snes)); PetscCall(DMDestroy(&mesh)); PetscCall(PetscFinalize()); return 0; } /*TEST testset: suffix: 2d_bdm requires: triangle args: -velocity_petscfe_default_quadrature_order 1 \ -velocity_petscspace_degree 1 \ -velocity_petscdualspace_type bdm \ -divErr_petscspace_degree 1 \ -divErr_petscdualspace_lagrange_continuity false \ -snes_error_if_not_converged \ -ksp_rtol 1e-10 \ -ksp_error_if_not_converged \ -pc_type fieldsplit\ -pc_fieldsplit_detect_saddle_point\ -pc_fieldsplit_type schur\ -pc_fieldsplit_schur_precondition full test: suffix: linear args: -sol_form linear -mesh_transform none test: suffix: sinusoidal args: -sol_form sinusoidal -mesh_transform none test: suffix: sinusoidal_skew args: -sol_form sinusoidal -mesh_transform skew test: suffix: sinusoidal_perturb args: -sol_form sinusoidal -mesh_transform perturb test: suffix: sinusoidal_skew_perturb args: -sol_form sinusoidal -mesh_transform skew_perturb testset: TODO: broken suffix: 2d_bdmq output_file: output/empty.out args: -dm_plex_simplex false \ -velocity_petscspace_degree 1 \ -velocity_petscdualspace_type bdm \ -velocity_petscdualspace_lagrange_tensor 1 \ -divErr_petscspace_degree 1 \ -divErr_petscdualspace_lagrange_continuity false \ -snes_error_if_not_converged \ -ksp_rtol 1e-10 \ -ksp_error_if_not_converged \ -pc_type fieldsplit\ -pc_fieldsplit_detect_saddle_point\ -pc_fieldsplit_type schur\ -pc_fieldsplit_schur_precondition full test: suffix: linear args: -sol_form linear -mesh_transform none test: suffix: sinusoidal args: -sol_form sinusoidal -mesh_transform none test: suffix: sinusoidal_skew args: -sol_form sinusoidal -mesh_transform skew test: suffix: sinusoidal_perturb args: -sol_form sinusoidal -mesh_transform perturb test: suffix: sinusoidal_skew_perturb args: -sol_form sinusoidal -mesh_transform skew_perturb testset: suffix: 3d_bdm requires: ctetgen args: -dm_plex_dim 3 \ -velocity_petscspace_degree 1 \ -velocity_petscdualspace_type bdm \ -divErr_petscspace_degree 1 \ -divErr_petscdualspace_lagrange_continuity false \ -snes_error_if_not_converged \ -ksp_rtol 1e-10 \ -ksp_error_if_not_converged \ -pc_type fieldsplit \ -pc_fieldsplit_detect_saddle_point \ -pc_fieldsplit_type schur \ -pc_fieldsplit_schur_precondition full test: suffix: linear args: -sol_form linear -mesh_transform none test: suffix: sinusoidal args: -sol_form sinusoidal -mesh_transform none test: suffix: sinusoidal_skew args: -sol_form sinusoidal -mesh_transform skew test: suffix: sinusoidal_perturb args: -sol_form sinusoidal -mesh_transform perturb test: suffix: sinusoidal_skew_perturb args: -sol_form sinusoidal -mesh_transform skew_perturb testset: TODO: broken suffix: 3d_bdmq output_file: output/empty.out requires: ctetgen args: -dm_plex_dim 3 \ -dm_plex_simplex false \ -velocity_petscspace_degree 1 \ -velocity_petscdualspace_type bdm \ -velocity_petscdualspace_lagrange_tensor 1 \ -divErr_petscspace_degree 1 \ -divErr_petscdualspace_lagrange_continuity false \ -snes_error_if_not_converged \ -ksp_rtol 1e-10 \ -ksp_error_if_not_converged \ -pc_type fieldsplit \ -pc_fieldsplit_detect_saddle_point \ -pc_fieldsplit_type schur \ -pc_fieldsplit_schur_precondition full test: suffix: linear args: -sol_form linear -mesh_transform none test: suffix: sinusoidal args: -sol_form sinusoidal -mesh_transform none test: suffix: sinusoidal_skew args: -sol_form sinusoidal -mesh_transform skew test: suffix: sinusoidal_perturb args: -sol_form sinusoidal -mesh_transform perturb test: suffix: sinusoidal_skew_perturb args: -sol_form sinusoidal -mesh_transform skew_perturb test: suffix: quad_rt_0 args: -dm_plex_simplex false -mesh_transform skew \ -divErr_petscspace_degree 1 \ -divErr_petscdualspace_lagrange_continuity false \ -snes_error_if_not_converged \ -ksp_rtol 1e-10 \ -ksp_error_if_not_converged \ -pc_type fieldsplit\ -pc_fieldsplit_detect_saddle_point\ -pc_fieldsplit_type schur\ -pc_fieldsplit_schur_precondition full \ -velocity_petscfe_default_quadrature_order 1 \ -velocity_petscspace_type sum \ -velocity_petscspace_variables 2 \ -velocity_petscspace_components 2 \ -velocity_petscspace_sum_spaces 2 \ -velocity_petscspace_sum_concatenate true \ -velocity_sumcomp_0_petscspace_variables 2 \ -velocity_sumcomp_0_petscspace_type tensor \ -velocity_sumcomp_0_petscspace_tensor_spaces 2 \ -velocity_sumcomp_0_petscspace_tensor_uniform false \ -velocity_sumcomp_0_tensorcomp_0_petscspace_degree 1 \ -velocity_sumcomp_0_tensorcomp_1_petscspace_degree 0 \ -velocity_sumcomp_1_petscspace_variables 2 \ -velocity_sumcomp_1_petscspace_type tensor \ -velocity_sumcomp_1_petscspace_tensor_spaces 2 \ -velocity_sumcomp_1_petscspace_tensor_uniform false \ -velocity_sumcomp_1_tensorcomp_0_petscspace_degree 0 \ -velocity_sumcomp_1_tensorcomp_1_petscspace_degree 1 \ -velocity_petscdualspace_form_degree -1 \ -velocity_petscdualspace_order 1 \ -velocity_petscdualspace_lagrange_trimmed true TEST*/