xref: /petsc/src/dm/dt/tests/ex13.c (revision 3fc23ce8277e00a65c4b1be6e2b4535b56ba7978)
1 const char help[] = "Tests PetscDTPTrimmedEvalJet()";
2 
3 #include <petscdt.h>
4 #include <petscblaslapack.h>
5 #include <petscmat.h>
6 
7 static PetscErrorCode constructTabulationAndMass(PetscInt dim, PetscInt deg, PetscInt form, PetscInt jetDegree, PetscInt npoints,
8                                                  const PetscReal *points, const PetscReal *weights,
9                                                  PetscInt *_Nb, PetscInt *_Nf, PetscInt *_Nk,
10                                                  PetscReal **B, PetscScalar **M)
11 {
12   PetscInt       Nf; // Number of form components
13   PetscInt       Nbpt; // number of trimmed polynomials
14   PetscInt       Nk; // jet size
15   PetscReal     *p_trimmed;
16 
17   PetscFunctionBegin;
18   PetscCall(PetscDTBinomialInt(dim, PetscAbsInt(form), &Nf));
19   PetscCall(PetscDTPTrimmedSize(dim, deg, form, &Nbpt));
20   PetscCall(PetscDTBinomialInt(dim + jetDegree, dim, &Nk));
21   PetscCall(PetscMalloc1(Nbpt * Nf * Nk * npoints, &p_trimmed));
22   PetscCall(PetscDTPTrimmedEvalJet(dim, npoints, points, deg, form, jetDegree, p_trimmed));
23 
24   // compute the direct mass matrix
25   PetscScalar *M_trimmed;
26   PetscCall(PetscCalloc1(Nbpt * Nbpt, &M_trimmed));
27   for (PetscInt i = 0; i < Nbpt; i++) {
28     for (PetscInt j = 0; j < Nbpt; j++) {
29       PetscReal v = 0.;
30 
31       for (PetscInt f = 0; f < Nf; f++) {
32         const PetscReal *p_i = &p_trimmed[(i * Nf + f) * Nk * npoints];
33         const PetscReal *p_j = &p_trimmed[(j * Nf + f) * Nk * npoints];
34 
35         for (PetscInt pt = 0; pt < npoints; pt++) {
36           v += p_i[pt] * p_j[pt] * weights[pt];
37         }
38       }
39       M_trimmed[i * Nbpt + j] += v;
40     }
41   }
42   *_Nb = Nbpt;
43   *_Nf = Nf;
44   *_Nk = Nk;
45   *B = p_trimmed;
46   *M = M_trimmed;
47   PetscFunctionReturn(0);
48 }
49 
50 static PetscErrorCode test(PetscInt dim, PetscInt deg, PetscInt form, PetscInt jetDegree, PetscBool cond)
51 {
52   PetscQuadrature  q;
53   PetscInt         npoints;
54   const PetscReal *points;
55   const PetscReal *weights;
56   PetscInt         Nf; // Number of form components
57   PetscInt         Nk; // jet size
58   PetscInt         Nbpt; // number of trimmed polynomials
59   PetscReal       *p_trimmed;
60   PetscScalar     *M_trimmed;
61   PetscReal       *p_scalar;
62   PetscInt         Nbp; // number of scalar polynomials
63   PetscScalar     *Mcopy;
64   PetscScalar     *M_moments;
65   PetscReal        frob_err = 0.;
66   Mat              mat_trimmed;
67   Mat              mat_moments_T;
68   Mat              AinvB;
69   PetscInt         Nbm1;
70   Mat              Mm1;
71   PetscReal       *p_trimmed_copy;
72   PetscReal       *M_moment_real;
73 
74   PetscFunctionBegin;
75   // Construct an appropriate quadrature
76   PetscCall(PetscDTStroudConicalQuadrature(dim, 1, deg + 2, -1., 1., &q));
77   PetscCall(PetscQuadratureGetData(q, NULL, NULL, &npoints, &points, &weights));
78 
79   PetscCall(constructTabulationAndMass(dim, deg, form, jetDegree, npoints, points, weights, &Nbpt, &Nf, &Nk, &p_trimmed, &M_trimmed));
80 
81   PetscCall(PetscDTBinomialInt(dim + deg, dim, &Nbp));
82   PetscCall(PetscMalloc1(Nbp * Nk * npoints, &p_scalar));
83   PetscCall(PetscDTPKDEvalJet(dim, npoints, points, deg, jetDegree, p_scalar));
84 
85   PetscCall(PetscMalloc1(Nbpt * Nbpt, &Mcopy));
86   // Print the condition numbers (useful for testing out different bases internally in PetscDTPTrimmedEvalJet())
87 #if !defined(PETSC_USE_COMPLEX)
88   if (cond) {
89     PetscReal *S;
90     PetscScalar *work;
91     PetscBLASInt n = Nbpt;
92     PetscBLASInt lwork = 5 * Nbpt;
93     PetscBLASInt lierr;
94 
95     PetscCall(PetscMalloc1(Nbpt, &S));
96     PetscCall(PetscMalloc1(5*Nbpt, &work));
97     PetscCall(PetscArraycpy(Mcopy, M_trimmed, Nbpt * Nbpt));
98 
99     PetscStackCallBLAS("LAPACKgesvd",LAPACKgesvd_("N","N",&n,&n,Mcopy,&n,S,NULL,&n,NULL,&n,work,&lwork,&lierr));
100     PetscReal cond = S[0] / S[Nbpt - 1];
101     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "dimension %" PetscInt_FMT ", degree %" PetscInt_FMT ", form %" PetscInt_FMT ": condition number %g\n", dim, deg, form, (double) cond));
102     PetscCall(PetscFree(work));
103     PetscCall(PetscFree(S));
104   }
105 #endif
106 
107   // compute the moments with the orthonormal polynomials
108   PetscCall(PetscCalloc1(Nbpt * Nbp * Nf, &M_moments));
109   for (PetscInt i = 0; i < Nbp; i++) {
110     for (PetscInt j = 0; j < Nbpt; j++) {
111       for (PetscInt f = 0; f < Nf; f++) {
112         PetscReal        v = 0.;
113         const PetscReal *p_i = &p_scalar[i * Nk * npoints];
114         const PetscReal *p_j = &p_trimmed[(j * Nf + f) * Nk * npoints];
115 
116         for (PetscInt pt = 0; pt < npoints; pt++) {
117           v += p_i[pt] * p_j[pt] * weights[pt];
118         }
119         M_moments[(i * Nf + f) * Nbpt + j] += v;
120       }
121     }
122   }
123 
124   // subtract M_moments^T * M_moments from M_trimmed: because the trimmed polynomials should be contained in
125   // the full polynomials, the result should be zero
126   PetscCall(PetscArraycpy(Mcopy, M_trimmed, Nbpt * Nbpt));
127   {
128     PetscBLASInt m = Nbpt;
129     PetscBLASInt n = Nbpt;
130     PetscBLASInt k = Nbp * Nf;
131     PetscScalar mone = -1.;
132     PetscScalar one = 1.;
133 
134     PetscStackCallBLAS("BLASgemm",BLASgemm_("N","T",&m,&n,&k,&mone,M_moments,&m,M_moments,&m,&one,Mcopy,&m));
135   }
136 
137   frob_err = 0.;
138   for (PetscInt i = 0; i < Nbpt * Nbpt; i++) frob_err += PetscRealPart(Mcopy[i]) * PetscRealPart(Mcopy[i]);
139   frob_err = PetscSqrtReal(frob_err);
140 
141   if (frob_err > PETSC_SMALL) {
142     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_PLIB, "dimension %" PetscInt_FMT ", degree %" PetscInt_FMT ", form %" PetscInt_FMT ": trimmed projection error %g", dim, deg, form, (double) frob_err);
143   }
144 
145   // P trimmed is also supposed to contain the polynomials of one degree less: construction M_moment[0:sub,:] * M_trimmed^{-1} * M_moments[0:sub,:]^T should be the identity matrix
146   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, Nbpt, Nbpt, M_trimmed, &mat_trimmed));
147   PetscCall(PetscDTBinomialInt(dim + deg - 1, dim, &Nbm1));
148   PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, Nbpt, Nbm1 * Nf, M_moments, &mat_moments_T));
149   PetscCall(MatDuplicate(mat_moments_T, MAT_DO_NOT_COPY_VALUES, &AinvB));
150   PetscCall(MatLUFactor(mat_trimmed, NULL, NULL, NULL));
151   PetscCall(MatMatSolve(mat_trimmed, mat_moments_T, AinvB));
152   PetscCall(MatTransposeMatMult(mat_moments_T, AinvB, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &Mm1));
153   PetscCall(MatShift(Mm1, -1.));
154   PetscCall(MatNorm(Mm1, NORM_FROBENIUS, &frob_err));
155   if (frob_err > PETSC_SMALL) {
156     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_PLIB, "dimension %" PetscInt_FMT ", degree %" PetscInt_FMT ", form %" PetscInt_FMT ": trimmed reverse projection error %g", dim, deg, form, (double) frob_err);
157   }
158   PetscCall(MatDestroy(&Mm1));
159   PetscCall(MatDestroy(&AinvB));
160   PetscCall(MatDestroy(&mat_moments_T));
161 
162   // The Koszul differential applied to P trimmed (Lambda k+1) should be contained in P trimmed (Lambda k)
163   if (PetscAbsInt(form) < dim) {
164     PetscInt     Nf1, Nbpt1, Nk1;
165     PetscReal   *p_trimmed1;
166     PetscScalar *M_trimmed1;
167     PetscInt   (*pattern)[3];
168     PetscReal   *p_koszul;
169     PetscScalar *M_koszul;
170     PetscScalar *M_k_moment;
171     Mat          mat_koszul;
172     Mat          mat_k_moment_T;
173     Mat          AinvB;
174     Mat          prod;
175 
176     PetscCall(constructTabulationAndMass(dim, deg, form < 0 ? form - 1 : form + 1, 0, npoints, points, weights, &Nbpt1, &Nf1, &Nk1,
177                                        &p_trimmed1, &M_trimmed1));
178 
179     PetscCall(PetscMalloc1(Nf1 * (PetscAbsInt(form) + 1), &pattern));
180     PetscCall(PetscDTAltVInteriorPattern(dim, PetscAbsInt(form) + 1, pattern));
181 
182     // apply the Koszul operator
183     PetscCall(PetscCalloc1(Nbpt1 * Nf * npoints, &p_koszul));
184     for (PetscInt b = 0; b < Nbpt1; b++) {
185       for (PetscInt a = 0; a < Nf1 * (PetscAbsInt(form) + 1); a++) {
186         PetscInt         i,j,k;
187         PetscReal        sign;
188         PetscReal       *p_i;
189         const PetscReal *p_j;
190 
191         i = pattern[a][0];
192         if (form < 0) {
193           i = Nf-1-i;
194         }
195         j = pattern[a][1];
196         if (form < 0) {
197           j = Nf1-1-j;
198         }
199         k = pattern[a][2] < 0 ? -(pattern[a][2] + 1) : pattern[a][2];
200         sign = pattern[a][2] < 0 ? -1 : 1;
201         if (form < 0 && (i & 1) ^ (j & 1)) {
202           sign = -sign;
203         }
204 
205         p_i = &p_koszul[(b * Nf + i) * npoints];
206         p_j = &p_trimmed1[(b * Nf1 + j) * npoints];
207         for (PetscInt pt = 0; pt < npoints; pt++) {
208           p_i[pt] += p_j[pt] * points[pt * dim + k] * sign;
209         }
210       }
211     }
212 
213     // mass matrix of the result
214     PetscCall(PetscMalloc1(Nbpt1 * Nbpt1, &M_koszul));
215     for (PetscInt i = 0; i < Nbpt1; i++) {
216       for (PetscInt j = 0; j < Nbpt1; j++) {
217         PetscReal val = 0.;
218 
219         for (PetscInt v = 0; v < Nf; v++) {
220           const PetscReal *p_i = &p_koszul[(i * Nf + v) * npoints];
221           const PetscReal *p_j = &p_koszul[(j * Nf + v) * npoints];
222 
223           for (PetscInt pt = 0; pt < npoints; pt++) {
224             val += p_i[pt] * p_j[pt] * weights[pt];
225           }
226         }
227         M_koszul[i * Nbpt1 + j] = val;
228       }
229     }
230 
231     // moment matrix between the result and P trimmed
232     PetscCall(PetscMalloc1(Nbpt * Nbpt1, &M_k_moment));
233     for (PetscInt i = 0; i < Nbpt1; i++) {
234       for (PetscInt j = 0; j < Nbpt; j++) {
235         PetscReal val = 0.;
236 
237         for (PetscInt v = 0; v < Nf; v++) {
238           const PetscReal *p_i = &p_koszul[(i * Nf + v) * npoints];
239           const PetscReal *p_j = &p_trimmed[(j * Nf + v) * Nk * npoints];
240 
241           for (PetscInt pt = 0; pt < npoints; pt++) {
242             val += p_i[pt] * p_j[pt] * weights[pt];
243           }
244         }
245         M_k_moment[i * Nbpt + j] = val;
246       }
247     }
248 
249     // M_k_moment M_trimmed^{-1} M_k_moment^T == M_koszul
250     PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, Nbpt1, Nbpt1, M_koszul, &mat_koszul));
251     PetscCall(MatCreateSeqDense(PETSC_COMM_SELF, Nbpt, Nbpt1, M_k_moment, &mat_k_moment_T));
252     PetscCall(MatDuplicate(mat_k_moment_T, MAT_DO_NOT_COPY_VALUES, &AinvB));
253     PetscCall(MatMatSolve(mat_trimmed, mat_k_moment_T, AinvB));
254     PetscCall(MatTransposeMatMult(mat_k_moment_T, AinvB, MAT_INITIAL_MATRIX, PETSC_DEFAULT, &prod));
255     PetscCall(MatAXPY(prod, -1., mat_koszul, SAME_NONZERO_PATTERN));
256     PetscCall(MatNorm(prod, NORM_FROBENIUS, &frob_err));
257     if (frob_err > PETSC_SMALL) {
258       SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_PLIB, "dimension %" PetscInt_FMT ", degree %" PetscInt_FMT ", forms (%" PetscInt_FMT ", %" PetscInt_FMT "): koszul projection error %g", dim, deg, form, form < 0 ? (form-1):(form+1), (double) frob_err);
259     }
260 
261     PetscCall(MatDestroy(&prod));
262     PetscCall(MatDestroy(&AinvB));
263     PetscCall(MatDestroy(&mat_k_moment_T));
264     PetscCall(MatDestroy(&mat_koszul));
265     PetscCall(PetscFree(M_k_moment));
266     PetscCall(PetscFree(M_koszul));
267     PetscCall(PetscFree(p_koszul));
268     PetscCall(PetscFree(pattern));
269     PetscCall(PetscFree(p_trimmed1));
270     PetscCall(PetscFree(M_trimmed1));
271   }
272 
273   // M_moments has shape [Nbp][Nf][Nbpt]
274   // p_scalar has shape [Nbp][Nk][npoints]
275   // contracting on [Nbp] should be the same shape as
276   // p_trimmed, which is [Nbpt][Nf][Nk][npoints]
277   PetscCall(PetscCalloc1(Nbpt * Nf * Nk * npoints, &p_trimmed_copy));
278   PetscCall(PetscMalloc1(Nbp * Nf * Nbpt, &M_moment_real));
279   for (PetscInt i = 0; i < Nbp * Nf * Nbpt; i++) {
280     M_moment_real[i] = PetscRealPart(M_moments[i]);
281   }
282   for (PetscInt f = 0; f < Nf; f++) {
283     PetscBLASInt m = Nk * npoints;
284     PetscBLASInt n = Nbpt;
285     PetscBLASInt k = Nbp;
286     PetscBLASInt lda = Nk * npoints;
287     PetscBLASInt ldb = Nf * Nbpt;
288     PetscBLASInt ldc = Nf * Nk * npoints;
289     PetscReal    alpha = 1.0;
290     PetscReal    beta = 1.0;
291 
292     PetscStackCallBLAS("BLASREALgemm",BLASREALgemm_("N","T",&m,&n,&k,&alpha,p_scalar,&lda,&M_moment_real[f * Nbpt],&ldb,&beta,&p_trimmed_copy[f * Nk * npoints],&ldc));
293   }
294   frob_err = 0.;
295   for (PetscInt i = 0; i < Nbpt * Nf * Nk * npoints; i++) {
296     frob_err += (p_trimmed_copy[i] - p_trimmed[i]) * (p_trimmed_copy[i] - p_trimmed[i]);
297   }
298   frob_err = PetscSqrtReal(frob_err);
299 
300   if (frob_err > PETSC_SMALL) {
301     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_PLIB, "dimension %" PetscInt_FMT ", degree %" PetscInt_FMT ", form %" PetscInt_FMT ": jet error %g", dim, deg, form, (double) frob_err);
302   }
303 
304   PetscCall(PetscFree(M_moment_real));
305   PetscCall(PetscFree(p_trimmed_copy));
306   PetscCall(MatDestroy(&mat_trimmed));
307   PetscCall(PetscFree(Mcopy));
308   PetscCall(PetscFree(M_moments));
309   PetscCall(PetscFree(M_trimmed));
310   PetscCall(PetscFree(p_trimmed));
311   PetscCall(PetscFree(p_scalar));
312   PetscCall(PetscQuadratureDestroy(&q));
313   PetscFunctionReturn(0);
314 }
315 
316 int main(int argc, char **argv)
317 {
318   PetscInt       max_dim = 3;
319   PetscInt       max_deg = 4;
320   PetscInt       k       = 3;
321   PetscBool      cond    = PETSC_FALSE;
322 
323   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
324   PetscOptionsBegin(PETSC_COMM_WORLD,"","Options for PetscDTPTrimmedEvalJet() tests","none");
325   PetscCall(PetscOptionsInt("-max_dim", "Maximum dimension of the simplex",__FILE__,max_dim,&max_dim,NULL));
326   PetscCall(PetscOptionsInt("-max_degree", "Maximum degree of the trimmed polynomial space",__FILE__,max_deg,&max_deg,NULL));
327   PetscCall(PetscOptionsInt("-max_jet", "The number of derivatives to test",__FILE__,k,&k,NULL));
328   PetscCall(PetscOptionsBool("-cond", "Compute the condition numbers of the mass matrices of the bases",__FILE__,cond,&cond,NULL));
329   PetscOptionsEnd();
330   for (PetscInt dim = 2; dim <= max_dim; dim++) {
331     for (PetscInt deg = 1; deg <= max_deg; deg++) {
332       for (PetscInt form = -dim+1; form <= dim; form++) {
333         PetscCall(test(dim, deg, form, PetscMax(1, k), cond));
334       }
335     }
336   }
337   PetscCall(PetscFinalize());
338   return 0;
339 }
340 
341 /*TEST
342 
343   test:
344     requires: !single
345     args:
346 
347 TEST*/
348