xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-preconditioning.c (revision 2b730f8b5a9c809740a0b3b302db43a719c636b1)
13d8e8822SJeremy L Thompson // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
23d8e8822SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3eaf62fffSJeremy L Thompson //
43d8e8822SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
5eaf62fffSJeremy L Thompson //
63d8e8822SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
7eaf62fffSJeremy L Thompson 
8ed9e99e6SJeremy L Thompson #include <assert.h>
9*2b730f8bSJeremy L Thompson #include <ceed-impl.h>
10*2b730f8bSJeremy L Thompson #include <ceed/backend.h>
11*2b730f8bSJeremy L Thompson #include <ceed/ceed.h>
12*2b730f8bSJeremy L Thompson #include <math.h>
13eaf62fffSJeremy L Thompson #include <stdbool.h>
14eaf62fffSJeremy L Thompson #include <stdio.h>
15eaf62fffSJeremy L Thompson #include <string.h>
16eaf62fffSJeremy L Thompson 
17eaf62fffSJeremy L Thompson /// @file
18eaf62fffSJeremy L Thompson /// Implementation of CeedOperator preconditioning interfaces
19eaf62fffSJeremy L Thompson 
20eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
21eaf62fffSJeremy L Thompson /// CeedOperator Library Internal Preconditioning Functions
22eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
23eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorDeveloper
24eaf62fffSJeremy L Thompson /// @{
25eaf62fffSJeremy L Thompson 
26eaf62fffSJeremy L Thompson /**
279e77b9c8SJeremy L Thompson   @brief Duplicate a CeedQFunction with a reference Ceed to fallback for advanced
289e77b9c8SJeremy L Thompson          CeedOperator functionality
299e77b9c8SJeremy L Thompson 
3001ea9c81SJed Brown   @param[in] fallback_ceed Ceed on which to create fallback CeedQFunction
319e77b9c8SJeremy L Thompson   @param[in] qf            CeedQFunction to create fallback for
3201ea9c81SJed Brown   @param[out] qf_fallback  fallback CeedQFunction
339e77b9c8SJeremy L Thompson 
349e77b9c8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
359e77b9c8SJeremy L Thompson 
369e77b9c8SJeremy L Thompson   @ref Developer
379e77b9c8SJeremy L Thompson **/
38*2b730f8bSJeremy L Thompson static int CeedQFunctionCreateFallback(Ceed fallback_ceed, CeedQFunction qf, CeedQFunction *qf_fallback) {
399e77b9c8SJeremy L Thompson   // Check if NULL qf passed in
409e77b9c8SJeremy L Thompson   if (!qf) return CEED_ERROR_SUCCESS;
419e77b9c8SJeremy L Thompson 
42d04bbc78SJeremy L Thompson   CeedDebug256(qf->ceed, 1, "---------- CeedOperator Fallback ----------\n");
4313f886e9SJeremy L Thompson   CeedDebug(qf->ceed, "Creating fallback CeedQFunction\n");
44d04bbc78SJeremy L Thompson 
459e77b9c8SJeremy L Thompson   char *source_path_with_name = "";
469e77b9c8SJeremy L Thompson   if (qf->source_path) {
47*2b730f8bSJeremy L Thompson     size_t path_len = strlen(qf->source_path), name_len = strlen(qf->kernel_name);
48*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(path_len + name_len + 2, &source_path_with_name));
499e77b9c8SJeremy L Thompson     memcpy(source_path_with_name, qf->source_path, path_len);
509e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len], ":", 1);
519e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len + 1], qf->kernel_name, name_len);
529e77b9c8SJeremy L Thompson   } else {
53*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &source_path_with_name));
549e77b9c8SJeremy L Thompson   }
559e77b9c8SJeremy L Thompson 
56*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInterior(fallback_ceed, qf->vec_length, qf->function, source_path_with_name, qf_fallback));
579e77b9c8SJeremy L Thompson   {
589e77b9c8SJeremy L Thompson     CeedQFunctionContext ctx;
599e77b9c8SJeremy L Thompson 
60*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionGetContext(qf, &ctx));
61*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetContext(*qf_fallback, ctx));
629e77b9c8SJeremy L Thompson   }
639e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_input_fields; i++) {
64*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(*qf_fallback, qf->input_fields[i]->field_name, qf->input_fields[i]->size, qf->input_fields[i]->eval_mode));
659e77b9c8SJeremy L Thompson   }
669e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_output_fields; i++) {
67*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddOutput(*qf_fallback, qf->output_fields[i]->field_name, qf->output_fields[i]->size, qf->output_fields[i]->eval_mode));
689e77b9c8SJeremy L Thompson   }
69*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&source_path_with_name));
709e77b9c8SJeremy L Thompson 
719e77b9c8SJeremy L Thompson   return CEED_ERROR_SUCCESS;
729e77b9c8SJeremy L Thompson }
739e77b9c8SJeremy L Thompson 
749e77b9c8SJeremy L Thompson /**
75eaf62fffSJeremy L Thompson   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
76eaf62fffSJeremy L Thompson          CeedOperator functionality
77eaf62fffSJeremy L Thompson 
78eaf62fffSJeremy L Thompson   @param op  CeedOperator to create fallback for
79eaf62fffSJeremy L Thompson 
80eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
81eaf62fffSJeremy L Thompson 
82eaf62fffSJeremy L Thompson   @ref Developer
83eaf62fffSJeremy L Thompson **/
84d04bbc78SJeremy L Thompson static int CeedOperatorCreateFallback(CeedOperator op) {
859e77b9c8SJeremy L Thompson   Ceed ceed_fallback;
86eaf62fffSJeremy L Thompson 
87805fe78eSJeremy L Thompson   // Check not already created
88805fe78eSJeremy L Thompson   if (op->op_fallback) return CEED_ERROR_SUCCESS;
89805fe78eSJeremy L Thompson 
90eaf62fffSJeremy L Thompson   // Fallback Ceed
91*2b730f8bSJeremy L Thompson   CeedCall(CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback));
92d04bbc78SJeremy L Thompson   if (!ceed_fallback) return CEED_ERROR_SUCCESS;
93d04bbc78SJeremy L Thompson 
94d04bbc78SJeremy L Thompson   CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n");
9513f886e9SJeremy L Thompson   CeedDebug(op->ceed, "Creating fallback CeedOperator\n");
96eaf62fffSJeremy L Thompson 
97eaf62fffSJeremy L Thompson   // Clone Op
98805fe78eSJeremy L Thompson   CeedOperator op_fallback;
99805fe78eSJeremy L Thompson   if (op->is_composite) {
100*2b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorCreate(ceed_fallback, &op_fallback));
101805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->num_suboperators; i++) {
102d04bbc78SJeremy L Thompson       CeedOperator op_sub_fallback;
103d04bbc78SJeremy L Thompson 
104*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorGetFallback(op->sub_operators[i], &op_sub_fallback));
105*2b730f8bSJeremy L Thompson       CeedCall(CeedCompositeOperatorAddSub(op_fallback, op_sub_fallback));
106805fe78eSJeremy L Thompson     }
107805fe78eSJeremy L Thompson   } else {
1089e77b9c8SJeremy L Thompson     CeedQFunction qf_fallback = NULL, dqf_fallback = NULL, dqfT_fallback = NULL;
109*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->qf, &qf_fallback));
110*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqf, &dqf_fallback));
111*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqfT, &dqfT_fallback));
112*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreate(ceed_fallback, qf_fallback, dqf_fallback, dqfT_fallback, &op_fallback));
113805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
114*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(op_fallback, op->input_fields[i]->field_name, op->input_fields[i]->elem_restr, op->input_fields[i]->basis,
115*2b730f8bSJeremy L Thompson                                     op->input_fields[i]->vec));
116805fe78eSJeremy L Thompson     }
117805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
118*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(op_fallback, op->output_fields[i]->field_name, op->output_fields[i]->elem_restr, op->output_fields[i]->basis,
119*2b730f8bSJeremy L Thompson                                     op->output_fields[i]->vec));
120805fe78eSJeremy L Thompson     }
121*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op->qf_assembled, &op_fallback->qf_assembled));
122805fe78eSJeremy L Thompson     if (op_fallback->num_qpts == 0) {
123*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetNumQuadraturePoints(op_fallback, op->num_qpts));
124805fe78eSJeremy L Thompson     }
1259e77b9c8SJeremy L Thompson     // Cleanup
126*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&qf_fallback));
127*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&dqf_fallback));
128*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&dqfT_fallback));
129805fe78eSJeremy L Thompson   }
130*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetName(op_fallback, op->name));
131*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fallback));
132805fe78eSJeremy L Thompson   op->op_fallback = op_fallback;
133eaf62fffSJeremy L Thompson 
134eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
135eaf62fffSJeremy L Thompson }
136eaf62fffSJeremy L Thompson 
137eaf62fffSJeremy L Thompson /**
138d04bbc78SJeremy L Thompson   @brief Retreive fallback CeedOperator with a reference Ceed for advanced CeedOperator functionality
139d04bbc78SJeremy L Thompson 
140d04bbc78SJeremy L Thompson   @param[in] op            CeedOperator to retrieve fallback for
141d04bbc78SJeremy L Thompson   @param[out] op_fallback  Fallback CeedOperator
142d04bbc78SJeremy L Thompson 
143d04bbc78SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
144d04bbc78SJeremy L Thompson 
145d04bbc78SJeremy L Thompson   @ref Developer
146d04bbc78SJeremy L Thompson **/
147d04bbc78SJeremy L Thompson int CeedOperatorGetFallback(CeedOperator op, CeedOperator *op_fallback) {
148d04bbc78SJeremy L Thompson   // Create if needed
149d04bbc78SJeremy L Thompson   if (!op->op_fallback) {
150*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreateFallback(op));
151d04bbc78SJeremy L Thompson   }
152d04bbc78SJeremy L Thompson   if (op->op_fallback) {
153d04bbc78SJeremy L Thompson     bool is_debug;
154d04bbc78SJeremy L Thompson 
155*2b730f8bSJeremy L Thompson     CeedCall(CeedIsDebug(op->ceed, &is_debug));
156d04bbc78SJeremy L Thompson     if (is_debug) {
157d04bbc78SJeremy L Thompson       Ceed        ceed_fallback;
158d04bbc78SJeremy L Thompson       const char *resource, *resource_fallback;
159d04bbc78SJeremy L Thompson 
160*2b730f8bSJeremy L Thompson       CeedCall(CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback));
161*2b730f8bSJeremy L Thompson       CeedCall(CeedGetResource(op->ceed, &resource));
162*2b730f8bSJeremy L Thompson       CeedCall(CeedGetResource(ceed_fallback, &resource_fallback));
163d04bbc78SJeremy L Thompson 
164d04bbc78SJeremy L Thompson       CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n");
165*2b730f8bSJeremy L Thompson       CeedDebug(op->ceed, "Falling back from %s operator at address %ld to %s operator at address %ld\n", resource, op, resource_fallback,
166*2b730f8bSJeremy L Thompson                 op->op_fallback);
167d04bbc78SJeremy L Thompson     }
168d04bbc78SJeremy L Thompson   }
169d04bbc78SJeremy L Thompson   *op_fallback = op->op_fallback;
170d04bbc78SJeremy L Thompson 
171d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
172d04bbc78SJeremy L Thompson }
173d04bbc78SJeremy L Thompson 
174d04bbc78SJeremy L Thompson /**
175eaf62fffSJeremy L Thompson   @brief Select correct basis matrix pointer based on CeedEvalMode
176eaf62fffSJeremy L Thompson 
177eaf62fffSJeremy L Thompson   @param[in] eval_mode   Current basis evaluation mode
178eaf62fffSJeremy L Thompson   @param[in] identity    Pointer to identity matrix
179eaf62fffSJeremy L Thompson   @param[in] interp      Pointer to interpolation matrix
180eaf62fffSJeremy L Thompson   @param[in] grad        Pointer to gradient matrix
181eaf62fffSJeremy L Thompson   @param[out] basis_ptr  Basis pointer to set
182eaf62fffSJeremy L Thompson 
183eaf62fffSJeremy L Thompson   @ref Developer
184eaf62fffSJeremy L Thompson **/
185*2b730f8bSJeremy L Thompson static inline void CeedOperatorGetBasisPointer(CeedEvalMode eval_mode, const CeedScalar *identity, const CeedScalar *interp, const CeedScalar *grad,
186*2b730f8bSJeremy L Thompson                                                const CeedScalar **basis_ptr) {
187eaf62fffSJeremy L Thompson   switch (eval_mode) {
188eaf62fffSJeremy L Thompson     case CEED_EVAL_NONE:
189eaf62fffSJeremy L Thompson       *basis_ptr = identity;
190eaf62fffSJeremy L Thompson       break;
191eaf62fffSJeremy L Thompson     case CEED_EVAL_INTERP:
192eaf62fffSJeremy L Thompson       *basis_ptr = interp;
193eaf62fffSJeremy L Thompson       break;
194eaf62fffSJeremy L Thompson     case CEED_EVAL_GRAD:
195eaf62fffSJeremy L Thompson       *basis_ptr = grad;
196eaf62fffSJeremy L Thompson       break;
197eaf62fffSJeremy L Thompson     case CEED_EVAL_WEIGHT:
198eaf62fffSJeremy L Thompson     case CEED_EVAL_DIV:
199eaf62fffSJeremy L Thompson     case CEED_EVAL_CURL:
200eaf62fffSJeremy L Thompson       break;  // Caught by QF Assembly
201eaf62fffSJeremy L Thompson   }
202ed9e99e6SJeremy L Thompson   assert(*basis_ptr != NULL);
203eaf62fffSJeremy L Thompson }
204eaf62fffSJeremy L Thompson 
205eaf62fffSJeremy L Thompson /**
206eaf62fffSJeremy L Thompson   @brief Create point block restriction for active operator field
207eaf62fffSJeremy L Thompson 
208eaf62fffSJeremy L Thompson   @param[in] rstr              Original CeedElemRestriction for active field
209eaf62fffSJeremy L Thompson   @param[out] pointblock_rstr  Address of the variable where the newly created
210eaf62fffSJeremy L Thompson                                  CeedElemRestriction will be stored
211eaf62fffSJeremy L Thompson 
212eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
213eaf62fffSJeremy L Thompson 
214eaf62fffSJeremy L Thompson   @ref Developer
215eaf62fffSJeremy L Thompson **/
216*2b730f8bSJeremy L Thompson static int CeedOperatorCreateActivePointBlockRestriction(CeedElemRestriction rstr, CeedElemRestriction *pointblock_rstr) {
217eaf62fffSJeremy L Thompson   Ceed ceed;
218*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
219eaf62fffSJeremy L Thompson   const CeedInt *offsets;
220*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets));
221eaf62fffSJeremy L Thompson 
222eaf62fffSJeremy L Thompson   // Expand offsets
2237b63f5c6SJed Brown   CeedInt  num_elem, num_comp, elem_size, comp_stride, *pointblock_offsets;
2247b63f5c6SJed Brown   CeedSize l_size;
225*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
226*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
227*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
228*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetCompStride(rstr, &comp_stride));
229*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
230eaf62fffSJeremy L Thompson   CeedInt shift = num_comp;
231*2b730f8bSJeremy L Thompson   if (comp_stride != 1) shift *= num_comp;
232*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_elem * elem_size, &pointblock_offsets));
233eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_elem * elem_size; i++) {
234eaf62fffSJeremy L Thompson     pointblock_offsets[i] = offsets[i] * shift;
235eaf62fffSJeremy L Thompson   }
236eaf62fffSJeremy L Thompson 
237eaf62fffSJeremy L Thompson   // Create new restriction
238*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp * num_comp, 1, l_size * num_comp, CEED_MEM_HOST, CEED_OWN_POINTER,
239*2b730f8bSJeremy L Thompson                                      pointblock_offsets, pointblock_rstr));
240eaf62fffSJeremy L Thompson 
241eaf62fffSJeremy L Thompson   // Cleanup
242*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets));
243eaf62fffSJeremy L Thompson 
244eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
245eaf62fffSJeremy L Thompson }
246eaf62fffSJeremy L Thompson 
247eaf62fffSJeremy L Thompson /**
248eaf62fffSJeremy L Thompson   @brief Core logic for assembling operator diagonal or point block diagonal
249eaf62fffSJeremy L Thompson 
250eaf62fffSJeremy L Thompson   @param[in] op             CeedOperator to assemble point block diagonal
251eaf62fffSJeremy L Thompson   @param[in] request        Address of CeedRequest for non-blocking completion, else
252eaf62fffSJeremy L Thompson                               CEED_REQUEST_IMMEDIATE
253eaf62fffSJeremy L Thompson   @param[in] is_pointblock  Boolean flag to assemble diagonal or point block diagonal
254eaf62fffSJeremy L Thompson   @param[out] assembled     CeedVector to store assembled diagonal
255eaf62fffSJeremy L Thompson 
256eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
257eaf62fffSJeremy L Thompson 
258eaf62fffSJeremy L Thompson   @ref Developer
259eaf62fffSJeremy L Thompson **/
260*2b730f8bSJeremy L Thompson static inline int CeedSingleOperatorAssembleAddDiagonal_Core(CeedOperator op, CeedRequest *request, const bool is_pointblock, CeedVector assembled) {
261eaf62fffSJeremy L Thompson   Ceed ceed;
262*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
263eaf62fffSJeremy L Thompson 
264eaf62fffSJeremy L Thompson   // Assemble QFunction
265eaf62fffSJeremy L Thompson   CeedQFunction qf;
266*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
267eaf62fffSJeremy L Thompson   CeedInt num_input_fields, num_output_fields;
268*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields));
269eaf62fffSJeremy L Thompson   CeedVector          assembled_qf;
270eaf62fffSJeremy L Thompson   CeedElemRestriction rstr;
271*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &rstr, request));
272eaf62fffSJeremy L Thompson   CeedInt layout[3];
273*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr, &layout));
274*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr));
275eaf62fffSJeremy L Thompson 
276ed9e99e6SJeremy L Thompson   // Get assembly data
277ed9e99e6SJeremy L Thompson   CeedOperatorAssemblyData data;
278*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data));
279ed9e99e6SJeremy L Thompson   const CeedEvalMode *eval_mode_in, *eval_mode_out;
280ed9e99e6SJeremy L Thompson   CeedInt             num_eval_mode_in, num_eval_mode_out;
281*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_eval_mode_in, &eval_mode_in, &num_eval_mode_out, &eval_mode_out));
282ed9e99e6SJeremy L Thompson   CeedBasis basis_in, basis_out;
283*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetBases(data, &basis_in, NULL, &basis_out, NULL));
284ed9e99e6SJeremy L Thompson   CeedInt num_comp;
285*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis_in, &num_comp));
286eaf62fffSJeremy L Thompson 
287eaf62fffSJeremy L Thompson   // Assemble point block diagonal restriction, if needed
288ed9e99e6SJeremy L Thompson   CeedElemRestriction diag_rstr;
289*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &diag_rstr));
290eaf62fffSJeremy L Thompson   if (is_pointblock) {
291ed9e99e6SJeremy L Thompson     CeedElemRestriction point_block_rstr;
292*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreateActivePointBlockRestriction(diag_rstr, &point_block_rstr));
293ed9e99e6SJeremy L Thompson     diag_rstr = point_block_rstr;
294eaf62fffSJeremy L Thompson   }
295eaf62fffSJeremy L Thompson 
296eaf62fffSJeremy L Thompson   // Create diagonal vector
297eaf62fffSJeremy L Thompson   CeedVector elem_diag;
298*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionCreateVector(diag_rstr, NULL, &elem_diag));
299eaf62fffSJeremy L Thompson 
300eaf62fffSJeremy L Thompson   // Assemble element operator diagonals
3019c774eddSJeremy L Thompson   CeedScalar       *elem_diag_array;
3029c774eddSJeremy L Thompson   const CeedScalar *assembled_qf_array;
303*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(elem_diag, 0.0));
304*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array));
305*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array));
306eaf62fffSJeremy L Thompson   CeedInt num_elem, num_nodes, num_qpts;
307*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(diag_rstr, &num_elem));
308*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes(basis_in, &num_nodes));
309*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts));
310ed9e99e6SJeremy L Thompson 
311eaf62fffSJeremy L Thompson   // Basis matrices
312eaf62fffSJeremy L Thompson   const CeedScalar *interp_in, *interp_out, *grad_in, *grad_out;
313eaf62fffSJeremy L Thompson   CeedScalar       *identity      = NULL;
314ed9e99e6SJeremy L Thompson   bool              has_eval_none = false;
315ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_eval_mode_in; i++) {
316ed9e99e6SJeremy L Thompson     has_eval_none = has_eval_none || (eval_mode_in[i] == CEED_EVAL_NONE);
317ed9e99e6SJeremy L Thompson   }
318ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_eval_mode_out; i++) {
319ed9e99e6SJeremy L Thompson     has_eval_none = has_eval_none || (eval_mode_out[i] == CEED_EVAL_NONE);
320ed9e99e6SJeremy L Thompson   }
321ed9e99e6SJeremy L Thompson   if (has_eval_none) {
322*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
323*2b730f8bSJeremy L Thompson     for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) identity[i * num_nodes + i] = 1.0;
324eaf62fffSJeremy L Thompson   }
325*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetInterp(basis_in, &interp_in));
326*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetInterp(basis_out, &interp_out));
327*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetGrad(basis_in, &grad_in));
328*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetGrad(basis_out, &grad_out));
329eaf62fffSJeremy L Thompson   // Compute the diagonal of B^T D B
330eaf62fffSJeremy L Thompson   // Each element
331eaf62fffSJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
332eaf62fffSJeremy L Thompson     CeedInt d_out = -1;
333eaf62fffSJeremy L Thompson     // Each basis eval mode pair
334eaf62fffSJeremy L Thompson     for (CeedInt e_out = 0; e_out < num_eval_mode_out; e_out++) {
335eaf62fffSJeremy L Thompson       const CeedScalar *bt = NULL;
336*2b730f8bSJeremy L Thompson       if (eval_mode_out[e_out] == CEED_EVAL_GRAD) d_out += 1;
337*2b730f8bSJeremy L Thompson       CeedOperatorGetBasisPointer(eval_mode_out[e_out], identity, interp_out, &grad_out[d_out * num_qpts * num_nodes], &bt);
338eaf62fffSJeremy L Thompson       CeedInt d_in = -1;
339eaf62fffSJeremy L Thompson       for (CeedInt e_in = 0; e_in < num_eval_mode_in; e_in++) {
340eaf62fffSJeremy L Thompson         const CeedScalar *b = NULL;
341*2b730f8bSJeremy L Thompson         if (eval_mode_in[e_in] == CEED_EVAL_GRAD) d_in += 1;
342*2b730f8bSJeremy L Thompson         CeedOperatorGetBasisPointer(eval_mode_in[e_in], identity, interp_in, &grad_in[d_in * num_qpts * num_nodes], &b);
343eaf62fffSJeremy L Thompson         // Each component
344*2b730f8bSJeremy L Thompson         for (CeedInt c_out = 0; c_out < num_comp; c_out++) {
345eaf62fffSJeremy L Thompson           // Each qpoint/node pair
346*2b730f8bSJeremy L Thompson           for (CeedInt q = 0; q < num_qpts; q++) {
347eaf62fffSJeremy L Thompson             if (is_pointblock) {
348eaf62fffSJeremy L Thompson               // Point Block Diagonal
349eaf62fffSJeremy L Thompson               for (CeedInt c_in = 0; c_in < num_comp; c_in++) {
350eaf62fffSJeremy L Thompson                 const CeedScalar qf_value =
351*2b730f8bSJeremy L Thompson                     assembled_qf_array[q * layout[0] + (((e_in * num_comp + c_in) * num_eval_mode_out + e_out) * num_comp + c_out) * layout[1] +
352*2b730f8bSJeremy L Thompson                                        e * layout[2]];
353*2b730f8bSJeremy L Thompson                 for (CeedInt n = 0; n < num_nodes; n++) {
354eaf62fffSJeremy L Thompson                   elem_diag_array[((e * num_comp + c_out) * num_comp + c_in) * num_nodes + n] +=
355eaf62fffSJeremy L Thompson                       bt[q * num_nodes + n] * qf_value * b[q * num_nodes + n];
356eaf62fffSJeremy L Thompson                 }
357*2b730f8bSJeremy L Thompson               }
358eaf62fffSJeremy L Thompson             } else {
359eaf62fffSJeremy L Thompson               // Diagonal Only
360eaf62fffSJeremy L Thompson               const CeedScalar qf_value =
361*2b730f8bSJeremy L Thompson                   assembled_qf_array[q * layout[0] + (((e_in * num_comp + c_out) * num_eval_mode_out + e_out) * num_comp + c_out) * layout[1] +
362*2b730f8bSJeremy L Thompson                                      e * layout[2]];
363*2b730f8bSJeremy L Thompson               for (CeedInt n = 0; n < num_nodes; n++) {
364*2b730f8bSJeremy L Thompson                 elem_diag_array[(e * num_comp + c_out) * num_nodes + n] += bt[q * num_nodes + n] * qf_value * b[q * num_nodes + n];
365eaf62fffSJeremy L Thompson               }
366eaf62fffSJeremy L Thompson             }
367eaf62fffSJeremy L Thompson           }
368eaf62fffSJeremy L Thompson         }
369*2b730f8bSJeremy L Thompson       }
370*2b730f8bSJeremy L Thompson     }
371*2b730f8bSJeremy L Thompson   }
372*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(elem_diag, &elem_diag_array));
373*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array));
374eaf62fffSJeremy L Thompson 
375eaf62fffSJeremy L Thompson   // Assemble local operator diagonal
376*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionApply(diag_rstr, CEED_TRANSPOSE, elem_diag, assembled, request));
377eaf62fffSJeremy L Thompson 
378eaf62fffSJeremy L Thompson   // Cleanup
379eaf62fffSJeremy L Thompson   if (is_pointblock) {
380*2b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionDestroy(&diag_rstr));
381eaf62fffSJeremy L Thompson   }
382*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled_qf));
383*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&elem_diag));
384*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&identity));
385eaf62fffSJeremy L Thompson 
386eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
387eaf62fffSJeremy L Thompson }
388eaf62fffSJeremy L Thompson 
389eaf62fffSJeremy L Thompson /**
390eaf62fffSJeremy L Thompson   @brief Core logic for assembling composite operator diagonal
391eaf62fffSJeremy L Thompson 
392eaf62fffSJeremy L Thompson   @param[in] op             CeedOperator to assemble point block diagonal
393eaf62fffSJeremy L Thompson   @param[in] request        Address of CeedRequest for non-blocking completion, else
394eaf62fffSJeremy L Thompson                             CEED_REQUEST_IMMEDIATE
395eaf62fffSJeremy L Thompson   @param[in] is_pointblock  Boolean flag to assemble diagonal or point block diagonal
396eaf62fffSJeremy L Thompson   @param[out] assembled     CeedVector to store assembled diagonal
397eaf62fffSJeremy L Thompson 
398eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
399eaf62fffSJeremy L Thompson 
400eaf62fffSJeremy L Thompson   @ref Developer
401eaf62fffSJeremy L Thompson **/
402*2b730f8bSJeremy L Thompson static inline int CeedCompositeOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedRequest *request, const bool is_pointblock,
403eaf62fffSJeremy L Thompson                                                                  CeedVector assembled) {
404eaf62fffSJeremy L Thompson   CeedInt       num_sub;
405eaf62fffSJeremy L Thompson   CeedOperator *suboperators;
406*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetNumSub(op, &num_sub));
407*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetSubList(op, &suboperators));
408eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_sub; i++) {
4096aa95790SJeremy L Thompson     if (is_pointblock) {
410*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(suboperators[i], assembled, request));
4116aa95790SJeremy L Thompson     } else {
412*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddDiagonal(suboperators[i], assembled, request));
4136aa95790SJeremy L Thompson     }
414eaf62fffSJeremy L Thompson   }
415eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
416eaf62fffSJeremy L Thompson }
417eaf62fffSJeremy L Thompson 
418eaf62fffSJeremy L Thompson /**
419eaf62fffSJeremy L Thompson   @brief Build nonzero pattern for non-composite operator
420eaf62fffSJeremy L Thompson 
421eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssembleSymbolic()
422eaf62fffSJeremy L Thompson 
423eaf62fffSJeremy L Thompson   @param[in] op      CeedOperator to assemble nonzero pattern
424eaf62fffSJeremy L Thompson   @param[in] offset  Offset for number of entries
425eaf62fffSJeremy L Thompson   @param[out] rows   Row number for each entry
426eaf62fffSJeremy L Thompson   @param[out] cols   Column number for each entry
427eaf62fffSJeremy L Thompson 
428eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
429eaf62fffSJeremy L Thompson 
430eaf62fffSJeremy L Thompson   @ref Developer
431eaf62fffSJeremy L Thompson **/
432*2b730f8bSJeremy L Thompson static int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset, CeedInt *rows, CeedInt *cols) {
433eaf62fffSJeremy L Thompson   Ceed ceed = op->ceed;
434*2b730f8bSJeremy L Thompson   if (op->is_composite) {
435eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
436*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
437eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
438*2b730f8bSJeremy L Thompson   }
439eaf62fffSJeremy L Thompson 
440c9366a6bSJeremy L Thompson   CeedSize num_nodes;
441*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &num_nodes, NULL));
442eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_in;
443*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &rstr_in));
444e79b91d9SJeremy L Thompson   CeedInt num_elem, elem_size, num_comp;
445*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr_in, &num_elem));
446*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr_in, &elem_size));
447*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(rstr_in, &num_comp));
448eaf62fffSJeremy L Thompson   CeedInt layout_er[3];
449*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr_in, &layout_er));
450eaf62fffSJeremy L Thompson 
451eaf62fffSJeremy L Thompson   CeedInt local_num_entries = elem_size * num_comp * elem_size * num_comp * num_elem;
452eaf62fffSJeremy L Thompson 
453eaf62fffSJeremy L Thompson   // Determine elem_dof relation
454eaf62fffSJeremy L Thompson   CeedVector index_vec;
455*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed, num_nodes, &index_vec));
456eaf62fffSJeremy L Thompson   CeedScalar *array;
457*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayWrite(index_vec, CEED_MEM_HOST, &array));
458ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_nodes; i++) array[i] = i;
459*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(index_vec, &array));
460eaf62fffSJeremy L Thompson   CeedVector elem_dof;
461*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed, num_elem * elem_size * num_comp, &elem_dof));
462*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(elem_dof, 0.0));
463*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionApply(rstr_in, CEED_NOTRANSPOSE, index_vec, elem_dof, CEED_REQUEST_IMMEDIATE));
464eaf62fffSJeremy L Thompson   const CeedScalar *elem_dof_a;
465*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(elem_dof, CEED_MEM_HOST, &elem_dof_a));
466*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&index_vec));
467eaf62fffSJeremy L Thompson 
468eaf62fffSJeremy L Thompson   // Determine i, j locations for element matrices
469eaf62fffSJeremy L Thompson   CeedInt count = 0;
470ed9e99e6SJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
471ed9e99e6SJeremy L Thompson     for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) {
472ed9e99e6SJeremy L Thompson       for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) {
473ed9e99e6SJeremy L Thompson         for (CeedInt i = 0; i < elem_size; i++) {
474ed9e99e6SJeremy L Thompson           for (CeedInt j = 0; j < elem_size; j++) {
475*2b730f8bSJeremy L Thompson             const CeedInt elem_dof_index_row = i * layout_er[0] + (comp_out)*layout_er[1] + e * layout_er[2];
476*2b730f8bSJeremy L Thompson             const CeedInt elem_dof_index_col = j * layout_er[0] + comp_in * layout_er[1] + e * layout_er[2];
477eaf62fffSJeremy L Thompson 
478eaf62fffSJeremy L Thompson             const CeedInt row = elem_dof_a[elem_dof_index_row];
479eaf62fffSJeremy L Thompson             const CeedInt col = elem_dof_a[elem_dof_index_col];
480eaf62fffSJeremy L Thompson 
481eaf62fffSJeremy L Thompson             rows[offset + count] = row;
482eaf62fffSJeremy L Thompson             cols[offset + count] = col;
483eaf62fffSJeremy L Thompson             count++;
484eaf62fffSJeremy L Thompson           }
485eaf62fffSJeremy L Thompson         }
486eaf62fffSJeremy L Thompson       }
487eaf62fffSJeremy L Thompson     }
488eaf62fffSJeremy L Thompson   }
489*2b730f8bSJeremy L Thompson   if (count != local_num_entries) {
490eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
491eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing assembled entries");
492eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
493*2b730f8bSJeremy L Thompson   }
494*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(elem_dof, &elem_dof_a));
495*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&elem_dof));
496eaf62fffSJeremy L Thompson 
497eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
498eaf62fffSJeremy L Thompson }
499eaf62fffSJeremy L Thompson 
500eaf62fffSJeremy L Thompson /**
501eaf62fffSJeremy L Thompson   @brief Assemble nonzero entries for non-composite operator
502eaf62fffSJeremy L Thompson 
503eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssemble()
504eaf62fffSJeremy L Thompson 
505eaf62fffSJeremy L Thompson   @param[in] op       CeedOperator to assemble
50652b3e6a7SJed Brown   @param[in] offset   Offest for number of entries
507eaf62fffSJeremy L Thompson   @param[out] values  Values to assemble into matrix
508eaf62fffSJeremy L Thompson 
509eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
510eaf62fffSJeremy L Thompson 
511eaf62fffSJeremy L Thompson   @ref Developer
512eaf62fffSJeremy L Thompson **/
513*2b730f8bSJeremy L Thompson static int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset, CeedVector values) {
514eaf62fffSJeremy L Thompson   Ceed ceed = op->ceed;
515*2b730f8bSJeremy L Thompson   if (op->is_composite) {
516eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
517*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
518eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
519*2b730f8bSJeremy L Thompson   }
52052b3e6a7SJed Brown   if (op->num_elem == 0) return CEED_ERROR_SUCCESS;
521eaf62fffSJeremy L Thompson 
522cefa2673SJeremy L Thompson   if (op->LinearAssembleSingle) {
523cefa2673SJeremy L Thompson     // Backend version
524*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleSingle(op, offset, values));
525cefa2673SJeremy L Thompson     return CEED_ERROR_SUCCESS;
526cefa2673SJeremy L Thompson   } else {
527cefa2673SJeremy L Thompson     // Operator fallback
528cefa2673SJeremy L Thompson     CeedOperator op_fallback;
529cefa2673SJeremy L Thompson 
530*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
531cefa2673SJeremy L Thompson     if (op_fallback) {
532*2b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemble(op_fallback, offset, values));
533cefa2673SJeremy L Thompson       return CEED_ERROR_SUCCESS;
534cefa2673SJeremy L Thompson     }
535cefa2673SJeremy L Thompson   }
536cefa2673SJeremy L Thompson 
537eaf62fffSJeremy L Thompson   // Assemble QFunction
538eaf62fffSJeremy L Thompson   CeedQFunction qf;
539*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
540eaf62fffSJeremy L Thompson   CeedVector          assembled_qf;
541eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_q;
542*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &rstr_q, CEED_REQUEST_IMMEDIATE));
5431f9221feSJeremy L Thompson   CeedSize qf_length;
544*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetLength(assembled_qf, &qf_length));
545eaf62fffSJeremy L Thompson 
5467e7773b5SJeremy L Thompson   CeedInt            num_input_fields, num_output_fields;
547eaf62fffSJeremy L Thompson   CeedOperatorField *input_fields;
548eaf62fffSJeremy L Thompson   CeedOperatorField *output_fields;
549*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
550eaf62fffSJeremy L Thompson 
551ed9e99e6SJeremy L Thompson   // Get assembly data
552ed9e99e6SJeremy L Thompson   CeedOperatorAssemblyData data;
553*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data));
554ed9e99e6SJeremy L Thompson   const CeedEvalMode *eval_mode_in, *eval_mode_out;
555ed9e99e6SJeremy L Thompson   CeedInt             num_eval_mode_in, num_eval_mode_out;
556*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_eval_mode_in, &eval_mode_in, &num_eval_mode_out, &eval_mode_out));
557ed9e99e6SJeremy L Thompson   CeedBasis basis_in, basis_out;
558*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetBases(data, &basis_in, NULL, &basis_out, NULL));
559eaf62fffSJeremy L Thompson 
560*2b730f8bSJeremy L Thompson   if (num_eval_mode_in == 0 || num_eval_mode_out == 0) {
561eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
562*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Cannot assemble operator with out inputs/outputs");
563eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
564*2b730f8bSJeremy L Thompson   }
565eaf62fffSJeremy L Thompson 
566ed9e99e6SJeremy L Thompson   CeedElemRestriction active_rstr;
567eaf62fffSJeremy L Thompson   CeedInt             num_elem, elem_size, num_qpts, num_comp;
568*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &active_rstr));
569*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(active_rstr, &num_elem));
570*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(active_rstr, &elem_size));
571*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(active_rstr, &num_comp));
572*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts));
573eaf62fffSJeremy L Thompson 
574eaf62fffSJeremy L Thompson   CeedInt local_num_entries = elem_size * num_comp * elem_size * num_comp * num_elem;
575eaf62fffSJeremy L Thompson 
576eaf62fffSJeremy L Thompson   // loop over elements and put in data structure
577eaf62fffSJeremy L Thompson   const CeedScalar *interp_in, *grad_in;
578*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetInterp(basis_in, &interp_in));
579*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetGrad(basis_in, &grad_in));
580eaf62fffSJeremy L Thompson 
581eaf62fffSJeremy L Thompson   const CeedScalar *assembled_qf_array;
582*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array));
583eaf62fffSJeremy L Thompson 
584eaf62fffSJeremy L Thompson   CeedInt layout_qf[3];
585*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr_q, &layout_qf));
586*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_q));
587eaf62fffSJeremy L Thompson 
588eaf62fffSJeremy L Thompson   // we store B_mat_in, B_mat_out, BTD, elem_mat in row-major order
589ed9e99e6SJeremy L Thompson   const CeedScalar *B_mat_in, *B_mat_out;
590*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &B_mat_in, NULL, &B_mat_out));
591ed9e99e6SJeremy L Thompson   CeedScalar  BTD_mat[elem_size * num_qpts * num_eval_mode_in];
592eaf62fffSJeremy L Thompson   CeedScalar  elem_mat[elem_size * elem_size];
59392ae7e47SJeremy L Thompson   CeedInt     count = 0;
594eaf62fffSJeremy L Thompson   CeedScalar *vals;
595*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayWrite(values, CEED_MEM_HOST, &vals));
596ed9e99e6SJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
597ed9e99e6SJeremy L Thompson     for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) {
598ed9e99e6SJeremy L Thompson       for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) {
599ed9e99e6SJeremy L Thompson         // Compute B^T*D
600ed9e99e6SJeremy L Thompson         for (CeedInt n = 0; n < elem_size; n++) {
601ed9e99e6SJeremy L Thompson           for (CeedInt q = 0; q < num_qpts; q++) {
602ed9e99e6SJeremy L Thompson             for (CeedInt e_in = 0; e_in < num_eval_mode_in; e_in++) {
603*2b730f8bSJeremy L Thompson               const CeedInt btd_index = n * (num_qpts * num_eval_mode_in) + (num_eval_mode_in * q + e_in);
604067fd99fSJeremy L Thompson               CeedScalar    sum       = 0.0;
605067fd99fSJeremy L Thompson               for (CeedInt e_out = 0; e_out < num_eval_mode_out; e_out++) {
606ed9e99e6SJeremy L Thompson                 const CeedInt b_out_index     = (num_eval_mode_out * q + e_out) * elem_size + n;
607*2b730f8bSJeremy L Thompson                 const CeedInt eval_mode_index = ((e_in * num_comp + comp_in) * num_eval_mode_out + e_out) * num_comp + comp_out;
608*2b730f8bSJeremy L Thompson                 const CeedInt qf_index        = q * layout_qf[0] + eval_mode_index * layout_qf[1] + e * layout_qf[2];
609067fd99fSJeremy L Thompson                 sum += B_mat_out[b_out_index] * assembled_qf_array[qf_index];
610eaf62fffSJeremy L Thompson               }
611067fd99fSJeremy L Thompson               BTD_mat[btd_index] = sum;
612ed9e99e6SJeremy L Thompson             }
613ed9e99e6SJeremy L Thompson           }
614eaf62fffSJeremy L Thompson         }
615eaf62fffSJeremy L Thompson         // form element matrix itself (for each block component)
616*2b730f8bSJeremy L Thompson         CeedCall(CeedMatrixMatrixMultiply(ceed, BTD_mat, B_mat_in, elem_mat, elem_size, elem_size, num_qpts * num_eval_mode_in));
617eaf62fffSJeremy L Thompson 
618eaf62fffSJeremy L Thompson         // put element matrix in coordinate data structure
619ed9e99e6SJeremy L Thompson         for (CeedInt i = 0; i < elem_size; i++) {
620ed9e99e6SJeremy L Thompson           for (CeedInt j = 0; j < elem_size; j++) {
621eaf62fffSJeremy L Thompson             vals[offset + count] = elem_mat[i * elem_size + j];
622eaf62fffSJeremy L Thompson             count++;
623eaf62fffSJeremy L Thompson           }
624eaf62fffSJeremy L Thompson         }
625eaf62fffSJeremy L Thompson       }
626eaf62fffSJeremy L Thompson     }
627eaf62fffSJeremy L Thompson   }
628*2b730f8bSJeremy L Thompson   if (count != local_num_entries) {
629eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
630eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing entries");
631eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
632*2b730f8bSJeremy L Thompson   }
633*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(values, &vals));
634eaf62fffSJeremy L Thompson 
635*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array));
636*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled_qf));
637eaf62fffSJeremy L Thompson 
638eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
639eaf62fffSJeremy L Thompson }
640eaf62fffSJeremy L Thompson 
641eaf62fffSJeremy L Thompson /**
642eaf62fffSJeremy L Thompson   @brief Count number of entries for assembled CeedOperator
643eaf62fffSJeremy L Thompson 
644eaf62fffSJeremy L Thompson   @param[in] op            CeedOperator to assemble
645eaf62fffSJeremy L Thompson   @param[out] num_entries  Number of entries in assembled representation
646eaf62fffSJeremy L Thompson 
647eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
648eaf62fffSJeremy L Thompson 
649eaf62fffSJeremy L Thompson   @ref Utility
650eaf62fffSJeremy L Thompson **/
651*2b730f8bSJeremy L Thompson static int CeedSingleOperatorAssemblyCountEntries(CeedOperator op, CeedInt *num_entries) {
652eaf62fffSJeremy L Thompson   CeedElemRestriction rstr;
653eaf62fffSJeremy L Thompson   CeedInt             num_elem, elem_size, num_comp;
654eaf62fffSJeremy L Thompson 
655*2b730f8bSJeremy L Thompson   if (op->is_composite) {
656eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
657*2b730f8bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
658eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
659*2b730f8bSJeremy L Thompson   }
660*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &rstr));
661*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
662*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
663*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
664eaf62fffSJeremy L Thompson   *num_entries = elem_size * num_comp * elem_size * num_comp * num_elem;
665eaf62fffSJeremy L Thompson 
666eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
667eaf62fffSJeremy L Thompson }
668eaf62fffSJeremy L Thompson 
669eaf62fffSJeremy L Thompson /**
670eaf62fffSJeremy L Thompson   @brief Common code for creating a multigrid coarse operator and level
671eaf62fffSJeremy L Thompson            transfer operators for a CeedOperator
672eaf62fffSJeremy L Thompson 
673eaf62fffSJeremy L Thompson   @param[in] op_fine       Fine grid operator
674eaf62fffSJeremy L Thompson   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
675eaf62fffSJeremy L Thompson   @param[in] rstr_coarse   Coarse grid restriction
676eaf62fffSJeremy L Thompson   @param[in] basis_coarse  Coarse grid active vector basis
677eaf62fffSJeremy L Thompson   @param[in] basis_c_to_f  Basis for coarse to fine interpolation
678eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
679eaf62fffSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator
680eaf62fffSJeremy L Thompson   @param[out] op_restrict  Fine to coarse operator
681eaf62fffSJeremy L Thompson 
682eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
683eaf62fffSJeremy L Thompson 
684eaf62fffSJeremy L Thompson   @ref Developer
685eaf62fffSJeremy L Thompson **/
686*2b730f8bSJeremy L Thompson static int CeedSingleOperatorMultigridLevel(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
687*2b730f8bSJeremy L Thompson                                             CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_restrict) {
688eaf62fffSJeremy L Thompson   Ceed ceed;
689*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
690eaf62fffSJeremy L Thompson 
691eaf62fffSJeremy L Thompson   // Check for composite operator
692eaf62fffSJeremy L Thompson   bool is_composite;
693*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op_fine, &is_composite));
694*2b730f8bSJeremy L Thompson   if (is_composite) {
695eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
696*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Automatic multigrid setup for composite operators not supported");
697eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
698*2b730f8bSJeremy L Thompson   }
699eaf62fffSJeremy L Thompson 
700eaf62fffSJeremy L Thompson   // Coarse Grid
701*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT, op_coarse));
702eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_fine = NULL;
703eaf62fffSJeremy L Thompson   // -- Clone input fields
70492ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_input_fields; i++) {
705eaf62fffSJeremy L Thompson     if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
706eaf62fffSJeremy L Thompson       rstr_fine = op_fine->input_fields[i]->elem_restr;
707*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE));
708eaf62fffSJeremy L Thompson     } else {
709*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, op_fine->input_fields[i]->elem_restr,
710*2b730f8bSJeremy L Thompson                                     op_fine->input_fields[i]->basis, op_fine->input_fields[i]->vec));
711eaf62fffSJeremy L Thompson     }
712eaf62fffSJeremy L Thompson   }
713eaf62fffSJeremy L Thompson   // -- Clone output fields
71492ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_output_fields; i++) {
715eaf62fffSJeremy L Thompson     if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
716*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE));
717eaf62fffSJeremy L Thompson     } else {
718*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, op_fine->output_fields[i]->elem_restr,
719*2b730f8bSJeremy L Thompson                                     op_fine->output_fields[i]->basis, op_fine->output_fields[i]->vec));
720eaf62fffSJeremy L Thompson     }
721eaf62fffSJeremy L Thompson   }
722af99e877SJeremy L Thompson   // -- Clone QFunctionAssemblyData
723*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op_fine->qf_assembled, &(*op_coarse)->qf_assembled));
724eaf62fffSJeremy L Thompson 
725eaf62fffSJeremy L Thompson   // Multiplicity vector
726eaf62fffSJeremy L Thompson   CeedVector mult_vec, mult_e_vec;
727*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec));
728*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(mult_e_vec, 0.0));
729*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionApply(rstr_fine, CEED_NOTRANSPOSE, p_mult_fine, mult_e_vec, CEED_REQUEST_IMMEDIATE));
730*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(mult_vec, 0.0));
731*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionApply(rstr_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec, CEED_REQUEST_IMMEDIATE));
732*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&mult_e_vec));
733*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorReciprocal(mult_vec));
734eaf62fffSJeremy L Thompson 
735eaf62fffSJeremy L Thompson   // Restriction
736eaf62fffSJeremy L Thompson   CeedInt num_comp;
737*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis_coarse, &num_comp));
738eaf62fffSJeremy L Thompson   CeedQFunction qf_restrict;
739*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_restrict));
740eaf62fffSJeremy L Thompson   CeedInt *num_comp_r_data;
741*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, &num_comp_r_data));
742eaf62fffSJeremy L Thompson   num_comp_r_data[0] = num_comp;
743eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_r;
744*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextCreate(ceed, &ctx_r));
745*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_r_data), num_comp_r_data));
746*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetContext(qf_restrict, ctx_r));
747*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextDestroy(&ctx_r));
748*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_restrict, "input", num_comp, CEED_EVAL_NONE));
749*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_restrict, "scale", num_comp, CEED_EVAL_NONE));
750*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddOutput(qf_restrict, "output", num_comp, CEED_EVAL_INTERP));
751*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_restrict, num_comp));
752eaf62fffSJeremy L Thompson 
753*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed, qf_restrict, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_restrict));
754*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*op_restrict, "input", rstr_fine, CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE));
755*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*op_restrict, "scale", rstr_fine, CEED_BASIS_COLLOCATED, mult_vec));
756*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*op_restrict, "output", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE));
757eaf62fffSJeremy L Thompson 
758eaf62fffSJeremy L Thompson   // Prolongation
759eaf62fffSJeremy L Thompson   CeedQFunction qf_prolong;
760*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong));
761eaf62fffSJeremy L Thompson   CeedInt *num_comp_p_data;
762*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, &num_comp_p_data));
763eaf62fffSJeremy L Thompson   num_comp_p_data[0] = num_comp;
764eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_p;
765*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextCreate(ceed, &ctx_p));
766*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_p_data), num_comp_p_data));
767*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetContext(qf_prolong, ctx_p));
768*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextDestroy(&ctx_p));
769*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP));
770*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE));
771*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE));
772*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_prolong, num_comp));
773eaf62fffSJeremy L Thompson 
774*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_prolong));
775*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE));
776*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*op_prolong, "scale", rstr_fine, CEED_BASIS_COLLOCATED, mult_vec));
777*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*op_prolong, "output", rstr_fine, CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE));
778eaf62fffSJeremy L Thompson 
779ea6b5821SJeremy L Thompson   // Clone name
780ea6b5821SJeremy L Thompson   bool   has_name = op_fine->name;
781ea6b5821SJeremy L Thompson   size_t name_len = op_fine->name ? strlen(op_fine->name) : 0;
782*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetName(*op_coarse, op_fine->name));
783ea6b5821SJeremy L Thompson   {
784ea6b5821SJeremy L Thompson     char *prolongation_name;
785*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(18 + name_len, &prolongation_name));
786*2b730f8bSJeremy L Thompson     sprintf(prolongation_name, "prolongation%s%s", has_name ? " for " : "", has_name ? op_fine->name : "");
787*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetName(*op_prolong, prolongation_name));
788*2b730f8bSJeremy L Thompson     CeedCall(CeedFree(&prolongation_name));
789ea6b5821SJeremy L Thompson   }
790ea6b5821SJeremy L Thompson   {
791ea6b5821SJeremy L Thompson     char *restriction_name;
792*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(17 + name_len, &restriction_name));
793*2b730f8bSJeremy L Thompson     sprintf(restriction_name, "restriction%s%s", has_name ? " for " : "", has_name ? op_fine->name : "");
794*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetName(*op_restrict, restriction_name));
795*2b730f8bSJeremy L Thompson     CeedCall(CeedFree(&restriction_name));
796ea6b5821SJeremy L Thompson   }
797ea6b5821SJeremy L Thompson 
798eaf62fffSJeremy L Thompson   // Cleanup
799*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&mult_vec));
800*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&basis_c_to_f));
801*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionDestroy(&qf_restrict));
802*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionDestroy(&qf_prolong));
803805fe78eSJeremy L Thompson 
804eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
805eaf62fffSJeremy L Thompson }
806eaf62fffSJeremy L Thompson 
807eaf62fffSJeremy L Thompson /**
808eaf62fffSJeremy L Thompson   @brief Build 1D mass matrix and Laplacian with perturbation
809eaf62fffSJeremy L Thompson 
810eaf62fffSJeremy L Thompson   @param[in] interp_1d    Interpolation matrix in one dimension
811eaf62fffSJeremy L Thompson   @param[in] grad_1d      Gradient matrix in one dimension
812eaf62fffSJeremy L Thompson   @param[in] q_weight_1d  Quadrature weights in one dimension
813eaf62fffSJeremy L Thompson   @param[in] P_1d         Number of basis nodes in one dimension
814eaf62fffSJeremy L Thompson   @param[in] Q_1d         Number of quadrature points in one dimension
815eaf62fffSJeremy L Thompson   @param[in] dim          Dimension of basis
816eaf62fffSJeremy L Thompson   @param[out] mass        Assembled mass matrix in one dimension
817eaf62fffSJeremy L Thompson   @param[out] laplace     Assembled perturbed Laplacian in one dimension
818eaf62fffSJeremy L Thompson 
819eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
820eaf62fffSJeremy L Thompson 
821eaf62fffSJeremy L Thompson   @ref Developer
822eaf62fffSJeremy L Thompson **/
823*2b730f8bSJeremy L Thompson CeedPragmaOptimizeOff static int CeedBuildMassLaplace(const CeedScalar *interp_1d, const CeedScalar *grad_1d, const CeedScalar *q_weight_1d,
824*2b730f8bSJeremy L Thompson                                                       CeedInt P_1d, CeedInt Q_1d, CeedInt dim, CeedScalar *mass, CeedScalar *laplace) {
825*2b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
826eaf62fffSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) {
827eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
828*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < Q_1d; k++) sum += interp_1d[k * P_1d + i] * q_weight_1d[k] * interp_1d[k * P_1d + j];
829eaf62fffSJeremy L Thompson       mass[i + j * P_1d] = sum;
830eaf62fffSJeremy L Thompson     }
831*2b730f8bSJeremy L Thompson   }
832eaf62fffSJeremy L Thompson   // -- Laplacian
833*2b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
834eaf62fffSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) {
835eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
836*2b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < Q_1d; k++) sum += grad_1d[k * P_1d + i] * q_weight_1d[k] * grad_1d[k * P_1d + j];
837eaf62fffSJeremy L Thompson       laplace[i + j * P_1d] = sum;
838eaf62fffSJeremy L Thompson     }
839*2b730f8bSJeremy L Thompson   }
840eaf62fffSJeremy L Thompson   CeedScalar perturbation = dim > 2 ? 1e-6 : 1e-4;
841*2b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) laplace[i + P_1d * i] += perturbation;
842eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
843eaf62fffSJeremy L Thompson }
844eaf62fffSJeremy L Thompson CeedPragmaOptimizeOn
845eaf62fffSJeremy L Thompson 
846eaf62fffSJeremy L Thompson     /// @}
847eaf62fffSJeremy L Thompson 
848eaf62fffSJeremy L Thompson     /// ----------------------------------------------------------------------------
849480fae85SJeremy L Thompson     /// CeedOperator Backend API
850480fae85SJeremy L Thompson     /// ----------------------------------------------------------------------------
851480fae85SJeremy L Thompson     /// @addtogroup CeedOperatorBackend
852480fae85SJeremy L Thompson     /// @{
853480fae85SJeremy L Thompson 
854480fae85SJeremy L Thompson     /**
855480fae85SJeremy L Thompson       @brief Create object holding CeedQFunction assembly data for CeedOperator
856480fae85SJeremy L Thompson 
857480fae85SJeremy L Thompson       @param[in] ceed  A Ceed object where the CeedQFunctionAssemblyData will be created
858480fae85SJeremy L Thompson       @param[out] data Address of the variable where the newly created
859480fae85SJeremy L Thompson                          CeedQFunctionAssemblyData will be stored
860480fae85SJeremy L Thompson 
861480fae85SJeremy L Thompson       @return An error code: 0 - success, otherwise - failure
862480fae85SJeremy L Thompson 
863480fae85SJeremy L Thompson       @ref Backend
864480fae85SJeremy L Thompson     **/
865*2b730f8bSJeremy L Thompson     int
866*2b730f8bSJeremy L Thompson     CeedQFunctionAssemblyDataCreate(Ceed ceed, CeedQFunctionAssemblyData *data) {
867*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, data));
868480fae85SJeremy L Thompson   (*data)->ref_count = 1;
869480fae85SJeremy L Thompson   (*data)->ceed      = ceed;
870*2b730f8bSJeremy L Thompson   CeedCall(CeedReference(ceed));
871480fae85SJeremy L Thompson 
872480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
873480fae85SJeremy L Thompson }
874480fae85SJeremy L Thompson 
875480fae85SJeremy L Thompson /**
876480fae85SJeremy L Thompson   @brief Increment the reference counter for a CeedQFunctionAssemblyData
877480fae85SJeremy L Thompson 
878480fae85SJeremy L Thompson   @param data  CeedQFunctionAssemblyData to increment the reference counter
879480fae85SJeremy L Thompson 
880480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
881480fae85SJeremy L Thompson 
882480fae85SJeremy L Thompson   @ref Backend
883480fae85SJeremy L Thompson **/
884480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataReference(CeedQFunctionAssemblyData data) {
885480fae85SJeremy L Thompson   data->ref_count++;
886480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
887480fae85SJeremy L Thompson }
888480fae85SJeremy L Thompson 
889480fae85SJeremy L Thompson /**
890beecbf24SJeremy L Thompson   @brief Set re-use of CeedQFunctionAssemblyData
8918b919e6bSJeremy L Thompson 
892beecbf24SJeremy L Thompson   @param data       CeedQFunctionAssemblyData to mark for reuse
893beecbf24SJeremy L Thompson   @param reuse_data Boolean flag indicating data re-use
8948b919e6bSJeremy L Thompson 
8958b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
8968b919e6bSJeremy L Thompson 
8978b919e6bSJeremy L Thompson   @ref Backend
8988b919e6bSJeremy L Thompson **/
899*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetReuse(CeedQFunctionAssemblyData data, bool reuse_data) {
900beecbf24SJeremy L Thompson   data->reuse_data        = reuse_data;
901beecbf24SJeremy L Thompson   data->needs_data_update = true;
902beecbf24SJeremy L Thompson   return CEED_ERROR_SUCCESS;
903beecbf24SJeremy L Thompson }
904beecbf24SJeremy L Thompson 
905beecbf24SJeremy L Thompson /**
906beecbf24SJeremy L Thompson   @brief Mark QFunctionAssemblyData as stale
907beecbf24SJeremy L Thompson 
908beecbf24SJeremy L Thompson   @param data              CeedQFunctionAssemblyData to mark as stale
909beecbf24SJeremy L Thompson   @param needs_data_update Boolean flag indicating if update is needed or completed
910beecbf24SJeremy L Thompson 
911beecbf24SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
912beecbf24SJeremy L Thompson 
913beecbf24SJeremy L Thompson   @ref Backend
914beecbf24SJeremy L Thompson **/
915*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetUpdateNeeded(CeedQFunctionAssemblyData data, bool needs_data_update) {
916beecbf24SJeremy L Thompson   data->needs_data_update = needs_data_update;
9178b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
9188b919e6bSJeremy L Thompson }
9198b919e6bSJeremy L Thompson 
9208b919e6bSJeremy L Thompson /**
9218b919e6bSJeremy L Thompson   @brief Determine if QFunctionAssemblyData needs update
9228b919e6bSJeremy L Thompson 
9238b919e6bSJeremy L Thompson   @param[in] data              CeedQFunctionAssemblyData to mark as stale
9248b919e6bSJeremy L Thompson   @param[out] is_update_needed Boolean flag indicating if re-assembly is required
9258b919e6bSJeremy L Thompson 
9268b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
9278b919e6bSJeremy L Thompson 
9288b919e6bSJeremy L Thompson   @ref Backend
9298b919e6bSJeremy L Thompson **/
930*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataIsUpdateNeeded(CeedQFunctionAssemblyData data, bool *is_update_needed) {
931beecbf24SJeremy L Thompson   *is_update_needed = !data->reuse_data || data->needs_data_update;
9328b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
9338b919e6bSJeremy L Thompson }
9348b919e6bSJeremy L Thompson 
9358b919e6bSJeremy L Thompson /**
936480fae85SJeremy L Thompson   @brief Copy the pointer to a CeedQFunctionAssemblyData. Both pointers should
937480fae85SJeremy L Thompson            be destroyed with `CeedCeedQFunctionAssemblyDataDestroy()`;
938480fae85SJeremy L Thompson            Note: If `*data_copy` is non-NULL, then it is assumed that
939480fae85SJeremy L Thompson            `*data_copy` is a pointer to a CeedQFunctionAssemblyData. This
940480fae85SJeremy L Thompson            CeedQFunctionAssemblyData will be destroyed if `*data_copy` is
941480fae85SJeremy L Thompson            the only reference to this CeedQFunctionAssemblyData.
942480fae85SJeremy L Thompson 
943480fae85SJeremy L Thompson   @param data            CeedQFunctionAssemblyData to copy reference to
944480fae85SJeremy L Thompson   @param[out] data_copy  Variable to store copied reference
945480fae85SJeremy L Thompson 
946480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
947480fae85SJeremy L Thompson 
948480fae85SJeremy L Thompson   @ref Backend
949480fae85SJeremy L Thompson **/
950*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataReferenceCopy(CeedQFunctionAssemblyData data, CeedQFunctionAssemblyData *data_copy) {
951*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataReference(data));
952*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataDestroy(data_copy));
953480fae85SJeremy L Thompson   *data_copy = data;
954480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
955480fae85SJeremy L Thompson }
956480fae85SJeremy L Thompson 
957480fae85SJeremy L Thompson /**
958480fae85SJeremy L Thompson   @brief Get setup status for internal objects for CeedQFunctionAssemblyData
959480fae85SJeremy L Thompson 
960480fae85SJeremy L Thompson   @param[in] data      CeedQFunctionAssemblyData to retreive status
961480fae85SJeremy L Thompson   @param[out] is_setup Boolean flag for setup status
962480fae85SJeremy L Thompson 
963480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
964480fae85SJeremy L Thompson 
965480fae85SJeremy L Thompson   @ref Backend
966480fae85SJeremy L Thompson **/
967*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataIsSetup(CeedQFunctionAssemblyData data, bool *is_setup) {
968480fae85SJeremy L Thompson   *is_setup = data->is_setup;
969480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
970480fae85SJeremy L Thompson }
971480fae85SJeremy L Thompson 
972480fae85SJeremy L Thompson /**
973480fae85SJeremy L Thompson   @brief Set internal objects for CeedQFunctionAssemblyData
974480fae85SJeremy L Thompson 
975480fae85SJeremy L Thompson   @param[in] data  CeedQFunctionAssemblyData to set objects
976480fae85SJeremy L Thompson   @param[in] vec   CeedVector to store assembled CeedQFunction at quadrature points
977480fae85SJeremy L Thompson   @param[in] rstr  CeedElemRestriction for CeedVector containing assembled CeedQFunction
978480fae85SJeremy L Thompson 
979480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
980480fae85SJeremy L Thompson 
981480fae85SJeremy L Thompson   @ref Backend
982480fae85SJeremy L Thompson **/
983*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetObjects(CeedQFunctionAssemblyData data, CeedVector vec, CeedElemRestriction rstr) {
984*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorReferenceCopy(vec, &data->vec));
985*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &data->rstr));
986480fae85SJeremy L Thompson 
987480fae85SJeremy L Thompson   data->is_setup = true;
988480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
989480fae85SJeremy L Thompson }
990480fae85SJeremy L Thompson 
991*2b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataGetObjects(CeedQFunctionAssemblyData data, CeedVector *vec, CeedElemRestriction *rstr) {
992*2b730f8bSJeremy L Thompson   if (!data->is_setup) {
993480fae85SJeremy L Thompson     // LCOV_EXCL_START
994*2b730f8bSJeremy L Thompson     return CeedError(data->ceed, CEED_ERROR_INCOMPLETE, "Internal objects not set; must call CeedQFunctionAssemblyDataSetObjects first.");
995480fae85SJeremy L Thompson     // LCOV_EXCL_STOP
996*2b730f8bSJeremy L Thompson   }
997480fae85SJeremy L Thompson 
998*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorReferenceCopy(data->vec, vec));
999*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionReferenceCopy(data->rstr, rstr));
1000480fae85SJeremy L Thompson 
1001480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1002480fae85SJeremy L Thompson }
1003480fae85SJeremy L Thompson 
1004480fae85SJeremy L Thompson /**
1005480fae85SJeremy L Thompson   @brief Destroy CeedQFunctionAssemblyData
1006480fae85SJeremy L Thompson 
1007480fae85SJeremy L Thompson   @param[out] data  CeedQFunctionAssemblyData to destroy
1008480fae85SJeremy L Thompson 
1009480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1010480fae85SJeremy L Thompson 
1011480fae85SJeremy L Thompson   @ref Backend
1012480fae85SJeremy L Thompson **/
1013480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataDestroy(CeedQFunctionAssemblyData *data) {
1014480fae85SJeremy L Thompson   if (!*data || --(*data)->ref_count > 0) return CEED_ERROR_SUCCESS;
1015480fae85SJeremy L Thompson 
1016*2b730f8bSJeremy L Thompson   CeedCall(CeedDestroy(&(*data)->ceed));
1017*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&(*data)->vec));
1018*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&(*data)->rstr));
1019480fae85SJeremy L Thompson 
1020*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(data));
1021480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1022480fae85SJeremy L Thompson }
1023480fae85SJeremy L Thompson 
1024ed9e99e6SJeremy L Thompson /**
1025ed9e99e6SJeremy L Thompson   @brief Get CeedOperatorAssemblyData
1026ed9e99e6SJeremy L Thompson 
1027ed9e99e6SJeremy L Thompson   @param[in] op     CeedOperator to assemble
1028ed9e99e6SJeremy L Thompson   @param[out] data  CeedQFunctionAssemblyData
1029ed9e99e6SJeremy L Thompson 
1030ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1031ed9e99e6SJeremy L Thompson 
1032ed9e99e6SJeremy L Thompson   @ref Backend
1033ed9e99e6SJeremy L Thompson **/
1034*2b730f8bSJeremy L Thompson int CeedOperatorGetOperatorAssemblyData(CeedOperator op, CeedOperatorAssemblyData *data) {
1035ed9e99e6SJeremy L Thompson   if (!op->op_assembled) {
1036ed9e99e6SJeremy L Thompson     CeedOperatorAssemblyData data;
1037ed9e99e6SJeremy L Thompson 
1038*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorAssemblyDataCreate(op->ceed, op, &data));
1039ed9e99e6SJeremy L Thompson     op->op_assembled = data;
1040ed9e99e6SJeremy L Thompson   }
1041ed9e99e6SJeremy L Thompson   *data = op->op_assembled;
1042ed9e99e6SJeremy L Thompson 
1043ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1044ed9e99e6SJeremy L Thompson }
1045ed9e99e6SJeremy L Thompson 
1046ed9e99e6SJeremy L Thompson /**
1047ed9e99e6SJeremy L Thompson   @brief Create object holding CeedOperator assembly data
1048ed9e99e6SJeremy L Thompson 
1049ed9e99e6SJeremy L Thompson   @param[in] ceed   A Ceed object where the CeedOperatorAssemblyData will be created
1050ed9e99e6SJeremy L Thompson   @param[in] op     CeedOperator to be assembled
1051ed9e99e6SJeremy L Thompson   @param[out] data  Address of the variable where the newly created
1052ed9e99e6SJeremy L Thompson                       CeedOperatorAssemblyData will be stored
1053ed9e99e6SJeremy L Thompson 
1054ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1055ed9e99e6SJeremy L Thompson 
1056ed9e99e6SJeremy L Thompson   @ref Backend
1057ed9e99e6SJeremy L Thompson **/
1058*2b730f8bSJeremy L Thompson int CeedOperatorAssemblyDataCreate(Ceed ceed, CeedOperator op, CeedOperatorAssemblyData *data) {
1059*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, data));
1060ed9e99e6SJeremy L Thompson   (*data)->ceed = ceed;
1061*2b730f8bSJeremy L Thompson   CeedCall(CeedReference(ceed));
1062ed9e99e6SJeremy L Thompson 
1063ed9e99e6SJeremy L Thompson   // Build OperatorAssembly data
1064ed9e99e6SJeremy L Thompson   CeedQFunction       qf;
1065ed9e99e6SJeremy L Thompson   CeedQFunctionField *qf_fields;
1066ed9e99e6SJeremy L Thompson   CeedOperatorField  *op_fields;
1067ed9e99e6SJeremy L Thompson   CeedInt             num_input_fields;
1068*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
1069*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, &num_input_fields, &qf_fields, NULL, NULL));
1070*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, NULL, &op_fields, NULL, NULL));
1071ed9e99e6SJeremy L Thompson 
1072ed9e99e6SJeremy L Thompson   // Determine active input basis
1073ed9e99e6SJeremy L Thompson   CeedInt       num_eval_mode_in = 0, dim = 1;
1074ed9e99e6SJeremy L Thompson   CeedEvalMode *eval_mode_in = NULL;
1075ed9e99e6SJeremy L Thompson   CeedBasis     basis_in     = NULL;
1076ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_input_fields; i++) {
1077ed9e99e6SJeremy L Thompson     CeedVector vec;
1078*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
1079ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
1080*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_in));
1081*2b730f8bSJeremy L Thompson       CeedCall(CeedBasisGetDimension(basis_in, &dim));
1082ed9e99e6SJeremy L Thompson       CeedEvalMode eval_mode;
1083*2b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
1084ed9e99e6SJeremy L Thompson       switch (eval_mode) {
1085ed9e99e6SJeremy L Thompson         case CEED_EVAL_NONE:
1086ed9e99e6SJeremy L Thompson         case CEED_EVAL_INTERP:
1087*2b730f8bSJeremy L Thompson           CeedCall(CeedRealloc(num_eval_mode_in + 1, &eval_mode_in));
1088ed9e99e6SJeremy L Thompson           eval_mode_in[num_eval_mode_in] = eval_mode;
1089ed9e99e6SJeremy L Thompson           num_eval_mode_in += 1;
1090ed9e99e6SJeremy L Thompson           break;
1091ed9e99e6SJeremy L Thompson         case CEED_EVAL_GRAD:
1092*2b730f8bSJeremy L Thompson           CeedCall(CeedRealloc(num_eval_mode_in + dim, &eval_mode_in));
1093ed9e99e6SJeremy L Thompson           for (CeedInt d = 0; d < dim; d++) {
1094ed9e99e6SJeremy L Thompson             eval_mode_in[num_eval_mode_in + d] = eval_mode;
1095ed9e99e6SJeremy L Thompson           }
1096ed9e99e6SJeremy L Thompson           num_eval_mode_in += dim;
1097ed9e99e6SJeremy L Thompson           break;
1098ed9e99e6SJeremy L Thompson         case CEED_EVAL_WEIGHT:
1099ed9e99e6SJeremy L Thompson         case CEED_EVAL_DIV:
1100ed9e99e6SJeremy L Thompson         case CEED_EVAL_CURL:
1101ed9e99e6SJeremy L Thompson           break;  // Caught by QF Assembly
1102ed9e99e6SJeremy L Thompson       }
1103ed9e99e6SJeremy L Thompson     }
1104ed9e99e6SJeremy L Thompson   }
1105ed9e99e6SJeremy L Thompson   (*data)->num_eval_mode_in = num_eval_mode_in;
1106ed9e99e6SJeremy L Thompson   (*data)->eval_mode_in     = eval_mode_in;
1107*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisReferenceCopy(basis_in, &(*data)->basis_in));
1108ed9e99e6SJeremy L Thompson 
1109ed9e99e6SJeremy L Thompson   // Determine active output basis
1110ed9e99e6SJeremy L Thompson   CeedInt num_output_fields;
1111*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, NULL, NULL, &num_output_fields, &qf_fields));
1112*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, NULL, NULL, NULL, &op_fields));
1113ed9e99e6SJeremy L Thompson   CeedInt       num_eval_mode_out = 0;
1114ed9e99e6SJeremy L Thompson   CeedEvalMode *eval_mode_out     = NULL;
1115ed9e99e6SJeremy L Thompson   CeedBasis     basis_out         = NULL;
1116ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_output_fields; i++) {
1117ed9e99e6SJeremy L Thompson     CeedVector vec;
1118*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
1119ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
1120*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_out));
1121ed9e99e6SJeremy L Thompson       CeedEvalMode eval_mode;
1122*2b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
1123ed9e99e6SJeremy L Thompson       switch (eval_mode) {
1124ed9e99e6SJeremy L Thompson         case CEED_EVAL_NONE:
1125ed9e99e6SJeremy L Thompson         case CEED_EVAL_INTERP:
1126*2b730f8bSJeremy L Thompson           CeedCall(CeedRealloc(num_eval_mode_out + 1, &eval_mode_out));
1127ed9e99e6SJeremy L Thompson           eval_mode_out[num_eval_mode_out] = eval_mode;
1128ed9e99e6SJeremy L Thompson           num_eval_mode_out += 1;
1129ed9e99e6SJeremy L Thompson           break;
1130ed9e99e6SJeremy L Thompson         case CEED_EVAL_GRAD:
1131*2b730f8bSJeremy L Thompson           CeedCall(CeedRealloc(num_eval_mode_out + dim, &eval_mode_out));
1132ed9e99e6SJeremy L Thompson           for (CeedInt d = 0; d < dim; d++) {
1133ed9e99e6SJeremy L Thompson             eval_mode_out[num_eval_mode_out + d] = eval_mode;
1134ed9e99e6SJeremy L Thompson           }
1135ed9e99e6SJeremy L Thompson           num_eval_mode_out += dim;
1136ed9e99e6SJeremy L Thompson           break;
1137ed9e99e6SJeremy L Thompson         case CEED_EVAL_WEIGHT:
1138ed9e99e6SJeremy L Thompson         case CEED_EVAL_DIV:
1139ed9e99e6SJeremy L Thompson         case CEED_EVAL_CURL:
1140ed9e99e6SJeremy L Thompson           break;  // Caught by QF Assembly
1141ed9e99e6SJeremy L Thompson       }
1142ed9e99e6SJeremy L Thompson     }
1143ed9e99e6SJeremy L Thompson   }
1144ed9e99e6SJeremy L Thompson   (*data)->num_eval_mode_out = num_eval_mode_out;
1145ed9e99e6SJeremy L Thompson   (*data)->eval_mode_out     = eval_mode_out;
1146*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisReferenceCopy(basis_out, &(*data)->basis_out));
1147ed9e99e6SJeremy L Thompson 
1148ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1149ed9e99e6SJeremy L Thompson }
1150ed9e99e6SJeremy L Thompson 
1151ed9e99e6SJeremy L Thompson /**
1152ed9e99e6SJeremy L Thompson   @brief Get CeedOperator CeedEvalModes for assembly
1153ed9e99e6SJeremy L Thompson 
1154ed9e99e6SJeremy L Thompson   @param[in] data                CeedOperatorAssemblyData
1155ed9e99e6SJeremy L Thompson   @param[out] num_eval_mode_in   Pointer to hold number of input CeedEvalModes, or NULL
1156ed9e99e6SJeremy L Thompson   @param[out] eval_mode_in       Pointer to hold input CeedEvalModes, or NULL
1157ed9e99e6SJeremy L Thompson   @param[out] num_eval_mode_out  Pointer to hold number of output CeedEvalModes, or NULL
1158ed9e99e6SJeremy L Thompson   @param[out] eval_mode_out      Pointer to hold output CeedEvalModes, or NULL
1159ed9e99e6SJeremy L Thompson 
1160ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1161ed9e99e6SJeremy L Thompson 
1162ed9e99e6SJeremy L Thompson   @ref Backend
1163ed9e99e6SJeremy L Thompson **/
1164*2b730f8bSJeremy L Thompson int CeedOperatorAssemblyDataGetEvalModes(CeedOperatorAssemblyData data, CeedInt *num_eval_mode_in, const CeedEvalMode **eval_mode_in,
1165ed9e99e6SJeremy L Thompson                                          CeedInt *num_eval_mode_out, const CeedEvalMode **eval_mode_out) {
1166ed9e99e6SJeremy L Thompson   if (num_eval_mode_in) *num_eval_mode_in = data->num_eval_mode_in;
1167ed9e99e6SJeremy L Thompson   if (eval_mode_in) *eval_mode_in = data->eval_mode_in;
1168ed9e99e6SJeremy L Thompson   if (num_eval_mode_out) *num_eval_mode_out = data->num_eval_mode_out;
1169ed9e99e6SJeremy L Thompson   if (eval_mode_out) *eval_mode_out = data->eval_mode_out;
1170ed9e99e6SJeremy L Thompson 
1171ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1172ed9e99e6SJeremy L Thompson }
1173ed9e99e6SJeremy L Thompson 
1174ed9e99e6SJeremy L Thompson /**
1175ed9e99e6SJeremy L Thompson   @brief Get CeedOperator CeedBasis data for assembly
1176ed9e99e6SJeremy L Thompson 
1177ed9e99e6SJeremy L Thompson   @param[in] data        CeedOperatorAssemblyData
1178ed9e99e6SJeremy L Thompson   @param[out] basis_in   Pointer to hold active input CeedBasis, or NULL
1179ed9e99e6SJeremy L Thompson   @param[out] B_in       Pointer to hold assembled active input B, or NULL
1180ed9e99e6SJeremy L Thompson   @param[out] basis_out  Pointer to hold active output CeedBasis, or NULL
1181ed9e99e6SJeremy L Thompson   @param[out] B_out      Pointer to hold assembled active output B, or NULL
1182ed9e99e6SJeremy L Thompson 
1183ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1184ed9e99e6SJeremy L Thompson 
1185ed9e99e6SJeremy L Thompson   @ref Backend
1186ed9e99e6SJeremy L Thompson **/
1187*2b730f8bSJeremy L Thompson int CeedOperatorAssemblyDataGetBases(CeedOperatorAssemblyData data, CeedBasis *basis_in, const CeedScalar **B_in, CeedBasis *basis_out,
1188ed9e99e6SJeremy L Thompson                                      const CeedScalar **B_out) {
1189ed9e99e6SJeremy L Thompson   // Assemble B_in, B_out if needed
1190ed9e99e6SJeremy L Thompson   if (B_in && !data->B_in) {
1191ed9e99e6SJeremy L Thompson     CeedInt           num_qpts, elem_size;
1192ed9e99e6SJeremy L Thompson     CeedScalar       *B_in, *identity = NULL;
1193ed9e99e6SJeremy L Thompson     const CeedScalar *interp_in, *grad_in;
1194ed9e99e6SJeremy L Thompson     bool              has_eval_none = false;
1195ed9e99e6SJeremy L Thompson 
1196*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumQuadraturePoints(data->basis_in, &num_qpts));
1197*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumNodes(data->basis_in, &elem_size));
1198*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_qpts * elem_size * data->num_eval_mode_in, &B_in));
1199ed9e99e6SJeremy L Thompson 
1200ed9e99e6SJeremy L Thompson     for (CeedInt i = 0; i < data->num_eval_mode_in; i++) {
1201ed9e99e6SJeremy L Thompson       has_eval_none = has_eval_none || (data->eval_mode_in[i] == CEED_EVAL_NONE);
1202ed9e99e6SJeremy L Thompson     }
1203ed9e99e6SJeremy L Thompson     if (has_eval_none) {
1204*2b730f8bSJeremy L Thompson       CeedCall(CeedCalloc(num_qpts * elem_size, &identity));
1205ed9e99e6SJeremy L Thompson       for (CeedInt i = 0; i < (elem_size < num_qpts ? elem_size : num_qpts); i++) {
1206ed9e99e6SJeremy L Thompson         identity[i * elem_size + i] = 1.0;
1207ed9e99e6SJeremy L Thompson       }
1208ed9e99e6SJeremy L Thompson     }
1209*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetInterp(data->basis_in, &interp_in));
1210*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetGrad(data->basis_in, &grad_in));
1211ed9e99e6SJeremy L Thompson 
1212ed9e99e6SJeremy L Thompson     for (CeedInt q = 0; q < num_qpts; q++) {
1213ed9e99e6SJeremy L Thompson       for (CeedInt n = 0; n < elem_size; n++) {
1214ed9e99e6SJeremy L Thompson         CeedInt d_in = -1;
1215ed9e99e6SJeremy L Thompson         for (CeedInt e_in = 0; e_in < data->num_eval_mode_in; e_in++) {
1216ed9e99e6SJeremy L Thompson           const CeedInt     qq = data->num_eval_mode_in * q;
1217ed9e99e6SJeremy L Thompson           const CeedScalar *b  = NULL;
1218ed9e99e6SJeremy L Thompson 
1219ed9e99e6SJeremy L Thompson           if (data->eval_mode_in[e_in] == CEED_EVAL_GRAD) d_in++;
1220*2b730f8bSJeremy L Thompson           CeedOperatorGetBasisPointer(data->eval_mode_in[e_in], identity, interp_in, &grad_in[d_in * num_qpts * elem_size], &b);
1221ed9e99e6SJeremy L Thompson           B_in[(qq + e_in) * elem_size + n] = b[q * elem_size + n];
1222ed9e99e6SJeremy L Thompson         }
1223ed9e99e6SJeremy L Thompson       }
1224ed9e99e6SJeremy L Thompson     }
1225ed9e99e6SJeremy L Thompson     data->B_in = B_in;
1226ed9e99e6SJeremy L Thompson   }
1227ed9e99e6SJeremy L Thompson 
1228ed9e99e6SJeremy L Thompson   if (B_out && !data->B_out) {
1229ed9e99e6SJeremy L Thompson     CeedInt           num_qpts, elem_size;
1230ed9e99e6SJeremy L Thompson     CeedScalar       *B_out, *identity = NULL;
1231ed9e99e6SJeremy L Thompson     const CeedScalar *interp_out, *grad_out;
1232ed9e99e6SJeremy L Thompson     bool              has_eval_none = false;
1233ed9e99e6SJeremy L Thompson 
1234*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumQuadraturePoints(data->basis_out, &num_qpts));
1235*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumNodes(data->basis_out, &elem_size));
1236*2b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_qpts * elem_size * data->num_eval_mode_out, &B_out));
1237ed9e99e6SJeremy L Thompson 
1238ed9e99e6SJeremy L Thompson     for (CeedInt i = 0; i < data->num_eval_mode_out; i++) {
1239ed9e99e6SJeremy L Thompson       has_eval_none = has_eval_none || (data->eval_mode_out[i] == CEED_EVAL_NONE);
1240ed9e99e6SJeremy L Thompson     }
1241ed9e99e6SJeremy L Thompson     if (has_eval_none) {
1242*2b730f8bSJeremy L Thompson       CeedCall(CeedCalloc(num_qpts * elem_size, &identity));
1243ed9e99e6SJeremy L Thompson       for (CeedInt i = 0; i < (elem_size < num_qpts ? elem_size : num_qpts); i++) {
1244ed9e99e6SJeremy L Thompson         identity[i * elem_size + i] = 1.0;
1245ed9e99e6SJeremy L Thompson       }
1246ed9e99e6SJeremy L Thompson     }
1247*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetInterp(data->basis_out, &interp_out));
1248*2b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetGrad(data->basis_out, &grad_out));
1249ed9e99e6SJeremy L Thompson 
1250ed9e99e6SJeremy L Thompson     for (CeedInt q = 0; q < num_qpts; q++) {
1251ed9e99e6SJeremy L Thompson       for (CeedInt n = 0; n < elem_size; n++) {
1252ed9e99e6SJeremy L Thompson         CeedInt d_out = -1;
1253ed9e99e6SJeremy L Thompson         for (CeedInt e_out = 0; e_out < data->num_eval_mode_out; e_out++) {
1254ed9e99e6SJeremy L Thompson           const CeedInt     qq = data->num_eval_mode_out * q;
1255ed9e99e6SJeremy L Thompson           const CeedScalar *b  = NULL;
1256ed9e99e6SJeremy L Thompson 
1257ed9e99e6SJeremy L Thompson           if (data->eval_mode_out[e_out] == CEED_EVAL_GRAD) d_out++;
1258*2b730f8bSJeremy L Thompson           CeedOperatorGetBasisPointer(data->eval_mode_out[e_out], identity, interp_out, &grad_out[d_out * num_qpts * elem_size], &b);
1259ed9e99e6SJeremy L Thompson           B_out[(qq + e_out) * elem_size + n] = b[q * elem_size + n];
1260ed9e99e6SJeremy L Thompson         }
1261ed9e99e6SJeremy L Thompson       }
1262ed9e99e6SJeremy L Thompson     }
1263ed9e99e6SJeremy L Thompson     data->B_out = B_out;
1264ed9e99e6SJeremy L Thompson   }
1265ed9e99e6SJeremy L Thompson 
1266ed9e99e6SJeremy L Thompson   if (basis_in) *basis_in = data->basis_in;
1267ed9e99e6SJeremy L Thompson   if (B_in) *B_in = data->B_in;
1268ed9e99e6SJeremy L Thompson   if (basis_out) *basis_out = data->basis_out;
1269ed9e99e6SJeremy L Thompson   if (B_out) *B_out = data->B_out;
1270ed9e99e6SJeremy L Thompson 
1271ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1272ed9e99e6SJeremy L Thompson }
1273ed9e99e6SJeremy L Thompson 
1274ed9e99e6SJeremy L Thompson /**
1275ed9e99e6SJeremy L Thompson   @brief Destroy CeedOperatorAssemblyData
1276ed9e99e6SJeremy L Thompson 
1277ed9e99e6SJeremy L Thompson   @param[out] data  CeedOperatorAssemblyData to destroy
1278ed9e99e6SJeremy L Thompson 
1279ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1280ed9e99e6SJeremy L Thompson 
1281ed9e99e6SJeremy L Thompson   @ref Backend
1282ed9e99e6SJeremy L Thompson **/
1283ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataDestroy(CeedOperatorAssemblyData *data) {
1284ed9e99e6SJeremy L Thompson   if (!*data) return CEED_ERROR_SUCCESS;
1285ed9e99e6SJeremy L Thompson 
1286*2b730f8bSJeremy L Thompson   CeedCall(CeedDestroy(&(*data)->ceed));
1287*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&(*data)->basis_in));
1288*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&(*data)->basis_out));
1289*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_mode_in));
1290*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_mode_out));
1291*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&(*data)->B_in));
1292*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&(*data)->B_out));
1293ed9e99e6SJeremy L Thompson 
1294*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(data));
1295ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1296ed9e99e6SJeremy L Thompson }
1297ed9e99e6SJeremy L Thompson 
1298480fae85SJeremy L Thompson /// @}
1299480fae85SJeremy L Thompson 
1300480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
1301eaf62fffSJeremy L Thompson /// CeedOperator Public API
1302eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
1303eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorUser
1304eaf62fffSJeremy L Thompson /// @{
1305eaf62fffSJeremy L Thompson 
1306eaf62fffSJeremy L Thompson /**
1307eaf62fffSJeremy L Thompson   @brief Assemble a linear CeedQFunction associated with a CeedOperator
1308eaf62fffSJeremy L Thompson 
1309eaf62fffSJeremy L Thompson   This returns a CeedVector containing a matrix at each quadrature point
1310eaf62fffSJeremy L Thompson     providing the action of the CeedQFunction associated with the CeedOperator.
1311eaf62fffSJeremy L Thompson     The vector 'assembled' is of shape
1312eaf62fffSJeremy L Thompson       [num_elements, num_input_fields, num_output_fields, num_quad_points]
1313eaf62fffSJeremy L Thompson     and contains column-major matrices representing the action of the
1314eaf62fffSJeremy L Thompson     CeedQFunction for a corresponding quadrature point on an element. Inputs and
1315eaf62fffSJeremy L Thompson     outputs are in the order provided by the user when adding CeedOperator fields.
1316eaf62fffSJeremy L Thompson     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
1317eaf62fffSJeremy L Thompson     'v', provided in that order, would result in an assembled QFunction that
1318eaf62fffSJeremy L Thompson     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
1319eaf62fffSJeremy L Thompson     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
1320eaf62fffSJeremy L Thompson 
1321f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1322f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1323f04ea552SJeremy L Thompson 
1324eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1325eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedQFunction at
1326eaf62fffSJeremy L Thompson                            quadrature points
1327eaf62fffSJeremy L Thompson   @param[out] rstr       CeedElemRestriction for CeedVector containing assembled
1328eaf62fffSJeremy L Thompson                            CeedQFunction
1329eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1330eaf62fffSJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
1331eaf62fffSJeremy L Thompson 
1332eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1333eaf62fffSJeremy L Thompson 
1334eaf62fffSJeremy L Thompson   @ref User
1335eaf62fffSJeremy L Thompson **/
1336*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
1337*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1338eaf62fffSJeremy L Thompson 
1339eaf62fffSJeremy L Thompson   if (op->LinearAssembleQFunction) {
1340d04bbc78SJeremy L Thompson     // Backend version
1341*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleQFunction(op, assembled, rstr, request));
1342eaf62fffSJeremy L Thompson   } else {
1343d04bbc78SJeremy L Thompson     // Operator fallback
1344d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1345d04bbc78SJeremy L Thompson 
1346*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1347d04bbc78SJeremy L Thompson     if (op_fallback) {
1348*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleQFunction(op_fallback, assembled, rstr, request));
1349d04bbc78SJeremy L Thompson     } else {
1350d04bbc78SJeremy L Thompson       // LCOV_EXCL_START
1351*2b730f8bSJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunction");
1352d04bbc78SJeremy L Thompson       // LCOV_EXCL_STOP
1353d04bbc78SJeremy L Thompson     }
135470a7ffb3SJeremy L Thompson   }
1355eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1356eaf62fffSJeremy L Thompson }
135770a7ffb3SJeremy L Thompson 
135870a7ffb3SJeremy L Thompson /**
135970a7ffb3SJeremy L Thompson   @brief Assemble CeedQFunction and store result internall. Return copied
136070a7ffb3SJeremy L Thompson            references of stored data to the caller. Caller is responsible for
136170a7ffb3SJeremy L Thompson            ownership and destruction of the copied references. See also
136270a7ffb3SJeremy L Thompson            @ref CeedOperatorLinearAssembleQFunction
136370a7ffb3SJeremy L Thompson 
136470a7ffb3SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
136570a7ffb3SJeremy L Thompson   @param assembled       CeedVector to store assembled CeedQFunction at
136670a7ffb3SJeremy L Thompson                            quadrature points
136770a7ffb3SJeremy L Thompson   @param rstr            CeedElemRestriction for CeedVector containing assembled
136870a7ffb3SJeremy L Thompson                            CeedQFunction
136970a7ffb3SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
137070a7ffb3SJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
137170a7ffb3SJeremy L Thompson 
137270a7ffb3SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
137370a7ffb3SJeremy L Thompson 
137470a7ffb3SJeremy L Thompson   @ref User
137570a7ffb3SJeremy L Thompson **/
1376*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleQFunctionBuildOrUpdate(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
1377*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
137870a7ffb3SJeremy L Thompson 
137970a7ffb3SJeremy L Thompson   if (op->LinearAssembleQFunctionUpdate) {
1380d04bbc78SJeremy L Thompson     // Backend version
1381480fae85SJeremy L Thompson     bool                qf_assembled_is_setup;
13822efa2d85SJeremy L Thompson     CeedVector          assembled_vec  = NULL;
13832efa2d85SJeremy L Thompson     CeedElemRestriction assembled_rstr = NULL;
1384480fae85SJeremy L Thompson 
1385*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataIsSetup(op->qf_assembled, &qf_assembled_is_setup));
1386480fae85SJeremy L Thompson     if (qf_assembled_is_setup) {
1387d04bbc78SJeremy L Thompson       bool update_needed;
1388d04bbc78SJeremy L Thompson 
1389*2b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataGetObjects(op->qf_assembled, &assembled_vec, &assembled_rstr));
1390*2b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataIsUpdateNeeded(op->qf_assembled, &update_needed));
13918b919e6bSJeremy L Thompson       if (update_needed) {
1392*2b730f8bSJeremy L Thompson         CeedCall(op->LinearAssembleQFunctionUpdate(op, assembled_vec, assembled_rstr, request));
13938b919e6bSJeremy L Thompson       }
139470a7ffb3SJeremy L Thompson     } else {
1395*2b730f8bSJeremy L Thompson       CeedCall(op->LinearAssembleQFunction(op, &assembled_vec, &assembled_rstr, request));
1396*2b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataSetObjects(op->qf_assembled, assembled_vec, assembled_rstr));
139770a7ffb3SJeremy L Thompson     }
1398*2b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, false));
13992efa2d85SJeremy L Thompson 
1400d04bbc78SJeremy L Thompson     // Copy reference from internally held copy
140170a7ffb3SJeremy L Thompson     *assembled = NULL;
140270a7ffb3SJeremy L Thompson     *rstr      = NULL;
1403*2b730f8bSJeremy L Thompson     CeedCall(CeedVectorReferenceCopy(assembled_vec, assembled));
1404*2b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&assembled_vec));
1405*2b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionReferenceCopy(assembled_rstr, rstr));
1406*2b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionDestroy(&assembled_rstr));
140770a7ffb3SJeremy L Thompson   } else {
1408d04bbc78SJeremy L Thompson     // Operator fallback
1409d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1410d04bbc78SJeremy L Thompson 
1411*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1412d04bbc78SJeremy L Thompson     if (op_fallback) {
1413*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op_fallback, assembled, rstr, request));
1414d04bbc78SJeremy L Thompson     } else {
1415d04bbc78SJeremy L Thompson       // LCOV_EXCL_START
1416*2b730f8bSJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunctionUpdate");
1417d04bbc78SJeremy L Thompson       // LCOV_EXCL_STOP
141870a7ffb3SJeremy L Thompson     }
141970a7ffb3SJeremy L Thompson   }
142070a7ffb3SJeremy L Thompson 
142170a7ffb3SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1422eaf62fffSJeremy L Thompson }
1423eaf62fffSJeremy L Thompson 
1424eaf62fffSJeremy L Thompson /**
1425eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1426eaf62fffSJeremy L Thompson 
1427eaf62fffSJeremy L Thompson   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1428eaf62fffSJeremy L Thompson 
1429eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1430eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1431eaf62fffSJeremy L Thompson 
1432f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1433f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1434f04ea552SJeremy L Thompson 
1435eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1436eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
1437eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1438eaf62fffSJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
1439eaf62fffSJeremy L Thompson 
1440eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1441eaf62fffSJeremy L Thompson 
1442eaf62fffSJeremy L Thompson   @ref User
1443eaf62fffSJeremy L Thompson **/
1444*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1445*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1446eaf62fffSJeremy L Thompson 
1447c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1448*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
1449*2b730f8bSJeremy L Thompson   if (input_size != output_size) {
1450c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1451c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1452c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
1453*2b730f8bSJeremy L Thompson   }
1454c9366a6bSJeremy L Thompson 
1455eaf62fffSJeremy L Thompson   if (op->LinearAssembleDiagonal) {
1456d04bbc78SJeremy L Thompson     // Backend version
1457*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleDiagonal(op, assembled, request));
1458eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1459eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddDiagonal) {
1460d04bbc78SJeremy L Thompson     // Backend version with zeroing first
1461*2b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(assembled, 0.0));
1462*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request));
1463eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1464eaf62fffSJeremy L Thompson   } else {
1465d04bbc78SJeremy L Thompson     // Operator fallback
1466d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1467d04bbc78SJeremy L Thompson 
1468*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1469d04bbc78SJeremy L Thompson     if (op_fallback) {
1470*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleDiagonal(op_fallback, assembled, request));
1471eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1472eaf62fffSJeremy L Thompson     }
1473eaf62fffSJeremy L Thompson   }
1474eaf62fffSJeremy L Thompson   // Default interface implementation
1475*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(assembled, 0.0));
1476*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleAddDiagonal(op, assembled, request));
1477d04bbc78SJeremy L Thompson 
1478eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1479eaf62fffSJeremy L Thompson }
1480eaf62fffSJeremy L Thompson 
1481eaf62fffSJeremy L Thompson /**
1482eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1483eaf62fffSJeremy L Thompson 
1484eaf62fffSJeremy L Thompson   This sums into a CeedVector the diagonal of a linear CeedOperator.
1485eaf62fffSJeremy L Thompson 
1486eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1487eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1488eaf62fffSJeremy L Thompson 
1489f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1490f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1491f04ea552SJeremy L Thompson 
1492eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1493eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
1494eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1495eaf62fffSJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
1496eaf62fffSJeremy L Thompson 
1497eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1498eaf62fffSJeremy L Thompson 
1499eaf62fffSJeremy L Thompson   @ref User
1500eaf62fffSJeremy L Thompson **/
1501*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1502*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1503eaf62fffSJeremy L Thompson 
1504c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1505*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
1506*2b730f8bSJeremy L Thompson   if (input_size != output_size) {
1507c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1508c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1509c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
1510*2b730f8bSJeremy L Thompson   }
1511c9366a6bSJeremy L Thompson 
1512eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddDiagonal) {
1513d04bbc78SJeremy L Thompson     // Backend version
1514*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request));
1515eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1516eaf62fffSJeremy L Thompson   } else {
1517d04bbc78SJeremy L Thompson     // Operator fallback
1518d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1519d04bbc78SJeremy L Thompson 
1520*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1521d04bbc78SJeremy L Thompson     if (op_fallback) {
1522*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddDiagonal(op_fallback, assembled, request));
1523eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1524eaf62fffSJeremy L Thompson     }
1525eaf62fffSJeremy L Thompson   }
1526eaf62fffSJeremy L Thompson   // Default interface implementation
1527eaf62fffSJeremy L Thompson   bool is_composite;
1528*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1529eaf62fffSJeremy L Thompson   if (is_composite) {
1530*2b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, false, assembled));
1531eaf62fffSJeremy L Thompson   } else {
1532*2b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, false, assembled));
1533eaf62fffSJeremy L Thompson   }
1534d04bbc78SJeremy L Thompson 
1535d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1536eaf62fffSJeremy L Thompson }
1537eaf62fffSJeremy L Thompson 
1538eaf62fffSJeremy L Thompson /**
1539eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1540eaf62fffSJeremy L Thompson 
1541eaf62fffSJeremy L Thompson   This overwrites a CeedVector with the point block diagonal of a linear
1542eaf62fffSJeremy L Thompson     CeedOperator.
1543eaf62fffSJeremy L Thompson 
1544eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1545eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1546eaf62fffSJeremy L Thompson 
1547f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1548f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1549f04ea552SJeremy L Thompson 
1550eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1551eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
1552eaf62fffSJeremy L Thompson                            diagonal, provided in row-major form with an
1553eaf62fffSJeremy L Thompson                            @a num_comp * @a num_comp block at each node. The dimensions
1554eaf62fffSJeremy L Thompson                            of this vector are derived from the active vector
1555eaf62fffSJeremy L Thompson                            for the CeedOperator. The array has shape
1556eaf62fffSJeremy L Thompson                            [nodes, component out, component in].
1557eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1558eaf62fffSJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
1559eaf62fffSJeremy L Thompson 
1560eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1561eaf62fffSJeremy L Thompson 
1562eaf62fffSJeremy L Thompson   @ref User
1563eaf62fffSJeremy L Thompson **/
1564*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1565*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1566eaf62fffSJeremy L Thompson 
1567c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1568*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
1569*2b730f8bSJeremy L Thompson   if (input_size != output_size) {
1570c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1571c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1572c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
1573*2b730f8bSJeremy L Thompson   }
1574c9366a6bSJeremy L Thompson 
1575eaf62fffSJeremy L Thompson   if (op->LinearAssemblePointBlockDiagonal) {
1576d04bbc78SJeremy L Thompson     // Backend version
1577*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssemblePointBlockDiagonal(op, assembled, request));
1578eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1579eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddPointBlockDiagonal) {
1580d04bbc78SJeremy L Thompson     // Backend version with zeroing first
1581*2b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(assembled, 0.0));
1582*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request));
1583eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1584eaf62fffSJeremy L Thompson   } else {
1585d04bbc78SJeremy L Thompson     // Operator fallback
1586d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1587d04bbc78SJeremy L Thompson 
1588*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1589d04bbc78SJeremy L Thompson     if (op_fallback) {
1590*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssemblePointBlockDiagonal(op_fallback, assembled, request));
1591eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1592eaf62fffSJeremy L Thompson     }
1593eaf62fffSJeremy L Thompson   }
1594eaf62fffSJeremy L Thompson   // Default interface implementation
1595*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(assembled, 0.0));
1596*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request));
1597d04bbc78SJeremy L Thompson 
1598eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1599eaf62fffSJeremy L Thompson }
1600eaf62fffSJeremy L Thompson 
1601eaf62fffSJeremy L Thompson /**
1602eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1603eaf62fffSJeremy L Thompson 
1604eaf62fffSJeremy L Thompson   This sums into a CeedVector with the point block diagonal of a linear
1605eaf62fffSJeremy L Thompson     CeedOperator.
1606eaf62fffSJeremy L Thompson 
1607eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1608eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1609eaf62fffSJeremy L Thompson 
1610f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1611f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1612f04ea552SJeremy L Thompson 
1613eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1614eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
1615eaf62fffSJeremy L Thompson                            diagonal, provided in row-major form with an
1616eaf62fffSJeremy L Thompson                            @a num_comp * @a num_comp block at each node. The dimensions
1617eaf62fffSJeremy L Thompson                            of this vector are derived from the active vector
1618eaf62fffSJeremy L Thompson                            for the CeedOperator. The array has shape
1619eaf62fffSJeremy L Thompson                            [nodes, component out, component in].
1620eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1621eaf62fffSJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
1622eaf62fffSJeremy L Thompson 
1623eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1624eaf62fffSJeremy L Thompson 
1625eaf62fffSJeremy L Thompson   @ref User
1626eaf62fffSJeremy L Thompson **/
1627*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1628*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1629eaf62fffSJeremy L Thompson 
1630c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1631*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
1632*2b730f8bSJeremy L Thompson   if (input_size != output_size) {
1633c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1634c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1635c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
1636*2b730f8bSJeremy L Thompson   }
1637c9366a6bSJeremy L Thompson 
1638eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddPointBlockDiagonal) {
1639d04bbc78SJeremy L Thompson     // Backend version
1640*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddPointBlockDiagonal(op, assembled, request));
1641eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1642eaf62fffSJeremy L Thompson   } else {
1643d04bbc78SJeremy L Thompson     // Operator fallback
1644d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1645d04bbc78SJeremy L Thompson 
1646*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1647d04bbc78SJeremy L Thompson     if (op_fallback) {
1648*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op_fallback, assembled, request));
1649eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1650eaf62fffSJeremy L Thompson     }
1651eaf62fffSJeremy L Thompson   }
1652eaf62fffSJeremy L Thompson   // Default interface implemenation
1653eaf62fffSJeremy L Thompson   bool is_composite;
1654*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1655eaf62fffSJeremy L Thompson   if (is_composite) {
1656*2b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, true, assembled));
1657eaf62fffSJeremy L Thompson   } else {
1658*2b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, true, assembled));
1659eaf62fffSJeremy L Thompson   }
1660d04bbc78SJeremy L Thompson 
1661d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1662eaf62fffSJeremy L Thompson }
1663eaf62fffSJeremy L Thompson 
1664eaf62fffSJeremy L Thompson /**
1665eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero pattern of a linear operator.
1666eaf62fffSJeremy L Thompson 
1667eaf62fffSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssemble()
1668eaf62fffSJeremy L Thompson 
1669eaf62fffSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the
1670eaf62fffSJeremy L Thompson    form (i, j, value) which indicate that value should be added to the matrix
1671eaf62fffSJeremy L Thompson    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1672eaf62fffSJeremy L Thompson    This function returns the number of entries and their (i, j) locations,
1673eaf62fffSJeremy L Thompson    while CeedOperatorLinearAssemble() provides the values in the same
1674eaf62fffSJeremy L Thompson    ordering.
1675eaf62fffSJeremy L Thompson 
1676eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
1677eaf62fffSJeremy L Thompson 
1678f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1679f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1680f04ea552SJeremy L Thompson 
1681eaf62fffSJeremy L Thompson    @param[in]  op           CeedOperator to assemble
1682eaf62fffSJeremy L Thompson    @param[out] num_entries  Number of entries in coordinate nonzero pattern
1683eaf62fffSJeremy L Thompson    @param[out] rows         Row number for each entry
1684eaf62fffSJeremy L Thompson    @param[out] cols         Column number for each entry
1685eaf62fffSJeremy L Thompson 
1686eaf62fffSJeremy L Thompson    @ref User
1687eaf62fffSJeremy L Thompson **/
1688*2b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleSymbolic(CeedOperator op, CeedSize *num_entries, CeedInt **rows, CeedInt **cols) {
1689eaf62fffSJeremy L Thompson   CeedInt       num_suboperators, single_entries;
1690eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
1691eaf62fffSJeremy L Thompson   bool          is_composite;
1692*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1693eaf62fffSJeremy L Thompson 
1694eaf62fffSJeremy L Thompson   if (op->LinearAssembleSymbolic) {
1695d04bbc78SJeremy L Thompson     // Backend version
1696*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleSymbolic(op, num_entries, rows, cols));
1697eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1698eaf62fffSJeremy L Thompson   } else {
1699d04bbc78SJeremy L Thompson     // Operator fallback
1700d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1701d04bbc78SJeremy L Thompson 
1702*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1703d04bbc78SJeremy L Thompson     if (op_fallback) {
1704*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleSymbolic(op_fallback, num_entries, rows, cols));
1705eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1706eaf62fffSJeremy L Thompson     }
1707eaf62fffSJeremy L Thompson   }
1708eaf62fffSJeremy L Thompson 
1709eaf62fffSJeremy L Thompson   // Default interface implementation
1710eaf62fffSJeremy L Thompson 
1711eaf62fffSJeremy L Thompson   // count entries and allocate rows, cols arrays
1712*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1713eaf62fffSJeremy L Thompson   *num_entries = 0;
1714eaf62fffSJeremy L Thompson   if (is_composite) {
1715*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetNumSub(op, &num_suboperators));
1716*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetSubList(op, &sub_operators));
171792ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
1718*2b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
1719eaf62fffSJeremy L Thompson       *num_entries += single_entries;
1720eaf62fffSJeremy L Thompson     }
1721eaf62fffSJeremy L Thompson   } else {
1722*2b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssemblyCountEntries(op, &single_entries));
1723eaf62fffSJeremy L Thompson     *num_entries += single_entries;
1724eaf62fffSJeremy L Thompson   }
1725*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(*num_entries, rows));
1726*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(*num_entries, cols));
1727eaf62fffSJeremy L Thompson 
1728eaf62fffSJeremy L Thompson   // assemble nonzero locations
1729eaf62fffSJeremy L Thompson   CeedInt offset = 0;
1730eaf62fffSJeremy L Thompson   if (is_composite) {
1731*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetNumSub(op, &num_suboperators));
1732*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetSubList(op, &sub_operators));
173392ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
1734*2b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows, *cols));
1735*2b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
1736eaf62fffSJeremy L Thompson       offset += single_entries;
1737eaf62fffSJeremy L Thompson     }
1738eaf62fffSJeremy L Thompson   } else {
1739*2b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols));
1740eaf62fffSJeremy L Thompson   }
1741eaf62fffSJeremy L Thompson 
1742eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1743eaf62fffSJeremy L Thompson }
1744eaf62fffSJeremy L Thompson 
1745eaf62fffSJeremy L Thompson /**
1746eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero entries of a linear operator.
1747eaf62fffSJeremy L Thompson 
1748eaf62fffSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic()
1749eaf62fffSJeremy L Thompson 
1750eaf62fffSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the
1751eaf62fffSJeremy L Thompson    form (i, j, value) which indicate that value should be added to the matrix
1752eaf62fffSJeremy L Thompson    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1753eaf62fffSJeremy L Thompson    This function returns the values of the nonzero entries to be added, their
1754eaf62fffSJeremy L Thompson    (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic()
1755eaf62fffSJeremy L Thompson 
1756eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
1757eaf62fffSJeremy L Thompson 
1758f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1759f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1760f04ea552SJeremy L Thompson 
1761eaf62fffSJeremy L Thompson    @param[in]  op      CeedOperator to assemble
1762eaf62fffSJeremy L Thompson    @param[out] values  Values to assemble into matrix
1763eaf62fffSJeremy L Thompson 
1764eaf62fffSJeremy L Thompson    @ref User
1765eaf62fffSJeremy L Thompson **/
1766eaf62fffSJeremy L Thompson int CeedOperatorLinearAssemble(CeedOperator op, CeedVector values) {
1767eaf62fffSJeremy L Thompson   CeedInt       num_suboperators, single_entries = 0;
1768eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
1769*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1770eaf62fffSJeremy L Thompson 
1771eaf62fffSJeremy L Thompson   if (op->LinearAssemble) {
1772d04bbc78SJeremy L Thompson     // Backend version
1773*2b730f8bSJeremy L Thompson     CeedCall(op->LinearAssemble(op, values));
1774eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1775eaf62fffSJeremy L Thompson   } else {
1776d04bbc78SJeremy L Thompson     // Operator fallback
1777d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1778d04bbc78SJeremy L Thompson 
1779*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1780d04bbc78SJeremy L Thompson     if (op_fallback) {
1781*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssemble(op_fallback, values));
1782eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1783eaf62fffSJeremy L Thompson     }
1784eaf62fffSJeremy L Thompson   }
1785eaf62fffSJeremy L Thompson 
1786eaf62fffSJeremy L Thompson   // Default interface implementation
1787eaf62fffSJeremy L Thompson   bool is_composite;
1788*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1789eaf62fffSJeremy L Thompson 
1790eaf62fffSJeremy L Thompson   CeedInt offset = 0;
1791eaf62fffSJeremy L Thompson   if (is_composite) {
1792*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetNumSub(op, &num_suboperators));
1793*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetSubList(op, &sub_operators));
1794cefa2673SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; k++) {
1795*2b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemble(sub_operators[k], offset, values));
1796*2b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
1797eaf62fffSJeremy L Thompson       offset += single_entries;
1798eaf62fffSJeremy L Thompson     }
1799eaf62fffSJeremy L Thompson   } else {
1800*2b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssemble(op, offset, values));
1801eaf62fffSJeremy L Thompson   }
1802eaf62fffSJeremy L Thompson 
1803eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1804eaf62fffSJeremy L Thompson }
1805eaf62fffSJeremy L Thompson 
1806eaf62fffSJeremy L Thompson /**
1807eaf62fffSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1808eaf62fffSJeremy L Thompson            for a CeedOperator, creating the prolongation basis from the
1809eaf62fffSJeremy L Thompson            fine and coarse grid interpolation
1810eaf62fffSJeremy L Thompson 
1811f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1812f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1813f04ea552SJeremy L Thompson 
1814eaf62fffSJeremy L Thompson   @param[in] op_fine       Fine grid operator
1815eaf62fffSJeremy L Thompson   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
1816eaf62fffSJeremy L Thompson   @param[in] rstr_coarse   Coarse grid restriction
1817eaf62fffSJeremy L Thompson   @param[in] basis_coarse  Coarse grid active vector basis
1818eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
1819eaf62fffSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator
1820eaf62fffSJeremy L Thompson   @param[out] op_restrict  Fine to coarse operator
1821eaf62fffSJeremy L Thompson 
1822eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1823eaf62fffSJeremy L Thompson 
1824eaf62fffSJeremy L Thompson   @ref User
1825eaf62fffSJeremy L Thompson **/
1826*2b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreate(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
1827*2b730f8bSJeremy L Thompson                                      CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_restrict) {
1828*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
1829eaf62fffSJeremy L Thompson 
1830f113e5dcSJeremy L Thompson   // Build prolongation matrix
1831f113e5dcSJeremy L Thompson   CeedBasis basis_fine, basis_c_to_f;
1832*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
1833*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisCreateProjection(basis_coarse, basis_fine, &basis_c_to_f));
1834eaf62fffSJeremy L Thompson 
1835f113e5dcSJeremy L Thompson   // Core code
1836*2b730f8bSJeremy L Thompson   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
1837f113e5dcSJeremy L Thompson 
1838eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1839eaf62fffSJeremy L Thompson }
1840eaf62fffSJeremy L Thompson 
1841eaf62fffSJeremy L Thompson /**
1842eaf62fffSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1843eaf62fffSJeremy L Thompson            for a CeedOperator with a tensor basis for the active basis
1844eaf62fffSJeremy L Thompson 
1845f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1846f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1847f04ea552SJeremy L Thompson 
1848eaf62fffSJeremy L Thompson   @param[in] op_fine        Fine grid operator
1849eaf62fffSJeremy L Thompson   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
1850eaf62fffSJeremy L Thompson   @param[in] rstr_coarse    Coarse grid restriction
1851eaf62fffSJeremy L Thompson   @param[in] basis_coarse   Coarse grid active vector basis
1852eaf62fffSJeremy L Thompson   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
1853eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
1854eaf62fffSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator
1855eaf62fffSJeremy L Thompson   @param[out] op_restrict   Fine to coarse operator
1856eaf62fffSJeremy L Thompson 
1857eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1858eaf62fffSJeremy L Thompson 
1859eaf62fffSJeremy L Thompson   @ref User
1860eaf62fffSJeremy L Thompson **/
1861*2b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
1862*2b730f8bSJeremy L Thompson                                              const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
1863*2b730f8bSJeremy L Thompson                                              CeedOperator *op_restrict) {
1864*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
1865eaf62fffSJeremy L Thompson   Ceed ceed;
1866*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
1867eaf62fffSJeremy L Thompson 
1868eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
1869eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
1870*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
1871eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
1872*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f));
1873*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c));
1874*2b730f8bSJeremy L Thompson   if (Q_f != Q_c) {
1875eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
1876*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces");
1877eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
1878*2b730f8bSJeremy L Thompson   }
1879eaf62fffSJeremy L Thompson 
1880eaf62fffSJeremy L Thompson   // Coarse to fine basis
1881eaf62fffSJeremy L Thompson   CeedInt dim, num_comp, num_nodes_c, P_1d_f, P_1d_c;
1882*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetDimension(basis_fine, &dim));
1883*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp));
1884*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes1D(basis_fine, &P_1d_f));
1885*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c));
1886*2b730f8bSJeremy L Thompson   P_1d_c = dim == 1 ? num_nodes_c : dim == 2 ? sqrt(num_nodes_c) : cbrt(num_nodes_c);
1887eaf62fffSJeremy L Thompson   CeedScalar *q_ref, *q_weight, *grad;
1888*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d_f, &q_ref));
1889*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d_f, &q_weight));
1890*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d_f * P_1d_c * dim, &grad));
1891eaf62fffSJeremy L Thompson   CeedBasis basis_c_to_f;
1892*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d_c, P_1d_f, interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f));
1893*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_ref));
1894*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_weight));
1895*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&grad));
1896eaf62fffSJeremy L Thompson 
1897eaf62fffSJeremy L Thompson   // Core code
1898*2b730f8bSJeremy L Thompson   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
1899eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1900eaf62fffSJeremy L Thompson }
1901eaf62fffSJeremy L Thompson 
1902eaf62fffSJeremy L Thompson /**
1903eaf62fffSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
1904eaf62fffSJeremy L Thompson            for a CeedOperator with a non-tensor basis for the active vector
1905eaf62fffSJeremy L Thompson 
1906f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1907f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1908f04ea552SJeremy L Thompson 
1909eaf62fffSJeremy L Thompson   @param[in] op_fine        Fine grid operator
1910eaf62fffSJeremy L Thompson   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
1911eaf62fffSJeremy L Thompson   @param[in] rstr_coarse    Coarse grid restriction
1912eaf62fffSJeremy L Thompson   @param[in] basis_coarse   Coarse grid active vector basis
1913eaf62fffSJeremy L Thompson   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
1914eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
1915eaf62fffSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator
1916eaf62fffSJeremy L Thompson   @param[out] op_restrict   Fine to coarse operator
1917eaf62fffSJeremy L Thompson 
1918eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1919eaf62fffSJeremy L Thompson 
1920eaf62fffSJeremy L Thompson   @ref User
1921eaf62fffSJeremy L Thompson **/
1922*2b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
1923*2b730f8bSJeremy L Thompson                                        const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
1924eaf62fffSJeremy L Thompson                                        CeedOperator *op_restrict) {
1925*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
1926eaf62fffSJeremy L Thompson   Ceed ceed;
1927*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
1928eaf62fffSJeremy L Thompson 
1929eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
1930eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
1931*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
1932eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
1933*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f));
1934*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c));
1935*2b730f8bSJeremy L Thompson   if (Q_f != Q_c) {
1936eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
1937*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces");
1938eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
1939*2b730f8bSJeremy L Thompson   }
1940eaf62fffSJeremy L Thompson 
1941eaf62fffSJeremy L Thompson   // Coarse to fine basis
1942eaf62fffSJeremy L Thompson   CeedElemTopology topo;
1943*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetTopology(basis_fine, &topo));
1944eaf62fffSJeremy L Thompson   CeedInt dim, num_comp, num_nodes_c, num_nodes_f;
1945*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetDimension(basis_fine, &dim));
1946*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp));
1947*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes(basis_fine, &num_nodes_f));
1948*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c));
1949eaf62fffSJeremy L Thompson   CeedScalar *q_ref, *q_weight, *grad;
1950*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_nodes_f * dim, &q_ref));
1951*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_nodes_f, &q_weight));
1952*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_nodes_f * num_nodes_c * dim, &grad));
1953eaf62fffSJeremy L Thompson   CeedBasis basis_c_to_f;
1954*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisCreateH1(ceed, topo, num_comp, num_nodes_c, num_nodes_f, interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f));
1955*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_ref));
1956*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_weight));
1957*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&grad));
1958eaf62fffSJeremy L Thompson 
1959eaf62fffSJeremy L Thompson   // Core code
1960*2b730f8bSJeremy L Thompson   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
1961eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1962eaf62fffSJeremy L Thompson }
1963eaf62fffSJeremy L Thompson 
1964eaf62fffSJeremy L Thompson /**
1965eaf62fffSJeremy L Thompson   @brief Build a FDM based approximate inverse for each element for a
1966eaf62fffSJeremy L Thompson            CeedOperator
1967eaf62fffSJeremy L Thompson 
1968eaf62fffSJeremy L Thompson   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
1969eaf62fffSJeremy L Thompson     Method based approximate inverse. This function obtains the simultaneous
1970eaf62fffSJeremy L Thompson     diagonalization for the 1D mass and Laplacian operators,
1971eaf62fffSJeremy L Thompson       M = V^T V, K = V^T S V.
1972eaf62fffSJeremy L Thompson     The assembled QFunction is used to modify the eigenvalues from simultaneous
1973eaf62fffSJeremy L Thompson     diagonalization and obtain an approximate inverse of the form
1974eaf62fffSJeremy L Thompson       V^T S^hat V. The CeedOperator must be linear and non-composite. The
1975eaf62fffSJeremy L Thompson     associated CeedQFunction must therefore also be linear.
1976eaf62fffSJeremy L Thompson 
1977f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1978f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1979f04ea552SJeremy L Thompson 
1980eaf62fffSJeremy L Thompson   @param op            CeedOperator to create element inverses
1981eaf62fffSJeremy L Thompson   @param[out] fdm_inv  CeedOperator to apply the action of a FDM based inverse
1982eaf62fffSJeremy L Thompson                          for each element
1983eaf62fffSJeremy L Thompson   @param request       Address of CeedRequest for non-blocking completion, else
1984eaf62fffSJeremy L Thompson                          @ref CEED_REQUEST_IMMEDIATE
1985eaf62fffSJeremy L Thompson 
1986eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1987eaf62fffSJeremy L Thompson 
1988480fae85SJeremy L Thompson   @ref User
1989eaf62fffSJeremy L Thompson **/
1990*2b730f8bSJeremy L Thompson int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv, CeedRequest *request) {
1991*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1992eaf62fffSJeremy L Thompson 
1993eaf62fffSJeremy L Thompson   if (op->CreateFDMElementInverse) {
1994d04bbc78SJeremy L Thompson     // Backend version
1995*2b730f8bSJeremy L Thompson     CeedCall(op->CreateFDMElementInverse(op, fdm_inv, request));
1996eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1997eaf62fffSJeremy L Thompson   } else {
1998d04bbc78SJeremy L Thompson     // Operator fallback
1999d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2000d04bbc78SJeremy L Thompson 
2001*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2002d04bbc78SJeremy L Thompson     if (op_fallback) {
2003*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorCreateFDMElementInverse(op_fallback, fdm_inv, request));
2004eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2005eaf62fffSJeremy L Thompson     }
2006eaf62fffSJeremy L Thompson   }
2007eaf62fffSJeremy L Thompson 
2008d04bbc78SJeremy L Thompson   // Default interface implementation
2009eaf62fffSJeremy L Thompson   Ceed ceed, ceed_parent;
2010*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
2011*2b730f8bSJeremy L Thompson   CeedCall(CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent));
2012eaf62fffSJeremy L Thompson   ceed_parent = ceed_parent ? ceed_parent : ceed;
2013eaf62fffSJeremy L Thompson   CeedQFunction qf;
2014*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
2015eaf62fffSJeremy L Thompson 
2016eaf62fffSJeremy L Thompson   // Determine active input basis
2017eaf62fffSJeremy L Thompson   bool                interp = false, grad = false;
2018eaf62fffSJeremy L Thompson   CeedBasis           basis = NULL;
2019eaf62fffSJeremy L Thompson   CeedElemRestriction rstr  = NULL;
2020eaf62fffSJeremy L Thompson   CeedOperatorField  *op_fields;
2021eaf62fffSJeremy L Thompson   CeedQFunctionField *qf_fields;
2022eaf62fffSJeremy L Thompson   CeedInt             num_input_fields;
2023*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &op_fields, NULL, NULL));
2024*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, NULL, &qf_fields, NULL, NULL));
2025eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_input_fields; i++) {
2026eaf62fffSJeremy L Thompson     CeedVector vec;
2027*2b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
2028eaf62fffSJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
2029eaf62fffSJeremy L Thompson       CeedEvalMode eval_mode;
2030*2b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
2031eaf62fffSJeremy L Thompson       interp = interp || eval_mode == CEED_EVAL_INTERP;
2032eaf62fffSJeremy L Thompson       grad   = grad || eval_mode == CEED_EVAL_GRAD;
2033*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis));
2034*2b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr));
2035eaf62fffSJeremy L Thompson     }
2036eaf62fffSJeremy L Thompson   }
2037*2b730f8bSJeremy L Thompson   if (!basis) {
2038eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2039eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "No active field set");
2040eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
2041*2b730f8bSJeremy L Thompson   }
2042e79b91d9SJeremy L Thompson   CeedSize l_size = 1;
2043e79b91d9SJeremy L Thompson   CeedInt  P_1d, Q_1d, elem_size, num_qpts, dim, num_comp = 1, num_elem = 1;
2044*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes1D(basis, &P_1d));
2045*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes(basis, &elem_size));
2046*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d));
2047*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis, &num_qpts));
2048*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetDimension(basis, &dim));
2049*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis, &num_comp));
2050*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
2051*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
2052eaf62fffSJeremy L Thompson 
2053eaf62fffSJeremy L Thompson   // Build and diagonalize 1D Mass and Laplacian
2054eaf62fffSJeremy L Thompson   bool tensor_basis;
2055*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisIsTensor(basis, &tensor_basis));
2056*2b730f8bSJeremy L Thompson   if (!tensor_basis) {
2057eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2058*2b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "FDMElementInverse only supported for tensor bases");
2059eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
2060*2b730f8bSJeremy L Thompson   }
2061eaf62fffSJeremy L Thompson   CeedScalar *mass, *laplace, *x, *fdm_interp, *lambda;
2062*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &mass));
2063*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &laplace));
2064*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &x));
2065*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &fdm_interp));
2066*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &lambda));
2067eaf62fffSJeremy L Thompson   // -- Build matrices
2068eaf62fffSJeremy L Thompson   const CeedScalar *interp_1d, *grad_1d, *q_weight_1d;
2069*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetInterp1D(basis, &interp_1d));
2070*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetGrad1D(basis, &grad_1d));
2071*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetQWeights(basis, &q_weight_1d));
2072*2b730f8bSJeremy L Thompson   CeedCall(CeedBuildMassLaplace(interp_1d, grad_1d, q_weight_1d, P_1d, Q_1d, dim, mass, laplace));
2073eaf62fffSJeremy L Thompson 
2074eaf62fffSJeremy L Thompson   // -- Diagonalize
2075*2b730f8bSJeremy L Thompson   CeedCall(CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d));
2076*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&mass));
2077*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&laplace));
2078*2b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
2079*2b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) fdm_interp[i + j * P_1d] = x[j + i * P_1d];
2080*2b730f8bSJeremy L Thompson   }
2081*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&x));
2082eaf62fffSJeremy L Thompson 
2083eaf62fffSJeremy L Thompson   // Assemble QFunction
2084eaf62fffSJeremy L Thompson   CeedVector          assembled;
2085eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_qf;
2086*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled, &rstr_qf, request));
2087eaf62fffSJeremy L Thompson   CeedInt layout[3];
2088*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr_qf, &layout));
2089*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_qf));
2090eaf62fffSJeremy L Thompson   CeedScalar max_norm = 0;
2091*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm));
2092eaf62fffSJeremy L Thompson 
2093eaf62fffSJeremy L Thompson   // Calculate element averages
2094eaf62fffSJeremy L Thompson   CeedInt           num_modes = (interp ? 1 : 0) + (grad ? dim : 0);
2095eaf62fffSJeremy L Thompson   CeedScalar       *elem_avg;
2096eaf62fffSJeremy L Thompson   const CeedScalar *assembled_array, *q_weight_array;
2097eaf62fffSJeremy L Thompson   CeedVector        q_weight;
2098*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed_parent, num_qpts, &q_weight));
2099*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT, CEED_VECTOR_NONE, q_weight));
2100*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembled_array));
2101*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array));
2102*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_elem, &elem_avg));
2103eaf62fffSJeremy L Thompson   const CeedScalar qf_value_bound = max_norm * 100 * CEED_EPSILON;
2104eaf62fffSJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
2105eaf62fffSJeremy L Thompson     CeedInt count = 0;
2106*2b730f8bSJeremy L Thompson     for (CeedInt q = 0; q < num_qpts; q++) {
2107*2b730f8bSJeremy L Thompson       for (CeedInt i = 0; i < num_comp * num_comp * num_modes * num_modes; i++) {
2108*2b730f8bSJeremy L Thompson         if (fabs(assembled_array[q * layout[0] + i * layout[1] + e * layout[2]]) > qf_value_bound) {
2109*2b730f8bSJeremy L Thompson           elem_avg[e] += assembled_array[q * layout[0] + i * layout[1] + e * layout[2]] / q_weight_array[q];
2110eaf62fffSJeremy L Thompson           count++;
2111eaf62fffSJeremy L Thompson         }
2112*2b730f8bSJeremy L Thompson       }
2113*2b730f8bSJeremy L Thompson     }
2114eaf62fffSJeremy L Thompson     if (count) {
2115eaf62fffSJeremy L Thompson       elem_avg[e] /= count;
2116eaf62fffSJeremy L Thompson     } else {
2117eaf62fffSJeremy L Thompson       elem_avg[e] = 1.0;
2118eaf62fffSJeremy L Thompson     }
2119eaf62fffSJeremy L Thompson   }
2120*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled, &assembled_array));
2121*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled));
2122*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(q_weight, &q_weight_array));
2123*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&q_weight));
2124eaf62fffSJeremy L Thompson 
2125eaf62fffSJeremy L Thompson   // Build FDM diagonal
2126eaf62fffSJeremy L Thompson   CeedVector  q_data;
2127eaf62fffSJeremy L Thompson   CeedScalar *q_data_array, *fdm_diagonal;
2128*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_comp * elem_size, &fdm_diagonal));
2129eaf62fffSJeremy L Thompson   const CeedScalar fdm_diagonal_bound = elem_size * CEED_EPSILON;
2130*2b730f8bSJeremy L Thompson   for (CeedInt c = 0; c < num_comp; c++) {
2131eaf62fffSJeremy L Thompson     for (CeedInt n = 0; n < elem_size; n++) {
2132*2b730f8bSJeremy L Thompson       if (interp) fdm_diagonal[c * elem_size + n] = 1.0;
2133*2b730f8bSJeremy L Thompson       if (grad) {
2134eaf62fffSJeremy L Thompson         for (CeedInt d = 0; d < dim; d++) {
2135eaf62fffSJeremy L Thompson           CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d;
2136eaf62fffSJeremy L Thompson           fdm_diagonal[c * elem_size + n] += lambda[i];
2137eaf62fffSJeremy L Thompson         }
2138eaf62fffSJeremy L Thompson       }
2139*2b730f8bSJeremy L Thompson       if (fabs(fdm_diagonal[c * elem_size + n]) < fdm_diagonal_bound) fdm_diagonal[c * elem_size + n] = fdm_diagonal_bound;
2140*2b730f8bSJeremy L Thompson     }
2141*2b730f8bSJeremy L Thompson   }
2142*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed_parent, num_elem * num_comp * elem_size, &q_data));
2143*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(q_data, 0.0));
2144*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayWrite(q_data, CEED_MEM_HOST, &q_data_array));
2145*2b730f8bSJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
2146*2b730f8bSJeremy L Thompson     for (CeedInt c = 0; c < num_comp; c++) {
2147*2b730f8bSJeremy L Thompson       for (CeedInt n = 0; n < elem_size; n++) q_data_array[(e * num_comp + c) * elem_size + n] = 1. / (elem_avg[e] * fdm_diagonal[c * elem_size + n]);
2148*2b730f8bSJeremy L Thompson     }
2149*2b730f8bSJeremy L Thompson   }
2150*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&elem_avg));
2151*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&fdm_diagonal));
2152*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(q_data, &q_data_array));
2153eaf62fffSJeremy L Thompson 
2154eaf62fffSJeremy L Thompson   // Setup FDM operator
2155eaf62fffSJeremy L Thompson   // -- Basis
2156eaf62fffSJeremy L Thompson   CeedBasis   fdm_basis;
2157eaf62fffSJeremy L Thompson   CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy;
2158*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &grad_dummy));
2159*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &q_ref_dummy));
2160*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &q_weight_dummy));
2161*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisCreateTensorH1(ceed_parent, dim, num_comp, P_1d, P_1d, fdm_interp, grad_dummy, q_ref_dummy, q_weight_dummy, &fdm_basis));
2162*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&fdm_interp));
2163*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&grad_dummy));
2164*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_ref_dummy));
2165*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_weight_dummy));
2166*2b730f8bSJeremy L Thompson   CeedCall(CeedFree(&lambda));
2167eaf62fffSJeremy L Thompson 
2168eaf62fffSJeremy L Thompson   // -- Restriction
2169eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_qd_i;
2170eaf62fffSJeremy L Thompson   CeedInt             strides[3] = {1, elem_size, elem_size * num_comp};
2171*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionCreateStrided(ceed_parent, num_elem, elem_size, num_comp, num_elem * num_comp * elem_size, strides, &rstr_qd_i));
2172eaf62fffSJeremy L Thompson   // -- QFunction
2173eaf62fffSJeremy L Thompson   CeedQFunction qf_fdm;
2174*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInteriorByName(ceed_parent, "Scale", &qf_fdm));
2175*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_fdm, "input", num_comp, CEED_EVAL_INTERP));
2176*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_fdm, "scale", num_comp, CEED_EVAL_NONE));
2177*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddOutput(qf_fdm, "output", num_comp, CEED_EVAL_INTERP));
2178*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_fdm, num_comp));
2179eaf62fffSJeremy L Thompson   // -- QFunction context
2180eaf62fffSJeremy L Thompson   CeedInt *num_comp_data;
2181*2b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, &num_comp_data));
2182eaf62fffSJeremy L Thompson   num_comp_data[0] = num_comp;
2183eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_fdm;
2184*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextCreate(ceed, &ctx_fdm));
2185*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextSetData(ctx_fdm, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_data), num_comp_data));
2186*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetContext(qf_fdm, ctx_fdm));
2187*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextDestroy(&ctx_fdm));
2188eaf62fffSJeremy L Thompson   // -- Operator
2189*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed_parent, qf_fdm, NULL, NULL, fdm_inv));
2190*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "input", rstr, fdm_basis, CEED_VECTOR_ACTIVE));
2191*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "scale", rstr_qd_i, CEED_BASIS_COLLOCATED, q_data));
2192*2b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "output", rstr, fdm_basis, CEED_VECTOR_ACTIVE));
2193eaf62fffSJeremy L Thompson 
2194eaf62fffSJeremy L Thompson   // Cleanup
2195*2b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&q_data));
2196*2b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&fdm_basis));
2197*2b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_qd_i));
2198*2b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionDestroy(&qf_fdm));
2199eaf62fffSJeremy L Thompson 
2200eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2201eaf62fffSJeremy L Thompson }
2202eaf62fffSJeremy L Thompson 
2203eaf62fffSJeremy L Thompson /// @}
2204