1 #include <petsc/private/petscimpl.h> 2 #include <petsc/private/dtimpl.h> /*I "petscdt.h" I*/ 3 4 /*MC 5 PetscDTAltV - An interface for common operations on k-forms, also known as alternating algebraic forms or alternating k-linear maps. 6 The name of the interface comes from the notation "Alt V" for the algebra of all k-forms acting vectors in the space V, also known as the exterior algebra of V*. 7 8 A recommended reference for this material is Section 2 "Exterior algebra and exterior calculus" in "Finite element 9 exterior calculus, homological techniques, and applications", by Arnold, Falk, & Winther (2006, doi:10.1017/S0962492906210018). 10 11 A k-form w (k is called the "form degree" of w) is an alternating k-linear map acting on tuples (v_1, ..., v_k) of 12 vectors from a vector space V and producing a real number: 13 - alternating: swapping any two vectors in a tuple reverses the sign of the result, e.g. w(v_1, v_2, ..., v_k) = -w(v_2, v_1, ..., v_k) 14 - k-linear: w acts linear in each vector separately, e.g. w(a*v + b*y, v_2, ..., v_k) = a*w(v,v_2,...,v_k) + b*w(y,v_2,...,v_k) 15 This action is implemented as `PetscDTAltVApply()`. 16 17 The k-forms on a vector space form a vector space themselves, Alt^k V. The dimension of Alt^k V, if V is N dimensional, is N choose k. (This 18 shows that for an N dimensional space, only 0 <= k <= N are valid form degrees.) 19 The standard basis for Alt^k V, used in PetscDTAltV, has one basis k-form for each ordered subset of k coordinates of the N dimensional space: 20 For example, if the coordinate directions of a four dimensional space are (t, x, y, z), then there are 4 choose 2 = 6 ordered subsets of two coordinates. 21 They are, in lexicographic order, (t, x), (t, y), (t, z), (x, y), (x, z) and (y, z). PetscDTAltV also orders the basis of Alt^k V lexicographically 22 by the associated subsets. 23 24 The unit basis k-form associated with coordinates (c_1, ..., c_k) acts on a set of k vectors (v_1, ..., v_k) by creating a square matrix V where 25 V[i,j] = v_i[c_j] and taking the determinant of V. 26 27 If j + k <= N, then a j-form f and a k-form g can be multiplied to create a (j+k)-form using the wedge or exterior product, (f wedge g). 28 This is an anticommutative product, (f wedge g) = -(g wedge f). It is sufficient to describe the wedge product of two basis forms. 29 Let f be the basis j-form associated with coordinates (f_1,...,f_j) and g be the basis k-form associated with coordinates (g_1,...,g_k): 30 - If there is any coordinate in both sets, then (f wedge g) = 0. 31 - Otherwise, (f wedge g) is a multiple of the basis (j+k)-form h associated with (f_1,...,f_j,g_1,...,g_k). 32 - In fact it is equal to either h or -h depending on how (f_1,...,f_j,g_1,...,g_k) compares to the same list of coordinates given in ascending order: if it is an even permutation of that list, then (f wedge g) = h, otherwise (f wedge g) = -h. 33 The wedge product is implemented for either two inputs (f and g) in `PetscDTAltVWedge()`, or for one (just f, giving a 34 matrix to multiply against multiple choices of g) in `PetscDTAltVWedgeMatrix()`. 35 36 If k > 0, a k-form w and a vector v can combine to make a (k-1)-form through the interior product, (w int v), 37 defined by (w int v)(v_1,...,v_{k-1}) = w(v,v_1,...,v_{k-1}). 38 39 The interior product is implemented for either two inputs (w and v) in PetscDTAltVInterior, for one (just v, giving a 40 matrix to multiply against multiple choices of w) in `PetscDTAltVInteriorMatrix()`, 41 or for no inputs (giving the sparsity pattern of `PetscDTAltVInteriorMatrix()`) in `PetscDTAltVInteriorPattern()`. 42 43 When there is a linear map L: V -> W from an N dimensional vector space to an M dimensional vector space, 44 it induces the linear pullback map L^* : Alt^k W -> Alt^k V, defined by L^* w(v_1,...,v_k) = w(L v_1, ..., L v_k). 45 The pullback is implemented as `PetscDTAltVPullback()` (acting on a known w) and `PetscDTAltVPullbackMatrix()` (creating a matrix that computes the actin of L^*). 46 47 Alt^k V and Alt^(N-k) V have the same dimension, and the Hodge star operator maps between them. We note that Alt^N V is a one dimensional space, and its 48 basis vector is sometime called vol. The Hodge star operator has the property that (f wedge (star g)) = (f,g) vol, where (f,g) is the simple inner product 49 of the basis coefficients of f and g. 50 Powers of the Hodge star operator can be applied with PetscDTAltVStar 51 52 Level: intermediate 53 54 .seealso: `PetscDTAltVApply()`, `PetscDTAltVWedge()`, `PetscDTAltVInterior()`, `PetscDTAltVPullback()`, `PetscDTAltVStar()` 55 M*/ 56 57 /*@ 58 PetscDTAltVApply - Apply an a k-form (an alternating k-linear map) to a set of k N-dimensional vectors 59 60 Input Parameters: 61 + N - the dimension of the vector space, N >= 0 62 . k - the degree k of the k-form w, 0 <= k <= N 63 . w - a k-form, size [N choose k] (each degree of freedom of a k-form is associated with a subset of k coordinates of the N-dimensional vectors. 64 The degrees of freedom are ordered lexicographically by their associated subsets) 65 - v - a set of k vectors of size N, size [k x N], each vector stored contiguously 66 67 Output Parameter: 68 . wv - w(v_1,...,v_k) = \sum_i w_i * det(V_i): the degree of freedom w_i is associated with coordinates [s_{i,1},...,s_{i,k}], and the square matrix V_i has 69 entry (j,k) given by the s_{i,k}'th coordinate of v_j 70 71 Level: intermediate 72 73 .seealso: `PetscDTAltV`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 74 @*/ 75 PetscErrorCode PetscDTAltVApply(PetscInt N, PetscInt k, const PetscReal *w, const PetscReal *v, PetscReal *wv) 76 { 77 PetscFunctionBegin; 78 PetscCheck(N >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid dimension"); 79 PetscCheck(k >= 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 80 if (N <= 3) { 81 if (!k) { 82 *wv = w[0]; 83 } else { 84 if (N == 1) { 85 *wv = w[0] * v[0]; 86 } else if (N == 2) { 87 if (k == 1) { 88 *wv = w[0] * v[0] + w[1] * v[1]; 89 } else { 90 *wv = w[0] * (v[0] * v[3] - v[1] * v[2]); 91 } 92 } else { 93 if (k == 1) { 94 *wv = w[0] * v[0] + w[1] * v[1] + w[2] * v[2]; 95 } else if (k == 2) { 96 *wv = w[0] * (v[0] * v[4] - v[1] * v[3]) + w[1] * (v[0] * v[5] - v[2] * v[3]) + w[2] * (v[1] * v[5] - v[2] * v[4]); 97 } else { 98 *wv = w[0] * (v[0] * (v[4] * v[8] - v[5] * v[7]) + v[1] * (v[5] * v[6] - v[3] * v[8]) + v[2] * (v[3] * v[7] - v[4] * v[6])); 99 } 100 } 101 } 102 } else { 103 PetscInt Nk, Nf; 104 PetscInt *subset, *perm; 105 PetscInt i, j, l; 106 PetscReal sum = 0.; 107 108 PetscCall(PetscDTFactorialInt(k, &Nf)); 109 PetscCall(PetscDTBinomialInt(N, k, &Nk)); 110 PetscCall(PetscMalloc2(k, &subset, k, &perm)); 111 for (i = 0; i < Nk; i++) { 112 PetscReal subsum = 0.; 113 114 PetscCall(PetscDTEnumSubset(N, k, i, subset)); 115 for (j = 0; j < Nf; j++) { 116 PetscBool permOdd; 117 PetscReal prod; 118 119 PetscCall(PetscDTEnumPerm(k, j, perm, &permOdd)); 120 prod = permOdd ? -1. : 1.; 121 for (l = 0; l < k; l++) prod *= v[perm[l] * N + subset[l]]; 122 subsum += prod; 123 } 124 sum += w[i] * subsum; 125 } 126 PetscCall(PetscFree2(subset, perm)); 127 *wv = sum; 128 } 129 PetscFunctionReturn(PETSC_SUCCESS); 130 } 131 132 /*@ 133 PetscDTAltVWedge - Compute the wedge product of a j-form and a k-form, giving a (j+k) form 134 135 Input Parameters: 136 + N - the dimension of the vector space, N >= 0 137 . j - the degree j of the j-form a, 0 <= j <= N 138 . k - the degree k of the k-form b, 0 <= k <= N and 0 <= j+k <= N 139 . a - a j-form, size [N choose j] 140 - b - a k-form, size [N choose k] 141 142 Output Parameter: 143 . awedgeb - the (j+k)-form a wedge b, size [N choose (j+k)]: (a wedge b)(v_1,...,v_{j+k}) = \sum_{s} sign(s) a(v_{s_1},...,v_{s_j}) b(v_{s_{j+1}},...,v_{s_{j+k}}), 144 where the sum is over permutations s such that s_1 < s_2 < ... < s_j and s_{j+1} < s_{j+2} < ... < s_{j+k}. 145 146 Level: intermediate 147 148 .seealso: `PetscDTAltV`, `PetscDTAltVWedgeMatrix()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 149 @*/ 150 PetscErrorCode PetscDTAltVWedge(PetscInt N, PetscInt j, PetscInt k, const PetscReal *a, const PetscReal *b, PetscReal *awedgeb) 151 { 152 PetscInt i; 153 154 PetscFunctionBegin; 155 PetscCheck(N >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid dimension"); 156 PetscCheck(j >= 0 && k >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "negative form degree"); 157 PetscCheck(j + k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Wedge greater than dimension"); 158 if (N <= 3) { 159 PetscInt Njk; 160 161 PetscCall(PetscDTBinomialInt(N, j + k, &Njk)); 162 if (!j) { 163 for (i = 0; i < Njk; i++) awedgeb[i] = a[0] * b[i]; 164 } else if (!k) { 165 for (i = 0; i < Njk; i++) awedgeb[i] = a[i] * b[0]; 166 } else { 167 if (N == 2) { 168 awedgeb[0] = a[0] * b[1] - a[1] * b[0]; 169 } else { 170 if (j + k == 2) { 171 awedgeb[0] = a[0] * b[1] - a[1] * b[0]; 172 awedgeb[1] = a[0] * b[2] - a[2] * b[0]; 173 awedgeb[2] = a[1] * b[2] - a[2] * b[1]; 174 } else { 175 awedgeb[0] = a[0] * b[2] - a[1] * b[1] + a[2] * b[0]; 176 } 177 } 178 } 179 } else { 180 PetscInt Njk; 181 PetscInt JKj; 182 PetscInt *subset, *subsetjk, *subsetj, *subsetk; 183 PetscInt i; 184 185 PetscCall(PetscDTBinomialInt(N, j + k, &Njk)); 186 PetscCall(PetscDTBinomialInt(j + k, j, &JKj)); 187 PetscCall(PetscMalloc4(j + k, &subset, j + k, &subsetjk, j, &subsetj, k, &subsetk)); 188 for (i = 0; i < Njk; i++) { 189 PetscReal sum = 0.; 190 PetscInt l; 191 192 PetscCall(PetscDTEnumSubset(N, j + k, i, subset)); 193 for (l = 0; l < JKj; l++) { 194 PetscBool jkOdd; 195 PetscInt m, jInd, kInd; 196 197 PetscCall(PetscDTEnumSplit(j + k, j, l, subsetjk, &jkOdd)); 198 for (m = 0; m < j; m++) subsetj[m] = subset[subsetjk[m]]; 199 for (m = 0; m < k; m++) subsetk[m] = subset[subsetjk[j + m]]; 200 PetscCall(PetscDTSubsetIndex(N, j, subsetj, &jInd)); 201 PetscCall(PetscDTSubsetIndex(N, k, subsetk, &kInd)); 202 sum += jkOdd ? -(a[jInd] * b[kInd]) : (a[jInd] * b[kInd]); 203 } 204 awedgeb[i] = sum; 205 } 206 PetscCall(PetscFree4(subset, subsetjk, subsetj, subsetk)); 207 } 208 PetscFunctionReturn(PETSC_SUCCESS); 209 } 210 211 /*@ 212 PetscDTAltVWedgeMatrix - Compute the matrix defined by the wedge product with a given j-form that maps k-forms to (j+k)-forms 213 214 Input Parameters: 215 + N - the dimension of the vector space, N >= 0 216 . j - the degree j of the j-form a, 0 <= j <= N 217 . k - the degree k of the k-forms that (a wedge) will be applied to, 0 <= k <= N and 0 <= j+k <= N 218 - a - a j-form, size [N choose j] 219 220 Output Parameter: 221 . awedgeMat - (a wedge), an [(N choose j+k) x (N choose k)] matrix in row-major order, such that (a wedge) * b = a wedge b 222 223 Level: intermediate 224 225 .seealso: `PetscDTAltV`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 226 @*/ 227 PetscErrorCode PetscDTAltVWedgeMatrix(PetscInt N, PetscInt j, PetscInt k, const PetscReal *a, PetscReal *awedgeMat) 228 { 229 PetscInt i; 230 231 PetscFunctionBegin; 232 PetscCheck(N >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid dimension"); 233 PetscCheck(j >= 0 && k >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "negative form degree"); 234 PetscCheck(j + k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Wedge greater than dimension"); 235 if (N <= 3) { 236 PetscInt Njk; 237 238 PetscCall(PetscDTBinomialInt(N, j + k, &Njk)); 239 if (!j) { 240 for (i = 0; i < Njk * Njk; i++) awedgeMat[i] = 0.; 241 for (i = 0; i < Njk; i++) awedgeMat[i * (Njk + 1)] = a[0]; 242 } else if (!k) { 243 for (i = 0; i < Njk; i++) awedgeMat[i] = a[i]; 244 } else { 245 if (N == 2) { 246 awedgeMat[0] = -a[1]; 247 awedgeMat[1] = a[0]; 248 } else { 249 if (j + k == 2) { 250 awedgeMat[0] = -a[1]; 251 awedgeMat[1] = a[0]; 252 awedgeMat[2] = 0.; 253 awedgeMat[3] = -a[2]; 254 awedgeMat[4] = 0.; 255 awedgeMat[5] = a[0]; 256 awedgeMat[6] = 0.; 257 awedgeMat[7] = -a[2]; 258 awedgeMat[8] = a[1]; 259 } else { 260 awedgeMat[0] = a[2]; 261 awedgeMat[1] = -a[1]; 262 awedgeMat[2] = a[0]; 263 } 264 } 265 } 266 } else { 267 PetscInt Njk; 268 PetscInt Nk; 269 PetscInt JKj, i; 270 PetscInt *subset, *subsetjk, *subsetj, *subsetk; 271 272 PetscCall(PetscDTBinomialInt(N, k, &Nk)); 273 PetscCall(PetscDTBinomialInt(N, j + k, &Njk)); 274 PetscCall(PetscDTBinomialInt(j + k, j, &JKj)); 275 PetscCall(PetscMalloc4(j + k, &subset, j + k, &subsetjk, j, &subsetj, k, &subsetk)); 276 for (i = 0; i < Njk * Nk; i++) awedgeMat[i] = 0.; 277 for (i = 0; i < Njk; i++) { 278 PetscInt l; 279 280 PetscCall(PetscDTEnumSubset(N, j + k, i, subset)); 281 for (l = 0; l < JKj; l++) { 282 PetscBool jkOdd; 283 PetscInt m, jInd, kInd; 284 285 PetscCall(PetscDTEnumSplit(j + k, j, l, subsetjk, &jkOdd)); 286 for (m = 0; m < j; m++) subsetj[m] = subset[subsetjk[m]]; 287 for (m = 0; m < k; m++) subsetk[m] = subset[subsetjk[j + m]]; 288 PetscCall(PetscDTSubsetIndex(N, j, subsetj, &jInd)); 289 PetscCall(PetscDTSubsetIndex(N, k, subsetk, &kInd)); 290 awedgeMat[i * Nk + kInd] += jkOdd ? -a[jInd] : a[jInd]; 291 } 292 } 293 PetscCall(PetscFree4(subset, subsetjk, subsetj, subsetk)); 294 } 295 PetscFunctionReturn(PETSC_SUCCESS); 296 } 297 298 /*@ 299 PetscDTAltVPullback - Compute the pullback of a k-form under a linear transformation of the coordinate space 300 301 Input Parameters: 302 + N - the dimension of the origin vector space of the linear transformation, M >= 0 303 . M - the dimension of the image vector space of the linear transformation, N >= 0 304 . L - a linear transformation, an [M x N] matrix in row-major format 305 . k - the *signed* degree k of the |k|-form w, -(min(M,N)) <= k <= min(M,N). A negative form degree indicates that the pullback should be conjugated by 306 the Hodge star operator (see note). 307 - w - a |k|-form in the image space, size [M choose |k|] 308 309 Output Parameter: 310 . Lstarw - the pullback of w to a |k|-form in the origin space, size [N choose |k|]: (Lstarw)(v_1,...v_k) = w(L*v_1,...,L*v_k). 311 312 Level: intermediate 313 314 Note: 315 Negative form degrees accommodate, e.g., H-div conforming vector fields. An H-div conforming 316 vector field stores its degrees of freedom as (dx, dy, dz), like a 1-form, but its normal 317 trace is integrated on faces, like a 2-form. The correct pullback then is to apply the Hodge 318 star transformation from (M-2)-form to 2-form, pullback as a 2-form, then invert the Hodge 319 star transformation. 320 321 .seealso: `PetscDTAltV`, `PetscDTAltVPullbackMatrix()`, `PetscDTAltVStar()` 322 @*/ 323 PetscErrorCode PetscDTAltVPullback(PetscInt N, PetscInt M, const PetscReal *L, PetscInt k, const PetscReal *w, PetscReal *Lstarw) 324 { 325 PetscInt i, j, Nk, Mk; 326 327 PetscFunctionBegin; 328 PetscCheck(N >= 0 && M >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid dimensions"); 329 PetscCheck(PetscAbsInt(k) <= N && PetscAbsInt(k) <= M, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 330 if (N <= 3 && M <= 3) { 331 PetscCall(PetscDTBinomialInt(M, PetscAbsInt(k), &Mk)); 332 PetscCall(PetscDTBinomialInt(N, PetscAbsInt(k), &Nk)); 333 if (!k) { 334 Lstarw[0] = w[0]; 335 } else if (k == 1) { 336 for (i = 0; i < Nk; i++) { 337 PetscReal sum = 0.; 338 339 for (j = 0; j < Mk; j++) sum += L[j * Nk + i] * w[j]; 340 Lstarw[i] = sum; 341 } 342 } else if (k == -1) { 343 PetscReal mult[3] = {1., -1., 1.}; 344 345 for (i = 0; i < Nk; i++) { 346 PetscReal sum = 0.; 347 348 for (j = 0; j < Mk; j++) sum += L[(Mk - 1 - j) * Nk + (Nk - 1 - i)] * w[j] * mult[j]; 349 Lstarw[i] = mult[i] * sum; 350 } 351 } else if (k == 2) { 352 PetscInt pairs[3][2] = { 353 {0, 1}, 354 {0, 2}, 355 {1, 2} 356 }; 357 358 for (i = 0; i < Nk; i++) { 359 PetscReal sum = 0.; 360 for (j = 0; j < Mk; j++) sum += (L[pairs[j][0] * N + pairs[i][0]] * L[pairs[j][1] * N + pairs[i][1]] - L[pairs[j][1] * N + pairs[i][0]] * L[pairs[j][0] * N + pairs[i][1]]) * w[j]; 361 Lstarw[i] = sum; 362 } 363 } else if (k == -2) { 364 PetscInt pairs[3][2] = { 365 {1, 2}, 366 {2, 0}, 367 {0, 1} 368 }; 369 PetscInt offi = (N == 2) ? 2 : 0; 370 PetscInt offj = (M == 2) ? 2 : 0; 371 372 for (i = 0; i < Nk; i++) { 373 PetscReal sum = 0.; 374 375 for (j = 0; j < Mk; j++) sum += (L[pairs[offj + j][0] * N + pairs[offi + i][0]] * L[pairs[offj + j][1] * N + pairs[offi + i][1]] - L[pairs[offj + j][1] * N + pairs[offi + i][0]] * L[pairs[offj + j][0] * N + pairs[offi + i][1]]) * w[j]; 376 Lstarw[i] = sum; 377 } 378 } else { 379 PetscReal detL = L[0] * (L[4] * L[8] - L[5] * L[7]) + L[1] * (L[5] * L[6] - L[3] * L[8]) + L[2] * (L[3] * L[7] - L[4] * L[6]); 380 381 for (i = 0; i < Nk; i++) Lstarw[i] = detL * w[i]; 382 } 383 } else { 384 PetscInt Nf, l, p; 385 PetscReal *Lw, *Lwv; 386 PetscInt *subsetw, *subsetv; 387 PetscInt *perm; 388 PetscReal *walloc = NULL; 389 const PetscReal *ww = NULL; 390 PetscBool negative = PETSC_FALSE; 391 392 PetscCall(PetscDTBinomialInt(M, PetscAbsInt(k), &Mk)); 393 PetscCall(PetscDTBinomialInt(N, PetscAbsInt(k), &Nk)); 394 PetscCall(PetscDTFactorialInt(PetscAbsInt(k), &Nf)); 395 if (k < 0) { 396 negative = PETSC_TRUE; 397 k = -k; 398 PetscCall(PetscMalloc1(Mk, &walloc)); 399 PetscCall(PetscDTAltVStar(M, M - k, 1, w, walloc)); 400 ww = walloc; 401 } else { 402 ww = w; 403 } 404 PetscCall(PetscMalloc5(k, &subsetw, k, &subsetv, k, &perm, N * k, &Lw, k * k, &Lwv)); 405 for (i = 0; i < Nk; i++) Lstarw[i] = 0.; 406 for (i = 0; i < Mk; i++) { 407 PetscCall(PetscDTEnumSubset(M, k, i, subsetw)); 408 for (j = 0; j < Nk; j++) { 409 PetscCall(PetscDTEnumSubset(N, k, j, subsetv)); 410 for (p = 0; p < Nf; p++) { 411 PetscReal prod; 412 PetscBool isOdd; 413 414 PetscCall(PetscDTEnumPerm(k, p, perm, &isOdd)); 415 prod = isOdd ? -ww[i] : ww[i]; 416 for (l = 0; l < k; l++) prod *= L[subsetw[perm[l]] * N + subsetv[l]]; 417 Lstarw[j] += prod; 418 } 419 } 420 } 421 if (negative) { 422 PetscReal *sLsw; 423 424 PetscCall(PetscMalloc1(Nk, &sLsw)); 425 PetscCall(PetscDTAltVStar(N, N - k, -1, Lstarw, sLsw)); 426 for (i = 0; i < Nk; i++) Lstarw[i] = sLsw[i]; 427 PetscCall(PetscFree(sLsw)); 428 } 429 PetscCall(PetscFree5(subsetw, subsetv, perm, Lw, Lwv)); 430 PetscCall(PetscFree(walloc)); 431 } 432 PetscFunctionReturn(PETSC_SUCCESS); 433 } 434 435 /*@ 436 PetscDTAltVPullbackMatrix - Compute the pullback matrix for k-forms under a linear transformation 437 438 Input Parameters: 439 + N - the dimension of the origin vector space of the linear transformation, N >= 0 440 . M - the dimension of the image vector space of the linear transformation, M >= 0 441 . L - a linear transformation, an [M x N] matrix in row-major format 442 - k - the *signed* degree k of the |k|-forms on which Lstar acts, -(min(M,N)) <= k <= min(M,N). 443 A negative form degree indicates that the pullback should be conjugated by the Hodge star operator (see note in `PetscDTAltvPullback()`) 444 445 Output Parameter: 446 . Lstar - the pullback matrix, an [(N choose |k|) x (M choose |k|)] matrix in row-major format such that Lstar * w = L^* w 447 448 Level: intermediate 449 450 .seealso: `PetscDTAltV`, `PetscDTAltVPullback()`, `PetscDTAltVStar()` 451 @*/ 452 PetscErrorCode PetscDTAltVPullbackMatrix(PetscInt N, PetscInt M, const PetscReal *L, PetscInt k, PetscReal *Lstar) 453 { 454 PetscInt Nk, Mk, Nf, i, j, l, p; 455 PetscReal *Lw, *Lwv; 456 PetscInt *subsetw, *subsetv; 457 PetscInt *perm; 458 PetscBool negative = PETSC_FALSE; 459 460 PetscFunctionBegin; 461 PetscCheck(N >= 0 && M >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid dimensions"); 462 PetscCheck(PetscAbsInt(k) <= N && PetscAbsInt(k) <= M, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 463 if (N <= 3 && M <= 3) { 464 PetscReal mult[3] = {1., -1., 1.}; 465 466 PetscCall(PetscDTBinomialInt(M, PetscAbsInt(k), &Mk)); 467 PetscCall(PetscDTBinomialInt(N, PetscAbsInt(k), &Nk)); 468 if (!k) { 469 Lstar[0] = 1.; 470 } else if (k == 1) { 471 for (i = 0; i < Nk; i++) { 472 for (j = 0; j < Mk; j++) Lstar[i * Mk + j] = L[j * Nk + i]; 473 } 474 } else if (k == -1) { 475 for (i = 0; i < Nk; i++) { 476 for (j = 0; j < Mk; j++) Lstar[i * Mk + j] = L[(Mk - 1 - j) * Nk + (Nk - 1 - i)] * mult[i] * mult[j]; 477 } 478 } else if (k == 2) { 479 PetscInt pairs[3][2] = { 480 {0, 1}, 481 {0, 2}, 482 {1, 2} 483 }; 484 485 for (i = 0; i < Nk; i++) { 486 for (j = 0; j < Mk; j++) Lstar[i * Mk + j] = L[pairs[j][0] * N + pairs[i][0]] * L[pairs[j][1] * N + pairs[i][1]] - L[pairs[j][1] * N + pairs[i][0]] * L[pairs[j][0] * N + pairs[i][1]]; 487 } 488 } else if (k == -2) { 489 PetscInt pairs[3][2] = { 490 {1, 2}, 491 {2, 0}, 492 {0, 1} 493 }; 494 PetscInt offi = (N == 2) ? 2 : 0; 495 PetscInt offj = (M == 2) ? 2 : 0; 496 497 for (i = 0; i < Nk; i++) { 498 for (j = 0; j < Mk; j++) Lstar[i * Mk + j] = L[pairs[offj + j][0] * N + pairs[offi + i][0]] * L[pairs[offj + j][1] * N + pairs[offi + i][1]] - L[pairs[offj + j][1] * N + pairs[offi + i][0]] * L[pairs[offj + j][0] * N + pairs[offi + i][1]]; 499 } 500 } else { 501 PetscReal detL = L[0] * (L[4] * L[8] - L[5] * L[7]) + L[1] * (L[5] * L[6] - L[3] * L[8]) + L[2] * (L[3] * L[7] - L[4] * L[6]); 502 503 for (i = 0; i < Nk; i++) Lstar[i] = detL; 504 } 505 } else { 506 if (k < 0) { 507 negative = PETSC_TRUE; 508 k = -k; 509 } 510 PetscCall(PetscDTBinomialInt(M, PetscAbsInt(k), &Mk)); 511 PetscCall(PetscDTBinomialInt(N, PetscAbsInt(k), &Nk)); 512 PetscCall(PetscDTFactorialInt(PetscAbsInt(k), &Nf)); 513 PetscCall(PetscMalloc5(M, &subsetw, N, &subsetv, k, &perm, N * k, &Lw, k * k, &Lwv)); 514 for (i = 0; i < Nk * Mk; i++) Lstar[i] = 0.; 515 for (i = 0; i < Mk; i++) { 516 PetscBool iOdd; 517 PetscInt iidx, jidx; 518 519 PetscCall(PetscDTEnumSplit(M, k, i, subsetw, &iOdd)); 520 iidx = negative ? Mk - 1 - i : i; 521 iOdd = negative ? (PetscBool)(iOdd ^ ((k * (M - k)) & 1)) : PETSC_FALSE; 522 for (j = 0; j < Nk; j++) { 523 PetscBool jOdd; 524 525 PetscCall(PetscDTEnumSplit(N, k, j, subsetv, &jOdd)); 526 jidx = negative ? Nk - 1 - j : j; 527 jOdd = negative ? (PetscBool)(iOdd ^ jOdd ^ ((k * (N - k)) & 1)) : PETSC_FALSE; 528 for (p = 0; p < Nf; p++) { 529 PetscReal prod; 530 PetscBool isOdd; 531 532 PetscCall(PetscDTEnumPerm(k, p, perm, &isOdd)); 533 isOdd = (PetscBool)(isOdd ^ jOdd); 534 prod = isOdd ? -1. : 1.; 535 for (l = 0; l < k; l++) prod *= L[subsetw[perm[l]] * N + subsetv[l]]; 536 Lstar[jidx * Mk + iidx] += prod; 537 } 538 } 539 } 540 PetscCall(PetscFree5(subsetw, subsetv, perm, Lw, Lwv)); 541 } 542 PetscFunctionReturn(PETSC_SUCCESS); 543 } 544 545 /*@ 546 PetscDTAltVInterior - Compute the interior product of a k-form with a vector 547 548 Input Parameters: 549 + N - the dimension of the vector space, N >= 0 550 . k - the degree k of the k-form w, 0 <= k <= N 551 . w - a k-form, size [N choose k] 552 - v - an N dimensional vector 553 554 Output Parameter: 555 . wIntv - the (k-1)-form (w int v), size [N choose (k-1)]: (w int v) is defined by its action on (k-1) vectors {v_1, ..., v_{k-1}} as (w inv v)(v_1, ..., v_{k-1}) = w(v, v_1, ..., v_{k-1}). 556 557 Level: intermediate 558 559 .seealso: `PetscDTAltV`, `PetscDTAltVInteriorMatrix()`, `PetscDTAltVInteriorPattern()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 560 @*/ 561 PetscErrorCode PetscDTAltVInterior(PetscInt N, PetscInt k, const PetscReal *w, const PetscReal *v, PetscReal *wIntv) 562 { 563 PetscInt i, Nk, Nkm; 564 565 PetscFunctionBegin; 566 PetscCheck(k > 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 567 PetscCall(PetscDTBinomialInt(N, k, &Nk)); 568 PetscCall(PetscDTBinomialInt(N, k - 1, &Nkm)); 569 if (N <= 3) { 570 if (k == 1) { 571 PetscReal sum = 0.; 572 573 for (i = 0; i < N; i++) sum += w[i] * v[i]; 574 wIntv[0] = sum; 575 } else if (k == N) { 576 PetscReal mult[3] = {1., -1., 1.}; 577 578 for (i = 0; i < N; i++) wIntv[N - 1 - i] = w[0] * v[i] * mult[i]; 579 } else { 580 wIntv[0] = -w[0] * v[1] - w[1] * v[2]; 581 wIntv[1] = w[0] * v[0] - w[2] * v[2]; 582 wIntv[2] = w[1] * v[0] + w[2] * v[1]; 583 } 584 } else { 585 PetscInt *subset, *work; 586 587 PetscCall(PetscMalloc2(k, &subset, k, &work)); 588 for (i = 0; i < Nkm; i++) wIntv[i] = 0.; 589 for (i = 0; i < Nk; i++) { 590 PetscInt j, l, m; 591 592 PetscCall(PetscDTEnumSubset(N, k, i, subset)); 593 for (j = 0; j < k; j++) { 594 PetscInt idx; 595 PetscBool flip = (PetscBool)(j & 1); 596 597 for (l = 0, m = 0; l < k; l++) { 598 if (l != j) work[m++] = subset[l]; 599 } 600 PetscCall(PetscDTSubsetIndex(N, k - 1, work, &idx)); 601 wIntv[idx] += flip ? -(w[i] * v[subset[j]]) : (w[i] * v[subset[j]]); 602 } 603 } 604 PetscCall(PetscFree2(subset, work)); 605 } 606 PetscFunctionReturn(PETSC_SUCCESS); 607 } 608 609 /*@ 610 PetscDTAltVInteriorMatrix - Compute the matrix of the linear transformation induced on a k-form by the interior product with a vector 611 612 Input Parameters: 613 + N - the dimension of the vector space, N >= 0 614 . k - the degree k of the k-forms on which intvMat acts, 0 <= k <= N 615 - v - an N dimensional vector 616 617 Output Parameter: 618 . intvMat - an [(N choose (k-1)) x (N choose k)] matrix, row-major: (intvMat) * w = (w int v) 619 620 Level: intermediate 621 622 .seealso: `PetscDTAltV`, `PetscDTAltVInterior()`, `PetscDTAltVInteriorPattern()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 623 @*/ 624 PetscErrorCode PetscDTAltVInteriorMatrix(PetscInt N, PetscInt k, const PetscReal *v, PetscReal *intvMat) 625 { 626 PetscInt i, Nk, Nkm; 627 628 PetscFunctionBegin; 629 PetscCheck(k > 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 630 PetscCall(PetscDTBinomialInt(N, k, &Nk)); 631 PetscCall(PetscDTBinomialInt(N, k - 1, &Nkm)); 632 if (N <= 3) { 633 if (k == 1) { 634 for (i = 0; i < N; i++) intvMat[i] = v[i]; 635 } else if (k == N) { 636 PetscReal mult[3] = {1., -1., 1.}; 637 638 for (i = 0; i < N; i++) intvMat[N - 1 - i] = v[i] * mult[i]; 639 } else { 640 intvMat[0] = -v[1]; 641 intvMat[1] = -v[2]; 642 intvMat[2] = 0.; 643 intvMat[3] = v[0]; 644 intvMat[4] = 0.; 645 intvMat[5] = -v[2]; 646 intvMat[6] = 0.; 647 intvMat[7] = v[0]; 648 intvMat[8] = v[1]; 649 } 650 } else { 651 PetscInt *subset, *work; 652 653 PetscCall(PetscMalloc2(k, &subset, k, &work)); 654 for (i = 0; i < Nk * Nkm; i++) intvMat[i] = 0.; 655 for (i = 0; i < Nk; i++) { 656 PetscInt j, l, m; 657 658 PetscCall(PetscDTEnumSubset(N, k, i, subset)); 659 for (j = 0; j < k; j++) { 660 PetscInt idx; 661 PetscBool flip = (PetscBool)(j & 1); 662 663 for (l = 0, m = 0; l < k; l++) { 664 if (l != j) work[m++] = subset[l]; 665 } 666 PetscCall(PetscDTSubsetIndex(N, k - 1, work, &idx)); 667 intvMat[idx * Nk + i] += flip ? -v[subset[j]] : v[subset[j]]; 668 } 669 } 670 PetscCall(PetscFree2(subset, work)); 671 } 672 PetscFunctionReturn(PETSC_SUCCESS); 673 } 674 675 /*@ 676 PetscDTAltVInteriorPattern - compute the sparsity and sign pattern of the interior product matrix computed in `PetscDTAltVInteriorMatrix()` 677 678 Input Parameters: 679 + N - the dimension of the vector space, $N \ge 0$ 680 - k - the degree of the k-forms on which `intvMat` from `PetscDTAltVInteriorMatrix()` acts, $ 0 le k le N $. 681 682 Output Parameter: 683 . indices - The interior product matrix `intvMat` has dimensions [(N choose (k-1)) x (N choose k)] and has (N choose k) * k 684 non-zeros. indices[i][0] and indices[i][1] are the row and column of a non-zero, and its value is equal to the vector 685 coordinate v[j] if indices[i][2] = j, or -v[j] if indices[i][2] = -(j+1) 686 687 Level: intermediate 688 689 Note: 690 This function is useful when the interior product needs to be computed at multiple locations, as when computing the Koszul differential 691 692 .seealso: `PetscDTAltV`, `PetscDTAltVInterior()`, `PetscDTAltVInteriorMatrix()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 693 @*/ 694 PetscErrorCode PetscDTAltVInteriorPattern(PetscInt N, PetscInt k, PetscInt (*indices)[3]) 695 { 696 PetscInt i, Nk, Nkm; 697 698 PetscFunctionBegin; 699 PetscCheck(k > 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 700 PetscCall(PetscDTBinomialInt(N, k, &Nk)); 701 PetscCall(PetscDTBinomialInt(N, k - 1, &Nkm)); 702 if (N <= 3) { 703 if (k == 1) { 704 for (i = 0; i < N; i++) { 705 indices[i][0] = 0; 706 indices[i][1] = i; 707 indices[i][2] = i; 708 } 709 } else if (k == N) { 710 PetscInt val[3] = {0, -2, 2}; 711 712 for (i = 0; i < N; i++) { 713 indices[i][0] = N - 1 - i; 714 indices[i][1] = 0; 715 indices[i][2] = val[i]; 716 } 717 } else { 718 indices[0][0] = 0; 719 indices[0][1] = 0; 720 indices[0][2] = -(1 + 1); 721 indices[1][0] = 0; 722 indices[1][1] = 1; 723 indices[1][2] = -(2 + 1); 724 indices[2][0] = 1; 725 indices[2][1] = 0; 726 indices[2][2] = 0; 727 indices[3][0] = 1; 728 indices[3][1] = 2; 729 indices[3][2] = -(2 + 1); 730 indices[4][0] = 2; 731 indices[4][1] = 1; 732 indices[4][2] = 0; 733 indices[5][0] = 2; 734 indices[5][1] = 2; 735 indices[5][2] = 1; 736 } 737 } else { 738 PetscInt *subset, *work; 739 740 PetscCall(PetscMalloc2(k, &subset, k, &work)); 741 for (i = 0; i < Nk; i++) { 742 PetscInt j, l, m; 743 744 PetscCall(PetscDTEnumSubset(N, k, i, subset)); 745 for (j = 0; j < k; j++) { 746 PetscInt idx; 747 PetscBool flip = (PetscBool)(j & 1); 748 749 for (l = 0, m = 0; l < k; l++) { 750 if (l != j) work[m++] = subset[l]; 751 } 752 PetscCall(PetscDTSubsetIndex(N, k - 1, work, &idx)); 753 indices[i * k + j][0] = idx; 754 indices[i * k + j][1] = i; 755 indices[i * k + j][2] = flip ? -(subset[j] + 1) : subset[j]; 756 } 757 } 758 PetscCall(PetscFree2(subset, work)); 759 } 760 PetscFunctionReturn(PETSC_SUCCESS); 761 } 762 763 /*@ 764 PetscDTAltVStar - Apply a power of the Hodge star operator, which maps k-forms to (N-k) forms, to a k-form 765 766 Input Parameters: 767 + N - the dimension of the vector space, N >= 0 768 . k - the degree k of the k-form w, 0 <= k <= N 769 . pow - the number of times to apply the Hodge star operator: pow < 0 indicates that the inverse of the Hodge star operator should be applied |pow| times. 770 - w - a k-form, size [N choose k] 771 772 Output Parameter: 773 . starw - (star)^pow w 774 775 Level: intermediate 776 777 Notes: 778 Each degree of freedom of a k-form is associated with a subset S of k coordinates of the N 779 dimensional vector space: the Hodge start operator (star) maps that degree of freedom to the 780 degree of freedom associated with S', the complement of S, with a sign change if the 781 permutation of coordinates {S[0], ... S[k-1], S'[0], starw- 1]} is an odd permutation. This 782 implies (star)^2 w = (-1)^{k(N-k)} w, and (star)^4 w = w. 783 784 .seealso: `PetscDTAltV`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()` 785 @*/ 786 PetscErrorCode PetscDTAltVStar(PetscInt N, PetscInt k, PetscInt pow, const PetscReal *w, PetscReal *starw) 787 { 788 PetscInt Nk, i; 789 790 PetscFunctionBegin; 791 PetscCheck(k >= 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree"); 792 PetscCall(PetscDTBinomialInt(N, k, &Nk)); 793 pow = pow % 4; 794 pow = (pow + 4) % 4; /* make non-negative */ 795 /* pow is now 0, 1, 2, 3 */ 796 if (N <= 3) { 797 if (pow & 1) { 798 PetscReal mult[3] = {1., -1., 1.}; 799 800 for (i = 0; i < Nk; i++) starw[Nk - 1 - i] = w[i] * mult[i]; 801 } else { 802 for (i = 0; i < Nk; i++) starw[i] = w[i]; 803 } 804 if (pow > 1 && ((k * (N - k)) & 1)) { 805 for (i = 0; i < Nk; i++) starw[i] = -starw[i]; 806 } 807 } else { 808 PetscInt *subset; 809 810 PetscCall(PetscMalloc1(N, &subset)); 811 if (pow % 2) { 812 PetscInt l = (pow == 1) ? k : N - k; 813 for (i = 0; i < Nk; i++) { 814 PetscBool sOdd; 815 PetscInt j, idx; 816 817 PetscCall(PetscDTEnumSplit(N, l, i, subset, &sOdd)); 818 PetscCall(PetscDTSubsetIndex(N, l, subset, &idx)); 819 PetscCall(PetscDTSubsetIndex(N, N - l, &subset[l], &j)); 820 starw[j] = sOdd ? -w[idx] : w[idx]; 821 } 822 } else { 823 for (i = 0; i < Nk; i++) starw[i] = w[i]; 824 } 825 /* star^2 = -1^(k * (N - k)) */ 826 if (pow > 1 && (k * (N - k)) % 2) { 827 for (i = 0; i < Nk; i++) starw[i] = -starw[i]; 828 } 829 PetscCall(PetscFree(subset)); 830 } 831 PetscFunctionReturn(PETSC_SUCCESS); 832 } 833