xref: /petsc/src/dm/dt/interface/dtaltv.c (revision a4e35b1925eceef64945ea472b84f2bf06a67b5e)
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)-formm 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++) {
499           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]];
500         }
501       }
502     } else {
503       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]);
504 
505       for (i = 0; i < Nk; i++) Lstar[i] = detL;
506     }
507   } else {
508     if (k < 0) {
509       negative = PETSC_TRUE;
510       k        = -k;
511     }
512     PetscCall(PetscDTBinomialInt(M, PetscAbsInt(k), &Mk));
513     PetscCall(PetscDTBinomialInt(N, PetscAbsInt(k), &Nk));
514     PetscCall(PetscDTFactorialInt(PetscAbsInt(k), &Nf));
515     PetscCall(PetscMalloc5(M, &subsetw, N, &subsetv, k, &perm, N * k, &Lw, k * k, &Lwv));
516     for (i = 0; i < Nk * Mk; i++) Lstar[i] = 0.;
517     for (i = 0; i < Mk; i++) {
518       PetscBool iOdd;
519       PetscInt  iidx, jidx;
520 
521       PetscCall(PetscDTEnumSplit(M, k, i, subsetw, &iOdd));
522       iidx = negative ? Mk - 1 - i : i;
523       iOdd = negative ? (PetscBool)(iOdd ^ ((k * (M - k)) & 1)) : PETSC_FALSE;
524       for (j = 0; j < Nk; j++) {
525         PetscBool jOdd;
526 
527         PetscCall(PetscDTEnumSplit(N, k, j, subsetv, &jOdd));
528         jidx = negative ? Nk - 1 - j : j;
529         jOdd = negative ? (PetscBool)(iOdd ^ jOdd ^ ((k * (N - k)) & 1)) : PETSC_FALSE;
530         for (p = 0; p < Nf; p++) {
531           PetscReal prod;
532           PetscBool isOdd;
533 
534           PetscCall(PetscDTEnumPerm(k, p, perm, &isOdd));
535           isOdd = (PetscBool)(isOdd ^ jOdd);
536           prod  = isOdd ? -1. : 1.;
537           for (l = 0; l < k; l++) prod *= L[subsetw[perm[l]] * N + subsetv[l]];
538           Lstar[jidx * Mk + iidx] += prod;
539         }
540       }
541     }
542     PetscCall(PetscFree5(subsetw, subsetv, perm, Lw, Lwv));
543   }
544   PetscFunctionReturn(PETSC_SUCCESS);
545 }
546 
547 /*@
548   PetscDTAltVInterior - Compute the interior product of a k-form with a vector
549 
550   Input Parameters:
551 + N - the dimension of the vector space, N >= 0
552 . k - the degree k of the k-form w, 0 <= k <= N
553 . w - a k-form, size [N choose k]
554 - v - an N dimensional vector
555 
556   Output Parameter:
557 . 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}).
558 
559   Level: intermediate
560 
561 .seealso: `PetscDTAltV`, `PetscDTAltVInteriorMatrix()`, `PetscDTAltVInteriorPattern()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
562 @*/
563 PetscErrorCode PetscDTAltVInterior(PetscInt N, PetscInt k, const PetscReal *w, const PetscReal *v, PetscReal *wIntv)
564 {
565   PetscInt i, Nk, Nkm;
566 
567   PetscFunctionBegin;
568   PetscCheck(k > 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree");
569   PetscCall(PetscDTBinomialInt(N, k, &Nk));
570   PetscCall(PetscDTBinomialInt(N, k - 1, &Nkm));
571   if (N <= 3) {
572     if (k == 1) {
573       PetscReal sum = 0.;
574 
575       for (i = 0; i < N; i++) sum += w[i] * v[i];
576       wIntv[0] = sum;
577     } else if (k == N) {
578       PetscReal mult[3] = {1., -1., 1.};
579 
580       for (i = 0; i < N; i++) wIntv[N - 1 - i] = w[0] * v[i] * mult[i];
581     } else {
582       wIntv[0] = -w[0] * v[1] - w[1] * v[2];
583       wIntv[1] = w[0] * v[0] - w[2] * v[2];
584       wIntv[2] = w[1] * v[0] + w[2] * v[1];
585     }
586   } else {
587     PetscInt *subset, *work;
588 
589     PetscCall(PetscMalloc2(k, &subset, k, &work));
590     for (i = 0; i < Nkm; i++) wIntv[i] = 0.;
591     for (i = 0; i < Nk; i++) {
592       PetscInt j, l, m;
593 
594       PetscCall(PetscDTEnumSubset(N, k, i, subset));
595       for (j = 0; j < k; j++) {
596         PetscInt  idx;
597         PetscBool flip = (PetscBool)(j & 1);
598 
599         for (l = 0, m = 0; l < k; l++) {
600           if (l != j) work[m++] = subset[l];
601         }
602         PetscCall(PetscDTSubsetIndex(N, k - 1, work, &idx));
603         wIntv[idx] += flip ? -(w[i] * v[subset[j]]) : (w[i] * v[subset[j]]);
604       }
605     }
606     PetscCall(PetscFree2(subset, work));
607   }
608   PetscFunctionReturn(PETSC_SUCCESS);
609 }
610 
611 /*@
612   PetscDTAltVInteriorMatrix - Compute the matrix of the linear transformation induced on a k-form by the interior product with a vector
613 
614   Input Parameters:
615 + N - the dimension of the vector space, N >= 0
616 . k - the degree k of the k-forms on which intvMat acts, 0 <= k <= N
617 - v - an N dimensional vector
618 
619   Output Parameter:
620 . intvMat - an [(N choose (k-1)) x (N choose k)] matrix, row-major: (intvMat) * w = (w int v)
621 
622   Level: intermediate
623 
624 .seealso: `PetscDTAltV`, `PetscDTAltVInterior()`, `PetscDTAltVInteriorPattern()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
625 @*/
626 PetscErrorCode PetscDTAltVInteriorMatrix(PetscInt N, PetscInt k, const PetscReal *v, PetscReal *intvMat)
627 {
628   PetscInt i, Nk, Nkm;
629 
630   PetscFunctionBegin;
631   PetscCheck(k > 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree");
632   PetscCall(PetscDTBinomialInt(N, k, &Nk));
633   PetscCall(PetscDTBinomialInt(N, k - 1, &Nkm));
634   if (N <= 3) {
635     if (k == 1) {
636       for (i = 0; i < N; i++) intvMat[i] = v[i];
637     } else if (k == N) {
638       PetscReal mult[3] = {1., -1., 1.};
639 
640       for (i = 0; i < N; i++) intvMat[N - 1 - i] = v[i] * mult[i];
641     } else {
642       intvMat[0] = -v[1];
643       intvMat[1] = -v[2];
644       intvMat[2] = 0.;
645       intvMat[3] = v[0];
646       intvMat[4] = 0.;
647       intvMat[5] = -v[2];
648       intvMat[6] = 0.;
649       intvMat[7] = v[0];
650       intvMat[8] = v[1];
651     }
652   } else {
653     PetscInt *subset, *work;
654 
655     PetscCall(PetscMalloc2(k, &subset, k, &work));
656     for (i = 0; i < Nk * Nkm; i++) intvMat[i] = 0.;
657     for (i = 0; i < Nk; i++) {
658       PetscInt j, l, m;
659 
660       PetscCall(PetscDTEnumSubset(N, k, i, subset));
661       for (j = 0; j < k; j++) {
662         PetscInt  idx;
663         PetscBool flip = (PetscBool)(j & 1);
664 
665         for (l = 0, m = 0; l < k; l++) {
666           if (l != j) work[m++] = subset[l];
667         }
668         PetscCall(PetscDTSubsetIndex(N, k - 1, work, &idx));
669         intvMat[idx * Nk + i] += flip ? -v[subset[j]] : v[subset[j]];
670       }
671     }
672     PetscCall(PetscFree2(subset, work));
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 /*@
678   PetscDTAltVInteriorPattern - compute the sparsity and sign pattern of the interior product matrix computed in `PetscDTAltVInteriorMatrix()`
679 
680   Input Parameters:
681 + N - the dimension of the vector space, N >= 0
682 - k - the degree of the k-forms on which intvMat from `PetscDTAltVInteriorMatrix()` acts, 0 <= k <= N.
683 
684   Output Parameter:
685 . indices - The interior product matrix intvMat has size [(N choose (k-1)) x (N choose k)] and has (N choose k) * k
686              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
687              coordinate v[j] if indices[i][2] = j, or -v[j] if indices[i][2] = -(j+1)
688 
689   Level: intermediate
690 
691   Note:
692   This function is useful when the interior product needs to be computed at multiple locations, as when computing the Koszul differential
693 
694 .seealso: `PetscDTAltV`, `PetscDTAltVInterior()`, `PetscDTAltVInteriorMatrix()`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
695 @*/
696 PetscErrorCode PetscDTAltVInteriorPattern(PetscInt N, PetscInt k, PetscInt (*indices)[3])
697 {
698   PetscInt i, Nk, Nkm;
699 
700   PetscFunctionBegin;
701   PetscCheck(k > 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree");
702   PetscCall(PetscDTBinomialInt(N, k, &Nk));
703   PetscCall(PetscDTBinomialInt(N, k - 1, &Nkm));
704   if (N <= 3) {
705     if (k == 1) {
706       for (i = 0; i < N; i++) {
707         indices[i][0] = 0;
708         indices[i][1] = i;
709         indices[i][2] = i;
710       }
711     } else if (k == N) {
712       PetscInt val[3] = {0, -2, 2};
713 
714       for (i = 0; i < N; i++) {
715         indices[i][0] = N - 1 - i;
716         indices[i][1] = 0;
717         indices[i][2] = val[i];
718       }
719     } else {
720       indices[0][0] = 0;
721       indices[0][1] = 0;
722       indices[0][2] = -(1 + 1);
723       indices[1][0] = 0;
724       indices[1][1] = 1;
725       indices[1][2] = -(2 + 1);
726       indices[2][0] = 1;
727       indices[2][1] = 0;
728       indices[2][2] = 0;
729       indices[3][0] = 1;
730       indices[3][1] = 2;
731       indices[3][2] = -(2 + 1);
732       indices[4][0] = 2;
733       indices[4][1] = 1;
734       indices[4][2] = 0;
735       indices[5][0] = 2;
736       indices[5][1] = 2;
737       indices[5][2] = 1;
738     }
739   } else {
740     PetscInt *subset, *work;
741 
742     PetscCall(PetscMalloc2(k, &subset, k, &work));
743     for (i = 0; i < Nk; i++) {
744       PetscInt j, l, m;
745 
746       PetscCall(PetscDTEnumSubset(N, k, i, subset));
747       for (j = 0; j < k; j++) {
748         PetscInt  idx;
749         PetscBool flip = (PetscBool)(j & 1);
750 
751         for (l = 0, m = 0; l < k; l++) {
752           if (l != j) work[m++] = subset[l];
753         }
754         PetscCall(PetscDTSubsetIndex(N, k - 1, work, &idx));
755         indices[i * k + j][0] = idx;
756         indices[i * k + j][1] = i;
757         indices[i * k + j][2] = flip ? -(subset[j] + 1) : subset[j];
758       }
759     }
760     PetscCall(PetscFree2(subset, work));
761   }
762   PetscFunctionReturn(PETSC_SUCCESS);
763 }
764 
765 /*@
766   PetscDTAltVStar - Apply a power of the Hodge star operator, which maps k-forms to (N-k) forms, to a k-form
767 
768   Input Parameters:
769 + N   - the dimension of the vector space, N >= 0
770 . k   - the degree k of the k-form w, 0 <= k <= N
771 . 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.
772 - w   - a k-form, size [N choose k]
773 
774   Output Parameter:
775 . starw - (star)^pow w
776 
777   Level: intermediate
778 
779   Notes:
780   Each degree of freedom of a k-form is associated with a subset S of k coordinates of the N
781   dimensional vector space: the Hodge start operator (star) maps that degree of freedom to the
782   degree of freedom associated with S', the complement of S, with a sign change if the
783   permutation of coordinates {S[0], ... S[k-1], S'[0], starw- 1]} is an odd permutation.  This
784   implies (star)^2 w = (-1)^{k(N-k)} w, and (star)^4 w = w.
785 
786 .seealso: `PetscDTAltV`, `PetscDTAltVPullback()`, `PetscDTAltVPullbackMatrix()`
787 @*/
788 PetscErrorCode PetscDTAltVStar(PetscInt N, PetscInt k, PetscInt pow, const PetscReal *w, PetscReal *starw)
789 {
790   PetscInt Nk, i;
791 
792   PetscFunctionBegin;
793   PetscCheck(k >= 0 && k <= N, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "invalid form degree");
794   PetscCall(PetscDTBinomialInt(N, k, &Nk));
795   pow = pow % 4;
796   pow = (pow + 4) % 4; /* make non-negative */
797   /* pow is now 0, 1, 2, 3 */
798   if (N <= 3) {
799     if (pow & 1) {
800       PetscReal mult[3] = {1., -1., 1.};
801 
802       for (i = 0; i < Nk; i++) starw[Nk - 1 - i] = w[i] * mult[i];
803     } else {
804       for (i = 0; i < Nk; i++) starw[i] = w[i];
805     }
806     if (pow > 1 && ((k * (N - k)) & 1)) {
807       for (i = 0; i < Nk; i++) starw[i] = -starw[i];
808     }
809   } else {
810     PetscInt *subset;
811 
812     PetscCall(PetscMalloc1(N, &subset));
813     if (pow % 2) {
814       PetscInt l = (pow == 1) ? k : N - k;
815       for (i = 0; i < Nk; i++) {
816         PetscBool sOdd;
817         PetscInt  j, idx;
818 
819         PetscCall(PetscDTEnumSplit(N, l, i, subset, &sOdd));
820         PetscCall(PetscDTSubsetIndex(N, l, subset, &idx));
821         PetscCall(PetscDTSubsetIndex(N, N - l, &subset[l], &j));
822         starw[j] = sOdd ? -w[idx] : w[idx];
823       }
824     } else {
825       for (i = 0; i < Nk; i++) starw[i] = w[i];
826     }
827     /* star^2 = -1^(k * (N - k)) */
828     if (pow > 1 && (k * (N - k)) % 2) {
829       for (i = 0; i < Nk; i++) starw[i] = -starw[i];
830     }
831     PetscCall(PetscFree(subset));
832   }
833   PetscFunctionReturn(PETSC_SUCCESS);
834 }
835