xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-preconditioning.c (revision 9fd66db6d1a7a1c9032418479851d404799ab3ba)
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 
82b730f8bSJeremy L Thompson #include <ceed-impl.h>
949aac155SJeremy L Thompson #include <ceed.h>
102b730f8bSJeremy L Thompson #include <ceed/backend.h>
11c85e8640SSebastian Grimberg #include <assert.h>
122b730f8bSJeremy 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 /**
27ea61e9acSJeremy L Thompson   @brief Duplicate a CeedQFunction with a reference Ceed to fallback for advanced CeedOperator functionality
289e77b9c8SJeremy L Thompson 
2901ea9c81SJed Brown   @param[in]  fallback_ceed Ceed on which to create fallback CeedQFunction
309e77b9c8SJeremy L Thompson   @param[in]  qf            CeedQFunction to create fallback for
3101ea9c81SJed Brown   @param[out] qf_fallback   fallback CeedQFunction
329e77b9c8SJeremy L Thompson 
339e77b9c8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
349e77b9c8SJeremy L Thompson 
359e77b9c8SJeremy L Thompson   @ref Developer
369e77b9c8SJeremy L Thompson **/
372b730f8bSJeremy L Thompson static int CeedQFunctionCreateFallback(Ceed fallback_ceed, CeedQFunction qf, CeedQFunction *qf_fallback) {
389e77b9c8SJeremy L Thompson   // Check if NULL qf passed in
399e77b9c8SJeremy L Thompson   if (!qf) return CEED_ERROR_SUCCESS;
409e77b9c8SJeremy L Thompson 
41d04bbc78SJeremy L Thompson   CeedDebug256(qf->ceed, 1, "---------- CeedOperator Fallback ----------\n");
4213f886e9SJeremy L Thompson   CeedDebug(qf->ceed, "Creating fallback CeedQFunction\n");
43d04bbc78SJeremy L Thompson 
449e77b9c8SJeremy L Thompson   char *source_path_with_name = "";
459e77b9c8SJeremy L Thompson   if (qf->source_path) {
462b730f8bSJeremy L Thompson     size_t path_len = strlen(qf->source_path), name_len = strlen(qf->kernel_name);
472b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(path_len + name_len + 2, &source_path_with_name));
489e77b9c8SJeremy L Thompson     memcpy(source_path_with_name, qf->source_path, path_len);
499e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len], ":", 1);
509e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len + 1], qf->kernel_name, name_len);
519e77b9c8SJeremy L Thompson   } else {
522b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &source_path_with_name));
539e77b9c8SJeremy L Thompson   }
549e77b9c8SJeremy L Thompson 
552b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInterior(fallback_ceed, qf->vec_length, qf->function, source_path_with_name, qf_fallback));
569e77b9c8SJeremy L Thompson   {
579e77b9c8SJeremy L Thompson     CeedQFunctionContext ctx;
589e77b9c8SJeremy L Thompson 
592b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionGetContext(qf, &ctx));
602b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetContext(*qf_fallback, ctx));
619e77b9c8SJeremy L Thompson   }
629e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_input_fields; i++) {
632b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(*qf_fallback, qf->input_fields[i]->field_name, qf->input_fields[i]->size, qf->input_fields[i]->eval_mode));
649e77b9c8SJeremy L Thompson   }
659e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_output_fields; i++) {
662b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddOutput(*qf_fallback, qf->output_fields[i]->field_name, qf->output_fields[i]->size, qf->output_fields[i]->eval_mode));
679e77b9c8SJeremy L Thompson   }
682b730f8bSJeremy L Thompson   CeedCall(CeedFree(&source_path_with_name));
699e77b9c8SJeremy L Thompson 
709e77b9c8SJeremy L Thompson   return CEED_ERROR_SUCCESS;
719e77b9c8SJeremy L Thompson }
729e77b9c8SJeremy L Thompson 
739e77b9c8SJeremy L Thompson /**
74ea61e9acSJeremy L Thompson   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced CeedOperator functionality
75eaf62fffSJeremy L Thompson 
76ea61e9acSJeremy L Thompson   @param[in,out] op CeedOperator to create fallback for
77eaf62fffSJeremy L Thompson 
78eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
79eaf62fffSJeremy L Thompson 
80eaf62fffSJeremy L Thompson   @ref Developer
81eaf62fffSJeremy L Thompson **/
82d04bbc78SJeremy L Thompson static int CeedOperatorCreateFallback(CeedOperator op) {
83b275c451SJeremy L Thompson   bool is_composite;
849e77b9c8SJeremy L Thompson   Ceed ceed_fallback;
85eaf62fffSJeremy L Thompson 
86805fe78eSJeremy L Thompson   // Check not already created
87805fe78eSJeremy L Thompson   if (op->op_fallback) return CEED_ERROR_SUCCESS;
88805fe78eSJeremy L Thompson 
89eaf62fffSJeremy L Thompson   // Fallback Ceed
902b730f8bSJeremy L Thompson   CeedCall(CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback));
91d04bbc78SJeremy L Thompson   if (!ceed_fallback) return CEED_ERROR_SUCCESS;
92d04bbc78SJeremy L Thompson 
93d04bbc78SJeremy L Thompson   CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n");
9413f886e9SJeremy L Thompson   CeedDebug(op->ceed, "Creating fallback CeedOperator\n");
95eaf62fffSJeremy L Thompson 
96eaf62fffSJeremy L Thompson   // Clone Op
97805fe78eSJeremy L Thompson   CeedOperator op_fallback;
98b275c451SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
99b275c451SJeremy L Thompson   if (is_composite) {
100b275c451SJeremy L Thompson     CeedInt       num_suboperators;
101b275c451SJeremy L Thompson     CeedOperator *sub_operators;
102b275c451SJeremy L Thompson 
1032b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorCreate(ceed_fallback, &op_fallback));
104b275c451SJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
105b275c451SJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
106b275c451SJeremy L Thompson     for (CeedInt i = 0; i < num_suboperators; i++) {
107d04bbc78SJeremy L Thompson       CeedOperator op_sub_fallback;
108d04bbc78SJeremy L Thompson 
109b275c451SJeremy L Thompson       CeedCall(CeedOperatorGetFallback(sub_operators[i], &op_sub_fallback));
1102b730f8bSJeremy L Thompson       CeedCall(CeedCompositeOperatorAddSub(op_fallback, op_sub_fallback));
111805fe78eSJeremy L Thompson     }
112805fe78eSJeremy L Thompson   } else {
1139e77b9c8SJeremy L Thompson     CeedQFunction qf_fallback = NULL, dqf_fallback = NULL, dqfT_fallback = NULL;
1142b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->qf, &qf_fallback));
1152b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqf, &dqf_fallback));
1162b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqfT, &dqfT_fallback));
1172b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreate(ceed_fallback, qf_fallback, dqf_fallback, dqfT_fallback, &op_fallback));
118805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
119437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(op_fallback, op->input_fields[i]->field_name, op->input_fields[i]->elem_rstr, op->input_fields[i]->basis,
1202b730f8bSJeremy L Thompson                                     op->input_fields[i]->vec));
121805fe78eSJeremy L Thompson     }
122805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
123437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(op_fallback, op->output_fields[i]->field_name, op->output_fields[i]->elem_rstr, op->output_fields[i]->basis,
1242b730f8bSJeremy L Thompson                                     op->output_fields[i]->vec));
125805fe78eSJeremy L Thompson     }
1262b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op->qf_assembled, &op_fallback->qf_assembled));
127805fe78eSJeremy L Thompson     if (op_fallback->num_qpts == 0) {
1282b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetNumQuadraturePoints(op_fallback, op->num_qpts));
129805fe78eSJeremy L Thompson     }
1309e77b9c8SJeremy L Thompson     // Cleanup
1312b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&qf_fallback));
1322b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&dqf_fallback));
1332b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&dqfT_fallback));
134805fe78eSJeremy L Thompson   }
1352b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetName(op_fallback, op->name));
1362b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fallback));
137805fe78eSJeremy L Thompson   op->op_fallback = op_fallback;
138eaf62fffSJeremy L Thompson 
139eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
140eaf62fffSJeremy L Thompson }
141eaf62fffSJeremy L Thompson 
142eaf62fffSJeremy L Thompson /**
143ea61e9acSJeremy L Thompson   @brief Retrieve fallback CeedOperator with a reference Ceed for advanced CeedOperator functionality
144d04bbc78SJeremy L Thompson 
145d04bbc78SJeremy L Thompson   @param[in]  op          CeedOperator to retrieve fallback for
146d04bbc78SJeremy L Thompson   @param[out] op_fallback Fallback CeedOperator
147d04bbc78SJeremy L Thompson 
148d04bbc78SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
149d04bbc78SJeremy L Thompson 
150d04bbc78SJeremy L Thompson   @ref Developer
151d04bbc78SJeremy L Thompson **/
152d04bbc78SJeremy L Thompson int CeedOperatorGetFallback(CeedOperator op, CeedOperator *op_fallback) {
153d04bbc78SJeremy L Thompson   // Create if needed
154d04bbc78SJeremy L Thompson   if (!op->op_fallback) {
1552b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreateFallback(op));
156d04bbc78SJeremy L Thompson   }
157d04bbc78SJeremy L Thompson   if (op->op_fallback) {
158d04bbc78SJeremy L Thompson     bool is_debug;
159d04bbc78SJeremy L Thompson 
1602b730f8bSJeremy L Thompson     CeedCall(CeedIsDebug(op->ceed, &is_debug));
161d04bbc78SJeremy L Thompson     if (is_debug) {
162b275c451SJeremy L Thompson       Ceed        ceed, ceed_fallback;
163d04bbc78SJeremy L Thompson       const char *resource, *resource_fallback;
164d04bbc78SJeremy L Thompson 
165b275c451SJeremy L Thompson       CeedCall(CeedOperatorGetCeed(op, &ceed));
166b275c451SJeremy L Thompson       CeedCall(CeedGetOperatorFallbackCeed(ceed, &ceed_fallback));
167b275c451SJeremy L Thompson       CeedCall(CeedGetResource(ceed, &resource));
1682b730f8bSJeremy L Thompson       CeedCall(CeedGetResource(ceed_fallback, &resource_fallback));
169d04bbc78SJeremy L Thompson 
170b275c451SJeremy L Thompson       CeedDebug256(ceed, 1, "---------- CeedOperator Fallback ----------\n");
171b275c451SJeremy L Thompson       CeedDebug(ceed, "Falling back from %s operator at address %ld to %s operator at address %ld\n", resource, op, resource_fallback,
1722b730f8bSJeremy L Thompson                 op->op_fallback);
173d04bbc78SJeremy L Thompson     }
174d04bbc78SJeremy L Thompson   }
175d04bbc78SJeremy L Thompson   *op_fallback = op->op_fallback;
176d04bbc78SJeremy L Thompson 
177d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
178d04bbc78SJeremy L Thompson }
179d04bbc78SJeremy L Thompson 
180d04bbc78SJeremy L Thompson /**
181eaf62fffSJeremy L Thompson   @brief Select correct basis matrix pointer based on CeedEvalMode
182eaf62fffSJeremy L Thompson 
183352a5e7cSSebastian Grimberg   @param[in]  basis     CeedBasis from which to get the basis matrix
184eaf62fffSJeremy L Thompson   @param[in]  eval_mode Current basis evaluation mode
185eaf62fffSJeremy L Thompson   @param[in]  identity  Pointer to identity matrix
186eaf62fffSJeremy L Thompson   @param[out] basis_ptr Basis pointer to set
187eaf62fffSJeremy L Thompson 
188eaf62fffSJeremy L Thompson   @ref Developer
189eaf62fffSJeremy L Thompson **/
190352a5e7cSSebastian Grimberg static inline int CeedOperatorGetBasisPointer(CeedBasis basis, CeedEvalMode eval_mode, const CeedScalar *identity, const CeedScalar **basis_ptr) {
191eaf62fffSJeremy L Thompson   switch (eval_mode) {
192eaf62fffSJeremy L Thompson     case CEED_EVAL_NONE:
193eaf62fffSJeremy L Thompson       *basis_ptr = identity;
194eaf62fffSJeremy L Thompson       break;
195eaf62fffSJeremy L Thompson     case CEED_EVAL_INTERP:
196352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetInterp(basis, basis_ptr));
197eaf62fffSJeremy L Thompson       break;
198eaf62fffSJeremy L Thompson     case CEED_EVAL_GRAD:
199352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetGrad(basis, basis_ptr));
200352a5e7cSSebastian Grimberg       break;
201352a5e7cSSebastian Grimberg     case CEED_EVAL_DIV:
202352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetDiv(basis, basis_ptr));
203352a5e7cSSebastian Grimberg       break;
204352a5e7cSSebastian Grimberg     case CEED_EVAL_CURL:
205352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetCurl(basis, basis_ptr));
206eaf62fffSJeremy L Thompson       break;
207eaf62fffSJeremy L Thompson     case CEED_EVAL_WEIGHT:
208eaf62fffSJeremy L Thompson       break;  // Caught by QF Assembly
209eaf62fffSJeremy L Thompson   }
210ed9e99e6SJeremy L Thompson   assert(*basis_ptr != NULL);
211352a5e7cSSebastian Grimberg 
212352a5e7cSSebastian Grimberg   return CEED_ERROR_SUCCESS;
213eaf62fffSJeremy L Thompson }
214eaf62fffSJeremy L Thompson 
215eaf62fffSJeremy L Thompson /**
216eaf62fffSJeremy L Thompson   @brief Create point block restriction for active operator field
217eaf62fffSJeremy L Thompson 
218eaf62fffSJeremy L Thompson   @param[in]  rstr            Original CeedElemRestriction for active field
219ea61e9acSJeremy L Thompson   @param[out] pointblock_rstr Address of the variable where the newly created CeedElemRestriction will be stored
220eaf62fffSJeremy L Thompson 
221eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
222eaf62fffSJeremy L Thompson 
223eaf62fffSJeremy L Thompson   @ref Developer
224eaf62fffSJeremy L Thompson **/
2252b730f8bSJeremy L Thompson static int CeedOperatorCreateActivePointBlockRestriction(CeedElemRestriction rstr, CeedElemRestriction *pointblock_rstr) {
226eaf62fffSJeremy L Thompson   Ceed ceed;
2272b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
228eaf62fffSJeremy L Thompson   const CeedInt *offsets;
2292b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets));
230eaf62fffSJeremy L Thompson 
231eaf62fffSJeremy L Thompson   // Expand offsets
2327b63f5c6SJed Brown   CeedInt  num_elem, num_comp, elem_size, comp_stride, *pointblock_offsets;
2337b63f5c6SJed Brown   CeedSize l_size;
2342b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
2352b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
2362b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
2372b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetCompStride(rstr, &comp_stride));
2382b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
239eaf62fffSJeremy L Thompson   CeedInt shift = num_comp;
2402b730f8bSJeremy L Thompson   if (comp_stride != 1) shift *= num_comp;
2412b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_elem * elem_size, &pointblock_offsets));
242eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_elem * elem_size; i++) {
243eaf62fffSJeremy L Thompson     pointblock_offsets[i] = offsets[i] * shift;
244eaf62fffSJeremy L Thompson   }
245eaf62fffSJeremy L Thompson 
246eaf62fffSJeremy L Thompson   // Create new restriction
2472b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp * num_comp, 1, l_size * num_comp, CEED_MEM_HOST, CEED_OWN_POINTER,
2482b730f8bSJeremy L Thompson                                      pointblock_offsets, pointblock_rstr));
249eaf62fffSJeremy L Thompson 
250eaf62fffSJeremy L Thompson   // Cleanup
2512b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets));
252eaf62fffSJeremy L Thompson 
253eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
254eaf62fffSJeremy L Thompson }
255eaf62fffSJeremy L Thompson 
256eaf62fffSJeremy L Thompson /**
257eaf62fffSJeremy L Thompson   @brief Core logic for assembling operator diagonal or point block diagonal
258eaf62fffSJeremy L Thompson 
259eaf62fffSJeremy L Thompson   @param[in]  op            CeedOperator to assemble point block diagonal
260ea61e9acSJeremy L Thompson   @param[in]  request       Address of CeedRequest for non-blocking completion, else CEED_REQUEST_IMMEDIATE
261eaf62fffSJeremy L Thompson   @param[in]  is_pointblock Boolean flag to assemble diagonal or point block diagonal
262eaf62fffSJeremy L Thompson   @param[out] assembled     CeedVector to store assembled diagonal
263eaf62fffSJeremy L Thompson 
264eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
265eaf62fffSJeremy L Thompson 
266eaf62fffSJeremy L Thompson   @ref Developer
267eaf62fffSJeremy L Thompson **/
2682b730f8bSJeremy L Thompson static inline int CeedSingleOperatorAssembleAddDiagonal_Core(CeedOperator op, CeedRequest *request, const bool is_pointblock, CeedVector assembled) {
269eaf62fffSJeremy L Thompson   Ceed ceed;
2702b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
271eaf62fffSJeremy L Thompson 
272eaf62fffSJeremy L Thompson   // Assemble QFunction
273eaf62fffSJeremy L Thompson   CeedQFunction       qf;
274437c7c90SJeremy L Thompson   const CeedScalar   *assembled_qf_array;
275eaf62fffSJeremy L Thompson   CeedVector          assembled_qf;
276437c7c90SJeremy L Thompson   CeedElemRestriction assembled_elem_rstr;
277437c7c90SJeremy L Thompson   CeedInt             num_input_fields, num_output_fields;
278eaf62fffSJeremy L Thompson   CeedInt             layout[3];
279437c7c90SJeremy L Thompson 
280437c7c90SJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
281437c7c90SJeremy L Thompson   CeedCall(CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields));
282437c7c90SJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &assembled_elem_rstr, request));
283437c7c90SJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(assembled_elem_rstr, &layout));
284437c7c90SJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&assembled_elem_rstr));
285437c7c90SJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array));
286eaf62fffSJeremy L Thompson 
287ed9e99e6SJeremy L Thompson   // Get assembly data
288ed9e99e6SJeremy L Thompson   CeedOperatorAssemblyData data;
289437c7c90SJeremy L Thompson   const CeedEvalMode     **eval_modes_in, **eval_modes_out;
290437c7c90SJeremy L Thompson   CeedInt                 *num_eval_modes_in, *num_eval_modes_out, num_active_bases;
291437c7c90SJeremy L Thompson   CeedSize               **eval_mode_offsets_in, **eval_mode_offsets_out, num_output_components;
292437c7c90SJeremy L Thompson   CeedBasis               *active_bases;
293437c7c90SJeremy L Thompson   CeedElemRestriction     *active_elem_rstrs;
294437c7c90SJeremy L Thompson   CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data));
295437c7c90SJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_active_bases, &num_eval_modes_in, &eval_modes_in, &eval_mode_offsets_in,
296437c7c90SJeremy L Thompson                                                 &num_eval_modes_out, &eval_modes_out, &eval_mode_offsets_out, &num_output_components));
297437c7c90SJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &active_bases, NULL, NULL));
298437c7c90SJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, NULL, &active_elem_rstrs));
299437c7c90SJeremy L Thompson 
300437c7c90SJeremy L Thompson   // Loop over all active bases
301437c7c90SJeremy L Thompson   for (CeedInt b = 0; b < num_active_bases; b++) {
302eaf62fffSJeremy L Thompson     // Assemble point block diagonal restriction, if needed
303437c7c90SJeremy L Thompson     CeedElemRestriction diag_elem_rstr = active_elem_rstrs[b];
304437c7c90SJeremy L Thompson 
305eaf62fffSJeremy L Thompson     if (is_pointblock) {
306437c7c90SJeremy L Thompson       CeedElemRestriction point_block_elem_rstr;
307437c7c90SJeremy L Thompson 
308437c7c90SJeremy L Thompson       CeedCall(CeedOperatorCreateActivePointBlockRestriction(diag_elem_rstr, &point_block_elem_rstr));
309437c7c90SJeremy L Thompson       diag_elem_rstr = point_block_elem_rstr;
310eaf62fffSJeremy L Thompson     }
311eaf62fffSJeremy L Thompson 
312eaf62fffSJeremy L Thompson     // Create diagonal vector
313eaf62fffSJeremy L Thompson     CeedVector elem_diag;
314437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionCreateVector(diag_elem_rstr, NULL, &elem_diag));
315eaf62fffSJeremy L Thompson 
316eaf62fffSJeremy L Thompson     // Assemble element operator diagonals
3179c774eddSJeremy L Thompson     CeedScalar *elem_diag_array;
318437c7c90SJeremy L Thompson     CeedInt     num_elem, num_nodes, num_qpts, num_components;
319437c7c90SJeremy L Thompson 
3202b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(elem_diag, 0.0));
3212b730f8bSJeremy L Thompson     CeedCall(CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array));
322437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionGetNumElements(diag_elem_rstr, &num_elem));
323437c7c90SJeremy L Thompson     CeedCall(CeedBasisGetNumNodes(active_bases[b], &num_nodes));
324437c7c90SJeremy L Thompson     CeedCall(CeedBasisGetNumComponents(active_bases[b], &num_components));
325437c7c90SJeremy L Thompson     CeedCall(CeedBasisGetNumQuadraturePoints(active_bases[b], &num_qpts));
326ed9e99e6SJeremy L Thompson 
327352a5e7cSSebastian Grimberg     // Construct identity matrix for basis if required
328ed9e99e6SJeremy L Thompson     bool        has_eval_none = false;
329352a5e7cSSebastian Grimberg     CeedScalar *identity      = NULL;
330437c7c90SJeremy L Thompson     for (CeedInt i = 0; i < num_eval_modes_in[b]; i++) {
331437c7c90SJeremy L Thompson       has_eval_none = has_eval_none || (eval_modes_in[b][i] == CEED_EVAL_NONE);
332ed9e99e6SJeremy L Thompson     }
333437c7c90SJeremy L Thompson     for (CeedInt i = 0; i < num_eval_modes_out[b]; i++) {
334437c7c90SJeremy L Thompson       has_eval_none = has_eval_none || (eval_modes_out[b][i] == CEED_EVAL_NONE);
335ed9e99e6SJeremy L Thompson     }
336ed9e99e6SJeremy L Thompson     if (has_eval_none) {
3372b730f8bSJeremy L Thompson       CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
3382b730f8bSJeremy L Thompson       for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) identity[i * num_nodes + i] = 1.0;
339eaf62fffSJeremy L Thompson     }
340352a5e7cSSebastian Grimberg 
341eaf62fffSJeremy L Thompson     // Compute the diagonal of B^T D B
342eaf62fffSJeremy L Thompson     // Each element
343eaf62fffSJeremy L Thompson     for (CeedInt e = 0; e < num_elem; e++) {
344eaf62fffSJeremy L Thompson       // Each basis eval mode pair
345352a5e7cSSebastian Grimberg       CeedInt      d_out              = 0, q_comp_out;
346352a5e7cSSebastian Grimberg       CeedEvalMode eval_mode_out_prev = CEED_EVAL_NONE;
347437c7c90SJeremy L Thompson       for (CeedInt e_out = 0; e_out < num_eval_modes_out[b]; e_out++) {
348437c7c90SJeremy L Thompson         const CeedScalar *B_t = NULL;
349352a5e7cSSebastian Grimberg         CeedOperatorGetBasisPointer(active_bases[b], eval_modes_out[b][e_out], identity, &B_t);
350352a5e7cSSebastian Grimberg         CeedCall(CeedBasisGetNumQuadratureComponents(active_bases[b], eval_modes_out[b][e_out], &q_comp_out));
351352a5e7cSSebastian Grimberg         if (q_comp_out > 1) {
352352a5e7cSSebastian Grimberg           if (e_out == 0 || eval_modes_out[b][e_out] != eval_mode_out_prev) d_out = 0;
353352a5e7cSSebastian Grimberg           else B_t = &B_t[(++d_out) * num_qpts * num_nodes];
354352a5e7cSSebastian Grimberg         }
355352a5e7cSSebastian Grimberg         eval_mode_out_prev = eval_modes_out[b][e_out];
356352a5e7cSSebastian Grimberg 
357352a5e7cSSebastian Grimberg         CeedInt      d_in              = 0, q_comp_in;
358352a5e7cSSebastian Grimberg         CeedEvalMode eval_mode_in_prev = CEED_EVAL_NONE;
359437c7c90SJeremy L Thompson         for (CeedInt e_in = 0; e_in < num_eval_modes_in[b]; e_in++) {
360437c7c90SJeremy L Thompson           const CeedScalar *B = NULL;
361352a5e7cSSebastian Grimberg           CeedOperatorGetBasisPointer(active_bases[b], eval_modes_in[b][e_in], identity, &B);
362352a5e7cSSebastian Grimberg           CeedCall(CeedBasisGetNumQuadratureComponents(active_bases[b], eval_modes_in[b][e_in], &q_comp_in));
363352a5e7cSSebastian Grimberg           if (q_comp_in > 1) {
364352a5e7cSSebastian Grimberg             if (e_in == 0 || eval_modes_in[b][e_in] != eval_mode_in_prev) d_in = 0;
365352a5e7cSSebastian Grimberg             else B = &B[(++d_in) * num_qpts * num_nodes];
366352a5e7cSSebastian Grimberg           }
367352a5e7cSSebastian Grimberg           eval_mode_in_prev = eval_modes_in[b][e_in];
368352a5e7cSSebastian Grimberg 
369eaf62fffSJeremy L Thompson           // Each component
370437c7c90SJeremy L Thompson           for (CeedInt c_out = 0; c_out < num_components; c_out++) {
371437c7c90SJeremy L Thompson             // Each qpt/node pair
3722b730f8bSJeremy L Thompson             for (CeedInt q = 0; q < num_qpts; q++) {
373eaf62fffSJeremy L Thompson               if (is_pointblock) {
374eaf62fffSJeremy L Thompson                 // Point Block Diagonal
375437c7c90SJeremy L Thompson                 for (CeedInt c_in = 0; c_in < num_components; c_in++) {
376437c7c90SJeremy L Thompson                   const CeedInt c_offset = (eval_mode_offsets_in[b][e_in] + c_in) * num_output_components + eval_mode_offsets_out[b][e_out] + c_out;
377437c7c90SJeremy L Thompson                   const CeedScalar qf_value = assembled_qf_array[q * layout[0] + c_offset * layout[1] + e * layout[2]];
3782b730f8bSJeremy L Thompson                   for (CeedInt n = 0; n < num_nodes; n++) {
379437c7c90SJeremy L Thompson                     elem_diag_array[((e * num_components + c_out) * num_components + c_in) * num_nodes + n] +=
380437c7c90SJeremy L Thompson                         B_t[q * num_nodes + n] * qf_value * B[q * num_nodes + n];
381eaf62fffSJeremy L Thompson                   }
3822b730f8bSJeremy L Thompson                 }
383eaf62fffSJeremy L Thompson               } else {
384eaf62fffSJeremy L Thompson                 // Diagonal Only
385437c7c90SJeremy L Thompson                 const CeedInt    c_offset = (eval_mode_offsets_in[b][e_in] + c_out) * num_output_components + eval_mode_offsets_out[b][e_out] + c_out;
386437c7c90SJeremy L Thompson                 const CeedScalar qf_value = assembled_qf_array[q * layout[0] + c_offset * layout[1] + e * layout[2]];
3872b730f8bSJeremy L Thompson                 for (CeedInt n = 0; n < num_nodes; n++) {
388437c7c90SJeremy L Thompson                   elem_diag_array[(e * num_components + c_out) * num_nodes + n] += B_t[q * num_nodes + n] * qf_value * B[q * num_nodes + n];
389eaf62fffSJeremy L Thompson                 }
390eaf62fffSJeremy L Thompson               }
391eaf62fffSJeremy L Thompson             }
392eaf62fffSJeremy L Thompson           }
3932b730f8bSJeremy L Thompson         }
3942b730f8bSJeremy L Thompson       }
3952b730f8bSJeremy L Thompson     }
3962b730f8bSJeremy L Thompson     CeedCall(CeedVectorRestoreArray(elem_diag, &elem_diag_array));
397eaf62fffSJeremy L Thompson 
398eaf62fffSJeremy L Thompson     // Assemble local operator diagonal
399437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionApply(diag_elem_rstr, CEED_TRANSPOSE, elem_diag, assembled, request));
400eaf62fffSJeremy L Thompson 
401eaf62fffSJeremy L Thompson     // Cleanup
402437c7c90SJeremy L Thompson     if (is_pointblock) CeedCall(CeedElemRestrictionDestroy(&diag_elem_rstr));
4032b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&elem_diag));
4042b730f8bSJeremy L Thompson     CeedCall(CeedFree(&identity));
405437c7c90SJeremy L Thompson   }
406437c7c90SJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array));
407437c7c90SJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled_qf));
408eaf62fffSJeremy L Thompson 
409eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
410eaf62fffSJeremy L Thompson }
411eaf62fffSJeremy L Thompson 
412eaf62fffSJeremy L Thompson /**
413eaf62fffSJeremy L Thompson   @brief Core logic for assembling composite operator diagonal
414eaf62fffSJeremy L Thompson 
415eaf62fffSJeremy L Thompson   @param[in]  op            CeedOperator to assemble point block diagonal
416ea61e9acSJeremy L Thompson   @param[in]  request       Address of CeedRequest for non-blocking completion, else CEED_REQUEST_IMMEDIATE
417eaf62fffSJeremy L Thompson   @param[in]  is_pointblock Boolean flag to assemble diagonal or point block diagonal
418eaf62fffSJeremy L Thompson   @param[out] assembled     CeedVector to store assembled diagonal
419eaf62fffSJeremy L Thompson 
420eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
421eaf62fffSJeremy L Thompson 
422eaf62fffSJeremy L Thompson   @ref Developer
423eaf62fffSJeremy L Thompson **/
4242b730f8bSJeremy L Thompson static inline int CeedCompositeOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedRequest *request, const bool is_pointblock,
425eaf62fffSJeremy L Thompson                                                                  CeedVector assembled) {
426eaf62fffSJeremy L Thompson   CeedInt       num_sub;
427eaf62fffSJeremy L Thompson   CeedOperator *suboperators;
428c6ebc35dSJeremy L Thompson   CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
429c6ebc35dSJeremy L Thompson   CeedCall(CeedCompositeOperatorGetSubList(op, &suboperators));
430eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_sub; i++) {
4316aa95790SJeremy L Thompson     if (is_pointblock) {
4322b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(suboperators[i], assembled, request));
4336aa95790SJeremy L Thompson     } else {
4342b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddDiagonal(suboperators[i], assembled, request));
4356aa95790SJeremy L Thompson     }
436eaf62fffSJeremy L Thompson   }
437eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
438eaf62fffSJeremy L Thompson }
439eaf62fffSJeremy L Thompson 
440eaf62fffSJeremy L Thompson /**
441eaf62fffSJeremy L Thompson   @brief Build nonzero pattern for non-composite operator
442eaf62fffSJeremy L Thompson 
443eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssembleSymbolic()
444eaf62fffSJeremy L Thompson 
445eaf62fffSJeremy L Thompson   @param[in]  op     CeedOperator to assemble nonzero pattern
446eaf62fffSJeremy L Thompson   @param[in]  offset Offset for number of entries
447eaf62fffSJeremy L Thompson   @param[out] rows   Row number for each entry
448eaf62fffSJeremy L Thompson   @param[out] cols   Column number for each entry
449eaf62fffSJeremy L Thompson 
450eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
451eaf62fffSJeremy L Thompson 
452eaf62fffSJeremy L Thompson   @ref Developer
453eaf62fffSJeremy L Thompson **/
4542b730f8bSJeremy L Thompson static int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset, CeedInt *rows, CeedInt *cols) {
455f3d47e36SJeremy L Thompson   Ceed ceed;
456f3d47e36SJeremy L Thompson   bool is_composite;
457f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
458f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
459f3d47e36SJeremy L Thompson 
460b275c451SJeremy L Thompson   if (is_composite) {
461eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
4622b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
463eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
4642b730f8bSJeremy L Thompson   }
465eaf62fffSJeremy L Thompson 
466c9366a6bSJeremy L Thompson   CeedSize num_nodes;
4672b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &num_nodes, NULL));
468eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_in;
4692b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &rstr_in));
470e79b91d9SJeremy L Thompson   CeedInt num_elem, elem_size, num_comp;
4712b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr_in, &num_elem));
4722b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr_in, &elem_size));
4732b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(rstr_in, &num_comp));
474eaf62fffSJeremy L Thompson   CeedInt layout_er[3];
4752b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr_in, &layout_er));
476eaf62fffSJeremy L Thompson 
477eaf62fffSJeremy L Thompson   CeedInt local_num_entries = elem_size * num_comp * elem_size * num_comp * num_elem;
478eaf62fffSJeremy L Thompson 
479eaf62fffSJeremy L Thompson   // Determine elem_dof relation
480eaf62fffSJeremy L Thompson   CeedVector index_vec;
4812b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed, num_nodes, &index_vec));
482eaf62fffSJeremy L Thompson   CeedScalar *array;
4832b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayWrite(index_vec, CEED_MEM_HOST, &array));
484ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_nodes; i++) array[i] = i;
4852b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(index_vec, &array));
486eaf62fffSJeremy L Thompson   CeedVector elem_dof;
4872b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed, num_elem * elem_size * num_comp, &elem_dof));
4882b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(elem_dof, 0.0));
4892b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionApply(rstr_in, CEED_NOTRANSPOSE, index_vec, elem_dof, CEED_REQUEST_IMMEDIATE));
490eaf62fffSJeremy L Thompson   const CeedScalar *elem_dof_a;
4912b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(elem_dof, CEED_MEM_HOST, &elem_dof_a));
4922b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&index_vec));
493eaf62fffSJeremy L Thompson 
494eaf62fffSJeremy L Thompson   // Determine i, j locations for element matrices
495eaf62fffSJeremy L Thompson   CeedInt count = 0;
496ed9e99e6SJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
497ed9e99e6SJeremy L Thompson     for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) {
498ed9e99e6SJeremy L Thompson       for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) {
499ed9e99e6SJeremy L Thompson         for (CeedInt i = 0; i < elem_size; i++) {
500ed9e99e6SJeremy L Thompson           for (CeedInt j = 0; j < elem_size; j++) {
5012b730f8bSJeremy L Thompson             const CeedInt elem_dof_index_row = i * layout_er[0] + (comp_out)*layout_er[1] + e * layout_er[2];
5022b730f8bSJeremy L Thompson             const CeedInt elem_dof_index_col = j * layout_er[0] + comp_in * layout_er[1] + e * layout_er[2];
503eaf62fffSJeremy L Thompson 
504eaf62fffSJeremy L Thompson             const CeedInt row = elem_dof_a[elem_dof_index_row];
505eaf62fffSJeremy L Thompson             const CeedInt col = elem_dof_a[elem_dof_index_col];
506eaf62fffSJeremy L Thompson 
507eaf62fffSJeremy L Thompson             rows[offset + count] = row;
508eaf62fffSJeremy L Thompson             cols[offset + count] = col;
509eaf62fffSJeremy L Thompson             count++;
510eaf62fffSJeremy L Thompson           }
511eaf62fffSJeremy L Thompson         }
512eaf62fffSJeremy L Thompson       }
513eaf62fffSJeremy L Thompson     }
514eaf62fffSJeremy L Thompson   }
5152b730f8bSJeremy L Thompson   if (count != local_num_entries) {
516eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
517eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing assembled entries");
518eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
5192b730f8bSJeremy L Thompson   }
5202b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(elem_dof, &elem_dof_a));
5212b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&elem_dof));
522eaf62fffSJeremy L Thompson 
523eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
524eaf62fffSJeremy L Thompson }
525eaf62fffSJeremy L Thompson 
526eaf62fffSJeremy L Thompson /**
527eaf62fffSJeremy L Thompson   @brief Assemble nonzero entries for non-composite operator
528eaf62fffSJeremy L Thompson 
529eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssemble()
530eaf62fffSJeremy L Thompson 
531eaf62fffSJeremy L Thompson   @param[in]  op     CeedOperator to assemble
532ea61e9acSJeremy L Thompson   @param[in]  offset Offset for number of entries
533eaf62fffSJeremy L Thompson   @param[out] values Values to assemble into matrix
534eaf62fffSJeremy L Thompson 
535eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
536eaf62fffSJeremy L Thompson 
537eaf62fffSJeremy L Thompson   @ref Developer
538eaf62fffSJeremy L Thompson **/
5392b730f8bSJeremy L Thompson static int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset, CeedVector values) {
540f3d47e36SJeremy L Thompson   Ceed ceed;
541f3d47e36SJeremy L Thompson   bool is_composite;
542f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
543f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
544f3d47e36SJeremy L Thompson 
545f3d47e36SJeremy L Thompson   if (is_composite) {
546eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
5472b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
548eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
5492b730f8bSJeremy L Thompson   }
550f3d47e36SJeremy L Thompson 
551f3d47e36SJeremy L Thompson   // Early exit for empty operator
552f3d47e36SJeremy L Thompson   {
553f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
554f3d47e36SJeremy L Thompson 
555f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
556f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
557f3d47e36SJeremy L Thompson   }
558eaf62fffSJeremy L Thompson 
559cefa2673SJeremy L Thompson   if (op->LinearAssembleSingle) {
560cefa2673SJeremy L Thompson     // Backend version
5612b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleSingle(op, offset, values));
562cefa2673SJeremy L Thompson     return CEED_ERROR_SUCCESS;
563cefa2673SJeremy L Thompson   } else {
564cefa2673SJeremy L Thompson     // Operator fallback
565cefa2673SJeremy L Thompson     CeedOperator op_fallback;
566cefa2673SJeremy L Thompson 
5672b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
568cefa2673SJeremy L Thompson     if (op_fallback) {
5692b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemble(op_fallback, offset, values));
570cefa2673SJeremy L Thompson       return CEED_ERROR_SUCCESS;
571cefa2673SJeremy L Thompson     }
572cefa2673SJeremy L Thompson   }
573cefa2673SJeremy L Thompson 
574eaf62fffSJeremy L Thompson   // Assemble QFunction
575eaf62fffSJeremy L Thompson   CeedQFunction qf;
5762b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
577eaf62fffSJeremy L Thompson   CeedVector          assembled_qf;
578eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_q;
5792b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &rstr_q, CEED_REQUEST_IMMEDIATE));
5801f9221feSJeremy L Thompson   CeedSize qf_length;
5812b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetLength(assembled_qf, &qf_length));
582eaf62fffSJeremy L Thompson 
5837e7773b5SJeremy L Thompson   CeedInt            num_input_fields, num_output_fields;
584eaf62fffSJeremy L Thompson   CeedOperatorField *input_fields;
585eaf62fffSJeremy L Thompson   CeedOperatorField *output_fields;
5862b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields));
587eaf62fffSJeremy L Thompson 
588ed9e99e6SJeremy L Thompson   // Get assembly data
589ed9e99e6SJeremy L Thompson   CeedOperatorAssemblyData data;
5902b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data));
591437c7c90SJeremy L Thompson   const CeedEvalMode **eval_modes_in, **eval_modes_out;
592437c7c90SJeremy L Thompson   CeedInt             *num_eval_modes_in, *num_eval_modes_out, num_active_bases;
593437c7c90SJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_active_bases, &num_eval_modes_in, &eval_modes_in, NULL, &num_eval_modes_out,
594437c7c90SJeremy L Thompson                                                 &eval_modes_out, NULL, NULL));
595437c7c90SJeremy L Thompson   CeedBasis *bases;
596437c7c90SJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &bases, NULL, NULL));
597437c7c90SJeremy L Thompson   CeedBasis basis_in = bases[0];
598eaf62fffSJeremy L Thompson 
599437c7c90SJeremy L Thompson   if (num_active_bases > 1) {
600437c7c90SJeremy L Thompson     // LCOV_EXCL_START
601437c7c90SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Cannot assemble operator with multiple active bases");
602437c7c90SJeremy L Thompson     // LCOV_EXCL_STOP
603437c7c90SJeremy L Thompson   }
604437c7c90SJeremy L Thompson   if (num_eval_modes_in[0] == 0 || num_eval_modes_out[0] == 0) {
605eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
6062b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Cannot assemble operator with out inputs/outputs");
607eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
6082b730f8bSJeremy L Thompson   }
609eaf62fffSJeremy L Thompson 
610ed9e99e6SJeremy L Thompson   CeedElemRestriction active_rstr;
611eaf62fffSJeremy L Thompson   CeedInt             num_elem, elem_size, num_qpts, num_comp;
6122b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &active_rstr));
6132b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(active_rstr, &num_elem));
6142b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(active_rstr, &elem_size));
6152b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(active_rstr, &num_comp));
6162b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts));
617eaf62fffSJeremy L Thompson 
618eaf62fffSJeremy L Thompson   CeedInt local_num_entries = elem_size * num_comp * elem_size * num_comp * num_elem;
619eaf62fffSJeremy L Thompson 
620eaf62fffSJeremy L Thompson   // loop over elements and put in data structure
621eaf62fffSJeremy L Thompson   const CeedScalar *assembled_qf_array;
6222b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array));
623eaf62fffSJeremy L Thompson 
624eaf62fffSJeremy L Thompson   CeedInt layout_qf[3];
6252b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr_q, &layout_qf));
6262b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_q));
627eaf62fffSJeremy L Thompson 
628eaf62fffSJeremy L Thompson   // we store B_mat_in, B_mat_out, BTD, elem_mat in row-major order
629437c7c90SJeremy L Thompson   const CeedScalar **B_mats_in, **B_mats_out;
630437c7c90SJeremy L Thompson   CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, NULL, &B_mats_in, &B_mats_out));
631437c7c90SJeremy L Thompson   const CeedScalar *B_mat_in = B_mats_in[0], *B_mat_out = B_mats_out[0];
632437c7c90SJeremy L Thompson   CeedScalar        BTD_mat[elem_size * num_qpts * num_eval_modes_in[0]];
633eaf62fffSJeremy L Thompson   CeedScalar        elem_mat[elem_size * elem_size];
63492ae7e47SJeremy L Thompson   CeedInt           count = 0;
635eaf62fffSJeremy L Thompson   CeedScalar       *vals;
63628ec399dSJeremy L Thompson   CeedCall(CeedVectorGetArray(values, CEED_MEM_HOST, &vals));
637ed9e99e6SJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
638ed9e99e6SJeremy L Thompson     for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) {
639ed9e99e6SJeremy L Thompson       for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) {
640ed9e99e6SJeremy L Thompson         // Compute B^T*D
641ed9e99e6SJeremy L Thompson         for (CeedInt n = 0; n < elem_size; n++) {
642ed9e99e6SJeremy L Thompson           for (CeedInt q = 0; q < num_qpts; q++) {
643437c7c90SJeremy L Thompson             for (CeedInt e_in = 0; e_in < num_eval_modes_in[0]; e_in++) {
644437c7c90SJeremy L Thompson               const CeedInt btd_index = n * (num_qpts * num_eval_modes_in[0]) + (num_eval_modes_in[0] * q + e_in);
645067fd99fSJeremy L Thompson               CeedScalar    sum       = 0.0;
646437c7c90SJeremy L Thompson               for (CeedInt e_out = 0; e_out < num_eval_modes_out[0]; e_out++) {
647437c7c90SJeremy L Thompson                 const CeedInt b_out_index     = (num_eval_modes_out[0] * q + e_out) * elem_size + n;
648437c7c90SJeremy L Thompson                 const CeedInt eval_mode_index = ((e_in * num_comp + comp_in) * num_eval_modes_out[0] + e_out) * num_comp + comp_out;
6492b730f8bSJeremy L Thompson                 const CeedInt qf_index        = q * layout_qf[0] + eval_mode_index * layout_qf[1] + e * layout_qf[2];
650067fd99fSJeremy L Thompson                 sum += B_mat_out[b_out_index] * assembled_qf_array[qf_index];
651eaf62fffSJeremy L Thompson               }
652067fd99fSJeremy L Thompson               BTD_mat[btd_index] = sum;
653ed9e99e6SJeremy L Thompson             }
654ed9e99e6SJeremy L Thompson           }
655eaf62fffSJeremy L Thompson         }
656eaf62fffSJeremy L Thompson         // form element matrix itself (for each block component)
657437c7c90SJeremy L Thompson         CeedCall(CeedMatrixMatrixMultiply(ceed, BTD_mat, B_mat_in, elem_mat, elem_size, elem_size, num_qpts * num_eval_modes_in[0]));
658eaf62fffSJeremy L Thompson 
659eaf62fffSJeremy L Thompson         // put element matrix in coordinate data structure
660ed9e99e6SJeremy L Thompson         for (CeedInt i = 0; i < elem_size; i++) {
661ed9e99e6SJeremy L Thompson           for (CeedInt j = 0; j < elem_size; j++) {
662eaf62fffSJeremy L Thompson             vals[offset + count] = elem_mat[i * elem_size + j];
663eaf62fffSJeremy L Thompson             count++;
664eaf62fffSJeremy L Thompson           }
665eaf62fffSJeremy L Thompson         }
666eaf62fffSJeremy L Thompson       }
667eaf62fffSJeremy L Thompson     }
668eaf62fffSJeremy L Thompson   }
6692b730f8bSJeremy L Thompson   if (count != local_num_entries) {
670eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
671eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing entries");
672eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
6732b730f8bSJeremy L Thompson   }
6742b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(values, &vals));
675eaf62fffSJeremy L Thompson 
6762b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array));
6772b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled_qf));
678eaf62fffSJeremy L Thompson 
679eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
680eaf62fffSJeremy L Thompson }
681eaf62fffSJeremy L Thompson 
682eaf62fffSJeremy L Thompson /**
683eaf62fffSJeremy L Thompson   @brief Count number of entries for assembled CeedOperator
684eaf62fffSJeremy L Thompson 
685eaf62fffSJeremy L Thompson   @param[in]  op          CeedOperator to assemble
686eaf62fffSJeremy L Thompson   @param[out] num_entries Number of entries in assembled representation
687eaf62fffSJeremy L Thompson 
688eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
689eaf62fffSJeremy L Thompson 
690eaf62fffSJeremy L Thompson   @ref Utility
691eaf62fffSJeremy L Thompson **/
6922b730f8bSJeremy L Thompson static int CeedSingleOperatorAssemblyCountEntries(CeedOperator op, CeedInt *num_entries) {
693b275c451SJeremy L Thompson   bool                is_composite;
694eaf62fffSJeremy L Thompson   CeedElemRestriction rstr;
695eaf62fffSJeremy L Thompson   CeedInt             num_elem, elem_size, num_comp;
696eaf62fffSJeremy L Thompson 
697b275c451SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
698b275c451SJeremy L Thompson   if (is_composite) {
699eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
7002b730f8bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
701eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
7022b730f8bSJeremy L Thompson   }
7032b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveElemRestriction(op, &rstr));
7042b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
7052b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
7062b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
707eaf62fffSJeremy L Thompson   *num_entries = elem_size * num_comp * elem_size * num_comp * num_elem;
708eaf62fffSJeremy L Thompson 
709eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
710eaf62fffSJeremy L Thompson }
711eaf62fffSJeremy L Thompson 
712eaf62fffSJeremy L Thompson /**
713ea61e9acSJeremy L Thompson   @brief Common code for creating a multigrid coarse operator and level transfer operators for a CeedOperator
714eaf62fffSJeremy L Thompson 
715eaf62fffSJeremy L Thompson   @param[in]  op_fine      Fine grid operator
71685bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine  L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
717eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse  Coarse grid restriction
718eaf62fffSJeremy L Thompson   @param[in]  basis_coarse Coarse grid active vector basis
71985bb9dcfSJeremy L Thompson   @param[in]  basis_c_to_f Basis for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators
720eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
72185bb9dcfSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator, or NULL
72285bb9dcfSJeremy L Thompson   @param[out] op_restrict  Fine to coarse operator, or NULL
723eaf62fffSJeremy L Thompson 
724eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
725eaf62fffSJeremy L Thompson 
726eaf62fffSJeremy L Thompson   @ref Developer
727eaf62fffSJeremy L Thompson **/
7282b730f8bSJeremy L Thompson static int CeedSingleOperatorMultigridLevel(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
7292b730f8bSJeremy L Thompson                                             CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_restrict) {
730eaf62fffSJeremy L Thompson   Ceed       ceed;
73185bb9dcfSJeremy L Thompson   CeedVector mult_vec = NULL;
7322b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
733eaf62fffSJeremy L Thompson 
734eaf62fffSJeremy L Thompson   // Check for composite operator
735eaf62fffSJeremy L Thompson   bool is_composite;
7362b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op_fine, &is_composite));
7372b730f8bSJeremy L Thompson   if (is_composite) {
738eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
7392b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Automatic multigrid setup for composite operators not supported");
740eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
7412b730f8bSJeremy L Thompson   }
742eaf62fffSJeremy L Thompson 
743eaf62fffSJeremy L Thompson   // Coarse Grid
7442b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT, op_coarse));
745eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_fine = NULL;
746eaf62fffSJeremy L Thompson   // -- Clone input fields
74792ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_input_fields; i++) {
748eaf62fffSJeremy L Thompson     if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
749437c7c90SJeremy L Thompson       rstr_fine = op_fine->input_fields[i]->elem_rstr;
7502b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE));
751eaf62fffSJeremy L Thompson     } else {
752437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, op_fine->input_fields[i]->elem_rstr,
7532b730f8bSJeremy L Thompson                                     op_fine->input_fields[i]->basis, op_fine->input_fields[i]->vec));
754eaf62fffSJeremy L Thompson     }
755eaf62fffSJeremy L Thompson   }
756eaf62fffSJeremy L Thompson   // -- Clone output fields
75792ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_output_fields; i++) {
758eaf62fffSJeremy L Thompson     if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
7592b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE));
760eaf62fffSJeremy L Thompson     } else {
761437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, op_fine->output_fields[i]->elem_rstr,
7622b730f8bSJeremy L Thompson                                     op_fine->output_fields[i]->basis, op_fine->output_fields[i]->vec));
763eaf62fffSJeremy L Thompson     }
764eaf62fffSJeremy L Thompson   }
765af99e877SJeremy L Thompson   // -- Clone QFunctionAssemblyData
7662b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op_fine->qf_assembled, &(*op_coarse)->qf_assembled));
767eaf62fffSJeremy L Thompson 
768eaf62fffSJeremy L Thompson   // Multiplicity vector
76985bb9dcfSJeremy L Thompson   if (op_restrict || op_prolong) {
77085bb9dcfSJeremy L Thompson     CeedVector mult_e_vec;
77185bb9dcfSJeremy L Thompson 
77285bb9dcfSJeremy L Thompson     if (!p_mult_fine) {
77385bb9dcfSJeremy L Thompson       // LCOV_EXCL_START
77485bb9dcfSJeremy L Thompson       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "Prolongation or restriction operator creation requires fine grid multiplicity vector");
77585bb9dcfSJeremy L Thompson       // LCOV_EXCL_STOP
77685bb9dcfSJeremy L Thompson     }
7772b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec));
7782b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(mult_e_vec, 0.0));
7792b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionApply(rstr_fine, CEED_NOTRANSPOSE, p_mult_fine, mult_e_vec, CEED_REQUEST_IMMEDIATE));
7802b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(mult_vec, 0.0));
7812b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionApply(rstr_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec, CEED_REQUEST_IMMEDIATE));
7822b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&mult_e_vec));
7832b730f8bSJeremy L Thompson     CeedCall(CeedVectorReciprocal(mult_vec));
78485bb9dcfSJeremy L Thompson   }
785eaf62fffSJeremy L Thompson 
786addd79feSZach Atkins   // Clone name
787addd79feSZach Atkins   bool   has_name = op_fine->name;
788addd79feSZach Atkins   size_t name_len = op_fine->name ? strlen(op_fine->name) : 0;
789addd79feSZach Atkins   CeedCall(CeedOperatorSetName(*op_coarse, op_fine->name));
790addd79feSZach Atkins 
79183d6adf3SZach Atkins   // Check that coarse to fine basis is provided if prolong/restrict operators are requested
79283d6adf3SZach Atkins   if ((op_restrict || op_prolong) && !basis_c_to_f) {
79383d6adf3SZach Atkins     // LCOV_EXCL_START
79483d6adf3SZach Atkins     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "Prolongation or restriction operator creation requires coarse-to-fine basis");
79583d6adf3SZach Atkins     // LCOV_EXCL_STOP
79683d6adf3SZach Atkins   }
79783d6adf3SZach Atkins 
79885bb9dcfSJeremy L Thompson   // Restriction/Prolongation Operators
799eaf62fffSJeremy L Thompson   CeedInt num_comp;
8002b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis_coarse, &num_comp));
801addd79feSZach Atkins 
802addd79feSZach Atkins   // Restriction
803addd79feSZach Atkins   if (op_restrict) {
804eaf62fffSJeremy L Thompson     CeedInt             *num_comp_r_data;
80585bb9dcfSJeremy L Thompson     CeedQFunction        qf_restrict;
80685bb9dcfSJeremy L Thompson     CeedQFunctionContext ctx_r;
80785bb9dcfSJeremy L Thompson 
80885bb9dcfSJeremy L Thompson     CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_restrict));
8092b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &num_comp_r_data));
810eaf62fffSJeremy L Thompson     num_comp_r_data[0] = num_comp;
8112b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextCreate(ceed, &ctx_r));
8122b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_r_data), num_comp_r_data));
8132b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetContext(qf_restrict, ctx_r));
8142b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextDestroy(&ctx_r));
8152b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(qf_restrict, "input", num_comp, CEED_EVAL_NONE));
8162b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(qf_restrict, "scale", num_comp, CEED_EVAL_NONE));
8172b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddOutput(qf_restrict, "output", num_comp, CEED_EVAL_INTERP));
8182b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_restrict, num_comp));
819eaf62fffSJeremy L Thompson 
8202b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreate(ceed, qf_restrict, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_restrict));
8212b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_restrict, "input", rstr_fine, CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE));
8222b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_restrict, "scale", rstr_fine, CEED_BASIS_COLLOCATED, mult_vec));
8232b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_restrict, "output", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE));
824eaf62fffSJeremy L Thompson 
825addd79feSZach Atkins     // Set name
826addd79feSZach Atkins     char *restriction_name;
827addd79feSZach Atkins     CeedCall(CeedCalloc(17 + name_len, &restriction_name));
828addd79feSZach Atkins     sprintf(restriction_name, "restriction%s%s", has_name ? " for " : "", has_name ? op_fine->name : "");
829addd79feSZach Atkins     CeedCall(CeedOperatorSetName(*op_restrict, restriction_name));
830addd79feSZach Atkins     CeedCall(CeedFree(&restriction_name));
831addd79feSZach Atkins 
832addd79feSZach Atkins     // Check
833addd79feSZach Atkins     CeedCall(CeedOperatorCheckReady(*op_restrict));
834addd79feSZach Atkins 
835addd79feSZach Atkins     // Cleanup
836addd79feSZach Atkins     CeedCall(CeedQFunctionDestroy(&qf_restrict));
837addd79feSZach Atkins   }
838addd79feSZach Atkins 
839eaf62fffSJeremy L Thompson   // Prolongation
840addd79feSZach Atkins   if (op_prolong) {
841eaf62fffSJeremy L Thompson     CeedInt             *num_comp_p_data;
84285bb9dcfSJeremy L Thompson     CeedQFunction        qf_prolong;
84385bb9dcfSJeremy L Thompson     CeedQFunctionContext ctx_p;
84485bb9dcfSJeremy L Thompson 
84585bb9dcfSJeremy L Thompson     CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong));
8462b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &num_comp_p_data));
847eaf62fffSJeremy L Thompson     num_comp_p_data[0] = num_comp;
8482b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextCreate(ceed, &ctx_p));
8492b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_p_data), num_comp_p_data));
8502b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetContext(qf_prolong, ctx_p));
8512b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextDestroy(&ctx_p));
8522b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP));
8532b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE));
8542b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE));
8552b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_prolong, num_comp));
856eaf62fffSJeremy L Thompson 
8572b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_prolong));
8582b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE));
8592b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_prolong, "scale", rstr_fine, CEED_BASIS_COLLOCATED, mult_vec));
8602b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_prolong, "output", rstr_fine, CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE));
861eaf62fffSJeremy L Thompson 
862addd79feSZach Atkins     // Set name
863ea6b5821SJeremy L Thompson     char *prolongation_name;
8642b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(18 + name_len, &prolongation_name));
8652b730f8bSJeremy L Thompson     sprintf(prolongation_name, "prolongation%s%s", has_name ? " for " : "", has_name ? op_fine->name : "");
8662b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetName(*op_prolong, prolongation_name));
8672b730f8bSJeremy L Thompson     CeedCall(CeedFree(&prolongation_name));
868addd79feSZach Atkins 
869addd79feSZach Atkins     // Check
870addd79feSZach Atkins     CeedCall(CeedOperatorCheckReady(*op_prolong));
871addd79feSZach Atkins 
872addd79feSZach Atkins     // Cleanup
873addd79feSZach Atkins     CeedCall(CeedQFunctionDestroy(&qf_prolong));
874ea6b5821SJeremy L Thompson   }
875ea6b5821SJeremy L Thompson 
87658e4b056SJeremy L Thompson   // Check
87758e4b056SJeremy L Thompson   CeedCall(CeedOperatorCheckReady(*op_coarse));
87858e4b056SJeremy L Thompson 
879eaf62fffSJeremy L Thompson   // Cleanup
8802b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&mult_vec));
8812b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&basis_c_to_f));
882805fe78eSJeremy L Thompson 
883eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
884eaf62fffSJeremy L Thompson }
885eaf62fffSJeremy L Thompson 
886eaf62fffSJeremy L Thompson /**
887eaf62fffSJeremy L Thompson   @brief Build 1D mass matrix and Laplacian with perturbation
888eaf62fffSJeremy L Thompson 
889eaf62fffSJeremy L Thompson   @param[in]  interp_1d   Interpolation matrix in one dimension
890eaf62fffSJeremy L Thompson   @param[in]  grad_1d     Gradient matrix in one dimension
891eaf62fffSJeremy L Thompson   @param[in]  q_weight_1d Quadrature weights in one dimension
892eaf62fffSJeremy L Thompson   @param[in]  P_1d        Number of basis nodes in one dimension
893eaf62fffSJeremy L Thompson   @param[in]  Q_1d        Number of quadrature points in one dimension
894eaf62fffSJeremy L Thompson   @param[in]  dim         Dimension of basis
895eaf62fffSJeremy L Thompson   @param[out] mass        Assembled mass matrix in one dimension
896eaf62fffSJeremy L Thompson   @param[out] laplace     Assembled perturbed Laplacian in one dimension
897eaf62fffSJeremy L Thompson 
898eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
899eaf62fffSJeremy L Thompson 
900eaf62fffSJeremy L Thompson   @ref Developer
901eaf62fffSJeremy L Thompson **/
9022c2ea1dbSJeremy L Thompson CeedPragmaOptimizeOff
9032c2ea1dbSJeremy L Thompson static int CeedBuildMassLaplace(const CeedScalar *interp_1d, const CeedScalar *grad_1d, const CeedScalar *q_weight_1d, CeedInt P_1d, CeedInt Q_1d,
9042c2ea1dbSJeremy L Thompson                                 CeedInt dim, CeedScalar *mass, CeedScalar *laplace) {
9052b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
906eaf62fffSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) {
907eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
9082b730f8bSJeremy 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];
909eaf62fffSJeremy L Thompson       mass[i + j * P_1d] = sum;
910eaf62fffSJeremy L Thompson     }
9112b730f8bSJeremy L Thompson   }
912eaf62fffSJeremy L Thompson   // -- Laplacian
9132b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
914eaf62fffSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) {
915eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
9162b730f8bSJeremy 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];
917eaf62fffSJeremy L Thompson       laplace[i + j * P_1d] = sum;
918eaf62fffSJeremy L Thompson     }
9192b730f8bSJeremy L Thompson   }
920eaf62fffSJeremy L Thompson   CeedScalar perturbation = dim > 2 ? 1e-6 : 1e-4;
9212b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) laplace[i + P_1d * i] += perturbation;
922eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
923eaf62fffSJeremy L Thompson }
9242c2ea1dbSJeremy L Thompson CeedPragmaOptimizeOn
925eaf62fffSJeremy L Thompson 
926eaf62fffSJeremy L Thompson /// @}
927eaf62fffSJeremy L Thompson 
928eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
929480fae85SJeremy L Thompson /// CeedOperator Backend API
930480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
931480fae85SJeremy L Thompson /// @addtogroup CeedOperatorBackend
932480fae85SJeremy L Thompson /// @{
933480fae85SJeremy L Thompson 
934480fae85SJeremy L Thompson /**
935480fae85SJeremy L Thompson   @brief Create object holding CeedQFunction assembly data for CeedOperator
936480fae85SJeremy L Thompson 
937480fae85SJeremy L Thompson   @param[in]  ceed A Ceed object where the CeedQFunctionAssemblyData will be created
938ea61e9acSJeremy L Thompson   @param[out] data Address of the variable where the newly created CeedQFunctionAssemblyData will be stored
939480fae85SJeremy L Thompson 
940480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
941480fae85SJeremy L Thompson 
942480fae85SJeremy L Thompson   @ref Backend
943480fae85SJeremy L Thompson **/
944ea61e9acSJeremy L Thompson int CeedQFunctionAssemblyDataCreate(Ceed ceed, CeedQFunctionAssemblyData *data) {
9452b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, data));
946480fae85SJeremy L Thompson   (*data)->ref_count = 1;
947480fae85SJeremy L Thompson   (*data)->ceed      = ceed;
9482b730f8bSJeremy L Thompson   CeedCall(CeedReference(ceed));
949480fae85SJeremy L Thompson 
950480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
951480fae85SJeremy L Thompson }
952480fae85SJeremy L Thompson 
953480fae85SJeremy L Thompson /**
954480fae85SJeremy L Thompson   @brief Increment the reference counter for a CeedQFunctionAssemblyData
955480fae85SJeremy L Thompson 
956ea61e9acSJeremy L Thompson   @param[in,out] data CeedQFunctionAssemblyData to increment the reference counter
957480fae85SJeremy L Thompson 
958480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
959480fae85SJeremy L Thompson 
960480fae85SJeremy L Thompson   @ref Backend
961480fae85SJeremy L Thompson **/
962480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataReference(CeedQFunctionAssemblyData data) {
963480fae85SJeremy L Thompson   data->ref_count++;
964480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
965480fae85SJeremy L Thompson }
966480fae85SJeremy L Thompson 
967480fae85SJeremy L Thompson /**
968beecbf24SJeremy L Thompson   @brief Set re-use of CeedQFunctionAssemblyData
9698b919e6bSJeremy L Thompson 
970ea61e9acSJeremy L Thompson   @param[in,out] data       CeedQFunctionAssemblyData to mark for reuse
971ea61e9acSJeremy L Thompson   @param[in]     reuse_data Boolean flag indicating data re-use
9728b919e6bSJeremy L Thompson 
9738b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
9748b919e6bSJeremy L Thompson 
9758b919e6bSJeremy L Thompson   @ref Backend
9768b919e6bSJeremy L Thompson **/
9772b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetReuse(CeedQFunctionAssemblyData data, bool reuse_data) {
978beecbf24SJeremy L Thompson   data->reuse_data        = reuse_data;
979beecbf24SJeremy L Thompson   data->needs_data_update = true;
980beecbf24SJeremy L Thompson   return CEED_ERROR_SUCCESS;
981beecbf24SJeremy L Thompson }
982beecbf24SJeremy L Thompson 
983beecbf24SJeremy L Thompson /**
984beecbf24SJeremy L Thompson   @brief Mark QFunctionAssemblyData as stale
985beecbf24SJeremy L Thompson 
986ea61e9acSJeremy L Thompson   @param[in,out] data              CeedQFunctionAssemblyData to mark as stale
987ea61e9acSJeremy L Thompson   @param[in]     needs_data_update Boolean flag indicating if update is needed or completed
988beecbf24SJeremy L Thompson 
989beecbf24SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
990beecbf24SJeremy L Thompson 
991beecbf24SJeremy L Thompson   @ref Backend
992beecbf24SJeremy L Thompson **/
9932b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetUpdateNeeded(CeedQFunctionAssemblyData data, bool needs_data_update) {
994beecbf24SJeremy L Thompson   data->needs_data_update = needs_data_update;
9958b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
9968b919e6bSJeremy L Thompson }
9978b919e6bSJeremy L Thompson 
9988b919e6bSJeremy L Thompson /**
9998b919e6bSJeremy L Thompson   @brief Determine if QFunctionAssemblyData needs update
10008b919e6bSJeremy L Thompson 
10018b919e6bSJeremy L Thompson   @param[in]  data             CeedQFunctionAssemblyData to mark as stale
10028b919e6bSJeremy L Thompson   @param[out] is_update_needed Boolean flag indicating if re-assembly is required
10038b919e6bSJeremy L Thompson 
10048b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
10058b919e6bSJeremy L Thompson 
10068b919e6bSJeremy L Thompson   @ref Backend
10078b919e6bSJeremy L Thompson **/
10082b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataIsUpdateNeeded(CeedQFunctionAssemblyData data, bool *is_update_needed) {
1009beecbf24SJeremy L Thompson   *is_update_needed = !data->reuse_data || data->needs_data_update;
10108b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
10118b919e6bSJeremy L Thompson }
10128b919e6bSJeremy L Thompson 
10138b919e6bSJeremy L Thompson /**
1014ea61e9acSJeremy L Thompson   @brief Copy the pointer to a CeedQFunctionAssemblyData.
10154385fb7fSSebastian Grimberg 
1016ea61e9acSJeremy L Thompson   Both pointers should be destroyed with `CeedCeedQFunctionAssemblyDataDestroy()`.
1017512bb800SJeremy L Thompson 
1018512bb800SJeremy L Thompson   Note: If the value of `data_copy` passed to this function is non-NULL, then it is assumed that `*data_copy` is a pointer to a
1019512bb800SJeremy L Thompson         CeedQFunctionAssemblyData. This CeedQFunctionAssemblyData will be destroyed if `data_copy` is the only reference to this
1020512bb800SJeremy L Thompson         CeedQFunctionAssemblyData.
1021480fae85SJeremy L Thompson 
1022ea61e9acSJeremy L Thompson   @param[in]     data      CeedQFunctionAssemblyData to copy reference to
1023ea61e9acSJeremy L Thompson   @param[in,out] data_copy Variable to store copied reference
1024480fae85SJeremy L Thompson 
1025480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1026480fae85SJeremy L Thompson 
1027480fae85SJeremy L Thompson   @ref Backend
1028480fae85SJeremy L Thompson **/
10292b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataReferenceCopy(CeedQFunctionAssemblyData data, CeedQFunctionAssemblyData *data_copy) {
10302b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataReference(data));
10312b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataDestroy(data_copy));
1032480fae85SJeremy L Thompson   *data_copy = data;
1033480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1034480fae85SJeremy L Thompson }
1035480fae85SJeremy L Thompson 
1036480fae85SJeremy L Thompson /**
1037480fae85SJeremy L Thompson   @brief Get setup status for internal objects for CeedQFunctionAssemblyData
1038480fae85SJeremy L Thompson 
1039ea61e9acSJeremy L Thompson   @param[in]  data     CeedQFunctionAssemblyData to retrieve status
1040480fae85SJeremy L Thompson   @param[out] is_setup Boolean flag for setup status
1041480fae85SJeremy L Thompson 
1042480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1043480fae85SJeremy L Thompson 
1044480fae85SJeremy L Thompson   @ref Backend
1045480fae85SJeremy L Thompson **/
10462b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataIsSetup(CeedQFunctionAssemblyData data, bool *is_setup) {
1047480fae85SJeremy L Thompson   *is_setup = data->is_setup;
1048480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1049480fae85SJeremy L Thompson }
1050480fae85SJeremy L Thompson 
1051480fae85SJeremy L Thompson /**
1052480fae85SJeremy L Thompson   @brief Set internal objects for CeedQFunctionAssemblyData
1053480fae85SJeremy L Thompson 
1054ea61e9acSJeremy L Thompson   @param[in,out] data CeedQFunctionAssemblyData to set objects
1055480fae85SJeremy L Thompson   @param[in]     vec  CeedVector to store assembled CeedQFunction at quadrature points
1056480fae85SJeremy L Thompson   @param[in]     rstr CeedElemRestriction for CeedVector containing assembled CeedQFunction
1057480fae85SJeremy L Thompson 
1058480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1059480fae85SJeremy L Thompson 
1060480fae85SJeremy L Thompson   @ref Backend
1061480fae85SJeremy L Thompson **/
10622b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetObjects(CeedQFunctionAssemblyData data, CeedVector vec, CeedElemRestriction rstr) {
10632b730f8bSJeremy L Thompson   CeedCall(CeedVectorReferenceCopy(vec, &data->vec));
10642b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &data->rstr));
1065480fae85SJeremy L Thompson 
1066480fae85SJeremy L Thompson   data->is_setup = true;
1067480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1068480fae85SJeremy L Thompson }
1069480fae85SJeremy L Thompson 
10702b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataGetObjects(CeedQFunctionAssemblyData data, CeedVector *vec, CeedElemRestriction *rstr) {
10712b730f8bSJeremy L Thompson   if (!data->is_setup) {
1072480fae85SJeremy L Thompson     // LCOV_EXCL_START
10732b730f8bSJeremy L Thompson     return CeedError(data->ceed, CEED_ERROR_INCOMPLETE, "Internal objects not set; must call CeedQFunctionAssemblyDataSetObjects first.");
1074480fae85SJeremy L Thompson     // LCOV_EXCL_STOP
10752b730f8bSJeremy L Thompson   }
1076480fae85SJeremy L Thompson 
10772b730f8bSJeremy L Thompson   CeedCall(CeedVectorReferenceCopy(data->vec, vec));
10782b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionReferenceCopy(data->rstr, rstr));
1079480fae85SJeremy L Thompson 
1080480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1081480fae85SJeremy L Thompson }
1082480fae85SJeremy L Thompson 
1083480fae85SJeremy L Thompson /**
1084480fae85SJeremy L Thompson   @brief Destroy CeedQFunctionAssemblyData
1085480fae85SJeremy L Thompson 
1086ea61e9acSJeremy L Thompson   @param[in,out] data  CeedQFunctionAssemblyData to destroy
1087480fae85SJeremy L Thompson 
1088480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1089480fae85SJeremy L Thompson 
1090480fae85SJeremy L Thompson   @ref Backend
1091480fae85SJeremy L Thompson **/
1092480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataDestroy(CeedQFunctionAssemblyData *data) {
1093ad6481ceSJeremy L Thompson   if (!*data || --(*data)->ref_count > 0) {
1094ad6481ceSJeremy L Thompson     *data = NULL;
1095ad6481ceSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1096ad6481ceSJeremy L Thompson   }
10972b730f8bSJeremy L Thompson   CeedCall(CeedDestroy(&(*data)->ceed));
10982b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&(*data)->vec));
10992b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&(*data)->rstr));
1100480fae85SJeremy L Thompson 
11012b730f8bSJeremy L Thompson   CeedCall(CeedFree(data));
1102480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1103480fae85SJeremy L Thompson }
1104480fae85SJeremy L Thompson 
1105ed9e99e6SJeremy L Thompson /**
1106ed9e99e6SJeremy L Thompson   @brief Get CeedOperatorAssemblyData
1107ed9e99e6SJeremy L Thompson 
1108ed9e99e6SJeremy L Thompson   @param[in]  op   CeedOperator to assemble
1109ed9e99e6SJeremy L Thompson   @param[out] data CeedQFunctionAssemblyData
1110ed9e99e6SJeremy L Thompson 
1111ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1112ed9e99e6SJeremy L Thompson 
1113ed9e99e6SJeremy L Thompson   @ref Backend
1114ed9e99e6SJeremy L Thompson **/
11152b730f8bSJeremy L Thompson int CeedOperatorGetOperatorAssemblyData(CeedOperator op, CeedOperatorAssemblyData *data) {
1116ed9e99e6SJeremy L Thompson   if (!op->op_assembled) {
1117ed9e99e6SJeremy L Thompson     CeedOperatorAssemblyData data;
1118ed9e99e6SJeremy L Thompson 
11192b730f8bSJeremy L Thompson     CeedCall(CeedOperatorAssemblyDataCreate(op->ceed, op, &data));
1120ed9e99e6SJeremy L Thompson     op->op_assembled = data;
1121ed9e99e6SJeremy L Thompson   }
1122ed9e99e6SJeremy L Thompson   *data = op->op_assembled;
1123ed9e99e6SJeremy L Thompson 
1124ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1125ed9e99e6SJeremy L Thompson }
1126ed9e99e6SJeremy L Thompson 
1127ed9e99e6SJeremy L Thompson /**
1128ba746a46SJeremy L Thompson   @brief Create object holding CeedOperator assembly data.
1129ba746a46SJeremy L Thompson 
1130ba746a46SJeremy L Thompson   The CeedOperatorAssemblyData holds an array with references to every active CeedBasis used in the CeedOperator.
1131ba746a46SJeremy L Thompson   An array with references to the corresponding active CeedElemRestrictions is also stored.
1132ba746a46SJeremy L Thompson   For each active CeedBasis, the CeedOperatorAssemblyData holds an array of all input and output CeedEvalModes for this CeedBasis.
1133ba746a46SJeremy L Thompson   The CeedOperatorAssemblyData holds an array of offsets for indexing into the assembled CeedQFunction arrays to the row representing each
1134*9fd66db6SSebastian Grimberg CeedEvalMode.
1135*9fd66db6SSebastian Grimberg   The number of input columns across all active bases for the assembled CeedQFunction is also stored.
1136*9fd66db6SSebastian Grimberg   Lastly, the CeedOperatorAssembly data holds assembled matrices representing the full action of the CeedBasis for all CeedEvalModes.
1137ed9e99e6SJeremy L Thompson 
1138ea61e9acSJeremy L Thompson   @param[in]  ceed Ceed object where the CeedOperatorAssemblyData will be created
1139ed9e99e6SJeremy L Thompson   @param[in]  op   CeedOperator to be assembled
1140ea61e9acSJeremy L Thompson   @param[out] data Address of the variable where the newly created CeedOperatorAssemblyData will be stored
1141ed9e99e6SJeremy L Thompson 
1142ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1143ed9e99e6SJeremy L Thompson 
1144ed9e99e6SJeremy L Thompson   @ref Backend
1145ed9e99e6SJeremy L Thompson **/
11462b730f8bSJeremy L Thompson int CeedOperatorAssemblyDataCreate(Ceed ceed, CeedOperator op, CeedOperatorAssemblyData *data) {
1147437c7c90SJeremy L Thompson   CeedInt num_active_bases = 0;
1148437c7c90SJeremy L Thompson 
1149437c7c90SJeremy L Thompson   // Allocate
11502b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, data));
1151ed9e99e6SJeremy L Thompson   (*data)->ceed = ceed;
11522b730f8bSJeremy L Thompson   CeedCall(CeedReference(ceed));
1153ed9e99e6SJeremy L Thompson 
1154ed9e99e6SJeremy L Thompson   // Build OperatorAssembly data
1155ed9e99e6SJeremy L Thompson   CeedQFunction       qf;
1156ed9e99e6SJeremy L Thompson   CeedQFunctionField *qf_fields;
1157ed9e99e6SJeremy L Thompson   CeedOperatorField  *op_fields;
1158ed9e99e6SJeremy L Thompson   CeedInt             num_input_fields;
11592b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
11602b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, &num_input_fields, &qf_fields, NULL, NULL));
11612b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, NULL, &op_fields, NULL, NULL));
1162ed9e99e6SJeremy L Thompson 
1163ed9e99e6SJeremy L Thompson   // Determine active input basis
1164437c7c90SJeremy L Thompson   CeedInt       *num_eval_modes_in = NULL, *num_eval_modes_out = NULL, offset = 0;
1165437c7c90SJeremy L Thompson   CeedEvalMode **eval_modes_in = NULL, **eval_modes_out = NULL;
1166437c7c90SJeremy L Thompson   CeedSize     **eval_mode_offsets_in = NULL, **eval_mode_offsets_out = NULL;
1167ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_input_fields; i++) {
1168ed9e99e6SJeremy L Thompson     CeedVector vec;
11692b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
1170ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
1171437c7c90SJeremy L Thompson       CeedBasis    basis_in = NULL;
1172437c7c90SJeremy L Thompson       CeedEvalMode eval_mode;
1173352a5e7cSSebastian Grimberg       CeedInt      index = -1, dim, num_comp, q_comp;
11742b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_in));
11752b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
1176352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetDimension(basis_in, &dim));
1177352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumComponents(basis_in, &num_comp));
1178352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumQuadratureComponents(basis_in, eval_mode, &q_comp));
1179437c7c90SJeremy L Thompson       for (CeedInt i = 0; i < num_active_bases; i++) {
1180437c7c90SJeremy L Thompson         if ((*data)->active_bases[i] == basis_in) index = i;
1181437c7c90SJeremy L Thompson       }
1182437c7c90SJeremy L Thompson       if (index == -1) {
1183437c7c90SJeremy L Thompson         CeedElemRestriction elem_rstr_in;
1184437c7c90SJeremy L Thompson         index = num_active_bases;
1185437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_bases));
1186437c7c90SJeremy L Thompson         (*data)->active_bases[num_active_bases] = NULL;
1187437c7c90SJeremy L Thompson         CeedCall(CeedBasisReferenceCopy(basis_in, &(*data)->active_bases[num_active_bases]));
1188437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_elem_rstrs));
1189437c7c90SJeremy L Thompson         (*data)->active_elem_rstrs[num_active_bases] = NULL;
1190437c7c90SJeremy L Thompson         CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_rstr_in));
1191437c7c90SJeremy L Thompson         CeedCall(CeedElemRestrictionReferenceCopy(elem_rstr_in, &(*data)->active_elem_rstrs[num_active_bases]));
1192437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_in));
1193437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_out));
1194437c7c90SJeremy L Thompson         num_eval_modes_in[index]  = 0;
1195437c7c90SJeremy L Thompson         num_eval_modes_out[index] = 0;
1196437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_in));
1197437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_out));
1198437c7c90SJeremy L Thompson         eval_modes_in[index]  = NULL;
1199437c7c90SJeremy L Thompson         eval_modes_out[index] = NULL;
1200437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_in));
1201437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_out));
1202437c7c90SJeremy L Thompson         eval_mode_offsets_in[index]  = NULL;
1203437c7c90SJeremy L Thompson         eval_mode_offsets_out[index] = NULL;
1204437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_in));
1205437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_out));
1206437c7c90SJeremy L Thompson         (*data)->assembled_bases_in[index]  = NULL;
1207437c7c90SJeremy L Thompson         (*data)->assembled_bases_out[index] = NULL;
1208437c7c90SJeremy L Thompson         num_active_bases++;
1209437c7c90SJeremy L Thompson       }
1210352a5e7cSSebastian Grimberg       if (eval_mode != CEED_EVAL_WEIGHT) {
1211352a5e7cSSebastian Grimberg         // q_comp = 1 if CEED_EVAL_NONE, CEED_EVAL_WEIGHT caught by QF Assembly
1212352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_in[index] + q_comp, &eval_modes_in[index]));
1213352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_in[index] + q_comp, &eval_mode_offsets_in[index]));
1214352a5e7cSSebastian Grimberg         for (CeedInt d = 0; d < q_comp; d++) {
1215437c7c90SJeremy L Thompson           eval_modes_in[index][num_eval_modes_in[index] + d]        = eval_mode;
1216437c7c90SJeremy L Thompson           eval_mode_offsets_in[index][num_eval_modes_in[index] + d] = offset;
1217352a5e7cSSebastian Grimberg           offset += num_comp;
1218ed9e99e6SJeremy L Thompson         }
1219352a5e7cSSebastian Grimberg         num_eval_modes_in[index] += q_comp;
1220ed9e99e6SJeremy L Thompson       }
1221ed9e99e6SJeremy L Thompson     }
1222ed9e99e6SJeremy L Thompson   }
1223437c7c90SJeremy L Thompson   (*data)->num_eval_modes_in    = num_eval_modes_in;
1224437c7c90SJeremy L Thompson   (*data)->eval_modes_in        = eval_modes_in;
1225437c7c90SJeremy L Thompson   (*data)->eval_mode_offsets_in = eval_mode_offsets_in;
1226ed9e99e6SJeremy L Thompson 
1227ed9e99e6SJeremy L Thompson   // Determine active output basis
1228ed9e99e6SJeremy L Thompson   CeedInt num_output_fields;
12292b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, NULL, NULL, &num_output_fields, &qf_fields));
12302b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, NULL, NULL, NULL, &op_fields));
1231437c7c90SJeremy L Thompson   offset = 0;
1232ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_output_fields; i++) {
1233ed9e99e6SJeremy L Thompson     CeedVector vec;
12342b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
1235ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
1236437c7c90SJeremy L Thompson       CeedBasis    basis_out = NULL;
1237ed9e99e6SJeremy L Thompson       CeedEvalMode eval_mode;
1238352a5e7cSSebastian Grimberg       CeedInt      index = -1, dim, num_comp, q_comp;
1239437c7c90SJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_out));
12402b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
1241352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetDimension(basis_out, &dim));
1242352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumComponents(basis_out, &num_comp));
1243352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumQuadratureComponents(basis_out, eval_mode, &q_comp));
1244437c7c90SJeremy L Thompson       for (CeedInt i = 0; i < num_active_bases; i++) {
1245437c7c90SJeremy L Thompson         if ((*data)->active_bases[i] == basis_out) index = i;
1246437c7c90SJeremy L Thompson       }
1247437c7c90SJeremy L Thompson       if (index == -1) {
1248437c7c90SJeremy L Thompson         CeedElemRestriction elem_rstr_out;
1249437c7c90SJeremy L Thompson 
1250437c7c90SJeremy L Thompson         index = num_active_bases;
1251437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_bases));
1252437c7c90SJeremy L Thompson         (*data)->active_bases[num_active_bases] = NULL;
1253437c7c90SJeremy L Thompson         CeedCall(CeedBasisReferenceCopy(basis_out, &(*data)->active_bases[num_active_bases]));
1254437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_elem_rstrs));
1255437c7c90SJeremy L Thompson         (*data)->active_elem_rstrs[num_active_bases] = NULL;
1256437c7c90SJeremy L Thompson         CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_rstr_out));
1257437c7c90SJeremy L Thompson         CeedCall(CeedElemRestrictionReferenceCopy(elem_rstr_out, &(*data)->active_elem_rstrs[num_active_bases]));
1258437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_in));
1259437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_out));
1260437c7c90SJeremy L Thompson         num_eval_modes_in[index]  = 0;
1261437c7c90SJeremy L Thompson         num_eval_modes_out[index] = 0;
1262437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_in));
1263437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_out));
1264437c7c90SJeremy L Thompson         eval_modes_in[index]  = NULL;
1265437c7c90SJeremy L Thompson         eval_modes_out[index] = NULL;
1266437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_in));
1267437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_out));
1268437c7c90SJeremy L Thompson         eval_mode_offsets_in[index]  = NULL;
1269437c7c90SJeremy L Thompson         eval_mode_offsets_out[index] = NULL;
1270437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_in));
1271437c7c90SJeremy L Thompson         CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_out));
1272437c7c90SJeremy L Thompson         (*data)->assembled_bases_in[index]  = NULL;
1273437c7c90SJeremy L Thompson         (*data)->assembled_bases_out[index] = NULL;
1274437c7c90SJeremy L Thompson         num_active_bases++;
1275437c7c90SJeremy L Thompson       }
1276352a5e7cSSebastian Grimberg       if (eval_mode != CEED_EVAL_WEIGHT) {
1277352a5e7cSSebastian Grimberg         // q_comp = 1 if CEED_EVAL_NONE, CEED_EVAL_WEIGHT caught by QF Assembly
1278352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_out[index] + q_comp, &eval_modes_out[index]));
1279352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_out[index] + q_comp, &eval_mode_offsets_out[index]));
1280352a5e7cSSebastian Grimberg         for (CeedInt d = 0; d < q_comp; d++) {
1281437c7c90SJeremy L Thompson           eval_modes_out[index][num_eval_modes_out[index] + d]        = eval_mode;
1282437c7c90SJeremy L Thompson           eval_mode_offsets_out[index][num_eval_modes_out[index] + d] = offset;
1283352a5e7cSSebastian Grimberg           offset += num_comp;
1284ed9e99e6SJeremy L Thompson         }
1285352a5e7cSSebastian Grimberg         num_eval_modes_out[index] += q_comp;
1286ed9e99e6SJeremy L Thompson       }
1287ed9e99e6SJeremy L Thompson     }
1288ed9e99e6SJeremy L Thompson   }
1289437c7c90SJeremy L Thompson   (*data)->num_output_components = offset;
1290437c7c90SJeremy L Thompson   (*data)->num_eval_modes_out    = num_eval_modes_out;
1291437c7c90SJeremy L Thompson   (*data)->eval_modes_out        = eval_modes_out;
1292437c7c90SJeremy L Thompson   (*data)->eval_mode_offsets_out = eval_mode_offsets_out;
1293437c7c90SJeremy L Thompson   (*data)->num_active_bases      = num_active_bases;
1294ed9e99e6SJeremy L Thompson 
1295ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1296ed9e99e6SJeremy L Thompson }
1297ed9e99e6SJeremy L Thompson 
1298ed9e99e6SJeremy L Thompson /**
1299ba746a46SJeremy L Thompson   @brief Get CeedOperator CeedEvalModes for assembly.
1300ba746a46SJeremy L Thompson 
1301ba746a46SJeremy L Thompson   Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object.
1302ed9e99e6SJeremy L Thompson 
1303ed9e99e6SJeremy L Thompson   @param[in]  data                  CeedOperatorAssemblyData
1304ba746a46SJeremy L Thompson   @param[out] num_active_bases      Total number of active bases
1305c5d0f995SJed Brown   @param[out] num_eval_modes_in     Pointer to hold array of numbers of input CeedEvalModes, or NULL.
1306ba746a46SJeremy L Thompson                                       `eval_modes_in[0]` holds an array of eval modes for the first active basis.
1307c5d0f995SJed Brown   @param[out] eval_modes_in         Pointer to hold arrays of input CeedEvalModes, or NULL.
1308ba746a46SJeremy L Thompson   @param[out] eval_mode_offsets_in  Pointer to hold arrays of input offsets at each quadrature point.
1309c5d0f995SJed Brown   @param[out] num_eval_modes_out    Pointer to hold array of numbers of output CeedEvalModes, or NULL
1310c5d0f995SJed Brown   @param[out] eval_modes_out        Pointer to hold arrays of output CeedEvalModes, or NULL.
1311437c7c90SJeremy L Thompson   @param[out] eval_mode_offsets_out Pointer to hold arrays of output offsets at each quadrature point
1312ba746a46SJeremy L Thompson   @param[out] num_output_components The number of columns in the assembled CeedQFunction matrix for each quadrature point,
1313ba746a46SJeremy L Thompson                                       including contributions of all active bases
1314ed9e99e6SJeremy L Thompson 
1315ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1316ed9e99e6SJeremy L Thompson 
1317c5d0f995SJed Brown 
1318ed9e99e6SJeremy L Thompson   @ref Backend
1319ed9e99e6SJeremy L Thompson **/
1320437c7c90SJeremy L Thompson int CeedOperatorAssemblyDataGetEvalModes(CeedOperatorAssemblyData data, CeedInt *num_active_bases, CeedInt **num_eval_modes_in,
1321437c7c90SJeremy L Thompson                                          const CeedEvalMode ***eval_modes_in, CeedSize ***eval_mode_offsets_in, CeedInt **num_eval_modes_out,
1322437c7c90SJeremy L Thompson                                          const CeedEvalMode ***eval_modes_out, CeedSize ***eval_mode_offsets_out, CeedSize *num_output_components) {
1323437c7c90SJeremy L Thompson   if (num_active_bases) *num_active_bases = data->num_active_bases;
1324437c7c90SJeremy L Thompson   if (num_eval_modes_in) *num_eval_modes_in = data->num_eval_modes_in;
1325437c7c90SJeremy L Thompson   if (eval_modes_in) *eval_modes_in = (const CeedEvalMode **)data->eval_modes_in;
1326437c7c90SJeremy L Thompson   if (eval_mode_offsets_in) *eval_mode_offsets_in = data->eval_mode_offsets_in;
1327437c7c90SJeremy L Thompson   if (num_eval_modes_out) *num_eval_modes_out = data->num_eval_modes_out;
1328437c7c90SJeremy L Thompson   if (eval_modes_out) *eval_modes_out = (const CeedEvalMode **)data->eval_modes_out;
1329437c7c90SJeremy L Thompson   if (eval_mode_offsets_out) *eval_mode_offsets_out = data->eval_mode_offsets_out;
1330437c7c90SJeremy L Thompson   if (num_output_components) *num_output_components = data->num_output_components;
1331ed9e99e6SJeremy L Thompson 
1332ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1333ed9e99e6SJeremy L Thompson }
1334ed9e99e6SJeremy L Thompson 
1335ed9e99e6SJeremy L Thompson /**
1336ba746a46SJeremy L Thompson   @brief Get CeedOperator CeedBasis data for assembly.
1337ba746a46SJeremy L Thompson 
1338ba746a46SJeremy L Thompson   Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object.
1339ed9e99e6SJeremy L Thompson 
1340ed9e99e6SJeremy L Thompson   @param[in]  data                CeedOperatorAssemblyData
1341437c7c90SJeremy L Thompson   @param[out] num_active_bases    Number of active bases, or NULL
1342437c7c90SJeremy L Thompson   @param[out] active_bases        Pointer to hold active CeedBasis, or NULL
1343437c7c90SJeremy L Thompson   @param[out] assembled_bases_in  Pointer to hold assembled active input B, or NULL
1344437c7c90SJeremy L Thompson   @param[out] assembled_bases_out Pointer to hold assembled active output B, or NULL
1345ed9e99e6SJeremy L Thompson 
1346ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1347ed9e99e6SJeremy L Thompson 
1348ed9e99e6SJeremy L Thompson   @ref Backend
1349ed9e99e6SJeremy L Thompson **/
1350437c7c90SJeremy L Thompson int CeedOperatorAssemblyDataGetBases(CeedOperatorAssemblyData data, CeedInt *num_active_bases, CeedBasis **active_bases,
1351437c7c90SJeremy L Thompson                                      const CeedScalar ***assembled_bases_in, const CeedScalar ***assembled_bases_out) {
1352ed9e99e6SJeremy L Thompson   // Assemble B_in, B_out if needed
1353437c7c90SJeremy L Thompson   if (assembled_bases_in && !data->assembled_bases_in[0]) {
1354437c7c90SJeremy L Thompson     CeedInt num_qpts;
1355437c7c90SJeremy L Thompson 
1356437c7c90SJeremy L Thompson     CeedCall(CeedBasisGetNumQuadraturePoints(data->active_bases[0], &num_qpts));
1357437c7c90SJeremy L Thompson     for (CeedInt b = 0; b < data->num_active_bases; b++) {
1358352a5e7cSSebastian Grimberg       CeedInt     num_nodes;
1359437c7c90SJeremy L Thompson       CeedScalar *B_in = NULL, *identity = NULL;
1360ed9e99e6SJeremy L Thompson       bool        has_eval_none = false;
1361ed9e99e6SJeremy L Thompson 
1362352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumNodes(data->active_bases[b], &num_nodes));
1363352a5e7cSSebastian Grimberg       CeedCall(CeedCalloc(num_qpts * num_nodes * data->num_eval_modes_in[b], &B_in));
1364ed9e99e6SJeremy L Thompson 
1365437c7c90SJeremy L Thompson       for (CeedInt i = 0; i < data->num_eval_modes_in[b]; i++) {
1366437c7c90SJeremy L Thompson         has_eval_none = has_eval_none || (data->eval_modes_in[b][i] == CEED_EVAL_NONE);
1367ed9e99e6SJeremy L Thompson       }
1368ed9e99e6SJeremy L Thompson       if (has_eval_none) {
1369352a5e7cSSebastian Grimberg         CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
1370352a5e7cSSebastian Grimberg         for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) {
1371352a5e7cSSebastian Grimberg           identity[i * num_nodes + i] = 1.0;
1372ed9e99e6SJeremy L Thompson         }
1373ed9e99e6SJeremy L Thompson       }
1374ed9e99e6SJeremy L Thompson 
1375ed9e99e6SJeremy L Thompson       for (CeedInt q = 0; q < num_qpts; q++) {
1376352a5e7cSSebastian Grimberg         for (CeedInt n = 0; n < num_nodes; n++) {
1377352a5e7cSSebastian Grimberg           CeedInt      d_in              = 0, q_comp_in;
1378352a5e7cSSebastian Grimberg           CeedEvalMode eval_mode_in_prev = CEED_EVAL_NONE;
1379437c7c90SJeremy L Thompson           for (CeedInt e_in = 0; e_in < data->num_eval_modes_in[b]; e_in++) {
1380437c7c90SJeremy L Thompson             const CeedInt     qq = data->num_eval_modes_in[b] * q;
1381437c7c90SJeremy L Thompson             const CeedScalar *B  = NULL;
1382352a5e7cSSebastian Grimberg             CeedOperatorGetBasisPointer(data->active_bases[b], data->eval_modes_in[b][e_in], identity, &B);
1383352a5e7cSSebastian Grimberg             CeedCall(CeedBasisGetNumQuadratureComponents(data->active_bases[b], data->eval_modes_in[b][e_in], &q_comp_in));
1384352a5e7cSSebastian Grimberg             if (q_comp_in > 1) {
1385352a5e7cSSebastian Grimberg               if (e_in == 0 || data->eval_modes_in[b][e_in] != eval_mode_in_prev) d_in = 0;
1386352a5e7cSSebastian Grimberg               else B = &B[(++d_in) * num_qpts * num_nodes];
1387352a5e7cSSebastian Grimberg             }
1388352a5e7cSSebastian Grimberg             eval_mode_in_prev                 = data->eval_modes_in[b][e_in];
1389352a5e7cSSebastian Grimberg             B_in[(qq + e_in) * num_nodes + n] = B[q * num_nodes + n];
1390ed9e99e6SJeremy L Thompson           }
1391ed9e99e6SJeremy L Thompson         }
1392ed9e99e6SJeremy L Thompson       }
1393437c7c90SJeremy L Thompson       if (identity) CeedCall(CeedFree(identity));
1394437c7c90SJeremy L Thompson       data->assembled_bases_in[b] = B_in;
1395437c7c90SJeremy L Thompson     }
1396ed9e99e6SJeremy L Thompson   }
1397ed9e99e6SJeremy L Thompson 
1398437c7c90SJeremy L Thompson   if (assembled_bases_out && !data->assembled_bases_out[0]) {
1399437c7c90SJeremy L Thompson     CeedInt num_qpts;
1400437c7c90SJeremy L Thompson 
1401437c7c90SJeremy L Thompson     CeedCall(CeedBasisGetNumQuadraturePoints(data->active_bases[0], &num_qpts));
1402437c7c90SJeremy L Thompson     for (CeedInt b = 0; b < data->num_active_bases; b++) {
1403352a5e7cSSebastian Grimberg       CeedInt     num_nodes;
1404ed9e99e6SJeremy L Thompson       bool        has_eval_none = false;
1405437c7c90SJeremy L Thompson       CeedScalar *B_out = NULL, *identity = NULL;
1406ed9e99e6SJeremy L Thompson 
1407352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumNodes(data->active_bases[b], &num_nodes));
1408352a5e7cSSebastian Grimberg       CeedCall(CeedCalloc(num_qpts * num_nodes * data->num_eval_modes_out[b], &B_out));
1409ed9e99e6SJeremy L Thompson 
1410437c7c90SJeremy L Thompson       for (CeedInt i = 0; i < data->num_eval_modes_out[b]; i++) {
1411437c7c90SJeremy L Thompson         has_eval_none = has_eval_none || (data->eval_modes_out[b][i] == CEED_EVAL_NONE);
1412ed9e99e6SJeremy L Thompson       }
1413ed9e99e6SJeremy L Thompson       if (has_eval_none) {
1414352a5e7cSSebastian Grimberg         CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
1415352a5e7cSSebastian Grimberg         for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) {
1416352a5e7cSSebastian Grimberg           identity[i * num_nodes + i] = 1.0;
1417ed9e99e6SJeremy L Thompson         }
1418ed9e99e6SJeremy L Thompson       }
1419ed9e99e6SJeremy L Thompson 
1420ed9e99e6SJeremy L Thompson       for (CeedInt q = 0; q < num_qpts; q++) {
1421352a5e7cSSebastian Grimberg         for (CeedInt n = 0; n < num_nodes; n++) {
1422352a5e7cSSebastian Grimberg           CeedInt      d_out              = 0, q_comp_out;
1423352a5e7cSSebastian Grimberg           CeedEvalMode eval_mode_out_prev = CEED_EVAL_NONE;
1424437c7c90SJeremy L Thompson           for (CeedInt e_out = 0; e_out < data->num_eval_modes_out[b]; e_out++) {
1425437c7c90SJeremy L Thompson             const CeedInt     qq = data->num_eval_modes_out[b] * q;
1426437c7c90SJeremy L Thompson             const CeedScalar *B  = NULL;
1427352a5e7cSSebastian Grimberg             CeedOperatorGetBasisPointer(data->active_bases[b], data->eval_modes_out[b][e_out], identity, &B);
1428352a5e7cSSebastian Grimberg             CeedCall(CeedBasisGetNumQuadratureComponents(data->active_bases[b], data->eval_modes_out[b][e_out], &q_comp_out));
1429352a5e7cSSebastian Grimberg             if (q_comp_out > 1) {
1430352a5e7cSSebastian Grimberg               if (e_out == 0 || data->eval_modes_out[b][e_out] != eval_mode_out_prev) d_out = 0;
1431352a5e7cSSebastian Grimberg               else B = &B[(++d_out) * num_qpts * num_nodes];
1432352a5e7cSSebastian Grimberg             }
1433352a5e7cSSebastian Grimberg             eval_mode_out_prev                  = data->eval_modes_out[b][e_out];
1434352a5e7cSSebastian Grimberg             B_out[(qq + e_out) * num_nodes + n] = B[q * num_nodes + n];
1435ed9e99e6SJeremy L Thompson           }
1436ed9e99e6SJeremy L Thompson         }
1437ed9e99e6SJeremy L Thompson       }
1438437c7c90SJeremy L Thompson       if (identity) CeedCall(CeedFree(identity));
1439437c7c90SJeremy L Thompson       data->assembled_bases_out[b] = B_out;
1440437c7c90SJeremy L Thompson     }
1441ed9e99e6SJeremy L Thompson   }
1442ed9e99e6SJeremy L Thompson 
1443437c7c90SJeremy L Thompson   // Pass out assembled data
1444437c7c90SJeremy L Thompson   if (active_bases) *active_bases = data->active_bases;
1445437c7c90SJeremy L Thompson   if (assembled_bases_in) *assembled_bases_in = (const CeedScalar **)data->assembled_bases_in;
1446437c7c90SJeremy L Thompson   if (assembled_bases_out) *assembled_bases_out = (const CeedScalar **)data->assembled_bases_out;
1447437c7c90SJeremy L Thompson 
1448437c7c90SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1449437c7c90SJeremy L Thompson }
1450437c7c90SJeremy L Thompson 
1451437c7c90SJeremy L Thompson /**
1452ba746a46SJeremy L Thompson   @brief Get CeedOperator CeedBasis data for assembly.
1453ba746a46SJeremy L Thompson 
1454ba746a46SJeremy L Thompson   Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object.
1455437c7c90SJeremy L Thompson 
1456437c7c90SJeremy L Thompson   @param[in]  data                  CeedOperatorAssemblyData
1457437c7c90SJeremy L Thompson   @param[out] num_active_elem_rstrs Number of active element restrictions, or NULL
1458437c7c90SJeremy L Thompson   @param[out] active_elem_rstrs     Pointer to hold active CeedElemRestrictions, or NULL
1459437c7c90SJeremy L Thompson 
1460437c7c90SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1461437c7c90SJeremy L Thompson 
1462437c7c90SJeremy L Thompson   @ref Backend
1463437c7c90SJeremy L Thompson **/
1464437c7c90SJeremy L Thompson int CeedOperatorAssemblyDataGetElemRestrictions(CeedOperatorAssemblyData data, CeedInt *num_active_elem_rstrs,
1465437c7c90SJeremy L Thompson                                                 CeedElemRestriction **active_elem_rstrs) {
1466437c7c90SJeremy L Thompson   if (num_active_elem_rstrs) *num_active_elem_rstrs = data->num_active_bases;
1467437c7c90SJeremy L Thompson   if (active_elem_rstrs) *active_elem_rstrs = data->active_elem_rstrs;
1468ed9e99e6SJeremy L Thompson 
1469ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1470ed9e99e6SJeremy L Thompson }
1471ed9e99e6SJeremy L Thompson 
1472ed9e99e6SJeremy L Thompson /**
1473ed9e99e6SJeremy L Thompson   @brief Destroy CeedOperatorAssemblyData
1474ed9e99e6SJeremy L Thompson 
1475ea61e9acSJeremy L Thompson   @param[in,out] data CeedOperatorAssemblyData to destroy
1476ed9e99e6SJeremy L Thompson 
1477ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1478ed9e99e6SJeremy L Thompson 
1479ed9e99e6SJeremy L Thompson   @ref Backend
1480ed9e99e6SJeremy L Thompson **/
1481ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataDestroy(CeedOperatorAssemblyData *data) {
1482ad6481ceSJeremy L Thompson   if (!*data) {
1483ad6481ceSJeremy L Thompson     *data = NULL;
1484ad6481ceSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1485ad6481ceSJeremy L Thompson   }
14862b730f8bSJeremy L Thompson   CeedCall(CeedDestroy(&(*data)->ceed));
1487437c7c90SJeremy L Thompson   for (CeedInt b = 0; b < (*data)->num_active_bases; b++) {
1488437c7c90SJeremy L Thompson     CeedCall(CeedBasisDestroy(&(*data)->active_bases[b]));
1489437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionDestroy(&(*data)->active_elem_rstrs[b]));
1490437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->eval_modes_in[b]));
1491437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->eval_modes_out[b]));
1492437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->eval_mode_offsets_in[b]));
1493437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->eval_mode_offsets_out[b]));
1494437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->assembled_bases_in[b]));
1495437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->assembled_bases_out[b]));
1496437c7c90SJeremy L Thompson   }
1497437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->active_bases));
1498437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->active_elem_rstrs));
1499437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->num_eval_modes_in));
1500437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->num_eval_modes_out));
1501437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_modes_in));
1502437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_modes_out));
1503437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_mode_offsets_in));
1504437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_mode_offsets_out));
1505437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->assembled_bases_in));
1506437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->assembled_bases_out));
1507ed9e99e6SJeremy L Thompson 
15082b730f8bSJeremy L Thompson   CeedCall(CeedFree(data));
1509ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1510ed9e99e6SJeremy L Thompson }
1511ed9e99e6SJeremy L Thompson 
1512480fae85SJeremy L Thompson /// @}
1513480fae85SJeremy L Thompson 
1514480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
1515eaf62fffSJeremy L Thompson /// CeedOperator Public API
1516eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
1517eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorUser
1518eaf62fffSJeremy L Thompson /// @{
1519eaf62fffSJeremy L Thompson 
1520eaf62fffSJeremy L Thompson /**
1521eaf62fffSJeremy L Thompson   @brief Assemble a linear CeedQFunction associated with a CeedOperator
1522eaf62fffSJeremy L Thompson 
1523ea61e9acSJeremy L Thompson   This returns a CeedVector containing a matrix at each quadrature point providing the action of the CeedQFunction associated with the CeedOperator.
1524859c15bbSJames Wright   The vector `assembled` is of shape `[num_elements, num_input_fields, num_output_fields, num_quad_points]` and contains column-major matrices
1525859c15bbSJames Wright representing the action of the CeedQFunction for a corresponding quadrature point on an element.
1526859c15bbSJames Wright 
1527*9fd66db6SSebastian Grimberg   Inputs and outputs are in the order provided by the user when adding CeedOperator fields.
1528*9fd66db6SSebastian Grimberg   For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and 'v', provided in that order, would result in an assembled QFunction
1529*9fd66db6SSebastian Grimberg that consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
1530eaf62fffSJeremy L Thompson 
1531ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1532f04ea552SJeremy L Thompson 
1533ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1534ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedQFunction at quadrature points
1535ea61e9acSJeremy L Thompson   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled CeedQFunction
1536ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1537eaf62fffSJeremy L Thompson 
1538eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1539eaf62fffSJeremy L Thompson 
1540eaf62fffSJeremy L Thompson   @ref User
1541eaf62fffSJeremy L Thompson **/
15422b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
15432b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1544eaf62fffSJeremy L Thompson 
1545eaf62fffSJeremy L Thompson   if (op->LinearAssembleQFunction) {
1546d04bbc78SJeremy L Thompson     // Backend version
15472b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleQFunction(op, assembled, rstr, request));
1548eaf62fffSJeremy L Thompson   } else {
1549d04bbc78SJeremy L Thompson     // Operator fallback
1550d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1551d04bbc78SJeremy L Thompson 
15522b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1553d04bbc78SJeremy L Thompson     if (op_fallback) {
15542b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleQFunction(op_fallback, assembled, rstr, request));
1555d04bbc78SJeremy L Thompson     } else {
1556d04bbc78SJeremy L Thompson       // LCOV_EXCL_START
15572b730f8bSJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunction");
1558d04bbc78SJeremy L Thompson       // LCOV_EXCL_STOP
1559d04bbc78SJeremy L Thompson     }
156070a7ffb3SJeremy L Thompson   }
1561eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1562eaf62fffSJeremy L Thompson }
156370a7ffb3SJeremy L Thompson 
156470a7ffb3SJeremy L Thompson /**
1565ea61e9acSJeremy L Thompson   @brief Assemble CeedQFunction and store result internally.
15664385fb7fSSebastian Grimberg 
1567ea61e9acSJeremy L Thompson   Return copied references of stored data to the caller.
1568ea61e9acSJeremy L Thompson   Caller is responsible for ownership and destruction of the copied references.
1569ea61e9acSJeremy L Thompson   See also @ref CeedOperatorLinearAssembleQFunction
157070a7ffb3SJeremy L Thompson 
1571ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1572ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedQFunction at quadrature points
1573ea61e9acSJeremy L Thompson   @param[out] rstr      CeedElemRestriction for CeedVector containing assembledCeedQFunction
1574ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
157570a7ffb3SJeremy L Thompson 
157670a7ffb3SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
157770a7ffb3SJeremy L Thompson 
157870a7ffb3SJeremy L Thompson   @ref User
157970a7ffb3SJeremy L Thompson **/
15802b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleQFunctionBuildOrUpdate(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
15812b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
158270a7ffb3SJeremy L Thompson 
158370a7ffb3SJeremy L Thompson   if (op->LinearAssembleQFunctionUpdate) {
1584d04bbc78SJeremy L Thompson     // Backend version
1585480fae85SJeremy L Thompson     bool                qf_assembled_is_setup;
15862efa2d85SJeremy L Thompson     CeedVector          assembled_vec  = NULL;
15872efa2d85SJeremy L Thompson     CeedElemRestriction assembled_rstr = NULL;
1588480fae85SJeremy L Thompson 
15892b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataIsSetup(op->qf_assembled, &qf_assembled_is_setup));
1590480fae85SJeremy L Thompson     if (qf_assembled_is_setup) {
1591d04bbc78SJeremy L Thompson       bool update_needed;
1592d04bbc78SJeremy L Thompson 
15932b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataGetObjects(op->qf_assembled, &assembled_vec, &assembled_rstr));
15942b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataIsUpdateNeeded(op->qf_assembled, &update_needed));
15958b919e6bSJeremy L Thompson       if (update_needed) {
15962b730f8bSJeremy L Thompson         CeedCall(op->LinearAssembleQFunctionUpdate(op, assembled_vec, assembled_rstr, request));
15978b919e6bSJeremy L Thompson       }
159870a7ffb3SJeremy L Thompson     } else {
15992b730f8bSJeremy L Thompson       CeedCall(op->LinearAssembleQFunction(op, &assembled_vec, &assembled_rstr, request));
16002b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataSetObjects(op->qf_assembled, assembled_vec, assembled_rstr));
160170a7ffb3SJeremy L Thompson     }
16022b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, false));
16032efa2d85SJeremy L Thompson 
1604d04bbc78SJeremy L Thompson     // Copy reference from internally held copy
160570a7ffb3SJeremy L Thompson     *assembled = NULL;
160670a7ffb3SJeremy L Thompson     *rstr      = NULL;
16072b730f8bSJeremy L Thompson     CeedCall(CeedVectorReferenceCopy(assembled_vec, assembled));
16082b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&assembled_vec));
16092b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionReferenceCopy(assembled_rstr, rstr));
16102b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionDestroy(&assembled_rstr));
161170a7ffb3SJeremy L Thompson   } else {
1612d04bbc78SJeremy L Thompson     // Operator fallback
1613d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1614d04bbc78SJeremy L Thompson 
16152b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1616d04bbc78SJeremy L Thompson     if (op_fallback) {
16172b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op_fallback, assembled, rstr, request));
1618d04bbc78SJeremy L Thompson     } else {
1619d04bbc78SJeremy L Thompson       // LCOV_EXCL_START
16202b730f8bSJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunctionUpdate");
1621d04bbc78SJeremy L Thompson       // LCOV_EXCL_STOP
162270a7ffb3SJeremy L Thompson     }
162370a7ffb3SJeremy L Thompson   }
162470a7ffb3SJeremy L Thompson 
162570a7ffb3SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1626eaf62fffSJeremy L Thompson }
1627eaf62fffSJeremy L Thompson 
1628eaf62fffSJeremy L Thompson /**
1629eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1630eaf62fffSJeremy L Thompson 
1631eaf62fffSJeremy L Thompson   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1632eaf62fffSJeremy L Thompson 
1633ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
1634eaf62fffSJeremy L Thompson 
1635ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1636f04ea552SJeremy L Thompson 
1637ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1638eaf62fffSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1639ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1640eaf62fffSJeremy L Thompson 
1641eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1642eaf62fffSJeremy L Thompson 
1643eaf62fffSJeremy L Thompson   @ref User
1644eaf62fffSJeremy L Thompson **/
16452b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1646f3d47e36SJeremy L Thompson   bool is_composite;
16472b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1648f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1649eaf62fffSJeremy L Thompson 
1650c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
16512b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
16522b730f8bSJeremy L Thompson   if (input_size != output_size) {
1653c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1654c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1655c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
16562b730f8bSJeremy L Thompson   }
1657c9366a6bSJeremy L Thompson 
1658f3d47e36SJeremy L Thompson   // Early exit for empty operator
1659f3d47e36SJeremy L Thompson   if (!is_composite) {
1660f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1661f3d47e36SJeremy L Thompson 
1662f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1663f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1664f3d47e36SJeremy L Thompson   }
1665f3d47e36SJeremy L Thompson 
1666eaf62fffSJeremy L Thompson   if (op->LinearAssembleDiagonal) {
1667d04bbc78SJeremy L Thompson     // Backend version
16682b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleDiagonal(op, assembled, request));
1669eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1670eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddDiagonal) {
1671d04bbc78SJeremy L Thompson     // Backend version with zeroing first
16722b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(assembled, 0.0));
16732b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request));
1674eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1675eaf62fffSJeremy L Thompson   } else {
1676d04bbc78SJeremy L Thompson     // Operator fallback
1677d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1678d04bbc78SJeremy L Thompson 
16792b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1680d04bbc78SJeremy L Thompson     if (op_fallback) {
16812b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleDiagonal(op_fallback, assembled, request));
1682eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1683eaf62fffSJeremy L Thompson     }
1684eaf62fffSJeremy L Thompson   }
1685eaf62fffSJeremy L Thompson   // Default interface implementation
16862b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(assembled, 0.0));
16872b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleAddDiagonal(op, assembled, request));
1688d04bbc78SJeremy L Thompson 
1689eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1690eaf62fffSJeremy L Thompson }
1691eaf62fffSJeremy L Thompson 
1692eaf62fffSJeremy L Thompson /**
1693eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1694eaf62fffSJeremy L Thompson 
1695eaf62fffSJeremy L Thompson   This sums into a CeedVector the diagonal of a linear CeedOperator.
1696eaf62fffSJeremy L Thompson 
1697ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
1698eaf62fffSJeremy L Thompson 
1699ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1700f04ea552SJeremy L Thompson 
1701ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1702eaf62fffSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1703ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1704eaf62fffSJeremy L Thompson 
1705eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1706eaf62fffSJeremy L Thompson 
1707eaf62fffSJeremy L Thompson   @ref User
1708eaf62fffSJeremy L Thompson **/
17092b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1710f3d47e36SJeremy L Thompson   bool is_composite;
17112b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1712f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1713eaf62fffSJeremy L Thompson 
1714c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
17152b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
17162b730f8bSJeremy L Thompson   if (input_size != output_size) {
1717c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1718c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1719c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
17202b730f8bSJeremy L Thompson   }
1721c9366a6bSJeremy L Thompson 
1722f3d47e36SJeremy L Thompson   // Early exit for empty operator
1723f3d47e36SJeremy L Thompson   if (!is_composite) {
1724f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1725f3d47e36SJeremy L Thompson 
1726f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1727f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1728f3d47e36SJeremy L Thompson   }
1729f3d47e36SJeremy L Thompson 
1730eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddDiagonal) {
1731d04bbc78SJeremy L Thompson     // Backend version
17322b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request));
1733eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1734eaf62fffSJeremy L Thompson   } else {
1735d04bbc78SJeremy L Thompson     // Operator fallback
1736d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1737d04bbc78SJeremy L Thompson 
17382b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1739d04bbc78SJeremy L Thompson     if (op_fallback) {
17402b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddDiagonal(op_fallback, assembled, request));
1741eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1742eaf62fffSJeremy L Thompson     }
1743eaf62fffSJeremy L Thompson   }
1744eaf62fffSJeremy L Thompson   // Default interface implementation
1745eaf62fffSJeremy L Thompson   if (is_composite) {
17462b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, false, assembled));
1747eaf62fffSJeremy L Thompson   } else {
17482b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, false, assembled));
1749eaf62fffSJeremy L Thompson   }
1750d04bbc78SJeremy L Thompson 
1751d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1752eaf62fffSJeremy L Thompson }
1753eaf62fffSJeremy L Thompson 
1754eaf62fffSJeremy L Thompson /**
1755eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1756eaf62fffSJeremy L Thompson 
1757ea61e9acSJeremy L Thompson   This overwrites a CeedVector with the point block diagonal of a linear CeedOperator.
1758eaf62fffSJeremy L Thompson 
1759ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
1760eaf62fffSJeremy L Thompson 
1761ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1762f04ea552SJeremy L Thompson 
1763ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1764ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator point block diagonal, provided in row-major form with an @a num_comp * @a num_comp
1765ea61e9acSJeremy L Thompson block at each node. The dimensions of this vector are derived from the active vector for the CeedOperator. The array has shape [nodes, component out,
1766ea61e9acSJeremy L Thompson component in].
1767ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1768eaf62fffSJeremy L Thompson 
1769eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1770eaf62fffSJeremy L Thompson 
1771eaf62fffSJeremy L Thompson   @ref User
1772eaf62fffSJeremy L Thompson **/
17732b730f8bSJeremy L Thompson int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1774f3d47e36SJeremy L Thompson   bool is_composite;
17752b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1776f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1777eaf62fffSJeremy L Thompson 
1778c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
17792b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
17802b730f8bSJeremy L Thompson   if (input_size != output_size) {
1781c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1782c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1783c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
17842b730f8bSJeremy L Thompson   }
1785c9366a6bSJeremy L Thompson 
1786f3d47e36SJeremy L Thompson   // Early exit for empty operator
1787f3d47e36SJeremy L Thompson   if (!is_composite) {
1788f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1789f3d47e36SJeremy L Thompson 
1790f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1791f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1792f3d47e36SJeremy L Thompson   }
1793f3d47e36SJeremy L Thompson 
1794eaf62fffSJeremy L Thompson   if (op->LinearAssemblePointBlockDiagonal) {
1795d04bbc78SJeremy L Thompson     // Backend version
17962b730f8bSJeremy L Thompson     CeedCall(op->LinearAssemblePointBlockDiagonal(op, assembled, request));
1797eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1798eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddPointBlockDiagonal) {
1799d04bbc78SJeremy L Thompson     // Backend version with zeroing first
18002b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(assembled, 0.0));
18012b730f8bSJeremy L Thompson     CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request));
1802eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1803eaf62fffSJeremy L Thompson   } else {
1804d04bbc78SJeremy L Thompson     // Operator fallback
1805d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1806d04bbc78SJeremy L Thompson 
18072b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1808d04bbc78SJeremy L Thompson     if (op_fallback) {
18092b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssemblePointBlockDiagonal(op_fallback, assembled, request));
1810eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1811eaf62fffSJeremy L Thompson     }
1812eaf62fffSJeremy L Thompson   }
1813eaf62fffSJeremy L Thompson   // Default interface implementation
18142b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(assembled, 0.0));
18152b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request));
1816d04bbc78SJeremy L Thompson 
1817eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1818eaf62fffSJeremy L Thompson }
1819eaf62fffSJeremy L Thompson 
1820eaf62fffSJeremy L Thompson /**
1821eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1822eaf62fffSJeremy L Thompson 
1823ea61e9acSJeremy L Thompson   This sums into a CeedVector with the point block diagonal of a linear CeedOperator.
1824eaf62fffSJeremy L Thompson 
1825ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
1826eaf62fffSJeremy L Thompson 
1827ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1828f04ea552SJeremy L Thompson 
1829ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1830ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator point block diagonal, provided in row-major form with an @a num_comp * @a num_comp
1831ea61e9acSJeremy L Thompson block at each node. The dimensions of this vector are derived from the active vector for the CeedOperator. The array has shape [nodes, component out,
1832ea61e9acSJeremy L Thompson component in].
1833ea61e9acSJeremy L Thompson   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1834eaf62fffSJeremy L Thompson 
1835eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1836eaf62fffSJeremy L Thompson 
1837eaf62fffSJeremy L Thompson   @ref User
1838eaf62fffSJeremy L Thompson **/
18392b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1840f3d47e36SJeremy L Thompson   bool is_composite;
18412b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1842f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1843eaf62fffSJeremy L Thompson 
1844c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
18452b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
18462b730f8bSJeremy L Thompson   if (input_size != output_size) {
1847c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1848c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1849c9366a6bSJeremy L Thompson     // LCOV_EXCL_STOP
18502b730f8bSJeremy L Thompson   }
1851c9366a6bSJeremy L Thompson 
1852f3d47e36SJeremy L Thompson   // Early exit for empty operator
1853f3d47e36SJeremy L Thompson   if (!is_composite) {
1854f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1855f3d47e36SJeremy L Thompson 
1856f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1857f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1858f3d47e36SJeremy L Thompson   }
1859f3d47e36SJeremy L Thompson 
1860eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddPointBlockDiagonal) {
1861d04bbc78SJeremy L Thompson     // Backend version
18622b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddPointBlockDiagonal(op, assembled, request));
1863eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1864eaf62fffSJeremy L Thompson   } else {
1865d04bbc78SJeremy L Thompson     // Operator fallback
1866d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1867d04bbc78SJeremy L Thompson 
18682b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1869d04bbc78SJeremy L Thompson     if (op_fallback) {
18702b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op_fallback, assembled, request));
1871eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1872eaf62fffSJeremy L Thompson     }
1873eaf62fffSJeremy L Thompson   }
1874ea61e9acSJeremy L Thompson   // Default interface implementation
1875eaf62fffSJeremy L Thompson   if (is_composite) {
18762b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, true, assembled));
1877eaf62fffSJeremy L Thompson   } else {
18782b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, true, assembled));
1879eaf62fffSJeremy L Thompson   }
1880d04bbc78SJeremy L Thompson 
1881d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1882eaf62fffSJeremy L Thompson }
1883eaf62fffSJeremy L Thompson 
1884eaf62fffSJeremy L Thompson /**
1885eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero pattern of a linear operator.
1886eaf62fffSJeremy L Thompson 
1887ea61e9acSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssemble().
1888eaf62fffSJeremy L Thompson 
1889ea61e9acSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the form (i, j, value) which indicate that value should be added to the
1890*9fd66db6SSebastian Grimberg matrix in entry (i, j).
1891*9fd66db6SSebastian Grimberg   Note that the (i, j) pairs are not unique and may repeat.
1892*9fd66db6SSebastian Grimberg   This function returns the number of entries and their (i, j) locations, while CeedOperatorLinearAssemble() provides the values in the same ordering.
1893eaf62fffSJeremy L Thompson 
1894eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
1895eaf62fffSJeremy L Thompson 
1896ea61e9acSJeremy L Thompson    Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1897f04ea552SJeremy L Thompson 
1898eaf62fffSJeremy L Thompson    @param[in]  op          CeedOperator to assemble
1899eaf62fffSJeremy L Thompson    @param[out] num_entries Number of entries in coordinate nonzero pattern
1900eaf62fffSJeremy L Thompson    @param[out] rows        Row number for each entry
1901eaf62fffSJeremy L Thompson    @param[out] cols        Column number for each entry
1902eaf62fffSJeremy L Thompson 
1903eaf62fffSJeremy L Thompson    @ref User
1904eaf62fffSJeremy L Thompson **/
19052b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleSymbolic(CeedOperator op, CeedSize *num_entries, CeedInt **rows, CeedInt **cols) {
1906eaf62fffSJeremy L Thompson   CeedInt       num_suboperators, single_entries;
1907eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
1908eaf62fffSJeremy L Thompson   bool          is_composite;
19092b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1910f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1911eaf62fffSJeremy L Thompson 
1912eaf62fffSJeremy L Thompson   if (op->LinearAssembleSymbolic) {
1913d04bbc78SJeremy L Thompson     // Backend version
19142b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleSymbolic(op, num_entries, rows, cols));
1915eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1916eaf62fffSJeremy L Thompson   } else {
1917d04bbc78SJeremy L Thompson     // Operator fallback
1918d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1919d04bbc78SJeremy L Thompson 
19202b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1921d04bbc78SJeremy L Thompson     if (op_fallback) {
19222b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleSymbolic(op_fallback, num_entries, rows, cols));
1923eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1924eaf62fffSJeremy L Thompson     }
1925eaf62fffSJeremy L Thompson   }
1926eaf62fffSJeremy L Thompson 
1927eaf62fffSJeremy L Thompson   // Default interface implementation
1928eaf62fffSJeremy L Thompson 
1929eaf62fffSJeremy L Thompson   // count entries and allocate rows, cols arrays
1930eaf62fffSJeremy L Thompson   *num_entries = 0;
1931eaf62fffSJeremy L Thompson   if (is_composite) {
1932c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1933c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
193492ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
19352b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
1936eaf62fffSJeremy L Thompson       *num_entries += single_entries;
1937eaf62fffSJeremy L Thompson     }
1938eaf62fffSJeremy L Thompson   } else {
19392b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssemblyCountEntries(op, &single_entries));
1940eaf62fffSJeremy L Thompson     *num_entries += single_entries;
1941eaf62fffSJeremy L Thompson   }
19422b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(*num_entries, rows));
19432b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(*num_entries, cols));
1944eaf62fffSJeremy L Thompson 
1945eaf62fffSJeremy L Thompson   // assemble nonzero locations
1946eaf62fffSJeremy L Thompson   CeedInt offset = 0;
1947eaf62fffSJeremy L Thompson   if (is_composite) {
1948c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
1949c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
195092ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
19512b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows, *cols));
19522b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
1953eaf62fffSJeremy L Thompson       offset += single_entries;
1954eaf62fffSJeremy L Thompson     }
1955eaf62fffSJeremy L Thompson   } else {
19562b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols));
1957eaf62fffSJeremy L Thompson   }
1958eaf62fffSJeremy L Thompson 
1959eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1960eaf62fffSJeremy L Thompson }
1961eaf62fffSJeremy L Thompson 
1962eaf62fffSJeremy L Thompson /**
1963eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero entries of a linear operator.
1964eaf62fffSJeremy L Thompson 
1965ea61e9acSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic().
1966eaf62fffSJeremy L Thompson 
1967ea61e9acSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the form (i, j, value) which indicate that value should be added to the
1968*9fd66db6SSebastian Grimberg matrix in entry (i, j).
1969*9fd66db6SSebastian Grimberg   Note that the (i, j) pairs are not unique and may repeat.
1970*9fd66db6SSebastian Grimberg   This function returns the values of the nonzero entries to be added, their (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic()
1971eaf62fffSJeremy L Thompson 
1972eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
1973eaf62fffSJeremy L Thompson 
1974ea61e9acSJeremy L Thompson    Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1975f04ea552SJeremy L Thompson 
1976eaf62fffSJeremy L Thompson    @param[in]  op     CeedOperator to assemble
1977eaf62fffSJeremy L Thompson    @param[out] values Values to assemble into matrix
1978eaf62fffSJeremy L Thompson 
1979eaf62fffSJeremy L Thompson    @ref User
1980eaf62fffSJeremy L Thompson **/
1981eaf62fffSJeremy L Thompson int CeedOperatorLinearAssemble(CeedOperator op, CeedVector values) {
1982eaf62fffSJeremy L Thompson   CeedInt       num_suboperators, single_entries = 0;
1983eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
1984f3d47e36SJeremy L Thompson   bool          is_composite;
19852b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1986f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1987f3d47e36SJeremy L Thompson 
1988f3d47e36SJeremy L Thompson   // Early exit for empty operator
1989f3d47e36SJeremy L Thompson   if (!is_composite) {
1990f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1991f3d47e36SJeremy L Thompson 
1992f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1993f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1994f3d47e36SJeremy L Thompson   }
1995eaf62fffSJeremy L Thompson 
1996eaf62fffSJeremy L Thompson   if (op->LinearAssemble) {
1997d04bbc78SJeremy L Thompson     // Backend version
19982b730f8bSJeremy L Thompson     CeedCall(op->LinearAssemble(op, values));
1999eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2000eaf62fffSJeremy L Thompson   } else {
2001d04bbc78SJeremy L Thompson     // Operator fallback
2002d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2003d04bbc78SJeremy L Thompson 
20042b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2005d04bbc78SJeremy L Thompson     if (op_fallback) {
20062b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssemble(op_fallback, values));
2007eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2008eaf62fffSJeremy L Thompson     }
2009eaf62fffSJeremy L Thompson   }
2010eaf62fffSJeremy L Thompson 
2011eaf62fffSJeremy L Thompson   // Default interface implementation
2012eaf62fffSJeremy L Thompson   CeedInt offset = 0;
201328ec399dSJeremy L Thompson   CeedCall(CeedVectorSetValue(values, 0.0));
2014eaf62fffSJeremy L Thompson   if (is_composite) {
2015c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
2016c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
2017cefa2673SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; k++) {
20182b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemble(sub_operators[k], offset, values));
20192b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
2020eaf62fffSJeremy L Thompson       offset += single_entries;
2021eaf62fffSJeremy L Thompson     }
2022eaf62fffSJeremy L Thompson   } else {
20232b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssemble(op, offset, values));
2024eaf62fffSJeremy L Thompson   }
2025eaf62fffSJeremy L Thompson 
2026eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2027eaf62fffSJeremy L Thompson }
2028eaf62fffSJeremy L Thompson 
2029eaf62fffSJeremy L Thompson /**
203075f0d5a4SJeremy L Thompson   @brief Get the multiplicity of nodes across suboperators in a composite CeedOperator
203175f0d5a4SJeremy L Thompson 
203275f0d5a4SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
203375f0d5a4SJeremy L Thompson 
203475f0d5a4SJeremy L Thompson   @param[in]  op               Composite CeedOperator
203575f0d5a4SJeremy L Thompson   @param[in]  num_skip_indices Number of suboperators to skip
203675f0d5a4SJeremy L Thompson   @param[in]  skip_indices     Array of indices of suboperators to skip
203775f0d5a4SJeremy L Thompson   @param[out] mult             Vector to store multiplicity (of size l_size)
203875f0d5a4SJeremy L Thompson 
203975f0d5a4SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
204075f0d5a4SJeremy L Thompson 
204175f0d5a4SJeremy L Thompson   @ref User
204275f0d5a4SJeremy L Thompson **/
204375f0d5a4SJeremy L Thompson int CeedCompositeOperatorGetMultiplicity(CeedOperator op, CeedInt num_skip_indices, CeedInt *skip_indices, CeedVector mult) {
204475f0d5a4SJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
204575f0d5a4SJeremy L Thompson 
204675f0d5a4SJeremy L Thompson   Ceed                ceed;
2047b275c451SJeremy L Thompson   CeedInt             num_suboperators;
204875f0d5a4SJeremy L Thompson   CeedSize            l_vec_len;
204975f0d5a4SJeremy L Thompson   CeedScalar         *mult_array;
205075f0d5a4SJeremy L Thompson   CeedVector          ones_l_vec;
2051437c7c90SJeremy L Thompson   CeedElemRestriction elem_rstr;
2052b275c451SJeremy L Thompson   CeedOperator       *sub_operators;
205375f0d5a4SJeremy L Thompson 
205475f0d5a4SJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
205575f0d5a4SJeremy L Thompson 
205675f0d5a4SJeremy L Thompson   // Zero mult vector
205775f0d5a4SJeremy L Thompson   CeedCall(CeedVectorSetValue(mult, 0.0));
205875f0d5a4SJeremy L Thompson 
205975f0d5a4SJeremy L Thompson   // Get suboperators
2060b275c451SJeremy L Thompson   CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
2061b275c451SJeremy L Thompson   CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
2062b275c451SJeremy L Thompson   if (num_suboperators == 0) return CEED_ERROR_SUCCESS;
206375f0d5a4SJeremy L Thompson 
206475f0d5a4SJeremy L Thompson   // Work vector
206575f0d5a4SJeremy L Thompson   CeedCall(CeedVectorGetLength(mult, &l_vec_len));
206675f0d5a4SJeremy L Thompson   CeedCall(CeedVectorCreate(ceed, l_vec_len, &ones_l_vec));
206775f0d5a4SJeremy L Thompson   CeedCall(CeedVectorSetValue(ones_l_vec, 1.0));
206875f0d5a4SJeremy L Thompson   CeedCall(CeedVectorGetArray(mult, CEED_MEM_HOST, &mult_array));
206975f0d5a4SJeremy L Thompson 
207075f0d5a4SJeremy L Thompson   // Compute multiplicity across suboperators
2071b275c451SJeremy L Thompson   for (CeedInt i = 0; i < num_suboperators; i++) {
207275f0d5a4SJeremy L Thompson     const CeedScalar *sub_mult_array;
207375f0d5a4SJeremy L Thompson     CeedVector        sub_mult_l_vec, ones_e_vec;
207475f0d5a4SJeremy L Thompson 
207575f0d5a4SJeremy L Thompson     // -- Check for suboperator to skip
207675f0d5a4SJeremy L Thompson     for (CeedInt j = 0; j < num_skip_indices; j++) {
207775f0d5a4SJeremy L Thompson       if (skip_indices[j] == i) continue;
207875f0d5a4SJeremy L Thompson     }
207975f0d5a4SJeremy L Thompson 
208075f0d5a4SJeremy L Thompson     // -- Sub operator multiplicity
2081437c7c90SJeremy L Thompson     CeedCall(CeedOperatorGetActiveElemRestriction(sub_operators[i], &elem_rstr));
2082437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionCreateVector(elem_rstr, &sub_mult_l_vec, &ones_e_vec));
208375f0d5a4SJeremy L Thompson     CeedCall(CeedVectorSetValue(sub_mult_l_vec, 0.0));
2084437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionApply(elem_rstr, CEED_NOTRANSPOSE, ones_l_vec, ones_e_vec, CEED_REQUEST_IMMEDIATE));
2085437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionApply(elem_rstr, CEED_TRANSPOSE, ones_e_vec, sub_mult_l_vec, CEED_REQUEST_IMMEDIATE));
208675f0d5a4SJeremy L Thompson     CeedCall(CeedVectorGetArrayRead(sub_mult_l_vec, CEED_MEM_HOST, &sub_mult_array));
208775f0d5a4SJeremy L Thompson     // ---- Flag every node present in the current suboperator
208875f0d5a4SJeremy L Thompson     for (CeedInt j = 0; j < l_vec_len; j++) {
208975f0d5a4SJeremy L Thompson       if (sub_mult_array[j] > 0.0) mult_array[j] += 1.0;
209075f0d5a4SJeremy L Thompson     }
209175f0d5a4SJeremy L Thompson     CeedCall(CeedVectorRestoreArrayRead(sub_mult_l_vec, &sub_mult_array));
209275f0d5a4SJeremy L Thompson     CeedCall(CeedVectorDestroy(&sub_mult_l_vec));
209375f0d5a4SJeremy L Thompson     CeedCall(CeedVectorDestroy(&ones_e_vec));
209475f0d5a4SJeremy L Thompson   }
209575f0d5a4SJeremy L Thompson   CeedCall(CeedVectorRestoreArray(mult, &mult_array));
2096811d0ccfSJeremy L Thompson   CeedCall(CeedVectorDestroy(&ones_l_vec));
209775f0d5a4SJeremy L Thompson 
209875f0d5a4SJeremy L Thompson   return CEED_ERROR_SUCCESS;
209975f0d5a4SJeremy L Thompson }
210075f0d5a4SJeremy L Thompson 
210175f0d5a4SJeremy L Thompson /**
2102ea61e9acSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator, creating the prolongation basis from the fine and coarse
2103ea61e9acSJeremy L Thompson grid interpolation
2104eaf62fffSJeremy L Thompson 
210558e4b056SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable.
2106f04ea552SJeremy L Thompson 
2107eaf62fffSJeremy L Thompson   @param[in]  op_fine      Fine grid operator
210885bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine  L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
2109eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse  Coarse grid restriction
2110eaf62fffSJeremy L Thompson   @param[in]  basis_coarse Coarse grid active vector basis
2111eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
211285bb9dcfSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator, or NULL
211385bb9dcfSJeremy L Thompson   @param[out] op_restrict  Fine to coarse operator, or NULL
2114eaf62fffSJeremy L Thompson 
2115eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2116eaf62fffSJeremy L Thompson 
2117eaf62fffSJeremy L Thompson   @ref User
2118eaf62fffSJeremy L Thompson **/
21192b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreate(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
21202b730f8bSJeremy L Thompson                                      CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_restrict) {
21212b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
2122eaf62fffSJeremy L Thompson 
212383d6adf3SZach Atkins   // Build prolongation matrix, if required
212483d6adf3SZach Atkins   CeedBasis basis_c_to_f = NULL;
212583d6adf3SZach Atkins   if (op_prolong || op_restrict) {
212683d6adf3SZach Atkins     CeedBasis basis_fine;
21272b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
21282b730f8bSJeremy L Thompson     CeedCall(CeedBasisCreateProjection(basis_coarse, basis_fine, &basis_c_to_f));
212983d6adf3SZach Atkins   }
2130eaf62fffSJeremy L Thompson 
2131f113e5dcSJeremy L Thompson   // Core code
21322b730f8bSJeremy L Thompson   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
2133f113e5dcSJeremy L Thompson 
2134eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2135eaf62fffSJeremy L Thompson }
2136eaf62fffSJeremy L Thompson 
2137eaf62fffSJeremy L Thompson /**
2138ea61e9acSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator with a tensor basis for the active basis
2139eaf62fffSJeremy L Thompson 
214058e4b056SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable.
2141f04ea552SJeremy L Thompson 
2142eaf62fffSJeremy L Thompson   @param[in]  op_fine       Fine grid operator
214385bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine   L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
2144eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse   Coarse grid restriction
2145eaf62fffSJeremy L Thompson   @param[in]  basis_coarse  Coarse grid active vector basis
214685bb9dcfSJeremy L Thompson   @param[in]  interp_c_to_f Matrix for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators
2147eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
214885bb9dcfSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator, or NULL
214985bb9dcfSJeremy L Thompson   @param[out] op_restrict   Fine to coarse operator, or NULL
2150eaf62fffSJeremy L Thompson 
2151eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2152eaf62fffSJeremy L Thompson 
2153eaf62fffSJeremy L Thompson   @ref User
2154eaf62fffSJeremy L Thompson **/
21552b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
21562b730f8bSJeremy L Thompson                                              const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
21572b730f8bSJeremy L Thompson                                              CeedOperator *op_restrict) {
21582b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
2159eaf62fffSJeremy L Thompson   Ceed ceed;
21602b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
2161eaf62fffSJeremy L Thompson 
2162eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
2163eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
21642b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
2165eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
21662b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f));
21672b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c));
21682b730f8bSJeremy L Thompson   if (Q_f != Q_c) {
2169eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
21702b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces");
2171eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
21722b730f8bSJeremy L Thompson   }
2173eaf62fffSJeremy L Thompson 
217483d6adf3SZach Atkins   // Create coarse to fine basis, if required
217583d6adf3SZach Atkins   CeedBasis basis_c_to_f = NULL;
217683d6adf3SZach Atkins   if (op_prolong || op_restrict) {
217783d6adf3SZach Atkins     // Check if interpolation matrix is provided
217883d6adf3SZach Atkins     if (!interp_c_to_f) {
217983d6adf3SZach Atkins       // LCOV_EXCL_START
218083d6adf3SZach Atkins       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "Prolongation or restriction operator creation requires coarse-to-fine interpolation matrix");
218183d6adf3SZach Atkins       // LCOV_EXCL_STOP
218283d6adf3SZach Atkins     }
2183eaf62fffSJeremy L Thompson     CeedInt dim, num_comp, num_nodes_c, P_1d_f, P_1d_c;
21842b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetDimension(basis_fine, &dim));
21852b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp));
21862b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumNodes1D(basis_fine, &P_1d_f));
21872b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c));
21882b730f8bSJeremy L Thompson     P_1d_c = dim == 1 ? num_nodes_c : dim == 2 ? sqrt(num_nodes_c) : cbrt(num_nodes_c);
2189eaf62fffSJeremy L Thompson     CeedScalar *q_ref, *q_weight, *grad;
21902b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d_f, &q_ref));
21912b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d_f, &q_weight));
21922b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d_f * P_1d_c * dim, &grad));
21932b730f8bSJeremy 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));
21942b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_ref));
21952b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_weight));
21962b730f8bSJeremy L Thompson     CeedCall(CeedFree(&grad));
219783d6adf3SZach Atkins   }
2198eaf62fffSJeremy L Thompson 
2199eaf62fffSJeremy L Thompson   // Core code
22002b730f8bSJeremy L Thompson   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
2201eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2202eaf62fffSJeremy L Thompson }
2203eaf62fffSJeremy L Thompson 
2204eaf62fffSJeremy L Thompson /**
2205ea61e9acSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator with a non-tensor basis for the active vector
2206eaf62fffSJeremy L Thompson 
220758e4b056SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable.
2208f04ea552SJeremy L Thompson 
2209eaf62fffSJeremy L Thompson   @param[in]  op_fine       Fine grid operator
221085bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine   L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
2211eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse   Coarse grid restriction
2212eaf62fffSJeremy L Thompson   @param[in]  basis_coarse  Coarse grid active vector basis
221385bb9dcfSJeremy L Thompson   @param[in]  interp_c_to_f Matrix for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators
2214eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
221585bb9dcfSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator, or NULL
221685bb9dcfSJeremy L Thompson   @param[out] op_restrict   Fine to coarse operator, or NULL
2217eaf62fffSJeremy L Thompson 
2218eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2219eaf62fffSJeremy L Thompson 
2220eaf62fffSJeremy L Thompson   @ref User
2221eaf62fffSJeremy L Thompson **/
22222b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
22232b730f8bSJeremy L Thompson                                        const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
2224eaf62fffSJeremy L Thompson                                        CeedOperator *op_restrict) {
22252b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
2226eaf62fffSJeremy L Thompson   Ceed ceed;
22272b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
2228eaf62fffSJeremy L Thompson 
2229eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
2230eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
22312b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
2232eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
22332b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f));
22342b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c));
22352b730f8bSJeremy L Thompson   if (Q_f != Q_c) {
2236eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
22372b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces");
2238eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
22392b730f8bSJeremy L Thompson   }
2240eaf62fffSJeremy L Thompson 
2241eaf62fffSJeremy L Thompson   // Coarse to fine basis
224283d6adf3SZach Atkins   CeedBasis basis_c_to_f = NULL;
224383d6adf3SZach Atkins   if (op_prolong || op_restrict) {
224483d6adf3SZach Atkins     // Check if interpolation matrix is provided
224583d6adf3SZach Atkins     if (!interp_c_to_f) {
224683d6adf3SZach Atkins       // LCOV_EXCL_START
224783d6adf3SZach Atkins       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE, "Prolongation or restriction operator creation requires coarse-to-fine interpolation matrix");
224883d6adf3SZach Atkins       // LCOV_EXCL_STOP
224983d6adf3SZach Atkins     }
2250eaf62fffSJeremy L Thompson     CeedElemTopology topo;
22512b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetTopology(basis_fine, &topo));
2252eaf62fffSJeremy L Thompson     CeedInt dim, num_comp, num_nodes_c, num_nodes_f;
22532b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetDimension(basis_fine, &dim));
22542b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp));
22552b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumNodes(basis_fine, &num_nodes_f));
22562b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c));
2257eaf62fffSJeremy L Thompson     CeedScalar *q_ref, *q_weight, *grad;
22582b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_nodes_f * dim, &q_ref));
22592b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_nodes_f, &q_weight));
22602b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_nodes_f * num_nodes_c * dim, &grad));
22612b730f8bSJeremy 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));
22622b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_ref));
22632b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_weight));
22642b730f8bSJeremy L Thompson     CeedCall(CeedFree(&grad));
226583d6adf3SZach Atkins   }
2266eaf62fffSJeremy L Thompson 
2267eaf62fffSJeremy L Thompson   // Core code
22682b730f8bSJeremy L Thompson   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
2269eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2270eaf62fffSJeremy L Thompson }
2271eaf62fffSJeremy L Thompson 
2272eaf62fffSJeremy L Thompson /**
2273ea61e9acSJeremy L Thompson   @brief Build a FDM based approximate inverse for each element for a CeedOperator
2274eaf62fffSJeremy L Thompson 
2275ea61e9acSJeremy L Thompson   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization Method based approximate inverse.
2276859c15bbSJames Wright   This function obtains the simultaneous diagonalization for the 1D mass and Laplacian operators, \f$M = V^T V, K = V^T S V\f$.
2277859c15bbSJames Wright   The assembled QFunction is used to modify the eigenvalues from simultaneous diagonalization and obtain an approximate inverse of the form \f$V^T
2278*9fd66db6SSebastian Grimberg \hat S V\f$.
2279*9fd66db6SSebastian Grimberg   The CeedOperator must be linear and non-composite.
2280*9fd66db6SSebastian Grimberg   The associated CeedQFunction must therefore also be linear.
2281eaf62fffSJeremy L Thompson 
2282ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
2283f04ea552SJeremy L Thompson 
2284ea61e9acSJeremy L Thompson   @param[in]  op      CeedOperator to create element inverses
2285ea61e9acSJeremy L Thompson   @param[out] fdm_inv CeedOperator to apply the action of a FDM based inverse for each element
2286ea61e9acSJeremy L Thompson   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
2287eaf62fffSJeremy L Thompson 
2288eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2289eaf62fffSJeremy L Thompson 
2290480fae85SJeremy L Thompson   @ref User
2291eaf62fffSJeremy L Thompson **/
22922b730f8bSJeremy L Thompson int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv, CeedRequest *request) {
22932b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
2294eaf62fffSJeremy L Thompson 
2295eaf62fffSJeremy L Thompson   if (op->CreateFDMElementInverse) {
2296d04bbc78SJeremy L Thompson     // Backend version
22972b730f8bSJeremy L Thompson     CeedCall(op->CreateFDMElementInverse(op, fdm_inv, request));
2298eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2299eaf62fffSJeremy L Thompson   } else {
2300d04bbc78SJeremy L Thompson     // Operator fallback
2301d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2302d04bbc78SJeremy L Thompson 
23032b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2304d04bbc78SJeremy L Thompson     if (op_fallback) {
23052b730f8bSJeremy L Thompson       CeedCall(CeedOperatorCreateFDMElementInverse(op_fallback, fdm_inv, request));
2306eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2307eaf62fffSJeremy L Thompson     }
2308eaf62fffSJeremy L Thompson   }
2309eaf62fffSJeremy L Thompson 
2310d04bbc78SJeremy L Thompson   // Default interface implementation
2311eaf62fffSJeremy L Thompson   Ceed ceed, ceed_parent;
23122b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
23132b730f8bSJeremy L Thompson   CeedCall(CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent));
2314eaf62fffSJeremy L Thompson   ceed_parent = ceed_parent ? ceed_parent : ceed;
2315eaf62fffSJeremy L Thompson   CeedQFunction qf;
23162b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
2317eaf62fffSJeremy L Thompson 
2318eaf62fffSJeremy L Thompson   // Determine active input basis
2319eaf62fffSJeremy L Thompson   bool                interp = false, grad = false;
2320eaf62fffSJeremy L Thompson   CeedBasis           basis = NULL;
2321eaf62fffSJeremy L Thompson   CeedElemRestriction rstr  = NULL;
2322eaf62fffSJeremy L Thompson   CeedOperatorField  *op_fields;
2323eaf62fffSJeremy L Thompson   CeedQFunctionField *qf_fields;
2324eaf62fffSJeremy L Thompson   CeedInt             num_input_fields;
23252b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &op_fields, NULL, NULL));
23262b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, NULL, &qf_fields, NULL, NULL));
2327eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_input_fields; i++) {
2328eaf62fffSJeremy L Thompson     CeedVector vec;
23292b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
2330eaf62fffSJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
2331eaf62fffSJeremy L Thompson       CeedEvalMode eval_mode;
23322b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
2333eaf62fffSJeremy L Thompson       interp = interp || eval_mode == CEED_EVAL_INTERP;
2334eaf62fffSJeremy L Thompson       grad   = grad || eval_mode == CEED_EVAL_GRAD;
23352b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis));
23362b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr));
2337eaf62fffSJeremy L Thompson     }
2338eaf62fffSJeremy L Thompson   }
23392b730f8bSJeremy L Thompson   if (!basis) {
2340eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2341eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "No active field set");
2342eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
23432b730f8bSJeremy L Thompson   }
2344e79b91d9SJeremy L Thompson   CeedSize l_size = 1;
2345352a5e7cSSebastian Grimberg   CeedInt  P_1d, Q_1d, num_nodes, num_qpts, dim, num_comp = 1, num_elem = 1;
23462b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes1D(basis, &P_1d));
2347352a5e7cSSebastian Grimberg   CeedCall(CeedBasisGetNumNodes(basis, &num_nodes));
23482b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d));
23492b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis, &num_qpts));
23502b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetDimension(basis, &dim));
23512b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis, &num_comp));
23522b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
23532b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
2354eaf62fffSJeremy L Thompson 
2355eaf62fffSJeremy L Thompson   // Build and diagonalize 1D Mass and Laplacian
2356eaf62fffSJeremy L Thompson   bool tensor_basis;
23572b730f8bSJeremy L Thompson   CeedCall(CeedBasisIsTensor(basis, &tensor_basis));
23582b730f8bSJeremy L Thompson   if (!tensor_basis) {
2359eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
23602b730f8bSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "FDMElementInverse only supported for tensor bases");
2361eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
23622b730f8bSJeremy L Thompson   }
2363eaf62fffSJeremy L Thompson   CeedScalar *mass, *laplace, *x, *fdm_interp, *lambda;
23642b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &mass));
23652b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &laplace));
23662b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &x));
23672b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &fdm_interp));
23682b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &lambda));
2369eaf62fffSJeremy L Thompson   // -- Build matrices
2370eaf62fffSJeremy L Thompson   const CeedScalar *interp_1d, *grad_1d, *q_weight_1d;
23712b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetInterp1D(basis, &interp_1d));
23722b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetGrad1D(basis, &grad_1d));
23732b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetQWeights(basis, &q_weight_1d));
23742b730f8bSJeremy L Thompson   CeedCall(CeedBuildMassLaplace(interp_1d, grad_1d, q_weight_1d, P_1d, Q_1d, dim, mass, laplace));
2375eaf62fffSJeremy L Thompson 
2376eaf62fffSJeremy L Thompson   // -- Diagonalize
23772b730f8bSJeremy L Thompson   CeedCall(CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d));
23782b730f8bSJeremy L Thompson   CeedCall(CeedFree(&mass));
23792b730f8bSJeremy L Thompson   CeedCall(CeedFree(&laplace));
23802b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
23812b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) fdm_interp[i + j * P_1d] = x[j + i * P_1d];
23822b730f8bSJeremy L Thompson   }
23832b730f8bSJeremy L Thompson   CeedCall(CeedFree(&x));
2384eaf62fffSJeremy L Thompson 
2385eaf62fffSJeremy L Thompson   // Assemble QFunction
2386eaf62fffSJeremy L Thompson   CeedVector          assembled;
2387eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_qf;
23882b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled, &rstr_qf, request));
2389eaf62fffSJeremy L Thompson   CeedInt layout[3];
23902b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetELayout(rstr_qf, &layout));
23912b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_qf));
2392eaf62fffSJeremy L Thompson   CeedScalar max_norm = 0;
23932b730f8bSJeremy L Thompson   CeedCall(CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm));
2394eaf62fffSJeremy L Thompson 
2395eaf62fffSJeremy L Thompson   // Calculate element averages
2396eaf62fffSJeremy L Thompson   CeedInt           num_modes = (interp ? 1 : 0) + (grad ? dim : 0);
2397eaf62fffSJeremy L Thompson   CeedScalar       *elem_avg;
2398eaf62fffSJeremy L Thompson   const CeedScalar *assembled_array, *q_weight_array;
2399eaf62fffSJeremy L Thompson   CeedVector        q_weight;
24002b730f8bSJeremy L Thompson   CeedCall(CeedVectorCreate(ceed_parent, num_qpts, &q_weight));
24012b730f8bSJeremy L Thompson   CeedCall(CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT, CEED_VECTOR_NONE, q_weight));
24022b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembled_array));
24032b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array));
24042b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(num_elem, &elem_avg));
2405eaf62fffSJeremy L Thompson   const CeedScalar qf_value_bound = max_norm * 100 * CEED_EPSILON;
2406eaf62fffSJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
2407eaf62fffSJeremy L Thompson     CeedInt count = 0;
24082b730f8bSJeremy L Thompson     for (CeedInt q = 0; q < num_qpts; q++) {
24092b730f8bSJeremy L Thompson       for (CeedInt i = 0; i < num_comp * num_comp * num_modes * num_modes; i++) {
24102b730f8bSJeremy L Thompson         if (fabs(assembled_array[q * layout[0] + i * layout[1] + e * layout[2]]) > qf_value_bound) {
24112b730f8bSJeremy L Thompson           elem_avg[e] += assembled_array[q * layout[0] + i * layout[1] + e * layout[2]] / q_weight_array[q];
2412eaf62fffSJeremy L Thompson           count++;
2413eaf62fffSJeremy L Thompson         }
24142b730f8bSJeremy L Thompson       }
24152b730f8bSJeremy L Thompson     }
2416eaf62fffSJeremy L Thompson     if (count) {
2417eaf62fffSJeremy L Thompson       elem_avg[e] /= count;
2418eaf62fffSJeremy L Thompson     } else {
2419eaf62fffSJeremy L Thompson       elem_avg[e] = 1.0;
2420eaf62fffSJeremy L Thompson     }
2421eaf62fffSJeremy L Thompson   }
24222b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled, &assembled_array));
24232b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled));
24242b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(q_weight, &q_weight_array));
24252b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&q_weight));
2426eaf62fffSJeremy L Thompson 
2427eaf62fffSJeremy L Thompson   // Build FDM diagonal
2428eaf62fffSJeremy L Thompson   CeedVector  q_data;
2429eaf62fffSJeremy L Thompson   CeedScalar *q_data_array, *fdm_diagonal;
2430352a5e7cSSebastian Grimberg   CeedCall(CeedCalloc(num_comp * num_nodes, &fdm_diagonal));
2431352a5e7cSSebastian Grimberg   const CeedScalar fdm_diagonal_bound = num_nodes * CEED_EPSILON;
24322b730f8bSJeremy L Thompson   for (CeedInt c = 0; c < num_comp; c++) {
2433352a5e7cSSebastian Grimberg     for (CeedInt n = 0; n < num_nodes; n++) {
2434352a5e7cSSebastian Grimberg       if (interp) fdm_diagonal[c * num_nodes + n] = 1.0;
24352b730f8bSJeremy L Thompson       if (grad) {
2436eaf62fffSJeremy L Thompson         for (CeedInt d = 0; d < dim; d++) {
2437eaf62fffSJeremy L Thompson           CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d;
2438352a5e7cSSebastian Grimberg           fdm_diagonal[c * num_nodes + n] += lambda[i];
2439eaf62fffSJeremy L Thompson         }
2440eaf62fffSJeremy L Thompson       }
2441352a5e7cSSebastian Grimberg       if (fabs(fdm_diagonal[c * num_nodes + n]) < fdm_diagonal_bound) fdm_diagonal[c * num_nodes + n] = fdm_diagonal_bound;
24422b730f8bSJeremy L Thompson     }
24432b730f8bSJeremy L Thompson   }
2444352a5e7cSSebastian Grimberg   CeedCall(CeedVectorCreate(ceed_parent, num_elem * num_comp * num_nodes, &q_data));
24452b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(q_data, 0.0));
24462b730f8bSJeremy L Thompson   CeedCall(CeedVectorGetArrayWrite(q_data, CEED_MEM_HOST, &q_data_array));
24472b730f8bSJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
24482b730f8bSJeremy L Thompson     for (CeedInt c = 0; c < num_comp; c++) {
2449352a5e7cSSebastian Grimberg       for (CeedInt n = 0; n < num_nodes; n++) q_data_array[(e * num_comp + c) * num_nodes + n] = 1. / (elem_avg[e] * fdm_diagonal[c * num_nodes + n]);
24502b730f8bSJeremy L Thompson     }
24512b730f8bSJeremy L Thompson   }
24522b730f8bSJeremy L Thompson   CeedCall(CeedFree(&elem_avg));
24532b730f8bSJeremy L Thompson   CeedCall(CeedFree(&fdm_diagonal));
24542b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(q_data, &q_data_array));
2455eaf62fffSJeremy L Thompson 
2456eaf62fffSJeremy L Thompson   // Setup FDM operator
2457eaf62fffSJeremy L Thompson   // -- Basis
2458eaf62fffSJeremy L Thompson   CeedBasis   fdm_basis;
2459eaf62fffSJeremy L Thompson   CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy;
24602b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &grad_dummy));
24612b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &q_ref_dummy));
24622b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &q_weight_dummy));
24632b730f8bSJeremy 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));
24642b730f8bSJeremy L Thompson   CeedCall(CeedFree(&fdm_interp));
24652b730f8bSJeremy L Thompson   CeedCall(CeedFree(&grad_dummy));
24662b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_ref_dummy));
24672b730f8bSJeremy L Thompson   CeedCall(CeedFree(&q_weight_dummy));
24682b730f8bSJeremy L Thompson   CeedCall(CeedFree(&lambda));
2469eaf62fffSJeremy L Thompson 
2470eaf62fffSJeremy L Thompson   // -- Restriction
2471eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_qd_i;
2472352a5e7cSSebastian Grimberg   CeedInt             strides[3] = {1, num_nodes, num_nodes * num_comp};
2473352a5e7cSSebastian Grimberg   CeedCall(CeedElemRestrictionCreateStrided(ceed_parent, num_elem, num_nodes, num_comp, num_elem * num_comp * num_nodes, strides, &rstr_qd_i));
2474eaf62fffSJeremy L Thompson   // -- QFunction
2475eaf62fffSJeremy L Thompson   CeedQFunction qf_fdm;
24762b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInteriorByName(ceed_parent, "Scale", &qf_fdm));
24772b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_fdm, "input", num_comp, CEED_EVAL_INTERP));
24782b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_fdm, "scale", num_comp, CEED_EVAL_NONE));
24792b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddOutput(qf_fdm, "output", num_comp, CEED_EVAL_INTERP));
24802b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_fdm, num_comp));
2481eaf62fffSJeremy L Thompson   // -- QFunction context
2482eaf62fffSJeremy L Thompson   CeedInt *num_comp_data;
24832b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, &num_comp_data));
2484eaf62fffSJeremy L Thompson   num_comp_data[0] = num_comp;
2485eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_fdm;
24862b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextCreate(ceed, &ctx_fdm));
24872b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextSetData(ctx_fdm, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_data), num_comp_data));
24882b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetContext(qf_fdm, ctx_fdm));
24892b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextDestroy(&ctx_fdm));
2490eaf62fffSJeremy L Thompson   // -- Operator
24912b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed_parent, qf_fdm, NULL, NULL, fdm_inv));
24922b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "input", rstr, fdm_basis, CEED_VECTOR_ACTIVE));
24932b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "scale", rstr_qd_i, CEED_BASIS_COLLOCATED, q_data));
24942b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "output", rstr, fdm_basis, CEED_VECTOR_ACTIVE));
2495eaf62fffSJeremy L Thompson 
2496eaf62fffSJeremy L Thompson   // Cleanup
24972b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&q_data));
24982b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&fdm_basis));
24992b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_qd_i));
25002b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionDestroy(&qf_fdm));
2501eaf62fffSJeremy L Thompson 
2502eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2503eaf62fffSJeremy L Thompson }
2504eaf62fffSJeremy L Thompson 
2505eaf62fffSJeremy L Thompson /// @}
2506