Actual source code: multishift.c
1: #include <../src/mat/impls/nest/matnestimpl.h>
3: static PetscErrorCode MatMultiShiftDestroy(PetscCtxRt ctx)
4: {
5: Mat_MultiShift mctx = *(Mat_MultiShift *)ctx;
7: PetscFunctionBegin;
8: PetscCall(MatDestroy(&mctx->K));
9: PetscCall(MatDestroy(&mctx->M));
10: PetscCall(PetscFree(mctx->sigma));
11: PetscCall(PetscFree(mctx->cmplx));
12: PetscCall(PetscFree(mctx));
13: PetscFunctionReturn(PETSC_SUCCESS);
14: }
16: /* Build G = K + sigma M, explicitly or not */
17: PetscErrorCode MatMultiShiftBuildShiftedMatrix_Internal(Mat K, PetscScalar sigma, Mat M, MatStructure str, PetscBool explicitmat, Mat *G)
18: {
19: PetscScalar scal[] = {1.0, 1.0};
21: PetscFunctionBegin;
22: if (explicitmat) {
23: PetscCall(MatDuplicate(K, MAT_COPY_VALUES, G));
24: if (M) PetscCall(MatAXPY(*G, sigma, M, str));
25: else PetscCall(MatShift(*G, sigma));
26: } else {
27: PetscInt Mk, Nk, mk, nk;
29: PetscCall(MatGetSize(K, &Mk, &Nk));
30: PetscCall(MatGetLocalSize(K, &mk, &nk));
31: PetscCall(MatCreate(PetscObjectComm((PetscObject)K), G));
32: PetscCall(MatSetSizes(*G, mk, nk, Mk, Nk));
33: PetscCall(MatSetType(*G, MATCOMPOSITE));
34: PetscCall(MatCompositeAddMat(*G, K));
35: if (M) PetscCall(MatCompositeAddMat(*G, M));
36: PetscCall(MatAssemblyBegin(*G, MAT_FINAL_ASSEMBLY));
37: PetscCall(MatAssemblyEnd(*G, MAT_FINAL_ASSEMBLY));
38: if (M) {
39: scal[1] = sigma;
40: PetscCall(MatCompositeSetScalings(*G, scal));
41: } else PetscCall(MatShift(*G, sigma));
42: }
43: PetscFunctionReturn(PETSC_SUCCESS);
44: }
46: #if !PetscDefined(USE_COMPLEX)
47: /* Build G = sigma M, explicitly or not, where M may be I; K is used for dimensions only */
48: static PetscErrorCode BuildScaledMatrix(Mat K, PetscScalar sigma, Mat M, PetscBool explicitmat, Mat *G)
49: {
50: PetscFunctionBegin;
51: if (explicitmat) {
52: if (M) {
53: PetscCall(MatDuplicate(M, MAT_COPY_VALUES, G));
54: PetscCall(MatScale(*G, sigma));
55: } else { // M=I
56: PetscCall(MatDuplicate(K, MAT_DO_NOT_COPY_VALUES, G));
57: PetscCall(MatZeroEntries(*G));
58: PetscCall(MatShift(*G, sigma));
59: }
60: } else {
61: PetscInt Mk, Nk, mk, nk;
63: PetscCall(MatGetSize(K, &Mk, &Nk));
64: PetscCall(MatGetLocalSize(K, &mk, &nk));
65: if (M) {
66: PetscCall(MatCreate(PetscObjectComm((PetscObject)K), G));
67: PetscCall(MatSetSizes(*G, mk, nk, Mk, Nk));
68: PetscCall(MatSetType(*G, MATCOMPOSITE));
69: PetscCall(MatCompositeAddMat(*G, M));
70: PetscCall(MatAssemblyBegin(*G, MAT_FINAL_ASSEMBLY));
71: PetscCall(MatAssemblyEnd(*G, MAT_FINAL_ASSEMBLY));
72: PetscCall(MatCompositeSetScalings(*G, &sigma));
73: } else PetscCall(MatCreateConstantDiagonal(PetscObjectComm((PetscObject)K), mk, nk, Mk, Nk, sigma, G));
74: }
75: PetscFunctionReturn(PETSC_SUCCESS);
76: }
77: #endif
79: /*@
80: MatCreateNestFromMultipleShifts - Creates a `MATNEST` matrix that represents a family of shifted matrices
81: $K + \sigma_i M$ for a number of shifts $\sigma_i$.
83: Collective
85: Input Parameters:
86: + K - the first `Mat` (stiffness) forming the shifted matrices
87: . nshift - number of shifts
88: . sigma - array of shifts $\sigma_i$, its length is `nshift`
89: . sigma_imaginary - imaginary parts of the shifts $\sigma_i$ in case of complex-conjugate pairs (can be `NULL`); only used when `PetscScalar` is `PetscReal`
90: . M - the second `Mat` (mass) forming the shifted matrices (if `NULL` the identity matrix is assumed)
91: . explicitmat - whether the shifted matrices should be built explicitly or not
92: - str - `MatStructure` flag
94: Output Parameter:
95: . A - the resulting matrix
97: Level: intermediate
99: Notes:
100: This is intended for solving a family of shifted linear systems, $(K + \sigma_i M) x_i = b$,
101: where in some applications $M = I$. This function returns a `MATNEST` `A` whose `nshift`
102: diagonal blocks are the matrices $K + \sigma_i M$, either built explicitly or not, depending
103: on the `explicitmat` argument. If not explicit, the diagonal blocks are created as `MATCOMPOSITE`.
104: If `explicitmat` is true, and $M$ is not the identity, then $K + \sigma_i M$ is built with a
105: call to `MatAXPY()`, where the flag `str` is used to indicate the relation between the sparsity
106: patterns of $K$ and $M$. The flag is also stored in `A` and reused for the same purpose by
107: solvers that build a shifted matrix explicitly from `A`, such as `KSPEKSM`, so it must describe
108: the relation between the two patterns even when `explicitmat` is `PETSC_FALSE`.
110: To solve all the shifted linear systems simultaneously, pass this matrix to a
111: `KSP` solver such as `KSPEKSM`, along with compatible solution and right-hand side vectors. Since
112: the right-hand side $b$ is the same for all the shifted linear systems, one can call
113: `MatCreateVecNestFromMultipleShifts()` to easily create a nested `Vec` containing `nshift` references to $b$.
115: When `PetscScalar` is `PetscReal` it is possible to provide complex conjugate pairs of shifts by
116: passing a nonzero imaginary part in `sigma_imaginary[i]`. In that case `A` is no longer block
117: diagonal but contains 2x2 diagonal blocks for each complex-conjugate pair.
119: .seealso: [](ch_matrices), `MatCreateVecNestFromMultipleShifts()`, `MatAXPY()`, `MATCOMPOSITE`, `KSP`, `KSPEKSM`
120: @*/
121: PetscErrorCode MatCreateNestFromMultipleShifts(Mat K, PetscInt nshift, const PetscScalar sigma[], const PetscScalar sigma_imaginary[], Mat M, PetscBool explicitmat, MatStructure str, Mat *A)
122: {
123: PetscInt i;
124: Mat *mats;
125: Mat_MultiShift mctx;
127: PetscFunctionBegin;
130: PetscAssertPointer(sigma, 3);
131: if (sigma_imaginary) PetscAssertPointer(sigma_imaginary, 4);
135: PetscAssertPointer(A, 8);
137: PetscCheck(nshift > 0, PetscObjectComm((PetscObject)K), PETSC_ERR_ARG_OUTOFRANGE, "The value nshift must be > 0");
139: /* build context and array of shifted matrices */
140: PetscCall(PetscNew(&mctx));
141: PetscCall(PetscObjectReference((PetscObject)K));
142: mctx->K = K;
143: PetscCall(PetscObjectReference((PetscObject)M));
144: mctx->M = M;
145: mctx->nshift = nshift;
146: mctx->str = str;
147: PetscCall(PetscMalloc1(nshift, &mctx->sigma));
148: if (!PetscDefined(USE_COMPLEX)) PetscCall(PetscCalloc1(nshift, &mctx->cmplx));
149: PetscCall(PetscCalloc1(nshift * nshift, &mats));
151: for (i = 0; i < nshift; i++) {
152: #if PetscDefined(USE_COMPLEX)
153: mctx->sigma[i] = sigma[i];
154: PetscCall(MatMultiShiftBuildShiftedMatrix_Internal(K, sigma[i], M, str, explicitmat, mats + i + i * nshift));
155: #else
156: mctx->sigma[i] = sigma[i];
157: PetscCall(MatMultiShiftBuildShiftedMatrix_Internal(K, mctx->sigma[i], M, str, explicitmat, mats + i + i * nshift));
158: if (sigma_imaginary && sigma_imaginary[i] != 0.0) {
159: PetscCheck(i < nshift - 1, PetscObjectComm((PetscObject)K), PETSC_ERR_ARG_WRONG, "The last shift of the array is complex; shifts must come in complex-conjugate pairs");
160: PetscCheck(sigma[i + 1] == sigma[i] && sigma_imaginary[i + 1] == -sigma_imaginary[i], PetscObjectComm((PetscObject)K), PETSC_ERR_ARG_WRONG, "The shifts must either be real or form a (consecutive) complex-conjugate pair");
161: mctx->sigma[i + 1] = sigma_imaginary[i];
162: mctx->cmplx[i] = PETSC_TRUE;
163: // 2x2 block in the nested matrix
164: PetscCall(PetscObjectReference((PetscObject)mats[i + i * nshift]));
165: mats[i + 1 + (i + 1) * nshift] = mats[i + i * nshift];
166: PetscCall(BuildScaledMatrix(K, sigma_imaginary[i], M, explicitmat, mats + i + (i + 1) * nshift));
167: PetscCall(BuildScaledMatrix(K, -sigma_imaginary[i], M, explicitmat, mats + i + 1 + i * nshift));
168: i++; // skip next shift
169: }
170: #endif
171: }
173: /* build MATNEST */
174: PetscCall(MatCreateNest(PetscObjectComm((PetscObject)K), nshift, NULL, nshift, NULL, mats, A));
175: for (i = 0; i < nshift * nshift; i++) PetscCall(MatDestroy(&mats[i]));
176: PetscCall(PetscFree(mats));
177: PetscCall(MatNestSetVecType(*A, VECNEST));
179: /* compose context */
180: PetscCall(MatGetState(*A, &mctx->state)); // used to detect later changes such as MatScale()
181: PetscCall(PetscObjectContainerCompose((PetscObject)*A, "MatMultiShift", mctx, MatMultiShiftDestroy));
182: PetscFunctionReturn(PETSC_SUCCESS);
183: }
185: /*@
186: MatCreateVecNestFromMultipleShifts - Creates a `VECNEST` that is compatible with a matrix
187: created with `MatCreateNestFromMultipleShifts()`.
189: Collective
191: Input Parameters:
192: + A - a `Mat` created with `MatCreateNestFromMultipleShifts()`
193: - v - an optional vector (set to `NULL` if not needed)
195: Output Parameter:
196: . vout - the resulting vector
198: Level: intermediate
200: Notes:
201: The result is a `VECNEST` compatible with `A`, so that it can, e.g., be multiplied against.
202: If the input vector `v` is passed, `vout` will contain `nshift` references to `v`, where
203: `nshift` is the number of subvectors of `vout`. Hence, it is intended as a read-only right-hand
204: side, not a solution vector.
206: In real scalars, in the case of a complex-conjugate pair only the first block of the pair is
207: set while the second is left zero. The reason is that in this case the second block represents
208: the imaginary part, which is zero since `v` is real.
210: .seealso: [](ch_matrices), `MatCreateNestFromMultipleShifts()`
211: @*/
212: PetscErrorCode MatCreateVecNestFromMultipleShifts(Mat A, Vec v, Vec *vout)
213: {
214: PetscInt i;
215: Mat_MultiShift mctx;
217: PetscFunctionBegin;
220: PetscAssertPointer(vout, 3);
221: MatCheckMultiShift(A, &mctx);
223: PetscCall(MatCreateVecs(A, NULL, vout));
224: if (v)
225: for (i = 0; i < mctx->nshift; i++) {
226: PetscCall(VecNestSetSubVec(*vout, i, v));
227: if (!PetscDefined(USE_COMPLEX) && mctx->cmplx[i]) i++; // complex shift, leave a zero block since b is real
228: }
229: PetscFunctionReturn(PETSC_SUCCESS);
230: }