xref: /petsc/src/dm/dt/space/impls/poly/spacepoly.c (revision a4e35b1925eceef64945ea472b84f2bf06a67b5e)
1 #include <petsc/private/petscfeimpl.h> /*I "petscfe.h" I*/
2 
3 static PetscErrorCode PetscSpaceSetFromOptions_Polynomial(PetscSpace sp, PetscOptionItems *PetscOptionsObject)
4 {
5   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
6 
7   PetscFunctionBegin;
8   PetscOptionsHeadBegin(PetscOptionsObject, "PetscSpace polynomial options");
9   PetscCall(PetscOptionsBool("-petscspace_poly_tensor", "Use the tensor product polynomials", "PetscSpacePolynomialSetTensor", poly->tensor, &poly->tensor, NULL));
10   PetscOptionsHeadEnd();
11   PetscFunctionReturn(PETSC_SUCCESS);
12 }
13 
14 static PetscErrorCode PetscSpacePolynomialView_Ascii(PetscSpace sp, PetscViewer v)
15 {
16   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
17 
18   PetscFunctionBegin;
19   PetscCall(PetscViewerASCIIPrintf(v, "%s space of degree %" PetscInt_FMT "\n", poly->tensor ? "Tensor polynomial" : "Polynomial", sp->degree));
20   PetscFunctionReturn(PETSC_SUCCESS);
21 }
22 
23 static PetscErrorCode PetscSpaceView_Polynomial(PetscSpace sp, PetscViewer viewer)
24 {
25   PetscBool iascii;
26 
27   PetscFunctionBegin;
28   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 1);
29   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
30   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
31   if (iascii) PetscCall(PetscSpacePolynomialView_Ascii(sp, viewer));
32   PetscFunctionReturn(PETSC_SUCCESS);
33 }
34 
35 static PetscErrorCode PetscSpaceDestroy_Polynomial(PetscSpace sp)
36 {
37   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
38 
39   PetscFunctionBegin;
40   PetscCall(PetscObjectComposeFunction((PetscObject)sp, "PetscSpacePolynomialGetTensor_C", NULL));
41   PetscCall(PetscObjectComposeFunction((PetscObject)sp, "PetscSpacePolynomialSetTensor_C", NULL));
42   if (poly->subspaces) {
43     PetscInt d;
44 
45     for (d = 0; d < sp->Nv; ++d) PetscCall(PetscSpaceDestroy(&poly->subspaces[d]));
46   }
47   PetscCall(PetscFree(poly->subspaces));
48   PetscCall(PetscFree(poly));
49   PetscFunctionReturn(PETSC_SUCCESS);
50 }
51 
52 static PetscErrorCode PetscSpaceSetUp_Polynomial(PetscSpace sp)
53 {
54   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
55 
56   PetscFunctionBegin;
57   if (poly->setupCalled) PetscFunctionReturn(PETSC_SUCCESS);
58   if (sp->Nv <= 1) poly->tensor = PETSC_FALSE;
59   if (sp->Nc != 1) {
60     PetscInt    Nc     = sp->Nc;
61     PetscBool   tensor = poly->tensor;
62     PetscInt    Nv     = sp->Nv;
63     PetscInt    degree = sp->degree;
64     const char *prefix;
65     const char *name;
66     char        subname[PETSC_MAX_PATH_LEN];
67     PetscSpace  subsp;
68 
69     PetscCall(PetscSpaceSetType(sp, PETSCSPACESUM));
70     PetscCall(PetscSpaceSumSetNumSubspaces(sp, Nc));
71     PetscCall(PetscSpaceCreate(PetscObjectComm((PetscObject)sp), &subsp));
72     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)sp, &prefix));
73     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)subsp, prefix));
74     PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)subsp, "sumcomp_"));
75     if (((PetscObject)sp)->name) {
76       PetscCall(PetscObjectGetName((PetscObject)sp, &name));
77       PetscCall(PetscSNPrintf(subname, PETSC_MAX_PATH_LEN - 1, "%s sum component", name));
78       PetscCall(PetscObjectSetName((PetscObject)subsp, subname));
79     } else PetscCall(PetscObjectSetName((PetscObject)subsp, "sum component"));
80     PetscCall(PetscSpaceSetType(subsp, PETSCSPACEPOLYNOMIAL));
81     PetscCall(PetscSpaceSetDegree(subsp, degree, PETSC_DETERMINE));
82     PetscCall(PetscSpaceSetNumComponents(subsp, 1));
83     PetscCall(PetscSpaceSetNumVariables(subsp, Nv));
84     PetscCall(PetscSpacePolynomialSetTensor(subsp, tensor));
85     PetscCall(PetscSpaceSetUp(subsp));
86     for (PetscInt i = 0; i < Nc; i++) PetscCall(PetscSpaceSumSetSubspace(sp, i, subsp));
87     PetscCall(PetscSpaceDestroy(&subsp));
88     PetscCall(PetscSpaceSetUp(sp));
89     PetscFunctionReturn(PETSC_SUCCESS);
90   }
91   if (poly->tensor) {
92     sp->maxDegree = PETSC_DETERMINE;
93     PetscCall(PetscSpaceSetType(sp, PETSCSPACETENSOR));
94     PetscCall(PetscSpaceSetUp(sp));
95     PetscFunctionReturn(PETSC_SUCCESS);
96   }
97   PetscCheck(sp->degree >= 0, PetscObjectComm((PetscObject)sp), PETSC_ERR_ARG_OUTOFRANGE, "Negative degree %" PetscInt_FMT " invalid", sp->degree);
98   sp->maxDegree     = sp->degree;
99   poly->setupCalled = PETSC_TRUE;
100   PetscFunctionReturn(PETSC_SUCCESS);
101 }
102 
103 static PetscErrorCode PetscSpaceGetDimension_Polynomial(PetscSpace sp, PetscInt *dim)
104 {
105   PetscInt deg = sp->degree;
106   PetscInt n   = sp->Nv;
107 
108   PetscFunctionBegin;
109   PetscCall(PetscDTBinomialInt(n + deg, n, dim));
110   *dim *= sp->Nc;
111   PetscFunctionReturn(PETSC_SUCCESS);
112 }
113 
114 static PetscErrorCode CoordinateBasis(PetscInt dim, PetscInt npoints, const PetscReal points[], PetscInt jet, PetscInt Njet, PetscReal pScalar[])
115 {
116   PetscFunctionBegin;
117   PetscCall(PetscArrayzero(pScalar, (1 + dim) * Njet * npoints));
118   for (PetscInt b = 0; b < 1 + dim; b++) {
119     for (PetscInt j = 0; j < PetscMin(1 + dim, Njet); j++) {
120       if (j == 0) {
121         if (b == 0) {
122           for (PetscInt pt = 0; pt < npoints; pt++) pScalar[b * Njet * npoints + j * npoints + pt] = 1.;
123         } else {
124           for (PetscInt pt = 0; pt < npoints; pt++) pScalar[b * Njet * npoints + j * npoints + pt] = points[pt * dim + (b - 1)];
125         }
126       } else if (j == b) {
127         for (PetscInt pt = 0; pt < npoints; pt++) pScalar[b * Njet * npoints + j * npoints + pt] = 1.;
128       }
129     }
130   }
131   PetscFunctionReturn(PETSC_SUCCESS);
132 }
133 
134 static PetscErrorCode PetscSpaceEvaluate_Polynomial(PetscSpace sp, PetscInt npoints, const PetscReal points[], PetscReal B[], PetscReal D[], PetscReal H[])
135 {
136   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
137   DM               dm   = sp->dm;
138   PetscInt         dim  = sp->Nv;
139   PetscInt         Nb, jet, Njet;
140   PetscReal       *pScalar;
141 
142   PetscFunctionBegin;
143   if (!poly->setupCalled) {
144     PetscCall(PetscSpaceSetUp(sp));
145     PetscCall(PetscSpaceEvaluate(sp, npoints, points, B, D, H));
146     PetscFunctionReturn(PETSC_SUCCESS);
147   }
148   PetscCheck(!poly->tensor && sp->Nc == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "tensor and multicomponent spaces should have been converted");
149   PetscCall(PetscDTBinomialInt(dim + sp->degree, dim, &Nb));
150   if (H) {
151     jet = 2;
152   } else if (D) {
153     jet = 1;
154   } else {
155     jet = 0;
156   }
157   PetscCall(PetscDTBinomialInt(dim + jet, dim, &Njet));
158   PetscCall(DMGetWorkArray(dm, Nb * Njet * npoints, MPIU_REAL, &pScalar));
159   // Why are we handling the case degree == 1 specially?  Because we don't want numerical noise when we evaluate hat
160   // functions at the vertices of a simplex, which happens when we invert the Vandermonde matrix of the PKD basis.
161   // We don't make any promise about which basis is used.
162   if (sp->degree == 1) {
163     PetscCall(CoordinateBasis(dim, npoints, points, jet, Njet, pScalar));
164   } else {
165     PetscCall(PetscDTPKDEvalJet(dim, npoints, points, sp->degree, jet, pScalar));
166   }
167   if (B) {
168     PetscInt p_strl = Nb;
169     PetscInt b_strl = 1;
170 
171     PetscInt b_strr = Njet * npoints;
172     PetscInt p_strr = 1;
173 
174     PetscCall(PetscArrayzero(B, npoints * Nb));
175     for (PetscInt b = 0; b < Nb; b++) {
176       for (PetscInt p = 0; p < npoints; p++) B[p * p_strl + b * b_strl] = pScalar[b * b_strr + p * p_strr];
177     }
178   }
179   if (D) {
180     PetscInt p_strl = dim * Nb;
181     PetscInt b_strl = dim;
182     PetscInt d_strl = 1;
183 
184     PetscInt b_strr = Njet * npoints;
185     PetscInt d_strr = npoints;
186     PetscInt p_strr = 1;
187 
188     PetscCall(PetscArrayzero(D, npoints * Nb * dim));
189     for (PetscInt d = 0; d < dim; d++) {
190       for (PetscInt b = 0; b < Nb; b++) {
191         for (PetscInt p = 0; p < npoints; p++) D[p * p_strl + b * b_strl + d * d_strl] = pScalar[b * b_strr + (1 + d) * d_strr + p * p_strr];
192       }
193     }
194   }
195   if (H) {
196     PetscInt p_strl  = dim * dim * Nb;
197     PetscInt b_strl  = dim * dim;
198     PetscInt d1_strl = dim;
199     PetscInt d2_strl = 1;
200 
201     PetscInt b_strr = Njet * npoints;
202     PetscInt j_strr = npoints;
203     PetscInt p_strr = 1;
204 
205     PetscInt *derivs;
206     PetscCall(PetscCalloc1(dim, &derivs));
207     PetscCall(PetscArrayzero(H, npoints * Nb * dim * dim));
208     for (PetscInt d1 = 0; d1 < dim; d1++) {
209       for (PetscInt d2 = 0; d2 < dim; d2++) {
210         PetscInt j;
211         derivs[d1]++;
212         derivs[d2]++;
213         PetscCall(PetscDTGradedOrderToIndex(dim, derivs, &j));
214         derivs[d1]--;
215         derivs[d2]--;
216         for (PetscInt b = 0; b < Nb; b++) {
217           for (PetscInt p = 0; p < npoints; p++) H[p * p_strl + b * b_strl + d1 * d1_strl + d2 * d2_strl] = pScalar[b * b_strr + j * j_strr + p * p_strr];
218         }
219       }
220     }
221     PetscCall(PetscFree(derivs));
222   }
223   PetscCall(DMRestoreWorkArray(dm, Nb * Njet * npoints, MPIU_REAL, &pScalar));
224   PetscFunctionReturn(PETSC_SUCCESS);
225 }
226 
227 /*@
228   PetscSpacePolynomialSetTensor - Set whether a function space is a space of tensor polynomials.
229 
230   Input Parameters:
231 + sp     - the function space object
232 - tensor - `PETSC_TRUE` for a tensor polynomial space, `PETSC_FALSE` for a polynomial space
233 
234   Options Database Key:
235 . -petscspace_poly_tensor <bool> - Whether to use tensor product polynomials in higher dimension
236 
237   Level: intermediate
238 
239   Notes:
240   It is a tensor space if it is spanned by polynomials whose degree in each variable is
241   bounded by the given order, as opposed to the space spanned by polynomials
242   whose total degree---summing over all variables---is bounded by the given order.
243 
244 .seealso: `PetscSpace`, `PetscSpacePolynomialGetTensor()`, `PetscSpaceSetDegree()`, `PetscSpaceSetNumVariables()`
245 @*/
246 PetscErrorCode PetscSpacePolynomialSetTensor(PetscSpace sp, PetscBool tensor)
247 {
248   PetscFunctionBegin;
249   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 1);
250   PetscTryMethod(sp, "PetscSpacePolynomialSetTensor_C", (PetscSpace, PetscBool), (sp, tensor));
251   PetscFunctionReturn(PETSC_SUCCESS);
252 }
253 
254 /*@
255   PetscSpacePolynomialGetTensor - Get whether a function space is a space of tensor
256   polynomials.
257 
258   Input Parameter:
259 . sp - the function space object
260 
261   Output Parameter:
262 . tensor - `PETSC_TRUE` for a tensor polynomial space, `PETSC_FALSE` for a polynomial space
263 
264   Level: intermediate
265 
266   Notes:
267   The space is a tensor space if it is spanned by polynomials whose degree in each variable is
268   bounded by the given order, as opposed to the space spanned by polynomials
269   whose total degree---summing over all variables---is bounded by the given order.
270 
271 .seealso: `PetscSpace`, `PetscSpacePolynomialSetTensor()`, `PetscSpaceSetDegree()`, `PetscSpaceSetNumVariables()`
272 @*/
273 PetscErrorCode PetscSpacePolynomialGetTensor(PetscSpace sp, PetscBool *tensor)
274 {
275   PetscFunctionBegin;
276   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 1);
277   PetscAssertPointer(tensor, 2);
278   PetscTryMethod(sp, "PetscSpacePolynomialGetTensor_C", (PetscSpace, PetscBool *), (sp, tensor));
279   PetscFunctionReturn(PETSC_SUCCESS);
280 }
281 
282 static PetscErrorCode PetscSpacePolynomialSetTensor_Polynomial(PetscSpace sp, PetscBool tensor)
283 {
284   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
285 
286   PetscFunctionBegin;
287   poly->tensor = tensor;
288   PetscFunctionReturn(PETSC_SUCCESS);
289 }
290 
291 static PetscErrorCode PetscSpacePolynomialGetTensor_Polynomial(PetscSpace sp, PetscBool *tensor)
292 {
293   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
294 
295   PetscFunctionBegin;
296   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 1);
297   PetscAssertPointer(tensor, 2);
298   *tensor = poly->tensor;
299   PetscFunctionReturn(PETSC_SUCCESS);
300 }
301 
302 static PetscErrorCode PetscSpaceGetHeightSubspace_Polynomial(PetscSpace sp, PetscInt height, PetscSpace *subsp)
303 {
304   PetscSpace_Poly *poly = (PetscSpace_Poly *)sp->data;
305   PetscInt         Nc, dim, order;
306   PetscBool        tensor;
307 
308   PetscFunctionBegin;
309   PetscCall(PetscSpaceGetNumComponents(sp, &Nc));
310   PetscCall(PetscSpaceGetNumVariables(sp, &dim));
311   PetscCall(PetscSpaceGetDegree(sp, &order, NULL));
312   PetscCall(PetscSpacePolynomialGetTensor(sp, &tensor));
313   PetscCheck(height <= dim && height >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Asked for space at height %" PetscInt_FMT " for dimension %" PetscInt_FMT " space", height, dim);
314   if (!poly->subspaces) PetscCall(PetscCalloc1(dim, &poly->subspaces));
315   if (height <= dim) {
316     if (!poly->subspaces[height - 1]) {
317       PetscSpace  sub;
318       const char *name;
319 
320       PetscCall(PetscSpaceCreate(PetscObjectComm((PetscObject)sp), &sub));
321       PetscCall(PetscObjectGetName((PetscObject)sp, &name));
322       PetscCall(PetscObjectSetName((PetscObject)sub, name));
323       PetscCall(PetscSpaceSetType(sub, PETSCSPACEPOLYNOMIAL));
324       PetscCall(PetscSpaceSetNumComponents(sub, Nc));
325       PetscCall(PetscSpaceSetDegree(sub, order, PETSC_DETERMINE));
326       PetscCall(PetscSpaceSetNumVariables(sub, dim - height));
327       PetscCall(PetscSpacePolynomialSetTensor(sub, tensor));
328       PetscCall(PetscSpaceSetUp(sub));
329       poly->subspaces[height - 1] = sub;
330     }
331     *subsp = poly->subspaces[height - 1];
332   } else {
333     *subsp = NULL;
334   }
335   PetscFunctionReturn(PETSC_SUCCESS);
336 }
337 
338 static PetscErrorCode PetscSpaceInitialize_Polynomial(PetscSpace sp)
339 {
340   PetscFunctionBegin;
341   sp->ops->setfromoptions    = PetscSpaceSetFromOptions_Polynomial;
342   sp->ops->setup             = PetscSpaceSetUp_Polynomial;
343   sp->ops->view              = PetscSpaceView_Polynomial;
344   sp->ops->destroy           = PetscSpaceDestroy_Polynomial;
345   sp->ops->getdimension      = PetscSpaceGetDimension_Polynomial;
346   sp->ops->evaluate          = PetscSpaceEvaluate_Polynomial;
347   sp->ops->getheightsubspace = PetscSpaceGetHeightSubspace_Polynomial;
348   PetscCall(PetscObjectComposeFunction((PetscObject)sp, "PetscSpacePolynomialGetTensor_C", PetscSpacePolynomialGetTensor_Polynomial));
349   PetscCall(PetscObjectComposeFunction((PetscObject)sp, "PetscSpacePolynomialSetTensor_C", PetscSpacePolynomialSetTensor_Polynomial));
350   PetscFunctionReturn(PETSC_SUCCESS);
351 }
352 
353 /*MC
354   PETSCSPACEPOLYNOMIAL = "poly" - A `PetscSpace` object that encapsulates a polynomial space, e.g. P1 is the space of
355   linear polynomials. The space is replicated for each component.
356 
357   Level: intermediate
358 
359 .seealso: `PetscSpace`, `PetscSpaceType`, `PetscSpaceCreate()`, `PetscSpaceSetType()`
360 M*/
361 
362 PETSC_EXTERN PetscErrorCode PetscSpaceCreate_Polynomial(PetscSpace sp)
363 {
364   PetscSpace_Poly *poly;
365 
366   PetscFunctionBegin;
367   PetscValidHeaderSpecific(sp, PETSCSPACE_CLASSID, 1);
368   PetscCall(PetscNew(&poly));
369   sp->data = poly;
370 
371   poly->tensor    = PETSC_FALSE;
372   poly->subspaces = NULL;
373 
374   PetscCall(PetscSpaceInitialize_Polynomial(sp));
375   PetscFunctionReturn(PETSC_SUCCESS);
376 }
377