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