xref: /honee/src/diff_flux_projection.c (revision dae7673a5083879fe11419a8756d38cb937fd63d)
1 // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors.
2 // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause
3 /// @file
4 /// Functions for setting up and projecting the divergence of the diffusive flux
5 
6 #include "../qfunctions/diff_flux_projection.h"
7 
8 #include <petscdmplex.h>
9 
10 #include <navierstokes.h>
11 
12 const char *const DivDiffFluxProjectionMethods[] = {"NONE", "DIRECT", "INDIRECT", "DivDiffFluxProjectionMethod", "DIV_DIFF_FLUX_PROJ_", NULL};
13 
14 /**
15   @brief Create `DivDiffFluxProjectionData` for solution DM in `honee`
16 
17   @param[in]  honee               `Honee` context
18   @param[in]  num_diff_flux_comps Number of components that makes up the diffusive flux (e.g. 1 for scalar advection-diffusion)
19   @param[out] pdiff_flux_proj     The `DivDiffFluxProjectionData` object created, or `NULL` if not created
20 **/
21 PetscErrorCode DivDiffFluxProjectionCreate(Honee honee, PetscInt num_diff_flux_comps, DivDiffFluxProjectionData *pdiff_flux_proj) {
22   PetscInt                  label_value = 0, height = 0, dm_field = 0, dim, degree = honee->app_ctx->degree, q_extra = honee->app_ctx->q_extra;
23   DMLabel                   domain_label = NULL;
24   DivDiffFluxProjectionData diff_flux_proj;
25   NodalProjectionData       projection;
26 
27   PetscFunctionBeginUser;
28   if (honee->app_ctx->divFdiffproj_method == DIV_DIFF_FLUX_PROJ_NONE) {
29     *pdiff_flux_proj = NULL;
30     PetscFunctionReturn(PETSC_SUCCESS);
31   }
32   PetscCall(PetscNew(pdiff_flux_proj));
33   diff_flux_proj = *pdiff_flux_proj;
34   PetscCall(PetscNew(&honee->diff_flux_proj->projection));
35   projection                          = honee->diff_flux_proj->projection;
36   diff_flux_proj->method              = honee->app_ctx->divFdiffproj_method;
37   diff_flux_proj->num_diff_flux_comps = num_diff_flux_comps;
38 
39   PetscCall(DMClone(honee->dm, &projection->dm));
40   PetscCall(DMSetMatrixPreallocateSkip(projection->dm, PETSC_TRUE));
41   PetscCall(DMGetDimension(projection->dm, &dim));
42   switch (diff_flux_proj->method) {
43     case DIV_DIFF_FLUX_PROJ_DIRECT: {
44       projection->num_comp = diff_flux_proj->num_diff_flux_comps;
45       PetscCall(PetscObjectSetName((PetscObject)projection->dm, "DivDiffFluxProj"));
46       PetscCall(DMSetupByOrder_FEM(PETSC_TRUE, PETSC_TRUE, degree, 1, q_extra, 1, &projection->num_comp, projection->dm));
47 
48       PetscCall(DMPlexCeedElemRestrictionCreate(honee->ceed, projection->dm, domain_label, label_value, height, dm_field,
49                                                 &diff_flux_proj->elem_restr_div_diff_flux));
50       PetscCallCeed(honee->ceed,
51                     CeedElemRestrictionCreateVector(diff_flux_proj->elem_restr_div_diff_flux, &diff_flux_proj->div_diff_flux_ceed, NULL));
52       PetscCall(CreateBasisFromPlex(honee->ceed, projection->dm, domain_label, label_value, height, dm_field, &diff_flux_proj->basis_div_diff_flux));
53       diff_flux_proj->eval_mode_div_diff_flux = CEED_EVAL_INTERP;
54 
55       {  // Create face labels on projection->dm for boundary integrals
56         DMLabel  face_sets_label;
57         PetscInt num_face_set_values, *face_set_values;
58 
59         PetscCall(DMGetLabel(honee->dm, "Face Sets", &face_sets_label));
60         PetscCall(DMLabelCreateGlobalValueArray(honee->dm, face_sets_label, &num_face_set_values, &face_set_values));
61         for (PetscInt f = 0; f < num_face_set_values; f++) {
62           DMLabel face_orientation_label;
63           char   *face_orientation_label_name;
64 
65           PetscCall(DMPlexCreateFaceLabel(honee->dm, face_set_values[f], &face_orientation_label_name));
66           PetscCall(DMGetLabel(honee->dm, face_orientation_label_name, &face_orientation_label));
67           PetscCall(DMAddLabel(projection->dm, face_orientation_label));
68           PetscCall(PetscFree(face_orientation_label_name));
69         }
70         PetscCall(PetscFree(face_set_values));
71       }
72     } break;
73     case DIV_DIFF_FLUX_PROJ_INDIRECT: {
74       projection->num_comp = diff_flux_proj->num_diff_flux_comps * dim;
75       PetscCall(PetscObjectSetName((PetscObject)projection->dm, "DiffFluxProj"));
76       PetscCall(DMSetupByOrder_FEM(PETSC_TRUE, PETSC_TRUE, degree, 1, q_extra, 1, &projection->num_comp, projection->dm));
77 
78       PetscCall(DMPlexCeedElemRestrictionQDataCreate(honee->ceed, projection->dm, domain_label, label_value, height,
79                                                      diff_flux_proj->num_diff_flux_comps, &diff_flux_proj->elem_restr_div_diff_flux));
80       PetscCallCeed(honee->ceed,
81                     CeedElemRestrictionCreateVector(diff_flux_proj->elem_restr_div_diff_flux, &diff_flux_proj->div_diff_flux_ceed, NULL));
82       diff_flux_proj->basis_div_diff_flux     = CEED_BASIS_NONE;
83       diff_flux_proj->eval_mode_div_diff_flux = CEED_EVAL_NONE;
84     } break;
85     case DIV_DIFF_FLUX_PROJ_NONE:
86       SETERRQ(PetscObjectComm((PetscObject)honee->dm), PETSC_ERR_ARG_WRONG, "Should not reach here with div_diff_flux_projection_method %s",
87               DivDiffFluxProjectionMethods[honee->app_ctx->divFdiffproj_method]);
88       break;
89   }
90   PetscFunctionReturn(PETSC_SUCCESS);
91 };
92 
93 /**
94   @brief Return the objects required for the Divergence of Diffusive flux to be read by a `CeedOperator`
95 
96   @param[in]  diff_flux_proj Projection object
97   @param[out] elem_restr     Element restriction for the divergence of diffusive flux, or `NULL`
98   @param[out] basis          Basis for the divergence of diffusive flux, or `NULL`
99   @param[out] vector         Vector for the divergence of diffusive flux, or `NULL`
100   @param[out] eval_mode      Eval mode for the divergence of diffusive flux, or `NULL`
101 **/
102 PetscErrorCode DivDiffFluxProjectionGetOperatorFieldData(DivDiffFluxProjectionData diff_flux_proj, CeedElemRestriction *elem_restr, CeedBasis *basis,
103                                                          CeedVector *vector, CeedEvalMode *eval_mode) {
104   Ceed ceed = CeedVectorReturnCeed(diff_flux_proj->div_diff_flux_ceed);
105 
106   PetscFunctionBeginUser;
107   if (elem_restr) PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(diff_flux_proj->elem_restr_div_diff_flux, elem_restr));
108   if (basis) PetscCallCeed(ceed, CeedBasisReferenceCopy(diff_flux_proj->basis_div_diff_flux, basis));
109   if (vector) PetscCallCeed(ceed, CeedVectorReferenceCopy(diff_flux_proj->div_diff_flux_ceed, vector));
110   if (eval_mode) *eval_mode = diff_flux_proj->eval_mode_div_diff_flux;
111   PetscFunctionReturn(PETSC_SUCCESS);
112 }
113 
114 /**
115   @brief Setup direct projection of divergence of diffusive flux
116 
117   @param[in]     honee          `Honee` context
118   @param[in,out] diff_flux_proj Flux projection object to setup
119 **/
120 static PetscErrorCode DivDiffFluxProjectionSetup_Direct(Honee honee, DivDiffFluxProjectionData diff_flux_proj) {
121   Ceed                ceed       = honee->ceed;
122   NodalProjectionData projection = diff_flux_proj->projection;
123   MPI_Comm            comm       = PetscObjectComm((PetscObject)projection->dm);
124 
125   PetscFunctionBeginUser;
126   {  // Create Projection RHS OperatorApplyContext
127     CeedOperator op_rhs;
128 
129     PetscCheck(diff_flux_proj->CreateRHSOperator_Direct, comm, PETSC_ERR_ARG_WRONGSTATE,
130                "Must define CreateRHSOperator_Direct to use indirect div_diff_flux projection");
131     PetscCall(diff_flux_proj->CreateRHSOperator_Direct(honee, diff_flux_proj, &op_rhs));
132     PetscCall(DMCreateLocalVector(projection->dm, &diff_flux_proj->DivDiffFlux_loc));
133     diff_flux_proj->ceed_vec_has_array = PETSC_FALSE;
134     PetscCall(OperatorApplyContextCreate(honee->dm, projection->dm, ceed, op_rhs, NULL, NULL, NULL, diff_flux_proj->DivDiffFlux_loc,
135                                          &projection->l2_rhs_ctx));
136     PetscCallCeed(ceed, CeedOperatorDestroy(&op_rhs));
137   }
138 
139   {  // -- Build Mass operator
140     CeedQFunction       qf_mass;
141     CeedOperator        op_mass;
142     CeedBasis           basis_div_diff_flux             = NULL;
143     CeedElemRestriction elem_restr_div_diff_flux_volume = NULL, elem_restr_qd;
144     CeedVector          q_data;
145     CeedInt             q_data_size;
146     PetscInt            label_value  = 0;
147     DMLabel             domain_label = NULL;
148 
149     PetscCall(DivDiffFluxProjectionGetOperatorFieldData(diff_flux_proj, &elem_restr_div_diff_flux_volume, &basis_div_diff_flux, NULL, NULL));
150     PetscCall(QDataGet(ceed, projection->dm, domain_label, label_value, honee->elem_restr_x, honee->basis_x, honee->x_coord, &elem_restr_qd, &q_data,
151                        &q_data_size));
152 
153     PetscCall(HoneeMassQFunctionCreate(ceed, projection->num_comp, q_data_size, &qf_mass));
154     PetscCallCeed(ceed, CeedOperatorCreate(ceed, qf_mass, NULL, NULL, &op_mass));
155     PetscCallCeed(ceed, CeedOperatorSetField(op_mass, "u", elem_restr_div_diff_flux_volume, basis_div_diff_flux, CEED_VECTOR_ACTIVE));
156     PetscCallCeed(ceed, CeedOperatorSetField(op_mass, "qdata", elem_restr_qd, CEED_BASIS_NONE, q_data));
157     PetscCallCeed(ceed, CeedOperatorSetField(op_mass, "v", elem_restr_div_diff_flux_volume, basis_div_diff_flux, CEED_VECTOR_ACTIVE));
158 
159     {  // -- Setup KSP for L^2 projection
160       Mat mat_mass;
161 
162       PetscCall(MatCreateCeed(projection->dm, projection->dm, op_mass, NULL, &mat_mass));
163 
164       PetscCall(KSPCreate(comm, &projection->ksp));
165       PetscCall(KSPSetOptionsPrefix(projection->ksp, "div_diff_flux_projection_"));
166       {  // lumped by default
167         PC pc;
168         PetscCall(KSPGetPC(projection->ksp, &pc));
169         PetscCall(PCSetType(pc, PCJACOBI));
170         PetscCall(PCJacobiSetType(pc, PC_JACOBI_ROWSUM));
171         PetscCall(KSPSetType(projection->ksp, KSPPREONLY));
172       }
173       PetscCall(KSPSetFromOptions_WithMatCeed(projection->ksp, mat_mass));
174       PetscCall(MatDestroy(&mat_mass));
175     }
176 
177     PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_div_diff_flux_volume));
178     PetscCallCeed(ceed, CeedBasisDestroy(&basis_div_diff_flux));
179     PetscCallCeed(ceed, CeedVectorDestroy(&q_data));
180     PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_qd));
181     PetscCallCeed(ceed, CeedQFunctionDestroy(&qf_mass));
182     PetscCallCeed(ceed, CeedOperatorDestroy(&op_mass));
183   }
184   PetscFunctionReturn(PETSC_SUCCESS);
185 }
186 
187 /**
188   @brief Setup indirect projection of divergence of diffusive flux
189 
190   @param[in]     honee          `Honee` context
191   @param[in,out] diff_flux_proj Flux projection object to setup
192 **/
193 static PetscErrorCode DivDiffFluxProjectionSetup_Indirect(Honee honee, DivDiffFluxProjectionData diff_flux_proj) {
194   Ceed                ceed       = honee->ceed;
195   NodalProjectionData projection = diff_flux_proj->projection;
196   CeedBasis           basis_diff_flux;
197   CeedElemRestriction elem_restr_diff_flux, elem_restr_qd;
198   CeedVector          q_data;
199   CeedInt             q_data_size;
200   MPI_Comm            comm = PetscObjectComm((PetscObject)projection->dm);
201 
202   PetscFunctionBeginUser;
203   {
204     PetscInt label_value = 0, height = 0, dm_field = 0;
205     DMLabel  domain_label = NULL;
206 
207     PetscCall(DMPlexCeedElemRestrictionCreate(ceed, projection->dm, domain_label, label_value, height, dm_field, &elem_restr_diff_flux));
208     PetscCall(CreateBasisFromPlex(ceed, projection->dm, domain_label, label_value, height, dm_field, &basis_diff_flux));
209     PetscCall(QDataGet(ceed, projection->dm, domain_label, label_value, honee->elem_restr_x, honee->basis_x, honee->x_coord, &elem_restr_qd, &q_data,
210                        &q_data_size));
211   }
212 
213   {
214     CeedOperator op_rhs;
215 
216     PetscCheck(diff_flux_proj->CreateRHSOperator_Indirect, comm, PETSC_ERR_ARG_WRONGSTATE,
217                "Must define CreateRHSOperator_Indirect to use indirect div_diff_flux projection");
218     PetscCall(diff_flux_proj->CreateRHSOperator_Indirect(honee, diff_flux_proj, &op_rhs));
219     PetscCall(OperatorApplyContextCreate(honee->dm, projection->dm, ceed, op_rhs, NULL, NULL, NULL, NULL, &projection->l2_rhs_ctx));
220     PetscCallCeed(ceed, CeedOperatorDestroy(&op_rhs));
221   }
222 
223   {  // -- Build Mass operator
224     CeedQFunction qf_mass;
225     CeedOperator  op_mass;
226 
227     PetscCall(HoneeMassQFunctionCreate(ceed, projection->num_comp, q_data_size, &qf_mass));
228     PetscCallCeed(ceed, CeedOperatorCreate(ceed, qf_mass, NULL, NULL, &op_mass));
229     PetscCallCeed(ceed, CeedOperatorSetField(op_mass, "u", elem_restr_diff_flux, basis_diff_flux, CEED_VECTOR_ACTIVE));
230     PetscCallCeed(ceed, CeedOperatorSetField(op_mass, "qdata", elem_restr_qd, CEED_BASIS_NONE, q_data));
231     PetscCallCeed(ceed, CeedOperatorSetField(op_mass, "v", elem_restr_diff_flux, basis_diff_flux, CEED_VECTOR_ACTIVE));
232 
233     {  // -- Setup KSP for L^2 projection
234       Mat mat_mass;
235 
236       PetscCall(MatCreateCeed(projection->dm, projection->dm, op_mass, NULL, &mat_mass));
237 
238       PetscCall(KSPCreate(comm, &projection->ksp));
239       PetscCall(KSPSetOptionsPrefix(projection->ksp, "div_diff_flux_projection_"));
240       {  // lumped by default
241         PC pc;
242         PetscCall(KSPGetPC(projection->ksp, &pc));
243         PetscCall(PCSetType(pc, PCJACOBI));
244         PetscCall(PCJacobiSetType(pc, PC_JACOBI_ROWSUM));
245         PetscCall(KSPSetType(projection->ksp, KSPPREONLY));
246       }
247       PetscCall(KSPSetFromOptions_WithMatCeed(projection->ksp, mat_mass));
248       PetscCall(MatDestroy(&mat_mass));
249     }
250     PetscCallCeed(ceed, CeedQFunctionDestroy(&qf_mass));
251     PetscCallCeed(ceed, CeedOperatorDestroy(&op_mass));
252   }
253 
254   {  // Create OperatorApplyContext to calculate divergence at quadrature points
255     CeedQFunction       qf_calc_divergence = NULL;
256     CeedOperator        op_calc_divergence;
257     CeedElemRestriction elem_restr_div_diff_flux = NULL;
258     PetscInt            dim;
259 
260     PetscCall(DMGetDimension(projection->dm, &dim));
261     PetscCall(DivDiffFluxProjectionGetOperatorFieldData(diff_flux_proj, &elem_restr_div_diff_flux, NULL, NULL, NULL));
262 
263     switch (dim) {
264       case 2:
265         switch (diff_flux_proj->num_diff_flux_comps) {
266           case 1:
267             PetscCallCeed(ceed,
268                           CeedQFunctionCreateInterior(ceed, 1, ComputeDivDiffusiveFlux2D_1, ComputeDivDiffusiveFlux2D_1_loc, &qf_calc_divergence));
269             break;
270         }
271         break;
272       case 3:
273         switch (diff_flux_proj->num_diff_flux_comps) {
274           case 1:
275             PetscCallCeed(ceed,
276                           CeedQFunctionCreateInterior(ceed, 1, ComputeDivDiffusiveFlux3D_1, ComputeDivDiffusiveFlux3D_1_loc, &qf_calc_divergence));
277             break;
278           case 4:
279             PetscCallCeed(ceed,
280                           CeedQFunctionCreateInterior(ceed, 1, ComputeDivDiffusiveFlux3D_4, ComputeDivDiffusiveFlux3D_4_loc, &qf_calc_divergence));
281             break;
282         }
283         break;
284     }
285     PetscCheck(qf_calc_divergence, comm, PETSC_ERR_SUP,
286                "QFunction for calculating divergence of diffusive flux does not exist for"
287                " %" PetscInt_FMT " dimensional grid and %" PetscInt_FMT
288                " number of components.\nA new qfunction can be easily added; see source code for pattern.",
289                dim, diff_flux_proj->num_diff_flux_comps);
290 
291     PetscCallCeed(ceed, CeedQFunctionAddInput(qf_calc_divergence, "Grad F_diff", projection->num_comp * dim, CEED_EVAL_GRAD));
292     PetscCallCeed(ceed, CeedQFunctionAddInput(qf_calc_divergence, "qdata", q_data_size, CEED_EVAL_NONE));
293     PetscCallCeed(ceed, CeedQFunctionAddOutput(qf_calc_divergence, "Div F_diff", diff_flux_proj->num_diff_flux_comps, CEED_EVAL_NONE));
294 
295     PetscCallCeed(ceed, CeedOperatorCreate(ceed, qf_calc_divergence, NULL, NULL, &op_calc_divergence));
296     PetscCallCeed(ceed, CeedOperatorSetField(op_calc_divergence, "Grad F_diff", elem_restr_diff_flux, basis_diff_flux, CEED_VECTOR_ACTIVE));
297     PetscCallCeed(ceed, CeedOperatorSetField(op_calc_divergence, "qdata", elem_restr_qd, CEED_BASIS_NONE, q_data));
298     PetscCallCeed(
299         ceed, CeedOperatorSetField(op_calc_divergence, "Div F_diff", elem_restr_div_diff_flux, CEED_BASIS_NONE, diff_flux_proj->div_diff_flux_ceed));
300 
301     PetscCall(
302         OperatorApplyContextCreate(projection->dm, NULL, ceed, op_calc_divergence, NULL, NULL, NULL, NULL, &diff_flux_proj->calc_div_diff_flux));
303 
304     PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_div_diff_flux));
305     PetscCallCeed(ceed, CeedQFunctionDestroy(&qf_calc_divergence));
306     PetscCallCeed(ceed, CeedOperatorDestroy(&op_calc_divergence));
307   }
308   PetscCallCeed(ceed, CeedBasisDestroy(&basis_diff_flux));
309   PetscCallCeed(ceed, CeedVectorDestroy(&q_data));
310   PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_qd));
311   PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_diff_flux));
312   PetscFunctionReturn(PETSC_SUCCESS);
313 }
314 
315 /**
316   @brief Setup projection of divergence of diffusive flux
317 
318   @param[in]     honee          `Honee` context
319   @param[in,out] diff_flux_proj Flux projection object to setup
320 **/
321 PetscErrorCode DivDiffFluxProjectionSetup(Honee honee, DivDiffFluxProjectionData diff_flux_proj) {
322   PetscFunctionBeginUser;
323   switch (honee->app_ctx->divFdiffproj_method) {
324     case DIV_DIFF_FLUX_PROJ_DIRECT:
325       PetscCall(DivDiffFluxProjectionSetup_Direct(honee, diff_flux_proj));
326       break;
327     case DIV_DIFF_FLUX_PROJ_INDIRECT:
328       PetscCall(DivDiffFluxProjectionSetup_Indirect(honee, diff_flux_proj));
329       break;
330     case DIV_DIFF_FLUX_PROJ_NONE:
331       SETERRQ(PetscObjectComm((PetscObject)honee->dm), PETSC_ERR_ARG_WRONG, "Should not reach here with div_diff_flux_projection_method %s",
332               DivDiffFluxProjectionMethods[honee->app_ctx->divFdiffproj_method]);
333       break;
334   }
335   PetscFunctionReturn(PETSC_SUCCESS);
336 }
337 
338 /**
339   @brief Project the divergence of diffusive flux
340 
341   This implicitly sets the `CeedVector` input (`div_diff_flux_ceed`) to the divergence of diffusive flux.
342 
343   @param[in]  diff_flux_proj `NodalProjectionData` for the projection
344   @param[in]  Q_loc          Localized solution vector
345 **/
346 PetscErrorCode DivDiffFluxProjectionApply(DivDiffFluxProjectionData diff_flux_proj, Vec Q_loc) {
347   NodalProjectionData projection = diff_flux_proj->projection;
348 
349   PetscFunctionBeginUser;
350   PetscCall(PetscLogEventBegin(HONEE_DivDiffFluxProjection, Q_loc, 0, 0, 0));
351   switch (diff_flux_proj->method) {
352     case DIV_DIFF_FLUX_PROJ_DIRECT: {
353       Vec DivDiffFlux;
354 
355       PetscCall(DMGetGlobalVector(projection->dm, &DivDiffFlux));
356       if (diff_flux_proj->ceed_vec_has_array) {
357         PetscCall(VecReadCeedToPetsc(diff_flux_proj->div_diff_flux_ceed, diff_flux_proj->DivDiffFlux_memtype, diff_flux_proj->DivDiffFlux_loc));
358         diff_flux_proj->ceed_vec_has_array = PETSC_FALSE;
359       }
360       PetscCall(ApplyCeedOperatorLocalToGlobal(Q_loc, DivDiffFlux, projection->l2_rhs_ctx));
361       PetscCall(VecViewFromOptions(DivDiffFlux, NULL, "-div_diff_flux_projection_rhs_view"));
362 
363       PetscCall(KSPSolve(projection->ksp, DivDiffFlux, DivDiffFlux));
364       PetscCall(VecViewFromOptions(DivDiffFlux, NULL, "-div_diff_flux_projection_view"));
365 
366       PetscCall(DMGlobalToLocal(projection->dm, DivDiffFlux, INSERT_VALUES, diff_flux_proj->DivDiffFlux_loc));
367       PetscCall(VecReadPetscToCeed(diff_flux_proj->DivDiffFlux_loc, &diff_flux_proj->DivDiffFlux_memtype, diff_flux_proj->div_diff_flux_ceed));
368       diff_flux_proj->ceed_vec_has_array = PETSC_TRUE;
369 
370       PetscCall(DMRestoreGlobalVector(projection->dm, &DivDiffFlux));
371       break;
372     }
373     case DIV_DIFF_FLUX_PROJ_INDIRECT: {
374       Vec DiffFlux;
375 
376       PetscCall(DMGetGlobalVector(projection->dm, &DiffFlux));
377       PetscCall(ApplyCeedOperatorLocalToGlobal(Q_loc, DiffFlux, projection->l2_rhs_ctx));
378       PetscCall(VecViewFromOptions(DiffFlux, NULL, "-div_diff_flux_projection_rhs_view"));
379 
380       PetscCall(KSPSolve(projection->ksp, DiffFlux, DiffFlux));
381       PetscCall(VecViewFromOptions(DiffFlux, NULL, "-div_diff_flux_projection_view"));
382 
383       PetscCall(ApplyCeedOperatorGlobalToLocal(DiffFlux, NULL, diff_flux_proj->calc_div_diff_flux));
384       PetscCall(DMRestoreGlobalVector(projection->dm, &DiffFlux));
385     } break;
386     case DIV_DIFF_FLUX_PROJ_NONE:
387       SETERRQ(PetscObjectComm((PetscObject)projection->dm), PETSC_ERR_ARG_WRONG, "Should not reach here with div_diff_flux_projection_method %s",
388               DivDiffFluxProjectionMethods[diff_flux_proj->method]);
389       break;
390   }
391   PetscCall(PetscLogEventEnd(HONEE_DivDiffFluxProjection, Q_loc, 0, 0, 0));
392   PetscFunctionReturn(PETSC_SUCCESS);
393 }
394 
395 /**
396   @brief Destroy `DivDiffFluxProjectionData` object
397 
398   @param[in,out] diff_flux_proj Object to destroy
399 **/
400 PetscErrorCode DivDiffFluxProjectionDataDestroy(DivDiffFluxProjectionData diff_flux_proj) {
401   PetscFunctionBeginUser;
402   if (diff_flux_proj == NULL) PetscFunctionReturn(PETSC_SUCCESS);
403   Ceed ceed = CeedVectorReturnCeed(diff_flux_proj->div_diff_flux_ceed);
404 
405   PetscCall(NodalProjectionDataDestroy(&diff_flux_proj->projection));
406   PetscCall(OperatorApplyContextDestroy(diff_flux_proj->calc_div_diff_flux));
407   if (diff_flux_proj->ceed_vec_has_array) {
408     PetscCall(VecReadCeedToPetsc(diff_flux_proj->div_diff_flux_ceed, diff_flux_proj->DivDiffFlux_memtype, diff_flux_proj->DivDiffFlux_loc));
409     diff_flux_proj->ceed_vec_has_array = PETSC_FALSE;
410   }
411   PetscCallCeed(ceed, CeedVectorDestroy(&diff_flux_proj->div_diff_flux_ceed));
412   PetscCallCeed(ceed, CeedElemRestrictionDestroy(&diff_flux_proj->elem_restr_div_diff_flux));
413   PetscCallCeed(ceed, CeedBasisDestroy(&diff_flux_proj->basis_div_diff_flux));
414   PetscCall(VecDestroy(&diff_flux_proj->DivDiffFlux_loc));
415   PetscCall(PetscFree(diff_flux_proj));
416   PetscFunctionReturn(PETSC_SUCCESS);
417 }
418