Actual source code: ex69.c
2: #include <petscdt.h>
3: #include <petscdraw.h>
4: #include <petscviewer.h>
5: #include <petscksp.h>
6: #include <petscdmda.h>
8: /*
9: Solves -Laplacian u = f, u(-1) = u(1) = 0 with multiple spectral elements
11: Uses DMDA to manage the parallelization of the elements
13: This is not intended to be highly optimized in either memory usage or time, but strifes for simplicity.
15: */
17: typedef struct {
18: PetscInt n; /* number of nodes */
19: PetscReal *nodes; /* GLL nodes */
20: PetscReal *weights; /* GLL weights */
21: } PetscGLL;
23: PetscErrorCode ComputeSolution(DM da, PetscGLL *gll, Vec u)
24: {
25: PetscInt j, xs, xn;
26: PetscScalar *uu, *xx;
27: PetscReal xd;
28: Vec x;
30: PetscFunctionBegin;
31: PetscCall(DMDAGetCorners(da, &xs, NULL, NULL, &xn, NULL, NULL));
32: PetscCall(DMGetCoordinates(da, &x));
33: PetscCall(DMDAVecGetArray(da, x, &xx));
34: PetscCall(DMDAVecGetArray(da, u, &uu));
35: /* loop over local nodes */
36: for (j = xs; j < xs + xn; j++) {
37: xd = xx[j];
38: uu[j] = (xd * xd - 1.0) * PetscCosReal(5. * PETSC_PI * xd);
39: }
40: PetscCall(DMDAVecRestoreArray(da, x, &xx));
41: PetscCall(DMDAVecRestoreArray(da, u, &uu));
42: PetscFunctionReturn(PETSC_SUCCESS);
43: }
45: /*
46: Evaluates \integral_{-1}^{1} f*v_i where v_i is the ith basis polynomial via the GLL nodes and weights, since the v_i
47: basis function is zero at all nodes except the ith one the integral is simply the weight_i * f(node_i)
48: */
49: PetscErrorCode ComputeRhs(DM da, PetscGLL *gll, Vec b)
50: {
51: PetscInt i, j, xs, xn, n = gll->n;
52: PetscScalar *bb, *xx;
53: PetscReal xd;
54: Vec blocal, xlocal;
56: PetscFunctionBegin;
57: PetscCall(DMDAGetCorners(da, &xs, NULL, NULL, &xn, NULL, NULL));
58: xs = xs / (n - 1);
59: xn = xn / (n - 1);
60: PetscCall(DMGetLocalVector(da, &blocal));
61: PetscCall(VecZeroEntries(blocal));
62: PetscCall(DMDAVecGetArray(da, blocal, &bb));
63: PetscCall(DMGetCoordinatesLocal(da, &xlocal));
64: PetscCall(DMDAVecGetArray(da, xlocal, &xx));
65: /* loop over local spectral elements */
66: for (j = xs; j < xs + xn; j++) {
67: /* loop over GLL points in each element */
68: for (i = 0; i < n; i++) {
69: xd = xx[j * (n - 1) + i];
70: bb[j * (n - 1) + i] += -gll->weights[i] * (-20. * PETSC_PI * xd * PetscSinReal(5. * PETSC_PI * xd) + (2. - (5. * PETSC_PI) * (5. * PETSC_PI) * (xd * xd - 1.)) * PetscCosReal(5. * PETSC_PI * xd));
71: }
72: }
73: PetscCall(DMDAVecRestoreArray(da, xlocal, &xx));
74: PetscCall(DMDAVecRestoreArray(da, blocal, &bb));
75: PetscCall(VecZeroEntries(b));
76: PetscCall(DMLocalToGlobalBegin(da, blocal, ADD_VALUES, b));
77: PetscCall(DMLocalToGlobalEnd(da, blocal, ADD_VALUES, b));
78: PetscCall(DMRestoreLocalVector(da, &blocal));
79: PetscFunctionReturn(PETSC_SUCCESS);
80: }
82: /*
83: Run with -build_twosided allreduce -pc_type bjacobi -sub_pc_type lu -q 16 -ksp_rtol 1.e-34 (or 1.e-14 for double precision)
85: -q <q> number of spectral elements to use
86: -N <N> maximum number of GLL points per element
88: */
89: int main(int argc, char **args)
90: {
91: PetscGLL gll;
92: PetscInt N = 80, n, q = 8, xs, xn, j, l;
93: PetscReal **A;
94: Mat K;
95: KSP ksp;
96: PC pc;
97: Vec x, b;
98: PetscInt *rows;
99: PetscReal norm, xc, yc, h;
100: PetscScalar *f;
101: PetscDraw draw;
102: PetscDrawLG lg;
103: PetscDrawAxis axis;
104: DM da;
105: PetscMPIInt rank, size;
107: PetscFunctionBeginUser;
108: PetscCall(PetscInitialize(&argc, &args, NULL, NULL));
109: PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
110: PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &size));
111: PetscCall(PetscOptionsGetInt(NULL, NULL, "-N", &N, NULL));
112: PetscCall(PetscOptionsGetInt(NULL, NULL, "-q", &q, NULL));
114: PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "Log(Error norm) vs Number of GLL points", 0, 0, 500, 500, &draw));
115: PetscCall(PetscDrawSetFromOptions(draw));
116: PetscCall(PetscDrawLGCreate(draw, 1, &lg));
117: PetscCall(PetscDrawLGSetUseMarkers(lg, PETSC_TRUE));
118: PetscCall(PetscDrawLGGetAxis(lg, &axis));
119: PetscCall(PetscDrawAxisSetLabels(axis, NULL, "Number of GLL points", "Log(Error Norm)"));
121: for (n = 4; n < N; n += 2) {
122: /*
123: da contains the information about the parallel layout of the elements
124: */
125: PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, q * (n - 1) + 1, 1, 1, NULL, &da));
126: PetscCall(DMSetFromOptions(da));
127: PetscCall(DMSetUp(da));
128: PetscCall(DMDAGetInfo(da, NULL, &q, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
129: q = (q - 1) / (n - 1); /* number of spectral elements */
131: /*
132: gll simply contains the GLL node and weight values
133: */
134: PetscCall(PetscMalloc2(n, &gll.nodes, n, &gll.weights));
135: PetscCall(PetscDTGaussLobattoLegendreQuadrature(n, PETSCGAUSSLOBATTOLEGENDRE_VIA_LINEAR_ALGEBRA, gll.nodes, gll.weights));
136: gll.n = n;
137: PetscCall(DMDASetGLLCoordinates(da, gll.n, gll.nodes));
139: /*
140: Creates the element stiffness matrix for the given gll
141: */
142: PetscCall(PetscGaussLobattoLegendreElementLaplacianCreate(gll.n, gll.nodes, gll.weights, &A));
144: /*
145: Scale the element stiffness and weights by the size of the element
146: */
147: h = 2.0 / q;
148: for (j = 0; j < n; j++) {
149: gll.weights[j] *= .5 * h;
150: for (l = 0; l < n; l++) A[j][l] = 2. * A[j][l] / h;
151: }
153: /*
154: Create the global stiffness matrix and add the element stiffness for each local element
155: */
156: PetscCall(DMCreateMatrix(da, &K));
157: PetscCall(MatSetOption(K, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
158: PetscCall(DMDAGetCorners(da, &xs, NULL, NULL, &xn, NULL, NULL));
159: xs = xs / (n - 1);
160: xn = xn / (n - 1);
161: PetscCall(PetscMalloc1(n, &rows));
162: /*
163: loop over local elements
164: */
165: for (j = xs; j < xs + xn; j++) {
166: for (l = 0; l < n; l++) rows[l] = j * (n - 1) + l;
167: PetscCall(MatSetValues(K, n, rows, n, rows, &A[0][0], ADD_VALUES));
168: }
169: PetscCall(MatAssemblyBegin(K, MAT_FINAL_ASSEMBLY));
170: PetscCall(MatAssemblyEnd(K, MAT_FINAL_ASSEMBLY));
172: PetscCall(MatCreateVecs(K, &x, &b));
173: PetscCall(ComputeRhs(da, &gll, b));
175: /*
176: Replace the first and last rows/columns of the matrix with the identity to obtain the zero Dirichlet boundary conditions
177: */
178: rows[0] = 0;
179: rows[1] = q * (n - 1);
180: PetscCall(MatZeroRowsColumns(K, 2, rows, 1.0, x, b));
181: PetscCall(PetscFree(rows));
183: PetscCall(KSPCreate(PETSC_COMM_WORLD, &ksp));
184: PetscCall(KSPSetOperators(ksp, K, K));
185: PetscCall(KSPGetPC(ksp, &pc));
186: PetscCall(PCSetType(pc, PCLU));
187: PetscCall(KSPSetFromOptions(ksp));
188: PetscCall(KSPSolve(ksp, b, x));
190: /* compute the error to the continium problem */
191: PetscCall(ComputeSolution(da, &gll, b));
192: PetscCall(VecAXPY(x, -1.0, b));
194: /* compute the L^2 norm of the error */
195: PetscCall(VecGetArray(x, &f));
196: PetscCall(PetscGaussLobattoLegendreIntegrate(gll.n, gll.nodes, gll.weights, f, &norm));
197: PetscCall(VecRestoreArray(x, &f));
198: norm = PetscSqrtReal(norm);
199: PetscCall(PetscViewerASCIIPrintf(PETSC_VIEWER_STDOUT_WORLD, "L^2 norm of the error %" PetscInt_FMT " %g\n", n, (double)norm));
200: PetscCheck(n <= 10 || norm <= 1.e-8, PETSC_COMM_WORLD, PETSC_ERR_PLIB, "Slower convergence than expected");
201: xc = (PetscReal)n;
202: yc = PetscLog10Real(norm);
203: PetscCall(PetscDrawLGAddPoint(lg, &xc, &yc));
204: PetscCall(PetscDrawLGDraw(lg));
206: PetscCall(VecDestroy(&b));
207: PetscCall(VecDestroy(&x));
208: PetscCall(KSPDestroy(&ksp));
209: PetscCall(MatDestroy(&K));
210: PetscCall(PetscGaussLobattoLegendreElementLaplacianDestroy(gll.n, gll.nodes, gll.weights, &A));
211: PetscCall(PetscFree2(gll.nodes, gll.weights));
212: PetscCall(DMDestroy(&da));
213: }
214: PetscCall(PetscDrawLGDestroy(&lg));
215: PetscCall(PetscDrawDestroy(&draw));
216: PetscCall(PetscFinalize());
217: return 0;
218: }
220: /*TEST
222: build:
223: requires: !complex
225: test:
226: requires: !single
228: test:
229: suffix: 2
230: nsize: 2
231: requires: superlu_dist
233: TEST*/