xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-preconditioning.c (revision 0e455453ae0cd7ca9f40a43c0df197c0a20f0d65)
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 
8eaf62fffSJeremy L Thompson #include <ceed/ceed.h>
9eaf62fffSJeremy L Thompson #include <ceed/backend.h>
10eaf62fffSJeremy L Thompson #include <ceed-impl.h>
11eaf62fffSJeremy L Thompson #include <math.h>
12ed9e99e6SJeremy L Thompson #include <assert.h>
13eaf62fffSJeremy L Thompson #include <stdbool.h>
14eaf62fffSJeremy L Thompson #include <stdio.h>
15eaf62fffSJeremy L Thompson #include <string.h>
16eaf62fffSJeremy L Thompson 
17eaf62fffSJeremy L Thompson /// @file
18eaf62fffSJeremy L Thompson /// Implementation of CeedOperator preconditioning interfaces
19eaf62fffSJeremy L Thompson 
20eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
21eaf62fffSJeremy L Thompson /// CeedOperator Library Internal Preconditioning Functions
22eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
23eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorDeveloper
24eaf62fffSJeremy L Thompson /// @{
25eaf62fffSJeremy L Thompson 
26eaf62fffSJeremy L Thompson /**
279e77b9c8SJeremy L Thompson   @brief Duplicate a CeedQFunction with a reference Ceed to fallback for advanced
289e77b9c8SJeremy L Thompson          CeedOperator functionality
299e77b9c8SJeremy L Thompson 
3001ea9c81SJed Brown   @param[in] fallback_ceed Ceed on which to create fallback CeedQFunction
319e77b9c8SJeremy L Thompson   @param[in] qf            CeedQFunction to create fallback for
3201ea9c81SJed Brown   @param[out] qf_fallback  fallback CeedQFunction
339e77b9c8SJeremy L Thompson 
349e77b9c8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
359e77b9c8SJeremy L Thompson 
369e77b9c8SJeremy L Thompson   @ref Developer
379e77b9c8SJeremy L Thompson **/
389e77b9c8SJeremy L Thompson static int CeedQFunctionCreateFallback(Ceed fallback_ceed, CeedQFunction qf,
399e77b9c8SJeremy L Thompson                                        CeedQFunction *qf_fallback) {
409e77b9c8SJeremy L Thompson   int ierr;
419e77b9c8SJeremy L Thompson 
429e77b9c8SJeremy L Thompson   // Check if NULL qf passed in
439e77b9c8SJeremy L Thompson   if (!qf) return CEED_ERROR_SUCCESS;
449e77b9c8SJeremy L Thompson 
45d04bbc78SJeremy L Thompson   CeedDebug256(qf->ceed, 1, "---------- CeedOperator Fallback ----------\n");
46d04bbc78SJeremy L Thompson   CeedDebug256(qf->ceed, 255, "Creating fallback CeedQFunction\n");
47d04bbc78SJeremy L Thompson 
489e77b9c8SJeremy L Thompson   char *source_path_with_name = "";
499e77b9c8SJeremy L Thompson   if (qf->source_path) {
509e77b9c8SJeremy L Thompson     size_t path_len = strlen(qf->source_path),
519e77b9c8SJeremy L Thompson            name_len = strlen(qf->kernel_name);
529e77b9c8SJeremy L Thompson     ierr = CeedCalloc(path_len + name_len + 2, &source_path_with_name);
539e77b9c8SJeremy L Thompson     CeedChk(ierr);
549e77b9c8SJeremy L Thompson     memcpy(source_path_with_name, qf->source_path, path_len);
559e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len], ":", 1);
569e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len + 1], qf->kernel_name, name_len);
579e77b9c8SJeremy L Thompson   } else {
589e77b9c8SJeremy L Thompson     ierr = CeedCalloc(1, &source_path_with_name); CeedChk(ierr);
599e77b9c8SJeremy L Thompson   }
609e77b9c8SJeremy L Thompson 
619e77b9c8SJeremy L Thompson   ierr = CeedQFunctionCreateInterior(fallback_ceed, qf->vec_length,
629e77b9c8SJeremy L Thompson                                      qf->function, source_path_with_name,
639e77b9c8SJeremy L Thompson                                      qf_fallback); CeedChk(ierr);
649e77b9c8SJeremy L Thompson   {
659e77b9c8SJeremy L Thompson     CeedQFunctionContext ctx;
669e77b9c8SJeremy L Thompson 
679e77b9c8SJeremy L Thompson     ierr = CeedQFunctionGetContext(qf, &ctx); CeedChk(ierr);
689e77b9c8SJeremy L Thompson     ierr = CeedQFunctionSetContext(*qf_fallback, ctx); CeedChk(ierr);
699e77b9c8SJeremy L Thompson   }
709e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_input_fields; i++) {
719e77b9c8SJeremy L Thompson     ierr = CeedQFunctionAddInput(*qf_fallback, qf->input_fields[i]->field_name,
729e77b9c8SJeremy L Thompson                                  qf->input_fields[i]->size,
739e77b9c8SJeremy L Thompson                                  qf->input_fields[i]->eval_mode); CeedChk(ierr);
749e77b9c8SJeremy L Thompson   }
759e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_output_fields; i++) {
769e77b9c8SJeremy L Thompson     ierr = CeedQFunctionAddOutput(*qf_fallback, qf->output_fields[i]->field_name,
779e77b9c8SJeremy L Thompson                                   qf->output_fields[i]->size,
789e77b9c8SJeremy L Thompson                                   qf->output_fields[i]->eval_mode); CeedChk(ierr);
799e77b9c8SJeremy L Thompson   }
809e77b9c8SJeremy L Thompson   ierr = CeedFree(&source_path_with_name); CeedChk(ierr);
819e77b9c8SJeremy L Thompson 
829e77b9c8SJeremy L Thompson   return CEED_ERROR_SUCCESS;
839e77b9c8SJeremy L Thompson }
849e77b9c8SJeremy L Thompson 
859e77b9c8SJeremy L Thompson /**
86eaf62fffSJeremy L Thompson   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
87eaf62fffSJeremy L Thompson          CeedOperator functionality
88eaf62fffSJeremy L Thompson 
89eaf62fffSJeremy L Thompson   @param op  CeedOperator to create fallback for
90eaf62fffSJeremy L Thompson 
91eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
92eaf62fffSJeremy L Thompson 
93eaf62fffSJeremy L Thompson   @ref Developer
94eaf62fffSJeremy L Thompson **/
95d04bbc78SJeremy L Thompson static int CeedOperatorCreateFallback(CeedOperator op) {
96eaf62fffSJeremy L Thompson   int ierr;
979e77b9c8SJeremy L Thompson   Ceed ceed_fallback;
98eaf62fffSJeremy L Thompson 
99805fe78eSJeremy L Thompson   // Check not already created
100805fe78eSJeremy L Thompson   if (op->op_fallback) return CEED_ERROR_SUCCESS;
101805fe78eSJeremy L Thompson 
102eaf62fffSJeremy L Thompson   // Fallback Ceed
1039e77b9c8SJeremy L Thompson   ierr = CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback); CeedChk(ierr);
104d04bbc78SJeremy L Thompson   if (!ceed_fallback) return CEED_ERROR_SUCCESS;
105d04bbc78SJeremy L Thompson 
106d04bbc78SJeremy L Thompson   CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n");
107d04bbc78SJeremy L Thompson   CeedDebug256(op->ceed, 255, "Creating fallback CeedOperator\n");
108eaf62fffSJeremy L Thompson 
109eaf62fffSJeremy L Thompson   // Clone Op
110805fe78eSJeremy L Thompson   CeedOperator op_fallback;
111805fe78eSJeremy L Thompson   if (op->is_composite) {
1129e77b9c8SJeremy L Thompson     ierr = CeedCompositeOperatorCreate(ceed_fallback, &op_fallback);
113805fe78eSJeremy L Thompson     CeedChk(ierr);
114805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->num_suboperators; i++) {
115d04bbc78SJeremy L Thompson       CeedOperator op_sub_fallback;
116d04bbc78SJeremy L Thompson 
117d04bbc78SJeremy L Thompson       ierr = CeedOperatorGetFallback(op->sub_operators[i], &op_sub_fallback);
118805fe78eSJeremy L Thompson       CeedChk(ierr);
119d04bbc78SJeremy L Thompson       ierr = CeedCompositeOperatorAddSub(op_fallback, op_sub_fallback); CeedChk(ierr);
120805fe78eSJeremy L Thompson     }
121805fe78eSJeremy L Thompson   } else {
1229e77b9c8SJeremy L Thompson     CeedQFunction qf_fallback = NULL, dqf_fallback = NULL, dqfT_fallback = NULL;
1239e77b9c8SJeremy L Thompson     ierr = CeedQFunctionCreateFallback(ceed_fallback, op->qf, &qf_fallback);
1249e77b9c8SJeremy L Thompson     CeedChk(ierr);
1259e77b9c8SJeremy L Thompson     ierr = CeedQFunctionCreateFallback(ceed_fallback, op->dqf, &dqf_fallback);
1269e77b9c8SJeremy L Thompson     CeedChk(ierr);
1279e77b9c8SJeremy L Thompson     ierr = CeedQFunctionCreateFallback(ceed_fallback, op->dqfT, &dqfT_fallback);
1289e77b9c8SJeremy L Thompson     CeedChk(ierr);
1299e77b9c8SJeremy L Thompson     ierr = CeedOperatorCreate(ceed_fallback, qf_fallback, dqf_fallback,
1309e77b9c8SJeremy L Thompson                               dqfT_fallback, &op_fallback); CeedChk(ierr);
131805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
132805fe78eSJeremy L Thompson       ierr = CeedOperatorSetField(op_fallback, op->input_fields[i]->field_name,
133805fe78eSJeremy L Thompson                                   op->input_fields[i]->elem_restr,
134805fe78eSJeremy L Thompson                                   op->input_fields[i]->basis,
135805fe78eSJeremy L Thompson                                   op->input_fields[i]->vec); CeedChk(ierr);
136805fe78eSJeremy L Thompson     }
137805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
138805fe78eSJeremy L Thompson       ierr = CeedOperatorSetField(op_fallback, op->output_fields[i]->field_name,
139805fe78eSJeremy L Thompson                                   op->output_fields[i]->elem_restr,
140805fe78eSJeremy L Thompson                                   op->output_fields[i]->basis,
141805fe78eSJeremy L Thompson                                   op->output_fields[i]->vec); CeedChk(ierr);
142805fe78eSJeremy L Thompson     }
143480fae85SJeremy L Thompson     ierr = CeedQFunctionAssemblyDataReferenceCopy(op->qf_assembled,
144805fe78eSJeremy L Thompson            &op_fallback->qf_assembled); CeedChk(ierr);
145805fe78eSJeremy L Thompson     if (op_fallback->num_qpts == 0) {
146805fe78eSJeremy L Thompson       ierr = CeedOperatorSetNumQuadraturePoints(op_fallback, op->num_qpts);
147805fe78eSJeremy L Thompson       CeedChk(ierr);
148805fe78eSJeremy L Thompson     }
1499e77b9c8SJeremy L Thompson     // Cleanup
1509e77b9c8SJeremy L Thompson     ierr = CeedQFunctionDestroy(&qf_fallback); CeedChk(ierr);
1519e77b9c8SJeremy L Thompson     ierr = CeedQFunctionDestroy(&dqf_fallback); CeedChk(ierr);
1529e77b9c8SJeremy L Thompson     ierr = CeedQFunctionDestroy(&dqfT_fallback); CeedChk(ierr);
153805fe78eSJeremy L Thompson   }
154805fe78eSJeremy L Thompson   ierr = CeedOperatorSetName(op_fallback, op->name); CeedChk(ierr);
155805fe78eSJeremy L Thompson   ierr = CeedOperatorCheckReady(op_fallback); CeedChk(ierr);
156805fe78eSJeremy L Thompson   op->op_fallback = op_fallback;
157eaf62fffSJeremy L Thompson 
158eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
159eaf62fffSJeremy L Thompson }
160eaf62fffSJeremy L Thompson 
161eaf62fffSJeremy L Thompson /**
162d04bbc78SJeremy L Thompson   @brief Retreive fallback CeedOperator with a reference Ceed for advanced CeedOperator functionality
163d04bbc78SJeremy L Thompson 
164d04bbc78SJeremy L Thompson   @param[in] op            CeedOperator to retrieve fallback for
165d04bbc78SJeremy L Thompson   @param[out] op_fallback  Fallback CeedOperator
166d04bbc78SJeremy L Thompson 
167d04bbc78SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
168d04bbc78SJeremy L Thompson 
169d04bbc78SJeremy L Thompson   @ref Developer
170d04bbc78SJeremy L Thompson **/
171d04bbc78SJeremy L Thompson int CeedOperatorGetFallback(CeedOperator op, CeedOperator *op_fallback) {
172d04bbc78SJeremy L Thompson   int ierr;
173d04bbc78SJeremy L Thompson 
174d04bbc78SJeremy L Thompson   // Create if needed
175d04bbc78SJeremy L Thompson   if (!op->op_fallback) {
176d04bbc78SJeremy L Thompson     ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
177d04bbc78SJeremy L Thompson   }
178d04bbc78SJeremy L Thompson   if (op->op_fallback) {
179d04bbc78SJeremy L Thompson     bool is_debug;
180d04bbc78SJeremy L Thompson 
181d04bbc78SJeremy L Thompson     ierr = CeedIsDebug(op->ceed, &is_debug); CeedChk(ierr);
182d04bbc78SJeremy L Thompson     if (is_debug) {
183d04bbc78SJeremy L Thompson       Ceed ceed_fallback;
184d04bbc78SJeremy L Thompson       const char *resource, *resource_fallback;
185d04bbc78SJeremy L Thompson 
186d04bbc78SJeremy L Thompson       ierr = CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback); CeedChk(ierr);
187d04bbc78SJeremy L Thompson       ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
188d04bbc78SJeremy L Thompson       ierr = CeedGetResource(ceed_fallback, &resource_fallback); CeedChk(ierr);
189d04bbc78SJeremy L Thompson 
190d04bbc78SJeremy L Thompson       CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n");
191d04bbc78SJeremy L Thompson       CeedDebug256(op->ceed, 255,
192d04bbc78SJeremy L Thompson                    "Falling back from %s operator at address %ld to %s operator at address %ld\n",
193d04bbc78SJeremy L Thompson                    resource, op, resource_fallback, op->op_fallback);
194d04bbc78SJeremy L Thompson     }
195d04bbc78SJeremy L Thompson   }
196d04bbc78SJeremy L Thompson   *op_fallback = op->op_fallback;
197d04bbc78SJeremy L Thompson 
198d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
199d04bbc78SJeremy L Thompson }
200d04bbc78SJeremy L Thompson 
201d04bbc78SJeremy L Thompson /**
202eaf62fffSJeremy L Thompson   @brief Select correct basis matrix pointer based on CeedEvalMode
203eaf62fffSJeremy L Thompson 
204eaf62fffSJeremy L Thompson   @param[in] eval_mode   Current basis evaluation mode
205eaf62fffSJeremy L Thompson   @param[in] identity    Pointer to identity matrix
206eaf62fffSJeremy L Thompson   @param[in] interp      Pointer to interpolation matrix
207eaf62fffSJeremy L Thompson   @param[in] grad        Pointer to gradient matrix
208eaf62fffSJeremy L Thompson   @param[out] basis_ptr  Basis pointer to set
209eaf62fffSJeremy L Thompson 
210eaf62fffSJeremy L Thompson   @ref Developer
211eaf62fffSJeremy L Thompson **/
212eaf62fffSJeremy L Thompson static inline void CeedOperatorGetBasisPointer(CeedEvalMode eval_mode,
213eaf62fffSJeremy L Thompson     const CeedScalar *identity, const CeedScalar *interp,
214eaf62fffSJeremy L Thompson     const CeedScalar *grad, const CeedScalar **basis_ptr) {
215eaf62fffSJeremy L Thompson   switch (eval_mode) {
216eaf62fffSJeremy L Thompson   case CEED_EVAL_NONE:
217eaf62fffSJeremy L Thompson     *basis_ptr = identity;
218eaf62fffSJeremy L Thompson     break;
219eaf62fffSJeremy L Thompson   case CEED_EVAL_INTERP:
220eaf62fffSJeremy L Thompson     *basis_ptr = interp;
221eaf62fffSJeremy L Thompson     break;
222eaf62fffSJeremy L Thompson   case CEED_EVAL_GRAD:
223eaf62fffSJeremy L Thompson     *basis_ptr = grad;
224eaf62fffSJeremy L Thompson     break;
225eaf62fffSJeremy L Thompson   case CEED_EVAL_WEIGHT:
226eaf62fffSJeremy L Thompson   case CEED_EVAL_DIV:
227eaf62fffSJeremy L Thompson   case CEED_EVAL_CURL:
228eaf62fffSJeremy L Thompson     break; // Caught by QF Assembly
229eaf62fffSJeremy L Thompson   }
230ed9e99e6SJeremy L Thompson   assert(*basis_ptr != NULL);
231eaf62fffSJeremy L Thompson }
232eaf62fffSJeremy L Thompson 
233eaf62fffSJeremy L Thompson /**
234eaf62fffSJeremy L Thompson   @brief Create point block restriction for active operator field
235eaf62fffSJeremy L Thompson 
236eaf62fffSJeremy L Thompson   @param[in] rstr              Original CeedElemRestriction for active field
237eaf62fffSJeremy L Thompson   @param[out] pointblock_rstr  Address of the variable where the newly created
238eaf62fffSJeremy L Thompson                                  CeedElemRestriction will be stored
239eaf62fffSJeremy L Thompson 
240eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
241eaf62fffSJeremy L Thompson 
242eaf62fffSJeremy L Thompson   @ref Developer
243eaf62fffSJeremy L Thompson **/
244eaf62fffSJeremy L Thompson static int CeedOperatorCreateActivePointBlockRestriction(
245eaf62fffSJeremy L Thompson   CeedElemRestriction rstr,
246eaf62fffSJeremy L Thompson   CeedElemRestriction *pointblock_rstr) {
247eaf62fffSJeremy L Thompson   int ierr;
248eaf62fffSJeremy L Thompson   Ceed ceed;
249eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetCeed(rstr, &ceed); CeedChk(ierr);
250eaf62fffSJeremy L Thompson   const CeedInt *offsets;
251eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets);
252eaf62fffSJeremy L Thompson   CeedChk(ierr);
253eaf62fffSJeremy L Thompson 
254eaf62fffSJeremy L Thompson   // Expand offsets
255eaf62fffSJeremy L Thompson   CeedInt num_elem, num_comp, elem_size, comp_stride, max = 1,
256eaf62fffSJeremy L Thompson                                                       *pointblock_offsets;
257eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChk(ierr);
258eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumComponents(rstr, &num_comp); CeedChk(ierr);
259eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetElementSize(rstr, &elem_size); CeedChk(ierr);
260eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetCompStride(rstr, &comp_stride); CeedChk(ierr);
261eaf62fffSJeremy L Thompson   CeedInt shift = num_comp;
262eaf62fffSJeremy L Thompson   if (comp_stride != 1)
263eaf62fffSJeremy L Thompson     shift *= num_comp;
264eaf62fffSJeremy L Thompson   ierr = CeedCalloc(num_elem*elem_size, &pointblock_offsets);
265eaf62fffSJeremy L Thompson   CeedChk(ierr);
266eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_elem*elem_size; i++) {
267eaf62fffSJeremy L Thompson     pointblock_offsets[i] = offsets[i]*shift;
268eaf62fffSJeremy L Thompson     if (pointblock_offsets[i] > max)
269eaf62fffSJeremy L Thompson       max = pointblock_offsets[i];
270eaf62fffSJeremy L Thompson   }
271eaf62fffSJeremy L Thompson 
272eaf62fffSJeremy L Thompson   // Create new restriction
273eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp*num_comp,
274eaf62fffSJeremy L Thompson                                    1, max + num_comp*num_comp, CEED_MEM_HOST,
275eaf62fffSJeremy L Thompson                                    CEED_OWN_POINTER, pointblock_offsets, pointblock_rstr);
276eaf62fffSJeremy L Thompson   CeedChk(ierr);
277eaf62fffSJeremy L Thompson 
278eaf62fffSJeremy L Thompson   // Cleanup
279eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionRestoreOffsets(rstr, &offsets); CeedChk(ierr);
280eaf62fffSJeremy L Thompson 
281eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
282eaf62fffSJeremy L Thompson }
283eaf62fffSJeremy L Thompson 
284eaf62fffSJeremy L Thompson /**
285eaf62fffSJeremy L Thompson   @brief Core logic for assembling operator diagonal or point block diagonal
286eaf62fffSJeremy L Thompson 
287eaf62fffSJeremy L Thompson   @param[in] op             CeedOperator to assemble point block diagonal
288eaf62fffSJeremy L Thompson   @param[in] request        Address of CeedRequest for non-blocking completion, else
289eaf62fffSJeremy L Thompson                               CEED_REQUEST_IMMEDIATE
290eaf62fffSJeremy L Thompson   @param[in] is_pointblock  Boolean flag to assemble diagonal or point block diagonal
291eaf62fffSJeremy L Thompson   @param[out] assembled     CeedVector to store assembled diagonal
292eaf62fffSJeremy L Thompson 
293eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
294eaf62fffSJeremy L Thompson 
295eaf62fffSJeremy L Thompson   @ref Developer
296eaf62fffSJeremy L Thompson **/
297*0e455453SJeremy L Thompson static inline int CeedSingleOperatorAssembleAddDiagonal_Core(CeedOperator op,
298eaf62fffSJeremy L Thompson     CeedRequest *request, const bool is_pointblock, CeedVector assembled) {
299eaf62fffSJeremy L Thompson   int ierr;
300eaf62fffSJeremy L Thompson   Ceed ceed;
301eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
302eaf62fffSJeremy L Thompson 
303eaf62fffSJeremy L Thompson   // Assemble QFunction
304eaf62fffSJeremy L Thompson   CeedQFunction qf;
305eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChk(ierr);
306eaf62fffSJeremy L Thompson   CeedInt num_input_fields, num_output_fields;
307eaf62fffSJeremy L Thompson   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
308eaf62fffSJeremy L Thompson   CeedChk(ierr);
309eaf62fffSJeremy L Thompson   CeedVector assembled_qf;
310eaf62fffSJeremy L Thompson   CeedElemRestriction rstr;
31170a7ffb3SJeremy L Thompson   ierr = CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op,  &assembled_qf,
31270a7ffb3SJeremy L Thompson          &rstr, request); CeedChk(ierr);
313eaf62fffSJeremy L Thompson   CeedInt layout[3];
314eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetELayout(rstr, &layout); CeedChk(ierr);
315eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr); CeedChk(ierr);
316eaf62fffSJeremy L Thompson 
317ed9e99e6SJeremy L Thompson   // Get assembly data
318ed9e99e6SJeremy L Thompson   CeedOperatorAssemblyData data;
319ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetOperatorAssemblyData(op, &data); CeedChk(ierr);
320ed9e99e6SJeremy L Thompson   const CeedEvalMode *eval_mode_in, *eval_mode_out;
321ed9e99e6SJeremy L Thompson   CeedInt num_eval_mode_in, num_eval_mode_out;
322ed9e99e6SJeremy L Thompson   ierr = CeedOperatorAssemblyDataGetEvalModes(data, &num_eval_mode_in,
323ed9e99e6SJeremy L Thompson          &eval_mode_in, &num_eval_mode_out, &eval_mode_out); CeedChk(ierr);
324ed9e99e6SJeremy L Thompson   CeedBasis basis_in, basis_out;
325ed9e99e6SJeremy L Thompson   ierr = CeedOperatorAssemblyDataGetBases(data, &basis_in, NULL, &basis_out,
326ed9e99e6SJeremy L Thompson                                           NULL); CeedChk(ierr);
327ed9e99e6SJeremy L Thompson   CeedInt num_comp;
328eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumComponents(basis_in, &num_comp); CeedChk(ierr);
329eaf62fffSJeremy L Thompson 
330eaf62fffSJeremy L Thompson   // Assemble point block diagonal restriction, if needed
331ed9e99e6SJeremy L Thompson   CeedElemRestriction diag_rstr;
332ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetActiveElemRestriction(op, &diag_rstr); CeedChk(ierr);
333eaf62fffSJeremy L Thompson   if (is_pointblock) {
334ed9e99e6SJeremy L Thompson     CeedElemRestriction point_block_rstr;
335ed9e99e6SJeremy L Thompson     ierr = CeedOperatorCreateActivePointBlockRestriction(diag_rstr,
336ed9e99e6SJeremy L Thompson            &point_block_rstr);
337eaf62fffSJeremy L Thompson     CeedChk(ierr);
338ed9e99e6SJeremy L Thompson     diag_rstr = point_block_rstr;
339eaf62fffSJeremy L Thompson   }
340eaf62fffSJeremy L Thompson 
341eaf62fffSJeremy L Thompson   // Create diagonal vector
342eaf62fffSJeremy L Thompson   CeedVector elem_diag;
343eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionCreateVector(diag_rstr, NULL, &elem_diag);
344eaf62fffSJeremy L Thompson   CeedChk(ierr);
345eaf62fffSJeremy L Thompson 
346eaf62fffSJeremy L Thompson   // Assemble element operator diagonals
3479c774eddSJeremy L Thompson   CeedScalar *elem_diag_array;
3489c774eddSJeremy L Thompson   const CeedScalar *assembled_qf_array;
349eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(elem_diag, 0.0); CeedChk(ierr);
350eaf62fffSJeremy L Thompson   ierr = CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array);
351eaf62fffSJeremy L Thompson   CeedChk(ierr);
3529c774eddSJeremy L Thompson   ierr = CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array);
353eaf62fffSJeremy L Thompson   CeedChk(ierr);
354eaf62fffSJeremy L Thompson   CeedInt num_elem, num_nodes, num_qpts;
355eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumElements(diag_rstr, &num_elem); CeedChk(ierr);
356eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumNodes(basis_in, &num_nodes); CeedChk(ierr);
357eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts); CeedChk(ierr);
358ed9e99e6SJeremy L Thompson 
359eaf62fffSJeremy L Thompson   // Basis matrices
360eaf62fffSJeremy L Thompson   const CeedScalar *interp_in, *interp_out, *grad_in, *grad_out;
361eaf62fffSJeremy L Thompson   CeedScalar *identity = NULL;
362ed9e99e6SJeremy L Thompson   bool has_eval_none = false;
363ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_eval_mode_in; i++) {
364ed9e99e6SJeremy L Thompson     has_eval_none = has_eval_none || (eval_mode_in[i] == CEED_EVAL_NONE);
365ed9e99e6SJeremy L Thompson   }
366ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i<num_eval_mode_out; i++) {
367ed9e99e6SJeremy L Thompson     has_eval_none = has_eval_none || (eval_mode_out[i] == CEED_EVAL_NONE);
368ed9e99e6SJeremy L Thompson   }
369ed9e99e6SJeremy L Thompson   if (has_eval_none) {
370eaf62fffSJeremy L Thompson     ierr = CeedCalloc(num_qpts*num_nodes, &identity); CeedChk(ierr);
371eaf62fffSJeremy L Thompson     for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++)
372eaf62fffSJeremy L Thompson       identity[i*num_nodes+i] = 1.0;
373eaf62fffSJeremy L Thompson   }
374eaf62fffSJeremy L Thompson   ierr = CeedBasisGetInterp(basis_in, &interp_in); CeedChk(ierr);
375eaf62fffSJeremy L Thompson   ierr = CeedBasisGetInterp(basis_out, &interp_out); CeedChk(ierr);
376eaf62fffSJeremy L Thompson   ierr = CeedBasisGetGrad(basis_in, &grad_in); CeedChk(ierr);
377eaf62fffSJeremy L Thompson   ierr = CeedBasisGetGrad(basis_out, &grad_out); CeedChk(ierr);
378eaf62fffSJeremy L Thompson   // Compute the diagonal of B^T D B
379eaf62fffSJeremy L Thompson   // Each element
380eaf62fffSJeremy L Thompson   for (CeedInt e=0; e<num_elem; e++) {
381eaf62fffSJeremy L Thompson     CeedInt d_out = -1;
382eaf62fffSJeremy L Thompson     // Each basis eval mode pair
383eaf62fffSJeremy L Thompson     for (CeedInt e_out=0; e_out<num_eval_mode_out; e_out++) {
384eaf62fffSJeremy L Thompson       const CeedScalar *bt = NULL;
385eaf62fffSJeremy L Thompson       if (eval_mode_out[e_out] == CEED_EVAL_GRAD)
386eaf62fffSJeremy L Thompson         d_out += 1;
387eaf62fffSJeremy L Thompson       CeedOperatorGetBasisPointer(eval_mode_out[e_out], identity, interp_out,
388eaf62fffSJeremy L Thompson                                   &grad_out[d_out*num_qpts*num_nodes], &bt);
389eaf62fffSJeremy L Thompson       CeedInt d_in = -1;
390eaf62fffSJeremy L Thompson       for (CeedInt e_in=0; e_in<num_eval_mode_in; e_in++) {
391eaf62fffSJeremy L Thompson         const CeedScalar *b = NULL;
392eaf62fffSJeremy L Thompson         if (eval_mode_in[e_in] == CEED_EVAL_GRAD)
393eaf62fffSJeremy L Thompson           d_in += 1;
394eaf62fffSJeremy L Thompson         CeedOperatorGetBasisPointer(eval_mode_in[e_in], identity, interp_in,
395eaf62fffSJeremy L Thompson                                     &grad_in[d_in*num_qpts*num_nodes], &b);
396eaf62fffSJeremy L Thompson         // Each component
397eaf62fffSJeremy L Thompson         for (CeedInt c_out=0; c_out<num_comp; c_out++)
398eaf62fffSJeremy L Thompson           // Each qpoint/node pair
399eaf62fffSJeremy L Thompson           for (CeedInt q=0; q<num_qpts; q++)
400eaf62fffSJeremy L Thompson             if (is_pointblock) {
401eaf62fffSJeremy L Thompson               // Point Block Diagonal
402eaf62fffSJeremy L Thompson               for (CeedInt c_in=0; c_in<num_comp; c_in++) {
403eaf62fffSJeremy L Thompson                 const CeedScalar qf_value =
404eaf62fffSJeremy L Thompson                   assembled_qf_array[q*layout[0] + (((e_in*num_comp+c_in)*
405eaf62fffSJeremy L Thompson                                                      num_eval_mode_out+e_out)*num_comp+c_out)*layout[1] + e*layout[2]];
406eaf62fffSJeremy L Thompson                 for (CeedInt n=0; n<num_nodes; n++)
407eaf62fffSJeremy L Thompson                   elem_diag_array[((e*num_comp+c_out)*num_comp+c_in)*num_nodes+n] +=
408eaf62fffSJeremy L Thompson                     bt[q*num_nodes+n] * qf_value * b[q*num_nodes+n];
409eaf62fffSJeremy L Thompson               }
410eaf62fffSJeremy L Thompson             } else {
411eaf62fffSJeremy L Thompson               // Diagonal Only
412eaf62fffSJeremy L Thompson               const CeedScalar qf_value =
413eaf62fffSJeremy L Thompson                 assembled_qf_array[q*layout[0] + (((e_in*num_comp+c_out)*
414eaf62fffSJeremy L Thompson                                                    num_eval_mode_out+e_out)*num_comp+c_out)*layout[1] + e*layout[2]];
415eaf62fffSJeremy L Thompson               for (CeedInt n=0; n<num_nodes; n++)
416eaf62fffSJeremy L Thompson                 elem_diag_array[(e*num_comp+c_out)*num_nodes+n] +=
417eaf62fffSJeremy L Thompson                   bt[q*num_nodes+n] * qf_value * b[q*num_nodes+n];
418eaf62fffSJeremy L Thompson             }
419eaf62fffSJeremy L Thompson       }
420eaf62fffSJeremy L Thompson     }
421eaf62fffSJeremy L Thompson   }
422eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArray(elem_diag, &elem_diag_array); CeedChk(ierr);
4239c774eddSJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array);
4249c774eddSJeremy L Thompson   CeedChk(ierr);
425eaf62fffSJeremy L Thompson 
426eaf62fffSJeremy L Thompson   // Assemble local operator diagonal
427eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionApply(diag_rstr, CEED_TRANSPOSE, elem_diag,
428eaf62fffSJeremy L Thompson                                   assembled, request); CeedChk(ierr);
429eaf62fffSJeremy L Thompson 
430eaf62fffSJeremy L Thompson   // Cleanup
431eaf62fffSJeremy L Thompson   if (is_pointblock) {
432eaf62fffSJeremy L Thompson     ierr = CeedElemRestrictionDestroy(&diag_rstr); CeedChk(ierr);
433eaf62fffSJeremy L Thompson   }
434eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&assembled_qf); CeedChk(ierr);
435eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&elem_diag); CeedChk(ierr);
436eaf62fffSJeremy L Thompson   ierr = CeedFree(&identity); CeedChk(ierr);
437eaf62fffSJeremy L Thompson 
438eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
439eaf62fffSJeremy L Thompson }
440eaf62fffSJeremy L Thompson 
441eaf62fffSJeremy L Thompson /**
442eaf62fffSJeremy L Thompson   @brief Core logic for assembling composite operator diagonal
443eaf62fffSJeremy L Thompson 
444eaf62fffSJeremy L Thompson   @param[in] op             CeedOperator to assemble point block diagonal
445eaf62fffSJeremy L Thompson   @param[in] request        Address of CeedRequest for non-blocking completion, else
446eaf62fffSJeremy L Thompson                             CEED_REQUEST_IMMEDIATE
447eaf62fffSJeremy L Thompson   @param[in] is_pointblock  Boolean flag to assemble diagonal or point block diagonal
448eaf62fffSJeremy L Thompson   @param[out] assembled     CeedVector to store assembled diagonal
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 **/
454eaf62fffSJeremy L Thompson static inline int CeedCompositeOperatorLinearAssembleAddDiagonal(
455eaf62fffSJeremy L Thompson   CeedOperator op, CeedRequest *request, const bool is_pointblock,
456eaf62fffSJeremy L Thompson   CeedVector assembled) {
457eaf62fffSJeremy L Thompson   int ierr;
458eaf62fffSJeremy L Thompson   CeedInt num_sub;
459eaf62fffSJeremy L Thompson   CeedOperator *suboperators;
460eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
461eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
462eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_sub; i++) {
4636aa95790SJeremy L Thompson     if (is_pointblock) {
4646aa95790SJeremy L Thompson       ierr = CeedOperatorLinearAssembleAddPointBlockDiagonal(suboperators[i],
4656aa95790SJeremy L Thompson              assembled, request); CeedChk(ierr);
4666aa95790SJeremy L Thompson     } else {
4676aa95790SJeremy L Thompson       ierr = CeedOperatorLinearAssembleAddDiagonal(suboperators[i], assembled,
4686aa95790SJeremy L Thompson              request); CeedChk(ierr);
4696aa95790SJeremy L Thompson     }
470eaf62fffSJeremy L Thompson   }
471eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
472eaf62fffSJeremy L Thompson }
473eaf62fffSJeremy L Thompson 
474eaf62fffSJeremy L Thompson /**
475eaf62fffSJeremy L Thompson   @brief Build nonzero pattern for non-composite operator
476eaf62fffSJeremy L Thompson 
477eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssembleSymbolic()
478eaf62fffSJeremy L Thompson 
479eaf62fffSJeremy L Thompson   @param[in] op      CeedOperator to assemble nonzero pattern
480eaf62fffSJeremy L Thompson   @param[in] offset  Offset for number of entries
481eaf62fffSJeremy L Thompson   @param[out] rows   Row number for each entry
482eaf62fffSJeremy L Thompson   @param[out] cols   Column number for each entry
483eaf62fffSJeremy L Thompson 
484eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
485eaf62fffSJeremy L Thompson 
486eaf62fffSJeremy L Thompson   @ref Developer
487eaf62fffSJeremy L Thompson **/
488eaf62fffSJeremy L Thompson static int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset,
489eaf62fffSJeremy L Thompson     CeedInt *rows, CeedInt *cols) {
490eaf62fffSJeremy L Thompson   int ierr;
491eaf62fffSJeremy L Thompson   Ceed ceed = op->ceed;
492f04ea552SJeremy L Thompson   if (op->is_composite)
493eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
494eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
495eaf62fffSJeremy L Thompson                      "Composite operator not supported");
496eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
497eaf62fffSJeremy L Thompson 
498c9366a6bSJeremy L Thompson   CeedSize num_nodes;
499c9366a6bSJeremy L Thompson   ierr = CeedOperatorGetActiveVectorLengths(op, &num_nodes, NULL); CeedChk(ierr);
500eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_in;
501eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetActiveElemRestriction(op, &rstr_in); CeedChk(ierr);
502e79b91d9SJeremy L Thompson   CeedInt num_elem, elem_size, num_comp;
503eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumElements(rstr_in, &num_elem); CeedChk(ierr);
504eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetElementSize(rstr_in, &elem_size); CeedChk(ierr);
505eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumComponents(rstr_in, &num_comp); CeedChk(ierr);
506eaf62fffSJeremy L Thompson   CeedInt layout_er[3];
507eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetELayout(rstr_in, &layout_er); CeedChk(ierr);
508eaf62fffSJeremy L Thompson 
509eaf62fffSJeremy L Thompson   CeedInt local_num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
510eaf62fffSJeremy L Thompson 
511eaf62fffSJeremy L Thompson   // Determine elem_dof relation
512eaf62fffSJeremy L Thompson   CeedVector index_vec;
513eaf62fffSJeremy L Thompson   ierr = CeedVectorCreate(ceed, num_nodes, &index_vec); CeedChk(ierr);
514eaf62fffSJeremy L Thompson   CeedScalar *array;
5159c774eddSJeremy L Thompson   ierr = CeedVectorGetArrayWrite(index_vec, CEED_MEM_HOST, &array); CeedChk(ierr);
516ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_nodes; i++) array[i] = i;
517eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArray(index_vec, &array); CeedChk(ierr);
518eaf62fffSJeremy L Thompson   CeedVector elem_dof;
519eaf62fffSJeremy L Thompson   ierr = CeedVectorCreate(ceed, num_elem * elem_size * num_comp, &elem_dof);
520eaf62fffSJeremy L Thompson   CeedChk(ierr);
521eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(elem_dof, 0.0); CeedChk(ierr);
522eaf62fffSJeremy L Thompson   CeedElemRestrictionApply(rstr_in, CEED_NOTRANSPOSE, index_vec,
523eaf62fffSJeremy L Thompson                            elem_dof, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
524eaf62fffSJeremy L Thompson   const CeedScalar *elem_dof_a;
525eaf62fffSJeremy L Thompson   ierr = CeedVectorGetArrayRead(elem_dof, CEED_MEM_HOST, &elem_dof_a);
526eaf62fffSJeremy L Thompson   CeedChk(ierr);
527eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&index_vec); CeedChk(ierr);
528eaf62fffSJeremy L Thompson 
529eaf62fffSJeremy L Thompson   // Determine i, j locations for element matrices
530eaf62fffSJeremy L Thompson   CeedInt count = 0;
531ed9e99e6SJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
532ed9e99e6SJeremy L Thompson     for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) {
533ed9e99e6SJeremy L Thompson       for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) {
534ed9e99e6SJeremy L Thompson         for (CeedInt i = 0; i < elem_size; i++) {
535ed9e99e6SJeremy L Thompson           for (CeedInt j = 0; j < elem_size; j++) {
536ed9e99e6SJeremy L Thompson             const CeedInt elem_dof_index_row = i*layout_er[0] +
537eaf62fffSJeremy L Thompson                                                (comp_out)*layout_er[1] + e*layout_er[2];
538ed9e99e6SJeremy L Thompson             const CeedInt elem_dof_index_col = j*layout_er[0] +
539ed9e99e6SJeremy L Thompson                                                comp_in*layout_er[1] + e*layout_er[2];
540eaf62fffSJeremy L Thompson 
541eaf62fffSJeremy L Thompson             const CeedInt row = elem_dof_a[elem_dof_index_row];
542eaf62fffSJeremy L Thompson             const CeedInt col = elem_dof_a[elem_dof_index_col];
543eaf62fffSJeremy L Thompson 
544eaf62fffSJeremy L Thompson             rows[offset + count] = row;
545eaf62fffSJeremy L Thompson             cols[offset + count] = col;
546eaf62fffSJeremy L Thompson             count++;
547eaf62fffSJeremy L Thompson           }
548eaf62fffSJeremy L Thompson         }
549eaf62fffSJeremy L Thompson       }
550eaf62fffSJeremy L Thompson     }
551eaf62fffSJeremy L Thompson   }
552eaf62fffSJeremy L Thompson   if (count != local_num_entries)
553eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
554eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing assembled entries");
555eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
556eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(elem_dof, &elem_dof_a); CeedChk(ierr);
557eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&elem_dof); CeedChk(ierr);
558eaf62fffSJeremy L Thompson 
559eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
560eaf62fffSJeremy L Thompson }
561eaf62fffSJeremy L Thompson 
562eaf62fffSJeremy L Thompson /**
563eaf62fffSJeremy L Thompson   @brief Assemble nonzero entries for non-composite operator
564eaf62fffSJeremy L Thompson 
565eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssemble()
566eaf62fffSJeremy L Thompson 
567eaf62fffSJeremy L Thompson   @param[in] op       CeedOperator to assemble
56852b3e6a7SJed Brown   @param[in] offset   Offest for number of entries
569eaf62fffSJeremy L Thompson   @param[out] values  Values to assemble into matrix
570eaf62fffSJeremy L Thompson 
571eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
572eaf62fffSJeremy L Thompson 
573eaf62fffSJeremy L Thompson   @ref Developer
574eaf62fffSJeremy L Thompson **/
575eaf62fffSJeremy L Thompson static int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset,
576eaf62fffSJeremy L Thompson                                       CeedVector values) {
577eaf62fffSJeremy L Thompson   int ierr;
578eaf62fffSJeremy L Thompson   Ceed ceed = op->ceed;
579f04ea552SJeremy L Thompson   if (op->is_composite)
580eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
581eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
582eaf62fffSJeremy L Thompson                      "Composite operator not supported");
583eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
58452b3e6a7SJed Brown   if (op->num_elem == 0) return CEED_ERROR_SUCCESS;
585eaf62fffSJeremy L Thompson 
586cefa2673SJeremy L Thompson   if (op->LinearAssembleSingle) {
587cefa2673SJeremy L Thompson     // Backend version
588cefa2673SJeremy L Thompson     ierr = op->LinearAssembleSingle(op, offset, values); CeedChk(ierr);
589cefa2673SJeremy L Thompson     return CEED_ERROR_SUCCESS;
590cefa2673SJeremy L Thompson   } else {
591cefa2673SJeremy L Thompson     // Operator fallback
592cefa2673SJeremy L Thompson     CeedOperator op_fallback;
593cefa2673SJeremy L Thompson 
594cefa2673SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
595cefa2673SJeremy L Thompson     if (op_fallback) {
596cefa2673SJeremy L Thompson       ierr = CeedSingleOperatorAssemble(op_fallback, offset, values);
597cefa2673SJeremy L Thompson       CeedChk(ierr);
598cefa2673SJeremy L Thompson       return CEED_ERROR_SUCCESS;
599cefa2673SJeremy L Thompson     }
600cefa2673SJeremy L Thompson   }
601cefa2673SJeremy L Thompson 
602eaf62fffSJeremy L Thompson   // Assemble QFunction
603eaf62fffSJeremy L Thompson   CeedQFunction qf;
604eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChk(ierr);
605eaf62fffSJeremy L Thompson   CeedVector assembled_qf;
606eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_q;
60770a7ffb3SJeremy L Thompson   ierr = CeedOperatorLinearAssembleQFunctionBuildOrUpdate(
608eaf62fffSJeremy L Thompson            op, &assembled_qf, &rstr_q, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
6091f9221feSJeremy L Thompson   CeedSize qf_length;
610eaf62fffSJeremy L Thompson   ierr = CeedVectorGetLength(assembled_qf, &qf_length); CeedChk(ierr);
611eaf62fffSJeremy L Thompson 
6127e7773b5SJeremy L Thompson   CeedInt num_input_fields, num_output_fields;
613eaf62fffSJeremy L Thompson   CeedOperatorField *input_fields;
614eaf62fffSJeremy L Thompson   CeedOperatorField *output_fields;
6157e7773b5SJeremy L Thompson   ierr = CeedOperatorGetFields(op, &num_input_fields, &input_fields,
6167e7773b5SJeremy L Thompson                                &num_output_fields, &output_fields); CeedChk(ierr);
617eaf62fffSJeremy L Thompson 
618ed9e99e6SJeremy L Thompson   // Get assembly data
619ed9e99e6SJeremy L Thompson   CeedOperatorAssemblyData data;
620ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetOperatorAssemblyData(op, &data); CeedChk(ierr);
621ed9e99e6SJeremy L Thompson   const CeedEvalMode *eval_mode_in, *eval_mode_out;
622ed9e99e6SJeremy L Thompson   CeedInt num_eval_mode_in, num_eval_mode_out;
623ed9e99e6SJeremy L Thompson   ierr = CeedOperatorAssemblyDataGetEvalModes(data, &num_eval_mode_in,
624ed9e99e6SJeremy L Thompson          &eval_mode_in, &num_eval_mode_out, &eval_mode_out); CeedChk(ierr);
625ed9e99e6SJeremy L Thompson   CeedBasis basis_in, basis_out;
626ed9e99e6SJeremy L Thompson   ierr = CeedOperatorAssemblyDataGetBases(data, &basis_in, NULL, &basis_out,
627ed9e99e6SJeremy L Thompson                                           NULL); CeedChk(ierr);
628eaf62fffSJeremy L Thompson 
629eaf62fffSJeremy L Thompson   if (num_eval_mode_in == 0 || num_eval_mode_out == 0)
630eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
631eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
632eaf62fffSJeremy L Thompson                      "Cannot assemble operator with out inputs/outputs");
633eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
634eaf62fffSJeremy L Thompson 
635ed9e99e6SJeremy L Thompson   CeedElemRestriction active_rstr;
636eaf62fffSJeremy L Thompson   CeedInt num_elem, elem_size, num_qpts, num_comp;
637ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetActiveElemRestriction(op, &active_rstr); CeedChk(ierr);
638ed9e99e6SJeremy L Thompson   ierr = CeedElemRestrictionGetNumElements(active_rstr, &num_elem); CeedChk(ierr);
639ed9e99e6SJeremy L Thompson   ierr = CeedElemRestrictionGetElementSize(active_rstr, &elem_size);
640ed9e99e6SJeremy L Thompson   CeedChk(ierr);
641ed9e99e6SJeremy L Thompson   ierr = CeedElemRestrictionGetNumComponents(active_rstr, &num_comp);
642ed9e99e6SJeremy L Thompson   CeedChk(ierr);
643eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts); CeedChk(ierr);
644eaf62fffSJeremy L Thompson 
645eaf62fffSJeremy L Thompson   CeedInt local_num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
646eaf62fffSJeremy L Thompson 
647eaf62fffSJeremy L Thompson   // loop over elements and put in data structure
648eaf62fffSJeremy L Thompson   const CeedScalar *interp_in, *grad_in;
649eaf62fffSJeremy L Thompson   ierr = CeedBasisGetInterp(basis_in, &interp_in); CeedChk(ierr);
650eaf62fffSJeremy L Thompson   ierr = CeedBasisGetGrad(basis_in, &grad_in); CeedChk(ierr);
651eaf62fffSJeremy L Thompson 
652eaf62fffSJeremy L Thompson   const CeedScalar *assembled_qf_array;
653eaf62fffSJeremy L Thompson   ierr = CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array);
654eaf62fffSJeremy L Thompson   CeedChk(ierr);
655eaf62fffSJeremy L Thompson 
656eaf62fffSJeremy L Thompson   CeedInt layout_qf[3];
657eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetELayout(rstr_q, &layout_qf); CeedChk(ierr);
658eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_q); CeedChk(ierr);
659eaf62fffSJeremy L Thompson 
660eaf62fffSJeremy L Thompson   // we store B_mat_in, B_mat_out, BTD, elem_mat in row-major order
661ed9e99e6SJeremy L Thompson   const CeedScalar *B_mat_in, *B_mat_out;
662ed9e99e6SJeremy L Thompson   ierr = CeedOperatorAssemblyDataGetBases(data, NULL, &B_mat_in, NULL,
663ed9e99e6SJeremy L Thompson                                           &B_mat_out); CeedChk(ierr);
664ed9e99e6SJeremy L Thompson   CeedScalar BTD_mat[elem_size * num_qpts*num_eval_mode_in];
665eaf62fffSJeremy L Thompson   CeedScalar elem_mat[elem_size * elem_size];
66692ae7e47SJeremy L Thompson   CeedInt count = 0;
667eaf62fffSJeremy L Thompson   CeedScalar *vals;
6689c774eddSJeremy L Thompson   ierr = CeedVectorGetArrayWrite(values, CEED_MEM_HOST, &vals); CeedChk(ierr);
669ed9e99e6SJeremy L Thompson   for (CeedInt e = 0; e < num_elem; e++) {
670ed9e99e6SJeremy L Thompson     for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) {
671ed9e99e6SJeremy L Thompson       for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) {
672ed9e99e6SJeremy L Thompson         // Compute B^T*D
673ed9e99e6SJeremy L Thompson         for (CeedInt n = 0; n < elem_size; n++) {
674ed9e99e6SJeremy L Thompson           for (CeedInt q = 0; q < num_qpts; q++) {
675ed9e99e6SJeremy L Thompson             for (CeedInt e_in = 0; e_in < num_eval_mode_in; e_in++) {
676ed9e99e6SJeremy L Thompson               const CeedInt btd_index = n*(num_qpts*num_eval_mode_in) +
677ed9e99e6SJeremy L Thompson                                         (num_eval_mode_in*q + e_in);
678067fd99fSJeremy L Thompson               CeedScalar sum = 0.0;
679067fd99fSJeremy L Thompson               for (CeedInt e_out = 0; e_out < num_eval_mode_out; e_out++) {
680ed9e99e6SJeremy L Thompson                 const CeedInt b_out_index = (num_eval_mode_out*q + e_out)*elem_size + n;
681ed9e99e6SJeremy L Thompson                 const CeedInt eval_mode_index = ((e_in*num_comp+comp_in)*num_eval_mode_out
682ed9e99e6SJeremy L Thompson                                                  +e_out)*num_comp + comp_out;
683ed9e99e6SJeremy L Thompson                 const CeedInt qf_index = q*layout_qf[0] + eval_mode_index*layout_qf[1] +
684ed9e99e6SJeremy L Thompson                                          e*layout_qf[2];
685067fd99fSJeremy L Thompson                 sum += B_mat_out[b_out_index] * assembled_qf_array[qf_index];
686eaf62fffSJeremy L Thompson               }
687067fd99fSJeremy L Thompson               BTD_mat[btd_index] = sum;
688ed9e99e6SJeremy L Thompson             }
689ed9e99e6SJeremy L Thompson           }
690eaf62fffSJeremy L Thompson         }
691eaf62fffSJeremy L Thompson         // form element matrix itself (for each block component)
692ed9e99e6SJeremy L Thompson         ierr = CeedMatrixMatrixMultiply(ceed, BTD_mat, B_mat_in, elem_mat, elem_size,
693eaf62fffSJeremy L Thompson                                         elem_size, num_qpts*num_eval_mode_in); CeedChk(ierr);
694eaf62fffSJeremy L Thompson 
695eaf62fffSJeremy L Thompson         // put element matrix in coordinate data structure
696ed9e99e6SJeremy L Thompson         for (CeedInt i = 0; i < elem_size; i++) {
697ed9e99e6SJeremy L Thompson           for (CeedInt j = 0; j < elem_size; j++) {
698eaf62fffSJeremy L Thompson             vals[offset + count] = elem_mat[i*elem_size + j];
699eaf62fffSJeremy L Thompson             count++;
700eaf62fffSJeremy L Thompson           }
701eaf62fffSJeremy L Thompson         }
702eaf62fffSJeremy L Thompson       }
703eaf62fffSJeremy L Thompson     }
704eaf62fffSJeremy L Thompson   }
705eaf62fffSJeremy L Thompson   if (count != local_num_entries)
706eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
707eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MAJOR, "Error computing entries");
708eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
709eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArray(values, &vals); CeedChk(ierr);
710eaf62fffSJeremy L Thompson 
711eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array);
712eaf62fffSJeremy L Thompson   CeedChk(ierr);
713eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&assembled_qf); CeedChk(ierr);
714eaf62fffSJeremy L Thompson 
715eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
716eaf62fffSJeremy L Thompson }
717eaf62fffSJeremy L Thompson 
718eaf62fffSJeremy L Thompson /**
719eaf62fffSJeremy L Thompson   @brief Count number of entries for assembled CeedOperator
720eaf62fffSJeremy L Thompson 
721eaf62fffSJeremy L Thompson   @param[in] op            CeedOperator to assemble
722eaf62fffSJeremy L Thompson   @param[out] num_entries  Number of entries in assembled representation
723eaf62fffSJeremy L Thompson 
724eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
725eaf62fffSJeremy L Thompson 
726eaf62fffSJeremy L Thompson   @ref Utility
727eaf62fffSJeremy L Thompson **/
728eaf62fffSJeremy L Thompson static int CeedSingleOperatorAssemblyCountEntries(CeedOperator op,
729eaf62fffSJeremy L Thompson     CeedInt *num_entries) {
730eaf62fffSJeremy L Thompson   int ierr;
731eaf62fffSJeremy L Thompson   CeedElemRestriction rstr;
732eaf62fffSJeremy L Thompson   CeedInt num_elem, elem_size, num_comp;
733eaf62fffSJeremy L Thompson 
734f04ea552SJeremy L Thompson   if (op->is_composite)
735eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
736eaf62fffSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
737eaf62fffSJeremy L Thompson                      "Composite operator not supported");
738eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
739eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetActiveElemRestriction(op, &rstr); CeedChk(ierr);
740eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChk(ierr);
741eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetElementSize(rstr, &elem_size); CeedChk(ierr);
742eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumComponents(rstr, &num_comp); CeedChk(ierr);
743eaf62fffSJeremy L Thompson   *num_entries = elem_size*num_comp * elem_size*num_comp * num_elem;
744eaf62fffSJeremy L Thompson 
745eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
746eaf62fffSJeremy L Thompson }
747eaf62fffSJeremy L Thompson 
748eaf62fffSJeremy L Thompson /**
749eaf62fffSJeremy L Thompson   @brief Common code for creating a multigrid coarse operator and level
750eaf62fffSJeremy L Thompson            transfer operators for a CeedOperator
751eaf62fffSJeremy L Thompson 
752eaf62fffSJeremy L Thompson   @param[in] op_fine       Fine grid operator
753eaf62fffSJeremy L Thompson   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
754eaf62fffSJeremy L Thompson   @param[in] rstr_coarse   Coarse grid restriction
755eaf62fffSJeremy L Thompson   @param[in] basis_coarse  Coarse grid active vector basis
756eaf62fffSJeremy L Thompson   @param[in] basis_c_to_f  Basis for coarse to fine interpolation
757eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
758eaf62fffSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator
759eaf62fffSJeremy L Thompson   @param[out] op_restrict  Fine to coarse operator
760eaf62fffSJeremy L Thompson 
761eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
762eaf62fffSJeremy L Thompson 
763eaf62fffSJeremy L Thompson   @ref Developer
764eaf62fffSJeremy L Thompson **/
765eaf62fffSJeremy L Thompson static int CeedSingleOperatorMultigridLevel(CeedOperator op_fine,
766eaf62fffSJeremy L Thompson     CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
767eaf62fffSJeremy L Thompson     CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
768eaf62fffSJeremy L Thompson     CeedOperator *op_restrict) {
769eaf62fffSJeremy L Thompson   int ierr;
770eaf62fffSJeremy L Thompson   Ceed ceed;
771eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
772eaf62fffSJeremy L Thompson 
773eaf62fffSJeremy L Thompson   // Check for composite operator
774eaf62fffSJeremy L Thompson   bool is_composite;
775eaf62fffSJeremy L Thompson   ierr = CeedOperatorIsComposite(op_fine, &is_composite); CeedChk(ierr);
776eaf62fffSJeremy L Thompson   if (is_composite)
777eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
778eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
779eaf62fffSJeremy L Thompson                      "Automatic multigrid setup for composite operators not supported");
780eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
781eaf62fffSJeremy L Thompson 
782eaf62fffSJeremy L Thompson   // Coarse Grid
783eaf62fffSJeremy L Thompson   ierr = CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT,
784eaf62fffSJeremy L Thompson                             op_coarse); CeedChk(ierr);
785eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_fine = NULL;
786eaf62fffSJeremy L Thompson   // -- Clone input fields
78792ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_input_fields; i++) {
788eaf62fffSJeremy L Thompson     if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
789eaf62fffSJeremy L Thompson       rstr_fine = op_fine->input_fields[i]->elem_restr;
790eaf62fffSJeremy L Thompson       ierr = CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name,
791eaf62fffSJeremy L Thompson                                   rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE);
792eaf62fffSJeremy L Thompson       CeedChk(ierr);
793eaf62fffSJeremy L Thompson     } else {
794eaf62fffSJeremy L Thompson       ierr = CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name,
795eaf62fffSJeremy L Thompson                                   op_fine->input_fields[i]->elem_restr,
796eaf62fffSJeremy L Thompson                                   op_fine->input_fields[i]->basis,
797eaf62fffSJeremy L Thompson                                   op_fine->input_fields[i]->vec); CeedChk(ierr);
798eaf62fffSJeremy L Thompson     }
799eaf62fffSJeremy L Thompson   }
800eaf62fffSJeremy L Thompson   // -- Clone output fields
80192ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_output_fields; i++) {
802eaf62fffSJeremy L Thompson     if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
803eaf62fffSJeremy L Thompson       ierr = CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name,
804eaf62fffSJeremy L Thompson                                   rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE);
805eaf62fffSJeremy L Thompson       CeedChk(ierr);
806eaf62fffSJeremy L Thompson     } else {
807eaf62fffSJeremy L Thompson       ierr = CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name,
808eaf62fffSJeremy L Thompson                                   op_fine->output_fields[i]->elem_restr,
809eaf62fffSJeremy L Thompson                                   op_fine->output_fields[i]->basis,
810eaf62fffSJeremy L Thompson                                   op_fine->output_fields[i]->vec); CeedChk(ierr);
811eaf62fffSJeremy L Thompson     }
812eaf62fffSJeremy L Thompson   }
813af99e877SJeremy L Thompson   // -- Clone QFunctionAssemblyData
814af99e877SJeremy L Thompson   ierr = CeedQFunctionAssemblyDataReferenceCopy(op_fine->qf_assembled,
815af99e877SJeremy L Thompson          &(*op_coarse)->qf_assembled); CeedChk(ierr);
816eaf62fffSJeremy L Thompson 
817eaf62fffSJeremy L Thompson   // Multiplicity vector
818eaf62fffSJeremy L Thompson   CeedVector mult_vec, mult_e_vec;
819eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec);
820eaf62fffSJeremy L Thompson   CeedChk(ierr);
821eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(mult_e_vec, 0.0); CeedChk(ierr);
822eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionApply(rstr_fine, CEED_NOTRANSPOSE, p_mult_fine,
823eaf62fffSJeremy L Thompson                                   mult_e_vec, CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
824eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(mult_vec, 0.0); CeedChk(ierr);
825eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionApply(rstr_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec,
826eaf62fffSJeremy L Thompson                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
827eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&mult_e_vec); CeedChk(ierr);
828eaf62fffSJeremy L Thompson   ierr = CeedVectorReciprocal(mult_vec); CeedChk(ierr);
829eaf62fffSJeremy L Thompson 
830eaf62fffSJeremy L Thompson   // Restriction
831eaf62fffSJeremy L Thompson   CeedInt num_comp;
832eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumComponents(basis_coarse, &num_comp); CeedChk(ierr);
833eaf62fffSJeremy L Thompson   CeedQFunction qf_restrict;
834eaf62fffSJeremy L Thompson   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_restrict);
835eaf62fffSJeremy L Thompson   CeedChk(ierr);
836eaf62fffSJeremy L Thompson   CeedInt *num_comp_r_data;
837eaf62fffSJeremy L Thompson   ierr = CeedCalloc(1, &num_comp_r_data); CeedChk(ierr);
838eaf62fffSJeremy L Thompson   num_comp_r_data[0] = num_comp;
839eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_r;
840eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextCreate(ceed, &ctx_r); CeedChk(ierr);
841eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER,
842eaf62fffSJeremy L Thompson                                      sizeof(*num_comp_r_data), num_comp_r_data);
843eaf62fffSJeremy L Thompson   CeedChk(ierr);
844eaf62fffSJeremy L Thompson   ierr = CeedQFunctionSetContext(qf_restrict, ctx_r); CeedChk(ierr);
845eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextDestroy(&ctx_r); CeedChk(ierr);
846eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddInput(qf_restrict, "input", num_comp, CEED_EVAL_NONE);
847eaf62fffSJeremy L Thompson   CeedChk(ierr);
848eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddInput(qf_restrict, "scale", num_comp, CEED_EVAL_NONE);
849eaf62fffSJeremy L Thompson   CeedChk(ierr);
850eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddOutput(qf_restrict, "output", num_comp,
851eaf62fffSJeremy L Thompson                                 CEED_EVAL_INTERP); CeedChk(ierr);
8526e15d496SJeremy L Thompson   ierr = CeedQFunctionSetUserFlopsEstimate(qf_restrict, num_comp); CeedChk(ierr);
853eaf62fffSJeremy L Thompson 
854eaf62fffSJeremy L Thompson   ierr = CeedOperatorCreate(ceed, qf_restrict, CEED_QFUNCTION_NONE,
855eaf62fffSJeremy L Thompson                             CEED_QFUNCTION_NONE, op_restrict);
856eaf62fffSJeremy L Thompson   CeedChk(ierr);
857eaf62fffSJeremy L Thompson   ierr = CeedOperatorSetField(*op_restrict, "input", rstr_fine,
858eaf62fffSJeremy L Thompson                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
859eaf62fffSJeremy L Thompson   CeedChk(ierr);
860eaf62fffSJeremy L Thompson   ierr = CeedOperatorSetField(*op_restrict, "scale", rstr_fine,
861eaf62fffSJeremy L Thompson                               CEED_BASIS_COLLOCATED, mult_vec);
862eaf62fffSJeremy L Thompson   CeedChk(ierr);
863eaf62fffSJeremy L Thompson   ierr = CeedOperatorSetField(*op_restrict, "output", rstr_coarse, basis_c_to_f,
864eaf62fffSJeremy L Thompson                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
865eaf62fffSJeremy L Thompson 
866eaf62fffSJeremy L Thompson   // Prolongation
867eaf62fffSJeremy L Thompson   CeedQFunction qf_prolong;
868eaf62fffSJeremy L Thompson   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong);
869eaf62fffSJeremy L Thompson   CeedChk(ierr);
870eaf62fffSJeremy L Thompson   CeedInt *num_comp_p_data;
871eaf62fffSJeremy L Thompson   ierr = CeedCalloc(1, &num_comp_p_data); CeedChk(ierr);
872eaf62fffSJeremy L Thompson   num_comp_p_data[0] = num_comp;
873eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_p;
874eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextCreate(ceed, &ctx_p); CeedChk(ierr);
875eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER,
876eaf62fffSJeremy L Thompson                                      sizeof(*num_comp_p_data), num_comp_p_data);
877eaf62fffSJeremy L Thompson   CeedChk(ierr);
878eaf62fffSJeremy L Thompson   ierr = CeedQFunctionSetContext(qf_prolong, ctx_p); CeedChk(ierr);
879eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextDestroy(&ctx_p); CeedChk(ierr);
880eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP);
881eaf62fffSJeremy L Thompson   CeedChk(ierr);
882eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE);
883eaf62fffSJeremy L Thompson   CeedChk(ierr);
884eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE);
885eaf62fffSJeremy L Thompson   CeedChk(ierr);
8866e15d496SJeremy L Thompson   ierr = CeedQFunctionSetUserFlopsEstimate(qf_prolong, num_comp); CeedChk(ierr);
887eaf62fffSJeremy L Thompson 
888eaf62fffSJeremy L Thompson   ierr = CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE,
889eaf62fffSJeremy L Thompson                             CEED_QFUNCTION_NONE, op_prolong);
890eaf62fffSJeremy L Thompson   CeedChk(ierr);
891eaf62fffSJeremy L Thompson   ierr = CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f,
892eaf62fffSJeremy L Thompson                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
893eaf62fffSJeremy L Thompson   ierr = CeedOperatorSetField(*op_prolong, "scale", rstr_fine,
894eaf62fffSJeremy L Thompson                               CEED_BASIS_COLLOCATED, mult_vec);
895eaf62fffSJeremy L Thompson   CeedChk(ierr);
896eaf62fffSJeremy L Thompson   ierr = CeedOperatorSetField(*op_prolong, "output", rstr_fine,
897eaf62fffSJeremy L Thompson                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
898eaf62fffSJeremy L Thompson   CeedChk(ierr);
899eaf62fffSJeremy L Thompson 
900ea6b5821SJeremy L Thompson   // Clone name
901ea6b5821SJeremy L Thompson   bool has_name = op_fine->name;
902ea6b5821SJeremy L Thompson   size_t name_len = op_fine->name ? strlen(op_fine->name) : 0;
903ea6b5821SJeremy L Thompson   ierr = CeedOperatorSetName(*op_coarse, op_fine->name); CeedChk(ierr);
904ea6b5821SJeremy L Thompson   {
905ea6b5821SJeremy L Thompson     char *prolongation_name;
906ea6b5821SJeremy L Thompson     ierr = CeedCalloc(18 + name_len, &prolongation_name); CeedChk(ierr);
907ea6b5821SJeremy L Thompson     sprintf(prolongation_name, "prolongation%s%s", has_name ? " for " : "",
9083129f025Srezgarshakeri             has_name ? op_fine->name : "");
909ea6b5821SJeremy L Thompson     ierr = CeedOperatorSetName(*op_prolong, prolongation_name); CeedChk(ierr);
910ea6b5821SJeremy L Thompson     ierr = CeedFree(&prolongation_name); CeedChk(ierr);
911ea6b5821SJeremy L Thompson   }
912ea6b5821SJeremy L Thompson   {
913ea6b5821SJeremy L Thompson     char *restriction_name;
914ea6b5821SJeremy L Thompson     ierr = CeedCalloc(17 + name_len, &restriction_name); CeedChk(ierr);
915ea6b5821SJeremy L Thompson     sprintf(restriction_name, "restriction%s%s", has_name ? " for " : "",
9163129f025Srezgarshakeri             has_name ? op_fine->name : "");
917ea6b5821SJeremy L Thompson     ierr = CeedOperatorSetName(*op_restrict, restriction_name); CeedChk(ierr);
918ea6b5821SJeremy L Thompson     ierr = CeedFree(&restriction_name); CeedChk(ierr);
919ea6b5821SJeremy L Thompson   }
920ea6b5821SJeremy L Thompson 
921eaf62fffSJeremy L Thompson   // Cleanup
922eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&mult_vec); CeedChk(ierr);
923eaf62fffSJeremy L Thompson   ierr = CeedBasisDestroy(&basis_c_to_f); CeedChk(ierr);
924eaf62fffSJeremy L Thompson   ierr = CeedQFunctionDestroy(&qf_restrict); CeedChk(ierr);
925eaf62fffSJeremy L Thompson   ierr = CeedQFunctionDestroy(&qf_prolong); CeedChk(ierr);
926805fe78eSJeremy L Thompson 
927eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
928eaf62fffSJeremy L Thompson }
929eaf62fffSJeremy L Thompson 
930eaf62fffSJeremy L Thompson /**
931eaf62fffSJeremy L Thompson   @brief Build 1D mass matrix and Laplacian with perturbation
932eaf62fffSJeremy L Thompson 
933eaf62fffSJeremy L Thompson   @param[in] interp_1d    Interpolation matrix in one dimension
934eaf62fffSJeremy L Thompson   @param[in] grad_1d      Gradient matrix in one dimension
935eaf62fffSJeremy L Thompson   @param[in] q_weight_1d  Quadrature weights in one dimension
936eaf62fffSJeremy L Thompson   @param[in] P_1d         Number of basis nodes in one dimension
937eaf62fffSJeremy L Thompson   @param[in] Q_1d         Number of quadrature points in one dimension
938eaf62fffSJeremy L Thompson   @param[in] dim          Dimension of basis
939eaf62fffSJeremy L Thompson   @param[out] mass        Assembled mass matrix in one dimension
940eaf62fffSJeremy L Thompson   @param[out] laplace     Assembled perturbed Laplacian in one dimension
941eaf62fffSJeremy L Thompson 
942eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
943eaf62fffSJeremy L Thompson 
944eaf62fffSJeremy L Thompson   @ref Developer
945eaf62fffSJeremy L Thompson **/
946eaf62fffSJeremy L Thompson CeedPragmaOptimizeOff
947eaf62fffSJeremy L Thompson static int CeedBuildMassLaplace(const CeedScalar *interp_1d,
948eaf62fffSJeremy L Thompson                                 const CeedScalar *grad_1d,
949eaf62fffSJeremy L Thompson                                 const CeedScalar *q_weight_1d, CeedInt P_1d,
950eaf62fffSJeremy L Thompson                                 CeedInt Q_1d, CeedInt dim,
951eaf62fffSJeremy L Thompson                                 CeedScalar *mass, CeedScalar *laplace) {
952eaf62fffSJeremy L Thompson 
953eaf62fffSJeremy L Thompson   for (CeedInt i=0; i<P_1d; i++)
954eaf62fffSJeremy L Thompson     for (CeedInt j=0; j<P_1d; j++) {
955eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
956eaf62fffSJeremy L Thompson       for (CeedInt k=0; k<Q_1d; k++)
957eaf62fffSJeremy L Thompson         sum += interp_1d[k*P_1d+i]*q_weight_1d[k]*interp_1d[k*P_1d+j];
958eaf62fffSJeremy L Thompson       mass[i+j*P_1d] = sum;
959eaf62fffSJeremy L Thompson     }
960eaf62fffSJeremy L Thompson   // -- Laplacian
961eaf62fffSJeremy L Thompson   for (CeedInt i=0; i<P_1d; i++)
962eaf62fffSJeremy L Thompson     for (CeedInt j=0; j<P_1d; j++) {
963eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
964eaf62fffSJeremy L Thompson       for (CeedInt k=0; k<Q_1d; k++)
965eaf62fffSJeremy L Thompson         sum += grad_1d[k*P_1d+i]*q_weight_1d[k]*grad_1d[k*P_1d+j];
966eaf62fffSJeremy L Thompson       laplace[i+j*P_1d] = sum;
967eaf62fffSJeremy L Thompson     }
968eaf62fffSJeremy L Thompson   CeedScalar perturbation = dim>2 ? 1e-6 : 1e-4;
969eaf62fffSJeremy L Thompson   for (CeedInt i=0; i<P_1d; i++)
970eaf62fffSJeremy L Thompson     laplace[i+P_1d*i] += perturbation;
971eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
972eaf62fffSJeremy L Thompson }
973eaf62fffSJeremy L Thompson CeedPragmaOptimizeOn
974eaf62fffSJeremy L Thompson 
975eaf62fffSJeremy L Thompson /// @}
976eaf62fffSJeremy L Thompson 
977eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
978480fae85SJeremy L Thompson /// CeedOperator Backend API
979480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
980480fae85SJeremy L Thompson /// @addtogroup CeedOperatorBackend
981480fae85SJeremy L Thompson /// @{
982480fae85SJeremy L Thompson 
983480fae85SJeremy L Thompson /**
984480fae85SJeremy L Thompson   @brief Create object holding CeedQFunction assembly data for CeedOperator
985480fae85SJeremy L Thompson 
986480fae85SJeremy L Thompson   @param[in] ceed  A Ceed object where the CeedQFunctionAssemblyData will be created
987480fae85SJeremy L Thompson   @param[out] data Address of the variable where the newly created
988480fae85SJeremy L Thompson                      CeedQFunctionAssemblyData will be stored
989480fae85SJeremy L Thompson 
990480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
991480fae85SJeremy L Thompson 
992480fae85SJeremy L Thompson   @ref Backend
993480fae85SJeremy L Thompson **/
994480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataCreate(Ceed ceed,
995480fae85SJeremy L Thompson                                     CeedQFunctionAssemblyData *data) {
996480fae85SJeremy L Thompson   int ierr;
997480fae85SJeremy L Thompson 
998480fae85SJeremy L Thompson   ierr = CeedCalloc(1, data); CeedChk(ierr);
999480fae85SJeremy L Thompson   (*data)->ref_count = 1;
1000480fae85SJeremy L Thompson   (*data)->ceed = ceed;
1001480fae85SJeremy L Thompson   ierr = CeedReference(ceed); CeedChk(ierr);
1002480fae85SJeremy L Thompson 
1003480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1004480fae85SJeremy L Thompson }
1005480fae85SJeremy L Thompson 
1006480fae85SJeremy L Thompson /**
1007480fae85SJeremy L Thompson   @brief Increment the reference counter for a CeedQFunctionAssemblyData
1008480fae85SJeremy L Thompson 
1009480fae85SJeremy L Thompson   @param data  CeedQFunctionAssemblyData to increment the reference counter
1010480fae85SJeremy L Thompson 
1011480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1012480fae85SJeremy L Thompson 
1013480fae85SJeremy L Thompson   @ref Backend
1014480fae85SJeremy L Thompson **/
1015480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataReference(CeedQFunctionAssemblyData data) {
1016480fae85SJeremy L Thompson   data->ref_count++;
1017480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1018480fae85SJeremy L Thompson }
1019480fae85SJeremy L Thompson 
1020480fae85SJeremy L Thompson /**
1021beecbf24SJeremy L Thompson   @brief Set re-use of CeedQFunctionAssemblyData
10228b919e6bSJeremy L Thompson 
1023beecbf24SJeremy L Thompson   @param data       CeedQFunctionAssemblyData to mark for reuse
1024beecbf24SJeremy L Thompson   @param reuse_data Boolean flag indicating data re-use
10258b919e6bSJeremy L Thompson 
10268b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
10278b919e6bSJeremy L Thompson 
10288b919e6bSJeremy L Thompson   @ref Backend
10298b919e6bSJeremy L Thompson **/
1030beecbf24SJeremy L Thompson int CeedQFunctionAssemblyDataSetReuse(CeedQFunctionAssemblyData data,
1031beecbf24SJeremy L Thompson                                       bool reuse_data) {
1032beecbf24SJeremy L Thompson   data->reuse_data = reuse_data;
1033beecbf24SJeremy L Thompson   data->needs_data_update = true;
1034beecbf24SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1035beecbf24SJeremy L Thompson }
1036beecbf24SJeremy L Thompson 
1037beecbf24SJeremy L Thompson /**
1038beecbf24SJeremy L Thompson   @brief Mark QFunctionAssemblyData as stale
1039beecbf24SJeremy L Thompson 
1040beecbf24SJeremy L Thompson   @param data              CeedQFunctionAssemblyData to mark as stale
1041beecbf24SJeremy L Thompson   @param needs_data_update Boolean flag indicating if update is needed or completed
1042beecbf24SJeremy L Thompson 
1043beecbf24SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1044beecbf24SJeremy L Thompson 
1045beecbf24SJeremy L Thompson   @ref Backend
1046beecbf24SJeremy L Thompson **/
1047beecbf24SJeremy L Thompson int CeedQFunctionAssemblyDataSetUpdateNeeded(CeedQFunctionAssemblyData data,
1048beecbf24SJeremy L Thompson     bool needs_data_update) {
1049beecbf24SJeremy L Thompson   data->needs_data_update = needs_data_update;
10508b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
10518b919e6bSJeremy L Thompson }
10528b919e6bSJeremy L Thompson 
10538b919e6bSJeremy L Thompson /**
10548b919e6bSJeremy L Thompson   @brief Determine if QFunctionAssemblyData needs update
10558b919e6bSJeremy L Thompson 
10568b919e6bSJeremy L Thompson   @param[in] data              CeedQFunctionAssemblyData to mark as stale
10578b919e6bSJeremy L Thompson   @param[out] is_update_needed Boolean flag indicating if re-assembly is required
10588b919e6bSJeremy L Thompson 
10598b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
10608b919e6bSJeremy L Thompson 
10618b919e6bSJeremy L Thompson   @ref Backend
10628b919e6bSJeremy L Thompson **/
10638b919e6bSJeremy L Thompson int CeedQFunctionAssemblyDataIsUpdateNeeded(CeedQFunctionAssemblyData data,
10648b919e6bSJeremy L Thompson     bool *is_update_needed) {
1065beecbf24SJeremy L Thompson   *is_update_needed = !data->reuse_data || data->needs_data_update;
10668b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
10678b919e6bSJeremy L Thompson }
10688b919e6bSJeremy L Thompson 
10698b919e6bSJeremy L Thompson /**
1070480fae85SJeremy L Thompson   @brief Copy the pointer to a CeedQFunctionAssemblyData. Both pointers should
1071480fae85SJeremy L Thompson            be destroyed with `CeedCeedQFunctionAssemblyDataDestroy()`;
1072480fae85SJeremy L Thompson            Note: If `*data_copy` is non-NULL, then it is assumed that
1073480fae85SJeremy L Thompson            `*data_copy` is a pointer to a CeedQFunctionAssemblyData. This
1074480fae85SJeremy L Thompson            CeedQFunctionAssemblyData will be destroyed if `*data_copy` is
1075480fae85SJeremy L Thompson            the only reference to this CeedQFunctionAssemblyData.
1076480fae85SJeremy L Thompson 
1077480fae85SJeremy L Thompson   @param data            CeedQFunctionAssemblyData to copy reference to
1078480fae85SJeremy L Thompson   @param[out] data_copy  Variable to store copied reference
1079480fae85SJeremy L Thompson 
1080480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1081480fae85SJeremy L Thompson 
1082480fae85SJeremy L Thompson   @ref Backend
1083480fae85SJeremy L Thompson **/
1084480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataReferenceCopy(CeedQFunctionAssemblyData data,
1085480fae85SJeremy L Thompson     CeedQFunctionAssemblyData *data_copy) {
1086480fae85SJeremy L Thompson   int ierr;
1087480fae85SJeremy L Thompson 
1088480fae85SJeremy L Thompson   ierr = CeedQFunctionAssemblyDataReference(data); CeedChk(ierr);
1089480fae85SJeremy L Thompson   ierr = CeedQFunctionAssemblyDataDestroy(data_copy); CeedChk(ierr);
1090480fae85SJeremy L Thompson   *data_copy = data;
1091480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1092480fae85SJeremy L Thompson }
1093480fae85SJeremy L Thompson 
1094480fae85SJeremy L Thompson /**
1095480fae85SJeremy L Thompson   @brief Get setup status for internal objects for CeedQFunctionAssemblyData
1096480fae85SJeremy L Thompson 
1097480fae85SJeremy L Thompson   @param[in] data      CeedQFunctionAssemblyData to retreive status
1098480fae85SJeremy L Thompson   @param[out] is_setup Boolean flag for setup status
1099480fae85SJeremy L Thompson 
1100480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1101480fae85SJeremy L Thompson 
1102480fae85SJeremy L Thompson   @ref Backend
1103480fae85SJeremy L Thompson **/
1104480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataIsSetup(CeedQFunctionAssemblyData data,
1105480fae85SJeremy L Thompson                                      bool *is_setup) {
1106480fae85SJeremy L Thompson   *is_setup = data->is_setup;
1107480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1108480fae85SJeremy L Thompson }
1109480fae85SJeremy L Thompson 
1110480fae85SJeremy L Thompson /**
1111480fae85SJeremy L Thompson   @brief Set internal objects for CeedQFunctionAssemblyData
1112480fae85SJeremy L Thompson 
1113480fae85SJeremy L Thompson   @param[in] data  CeedQFunctionAssemblyData to set objects
1114480fae85SJeremy L Thompson   @param[in] vec   CeedVector to store assembled CeedQFunction at quadrature points
1115480fae85SJeremy L Thompson   @param[in] rstr  CeedElemRestriction for CeedVector containing assembled CeedQFunction
1116480fae85SJeremy L Thompson 
1117480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1118480fae85SJeremy L Thompson 
1119480fae85SJeremy L Thompson   @ref Backend
1120480fae85SJeremy L Thompson **/
1121480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataSetObjects(CeedQFunctionAssemblyData data,
1122480fae85SJeremy L Thompson                                         CeedVector vec, CeedElemRestriction rstr) {
1123480fae85SJeremy L Thompson   int ierr;
1124480fae85SJeremy L Thompson 
11252efa2d85SJeremy L Thompson   ierr = CeedVectorReferenceCopy(vec, &data->vec); CeedChk(ierr);
11262efa2d85SJeremy L Thompson   ierr = CeedElemRestrictionReferenceCopy(rstr, &data->rstr); CeedChk(ierr);
1127480fae85SJeremy L Thompson 
1128480fae85SJeremy L Thompson   data->is_setup = true;
1129480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1130480fae85SJeremy L Thompson }
1131480fae85SJeremy L Thompson 
1132480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataGetObjects(CeedQFunctionAssemblyData data,
1133480fae85SJeremy L Thompson                                         CeedVector *vec, CeedElemRestriction *rstr) {
11342efa2d85SJeremy L Thompson   int ierr;
11352efa2d85SJeremy L Thompson 
1136480fae85SJeremy L Thompson   if (!data->is_setup)
1137480fae85SJeremy L Thompson     // LCOV_EXCL_START
1138480fae85SJeremy L Thompson     return CeedError(data->ceed, CEED_ERROR_INCOMPLETE,
1139480fae85SJeremy L Thompson                      "Internal objects not set; must call CeedQFunctionAssemblyDataSetObjects first.");
1140480fae85SJeremy L Thompson   // LCOV_EXCL_STOP
1141480fae85SJeremy L Thompson 
11422efa2d85SJeremy L Thompson   ierr = CeedVectorReferenceCopy(data->vec, vec); CeedChk(ierr);
11432efa2d85SJeremy L Thompson   ierr = CeedElemRestrictionReferenceCopy(data->rstr, rstr); CeedChk(ierr);
1144480fae85SJeremy L Thompson 
1145480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1146480fae85SJeremy L Thompson }
1147480fae85SJeremy L Thompson 
1148480fae85SJeremy L Thompson /**
1149480fae85SJeremy L Thompson   @brief Destroy CeedQFunctionAssemblyData
1150480fae85SJeremy L Thompson 
1151480fae85SJeremy L Thompson   @param[out] data  CeedQFunctionAssemblyData to destroy
1152480fae85SJeremy L Thompson 
1153480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1154480fae85SJeremy L Thompson 
1155480fae85SJeremy L Thompson   @ref Backend
1156480fae85SJeremy L Thompson **/
1157480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataDestroy(CeedQFunctionAssemblyData *data) {
1158480fae85SJeremy L Thompson   int ierr;
1159480fae85SJeremy L Thompson 
1160480fae85SJeremy L Thompson   if (!*data || --(*data)->ref_count > 0) return CEED_ERROR_SUCCESS;
1161480fae85SJeremy L Thompson 
1162480fae85SJeremy L Thompson   ierr = CeedDestroy(&(*data)->ceed); CeedChk(ierr);
1163480fae85SJeremy L Thompson   ierr = CeedVectorDestroy(&(*data)->vec); CeedChk(ierr);
1164480fae85SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&(*data)->rstr); CeedChk(ierr);
1165480fae85SJeremy L Thompson 
1166480fae85SJeremy L Thompson   ierr = CeedFree(data); CeedChk(ierr);
1167480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1168480fae85SJeremy L Thompson }
1169480fae85SJeremy L Thompson 
1170ed9e99e6SJeremy L Thompson /**
1171ed9e99e6SJeremy L Thompson   @brief Get CeedOperatorAssemblyData
1172ed9e99e6SJeremy L Thompson 
1173ed9e99e6SJeremy L Thompson   @param[in] op     CeedOperator to assemble
1174ed9e99e6SJeremy L Thompson   @param[out] data  CeedQFunctionAssemblyData
1175ed9e99e6SJeremy L Thompson 
1176ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1177ed9e99e6SJeremy L Thompson 
1178ed9e99e6SJeremy L Thompson   @ref Backend
1179ed9e99e6SJeremy L Thompson **/
1180ed9e99e6SJeremy L Thompson int CeedOperatorGetOperatorAssemblyData(CeedOperator op,
1181ed9e99e6SJeremy L Thompson                                         CeedOperatorAssemblyData *data) {
1182ed9e99e6SJeremy L Thompson   int ierr;
1183ed9e99e6SJeremy L Thompson 
1184ed9e99e6SJeremy L Thompson   if (!op->op_assembled) {
1185ed9e99e6SJeremy L Thompson     CeedOperatorAssemblyData data;
1186ed9e99e6SJeremy L Thompson 
1187ed9e99e6SJeremy L Thompson     ierr = CeedOperatorAssemblyDataCreate(op->ceed, op, &data); CeedChk(ierr);
1188ed9e99e6SJeremy L Thompson     op->op_assembled = data;
1189ed9e99e6SJeremy L Thompson   }
1190ed9e99e6SJeremy L Thompson   *data = op->op_assembled;
1191ed9e99e6SJeremy L Thompson 
1192ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1193ed9e99e6SJeremy L Thompson }
1194ed9e99e6SJeremy L Thompson 
1195ed9e99e6SJeremy L Thompson /**
1196ed9e99e6SJeremy L Thompson   @brief Create object holding CeedOperator assembly data
1197ed9e99e6SJeremy L Thompson 
1198ed9e99e6SJeremy L Thompson   @param[in] ceed   A Ceed object where the CeedOperatorAssemblyData will be created
1199ed9e99e6SJeremy L Thompson   @param[in] op     CeedOperator to be assembled
1200ed9e99e6SJeremy L Thompson   @param[out] data  Address of the variable where the newly created
1201ed9e99e6SJeremy L Thompson                       CeedOperatorAssemblyData will be stored
1202ed9e99e6SJeremy L Thompson 
1203ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1204ed9e99e6SJeremy L Thompson 
1205ed9e99e6SJeremy L Thompson   @ref Backend
1206ed9e99e6SJeremy L Thompson **/
1207ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataCreate(Ceed ceed, CeedOperator op,
1208ed9e99e6SJeremy L Thompson                                    CeedOperatorAssemblyData *data) {
1209ed9e99e6SJeremy L Thompson   int ierr;
1210ed9e99e6SJeremy L Thompson 
1211ed9e99e6SJeremy L Thompson   ierr = CeedCalloc(1, data); CeedChk(ierr);
1212ed9e99e6SJeremy L Thompson   (*data)->ceed = ceed;
1213ed9e99e6SJeremy L Thompson   ierr = CeedReference(ceed); CeedChk(ierr);
1214ed9e99e6SJeremy L Thompson 
1215ed9e99e6SJeremy L Thompson   // Build OperatorAssembly data
1216ed9e99e6SJeremy L Thompson   CeedQFunction qf;
1217ed9e99e6SJeremy L Thompson   CeedQFunctionField *qf_fields;
1218ed9e99e6SJeremy L Thompson   CeedOperatorField *op_fields;
1219ed9e99e6SJeremy L Thompson   CeedInt num_input_fields;
1220ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChk(ierr);
1221ed9e99e6SJeremy L Thompson   ierr = CeedQFunctionGetFields(qf, &num_input_fields, &qf_fields, NULL, NULL);
1222ed9e99e6SJeremy L Thompson   CeedChk(ierr);
1223ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetFields(op, NULL, &op_fields, NULL, NULL); CeedChk(ierr);
1224ed9e99e6SJeremy L Thompson 
1225ed9e99e6SJeremy L Thompson   // Determine active input basis
1226ed9e99e6SJeremy L Thompson   CeedInt num_eval_mode_in = 0, dim = 1;
1227ed9e99e6SJeremy L Thompson   CeedEvalMode *eval_mode_in = NULL;
1228ed9e99e6SJeremy L Thompson   CeedBasis basis_in = NULL;
1229ed9e99e6SJeremy L Thompson   for (CeedInt i=0; i<num_input_fields; i++) {
1230ed9e99e6SJeremy L Thompson     CeedVector vec;
1231ed9e99e6SJeremy L Thompson     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChk(ierr);
1232ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
1233ed9e99e6SJeremy L Thompson       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis_in);
1234ed9e99e6SJeremy L Thompson       CeedChk(ierr);
1235ed9e99e6SJeremy L Thompson       ierr = CeedBasisGetDimension(basis_in, &dim); CeedChk(ierr);
1236ed9e99e6SJeremy L Thompson       CeedEvalMode eval_mode;
1237ed9e99e6SJeremy L Thompson       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1238ed9e99e6SJeremy L Thompson       CeedChk(ierr);
1239ed9e99e6SJeremy L Thompson       switch (eval_mode) {
1240ed9e99e6SJeremy L Thompson       case CEED_EVAL_NONE:
1241ed9e99e6SJeremy L Thompson       case CEED_EVAL_INTERP:
1242ed9e99e6SJeremy L Thompson         ierr = CeedRealloc(num_eval_mode_in + 1, &eval_mode_in); CeedChk(ierr);
1243ed9e99e6SJeremy L Thompson         eval_mode_in[num_eval_mode_in] = eval_mode;
1244ed9e99e6SJeremy L Thompson         num_eval_mode_in += 1;
1245ed9e99e6SJeremy L Thompson         break;
1246ed9e99e6SJeremy L Thompson       case CEED_EVAL_GRAD:
1247ed9e99e6SJeremy L Thompson         ierr = CeedRealloc(num_eval_mode_in + dim, &eval_mode_in); CeedChk(ierr);
1248ed9e99e6SJeremy L Thompson         for (CeedInt d=0; d<dim; d++) {
1249ed9e99e6SJeremy L Thompson           eval_mode_in[num_eval_mode_in+d] = eval_mode;
1250ed9e99e6SJeremy L Thompson         }
1251ed9e99e6SJeremy L Thompson         num_eval_mode_in += dim;
1252ed9e99e6SJeremy L Thompson         break;
1253ed9e99e6SJeremy L Thompson       case CEED_EVAL_WEIGHT:
1254ed9e99e6SJeremy L Thompson       case CEED_EVAL_DIV:
1255ed9e99e6SJeremy L Thompson       case CEED_EVAL_CURL:
1256ed9e99e6SJeremy L Thompson         break; // Caught by QF Assembly
1257ed9e99e6SJeremy L Thompson       }
1258ed9e99e6SJeremy L Thompson     }
1259ed9e99e6SJeremy L Thompson   }
1260ed9e99e6SJeremy L Thompson   (*data)->num_eval_mode_in = num_eval_mode_in;
1261ed9e99e6SJeremy L Thompson   (*data)->eval_mode_in = eval_mode_in;
1262ed9e99e6SJeremy L Thompson   ierr = CeedBasisReferenceCopy(basis_in, &(*data)->basis_in); CeedChk(ierr);
1263ed9e99e6SJeremy L Thompson 
1264ed9e99e6SJeremy L Thompson   // Determine active output basis
1265ed9e99e6SJeremy L Thompson   CeedInt num_output_fields;
1266ed9e99e6SJeremy L Thompson   ierr = CeedQFunctionGetFields(qf, NULL, NULL, &num_output_fields, &qf_fields);
1267ed9e99e6SJeremy L Thompson   CeedChk(ierr);
1268ed9e99e6SJeremy L Thompson   ierr = CeedOperatorGetFields(op, NULL, NULL, NULL, &op_fields); CeedChk(ierr);
1269ed9e99e6SJeremy L Thompson   CeedInt num_eval_mode_out = 0;
1270ed9e99e6SJeremy L Thompson   CeedEvalMode *eval_mode_out = NULL;
1271ed9e99e6SJeremy L Thompson   CeedBasis basis_out = NULL;
1272ed9e99e6SJeremy L Thompson   for (CeedInt i=0; i<num_output_fields; i++) {
1273ed9e99e6SJeremy L Thompson     CeedVector vec;
1274ed9e99e6SJeremy L Thompson     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChk(ierr);
1275ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
1276ed9e99e6SJeremy L Thompson       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis_out);
1277ed9e99e6SJeremy L Thompson       CeedChk(ierr);
1278ed9e99e6SJeremy L Thompson       CeedEvalMode eval_mode;
1279ed9e99e6SJeremy L Thompson       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1280ed9e99e6SJeremy L Thompson       CeedChk(ierr);
1281ed9e99e6SJeremy L Thompson       switch (eval_mode) {
1282ed9e99e6SJeremy L Thompson       case CEED_EVAL_NONE:
1283ed9e99e6SJeremy L Thompson       case CEED_EVAL_INTERP:
1284ed9e99e6SJeremy L Thompson         ierr = CeedRealloc(num_eval_mode_out + 1, &eval_mode_out); CeedChk(ierr);
1285ed9e99e6SJeremy L Thompson         eval_mode_out[num_eval_mode_out] = eval_mode;
1286ed9e99e6SJeremy L Thompson         num_eval_mode_out += 1;
1287ed9e99e6SJeremy L Thompson         break;
1288ed9e99e6SJeremy L Thompson       case CEED_EVAL_GRAD:
1289ed9e99e6SJeremy L Thompson         ierr = CeedRealloc(num_eval_mode_out + dim, &eval_mode_out); CeedChk(ierr);
1290ed9e99e6SJeremy L Thompson         for (CeedInt d=0; d<dim; d++) {
1291ed9e99e6SJeremy L Thompson           eval_mode_out[num_eval_mode_out+d] = eval_mode;
1292ed9e99e6SJeremy L Thompson         }
1293ed9e99e6SJeremy L Thompson         num_eval_mode_out += dim;
1294ed9e99e6SJeremy L Thompson         break;
1295ed9e99e6SJeremy L Thompson       case CEED_EVAL_WEIGHT:
1296ed9e99e6SJeremy L Thompson       case CEED_EVAL_DIV:
1297ed9e99e6SJeremy L Thompson       case CEED_EVAL_CURL:
1298ed9e99e6SJeremy L Thompson         break; // Caught by QF Assembly
1299ed9e99e6SJeremy L Thompson       }
1300ed9e99e6SJeremy L Thompson     }
1301ed9e99e6SJeremy L Thompson   }
1302ed9e99e6SJeremy L Thompson   (*data)->num_eval_mode_out = num_eval_mode_out;
1303ed9e99e6SJeremy L Thompson   (*data)->eval_mode_out = eval_mode_out;
1304ed9e99e6SJeremy L Thompson   ierr = CeedBasisReferenceCopy(basis_out, &(*data)->basis_out); CeedChk(ierr);
1305ed9e99e6SJeremy L Thompson 
1306ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1307ed9e99e6SJeremy L Thompson }
1308ed9e99e6SJeremy L Thompson 
1309ed9e99e6SJeremy L Thompson /**
1310ed9e99e6SJeremy L Thompson   @brief Get CeedOperator CeedEvalModes for assembly
1311ed9e99e6SJeremy L Thompson 
1312ed9e99e6SJeremy L Thompson   @param[in] data                CeedOperatorAssemblyData
1313ed9e99e6SJeremy L Thompson   @param[out] num_eval_mode_in   Pointer to hold number of input CeedEvalModes, or NULL
1314ed9e99e6SJeremy L Thompson   @param[out] eval_mode_in       Pointer to hold input CeedEvalModes, or NULL
1315ed9e99e6SJeremy L Thompson   @param[out] num_eval_mode_out  Pointer to hold number of output CeedEvalModes, or NULL
1316ed9e99e6SJeremy L Thompson   @param[out] eval_mode_out      Pointer to hold output CeedEvalModes, or NULL
1317ed9e99e6SJeremy L Thompson 
1318ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1319ed9e99e6SJeremy L Thompson 
1320ed9e99e6SJeremy L Thompson   @ref Backend
1321ed9e99e6SJeremy L Thompson **/
1322ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataGetEvalModes(CeedOperatorAssemblyData data,
1323ed9e99e6SJeremy L Thompson     CeedInt *num_eval_mode_in, const CeedEvalMode **eval_mode_in,
1324ed9e99e6SJeremy L Thompson     CeedInt *num_eval_mode_out, const CeedEvalMode **eval_mode_out) {
1325ed9e99e6SJeremy L Thompson   if (num_eval_mode_in) *num_eval_mode_in   = data->num_eval_mode_in;
1326ed9e99e6SJeremy L Thompson   if (eval_mode_in) *eval_mode_in           = data->eval_mode_in;
1327ed9e99e6SJeremy L Thompson   if (num_eval_mode_out) *num_eval_mode_out = data->num_eval_mode_out;
1328ed9e99e6SJeremy L Thompson   if (eval_mode_out) *eval_mode_out         = data->eval_mode_out;
1329ed9e99e6SJeremy L Thompson 
1330ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1331ed9e99e6SJeremy L Thompson }
1332ed9e99e6SJeremy L Thompson 
1333ed9e99e6SJeremy L Thompson /**
1334ed9e99e6SJeremy L Thompson   @brief Get CeedOperator CeedBasis data for assembly
1335ed9e99e6SJeremy L Thompson 
1336ed9e99e6SJeremy L Thompson   @param[in] data        CeedOperatorAssemblyData
1337ed9e99e6SJeremy L Thompson   @param[out] basis_in   Pointer to hold active input CeedBasis, or NULL
1338ed9e99e6SJeremy L Thompson   @param[out] B_in       Pointer to hold assembled active input B, or NULL
1339ed9e99e6SJeremy L Thompson   @param[out] basis_out  Pointer to hold active output CeedBasis, or NULL
1340ed9e99e6SJeremy L Thompson   @param[out] B_out      Pointer to hold assembled active output B, or NULL
1341ed9e99e6SJeremy L Thompson 
1342ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1343ed9e99e6SJeremy L Thompson 
1344ed9e99e6SJeremy L Thompson   @ref Backend
1345ed9e99e6SJeremy L Thompson **/
1346ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataGetBases(CeedOperatorAssemblyData data,
1347ed9e99e6SJeremy L Thompson                                      CeedBasis *basis_in, const CeedScalar **B_in, CeedBasis *basis_out,
1348ed9e99e6SJeremy L Thompson                                      const CeedScalar **B_out) {
1349ed9e99e6SJeremy L Thompson   int ierr;
1350ed9e99e6SJeremy L Thompson 
1351ed9e99e6SJeremy L Thompson   // Assemble B_in, B_out if needed
1352ed9e99e6SJeremy L Thompson   if (B_in && !data->B_in) {
1353ed9e99e6SJeremy L Thompson     CeedInt num_qpts, elem_size;
1354ed9e99e6SJeremy L Thompson     CeedScalar *B_in, *identity = NULL;
1355ed9e99e6SJeremy L Thompson     const CeedScalar *interp_in, *grad_in;
1356ed9e99e6SJeremy L Thompson     bool has_eval_none = false;
1357ed9e99e6SJeremy L Thompson 
1358ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetNumQuadraturePoints(data->basis_in, &num_qpts);
1359ed9e99e6SJeremy L Thompson     CeedChk(ierr);
1360ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetNumNodes(data->basis_in, &elem_size); CeedChk(ierr);
1361ed9e99e6SJeremy L Thompson     ierr = CeedCalloc(num_qpts * elem_size * data->num_eval_mode_in, &B_in);
1362ed9e99e6SJeremy L Thompson     CeedChk(ierr);
1363ed9e99e6SJeremy L Thompson 
1364ed9e99e6SJeremy L Thompson     for (CeedInt i = 0; i < data->num_eval_mode_in; i++) {
1365ed9e99e6SJeremy L Thompson       has_eval_none = has_eval_none || (data->eval_mode_in[i] == CEED_EVAL_NONE);
1366ed9e99e6SJeremy L Thompson     }
1367ed9e99e6SJeremy L Thompson     if (has_eval_none) {
1368ed9e99e6SJeremy L Thompson       ierr = CeedCalloc(num_qpts * elem_size, &identity); CeedChk(ierr);
1369ed9e99e6SJeremy L Thompson       for (CeedInt i = 0; i < (elem_size < num_qpts ? elem_size : num_qpts); i++) {
1370ed9e99e6SJeremy L Thompson         identity[i * elem_size + i] = 1.0;
1371ed9e99e6SJeremy L Thompson       }
1372ed9e99e6SJeremy L Thompson     }
1373ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetInterp(data->basis_in, &interp_in); CeedChk(ierr);
1374ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetGrad(data->basis_in, &grad_in); CeedChk(ierr);
1375ed9e99e6SJeremy L Thompson 
1376ed9e99e6SJeremy L Thompson     for (CeedInt q = 0; q < num_qpts; q++) {
1377ed9e99e6SJeremy L Thompson       for (CeedInt n = 0; n < elem_size; n++) {
1378ed9e99e6SJeremy L Thompson         CeedInt d_in = -1;
1379ed9e99e6SJeremy L Thompson         for (CeedInt e_in = 0; e_in < data->num_eval_mode_in; e_in++) {
1380ed9e99e6SJeremy L Thompson           const CeedInt qq = data->num_eval_mode_in * q;
1381ed9e99e6SJeremy L Thompson           const CeedScalar *b = NULL;
1382ed9e99e6SJeremy L Thompson 
1383ed9e99e6SJeremy L Thompson           if (data->eval_mode_in[e_in] == CEED_EVAL_GRAD) d_in++;
1384ed9e99e6SJeremy L Thompson           CeedOperatorGetBasisPointer(data->eval_mode_in[e_in], identity,
1385ed9e99e6SJeremy L Thompson                                       interp_in, &grad_in[d_in * num_qpts * elem_size], &b); CeedChk(ierr);
1386ed9e99e6SJeremy L Thompson           B_in[(qq + e_in)*elem_size + n] = b[q * elem_size + n];
1387ed9e99e6SJeremy L Thompson         }
1388ed9e99e6SJeremy L Thompson       }
1389ed9e99e6SJeremy L Thompson     }
1390ed9e99e6SJeremy L Thompson     data->B_in = B_in;
1391ed9e99e6SJeremy L Thompson   }
1392ed9e99e6SJeremy L Thompson 
1393ed9e99e6SJeremy L Thompson   if (B_out && !data->B_out) {
1394ed9e99e6SJeremy L Thompson     CeedInt num_qpts, elem_size;
1395ed9e99e6SJeremy L Thompson     CeedScalar *B_out, *identity = NULL;
1396ed9e99e6SJeremy L Thompson     const CeedScalar *interp_out, *grad_out;
1397ed9e99e6SJeremy L Thompson     bool has_eval_none = false;
1398ed9e99e6SJeremy L Thompson 
1399ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetNumQuadraturePoints(data->basis_out, &num_qpts);
1400ed9e99e6SJeremy L Thompson     CeedChk(ierr);
1401ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetNumNodes(data->basis_out, &elem_size); CeedChk(ierr);
1402ed9e99e6SJeremy L Thompson     ierr = CeedCalloc(num_qpts * elem_size * data->num_eval_mode_out, &B_out);
1403ed9e99e6SJeremy L Thompson     CeedChk(ierr);
1404ed9e99e6SJeremy L Thompson 
1405ed9e99e6SJeremy L Thompson     for (CeedInt i = 0; i < data->num_eval_mode_out; i++) {
1406ed9e99e6SJeremy L Thompson       has_eval_none = has_eval_none || (data->eval_mode_out[i] == CEED_EVAL_NONE);
1407ed9e99e6SJeremy L Thompson     }
1408ed9e99e6SJeremy L Thompson     if (has_eval_none) {
1409ed9e99e6SJeremy L Thompson       ierr = CeedCalloc(num_qpts * elem_size, &identity); CeedChk(ierr);
1410ed9e99e6SJeremy L Thompson       for (CeedInt i = 0; i < (elem_size < num_qpts ? elem_size : num_qpts); i++) {
1411ed9e99e6SJeremy L Thompson         identity[i * elem_size + i] = 1.0;
1412ed9e99e6SJeremy L Thompson       }
1413ed9e99e6SJeremy L Thompson     }
1414ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetInterp(data->basis_out, &interp_out); CeedChk(ierr);
1415ed9e99e6SJeremy L Thompson     ierr = CeedBasisGetGrad(data->basis_out, &grad_out); CeedChk(ierr);
1416ed9e99e6SJeremy L Thompson 
1417ed9e99e6SJeremy L Thompson     for (CeedInt q = 0; q < num_qpts; q++) {
1418ed9e99e6SJeremy L Thompson       for (CeedInt n = 0; n < elem_size; n++) {
1419ed9e99e6SJeremy L Thompson         CeedInt d_out = -1;
1420ed9e99e6SJeremy L Thompson         for (CeedInt e_out = 0; e_out < data->num_eval_mode_out; e_out++) {
1421ed9e99e6SJeremy L Thompson           const CeedInt qq = data->num_eval_mode_out * q;
1422ed9e99e6SJeremy L Thompson           const CeedScalar *b = NULL;
1423ed9e99e6SJeremy L Thompson 
1424ed9e99e6SJeremy L Thompson           if (data->eval_mode_out[e_out] == CEED_EVAL_GRAD) d_out++;
1425ed9e99e6SJeremy L Thompson           CeedOperatorGetBasisPointer(data->eval_mode_out[e_out], identity,
1426ed9e99e6SJeremy L Thompson                                       interp_out, &grad_out[d_out * num_qpts * elem_size], &b); CeedChk(ierr);
1427ed9e99e6SJeremy L Thompson           B_out[(qq + e_out)*elem_size + n] = b[q * elem_size + n];
1428ed9e99e6SJeremy L Thompson         }
1429ed9e99e6SJeremy L Thompson       }
1430ed9e99e6SJeremy L Thompson     }
1431ed9e99e6SJeremy L Thompson     data->B_out = B_out;
1432ed9e99e6SJeremy L Thompson   }
1433ed9e99e6SJeremy L Thompson 
1434ed9e99e6SJeremy L Thompson   if (basis_in) *basis_in   = data->basis_in;
1435ed9e99e6SJeremy L Thompson   if (B_in) *B_in           = data->B_in;
1436ed9e99e6SJeremy L Thompson   if (basis_out) *basis_out = data->basis_out;
1437ed9e99e6SJeremy L Thompson   if (B_out) *B_out         = data->B_out;
1438ed9e99e6SJeremy L Thompson 
1439ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1440ed9e99e6SJeremy L Thompson }
1441ed9e99e6SJeremy L Thompson 
1442ed9e99e6SJeremy L Thompson /**
1443ed9e99e6SJeremy L Thompson   @brief Destroy CeedOperatorAssemblyData
1444ed9e99e6SJeremy L Thompson 
1445ed9e99e6SJeremy L Thompson   @param[out] data  CeedOperatorAssemblyData to destroy
1446ed9e99e6SJeremy L Thompson 
1447ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1448ed9e99e6SJeremy L Thompson 
1449ed9e99e6SJeremy L Thompson   @ref Backend
1450ed9e99e6SJeremy L Thompson **/
1451ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataDestroy(CeedOperatorAssemblyData *data) {
1452ed9e99e6SJeremy L Thompson   int ierr;
1453ed9e99e6SJeremy L Thompson 
1454ed9e99e6SJeremy L Thompson   if (!*data) return CEED_ERROR_SUCCESS;
1455ed9e99e6SJeremy L Thompson 
1456ed9e99e6SJeremy L Thompson   ierr = CeedDestroy(&(*data)->ceed); CeedChk(ierr);
1457ed9e99e6SJeremy L Thompson   ierr = CeedBasisDestroy(&(*data)->basis_in); CeedChk(ierr);
1458ed9e99e6SJeremy L Thompson   ierr = CeedBasisDestroy(&(*data)->basis_out); CeedChk(ierr);
1459ed9e99e6SJeremy L Thompson   ierr = CeedFree(&(*data)->B_in); CeedChk(ierr);
1460ed9e99e6SJeremy L Thompson   ierr = CeedFree(&(*data)->B_out); CeedChk(ierr);
1461ed9e99e6SJeremy L Thompson 
1462ed9e99e6SJeremy L Thompson   ierr = CeedFree(data); CeedChk(ierr);
1463ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1464ed9e99e6SJeremy L Thompson }
1465ed9e99e6SJeremy L Thompson 
1466480fae85SJeremy L Thompson /// @}
1467480fae85SJeremy L Thompson 
1468480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
1469eaf62fffSJeremy L Thompson /// CeedOperator Public API
1470eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
1471eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorUser
1472eaf62fffSJeremy L Thompson /// @{
1473eaf62fffSJeremy L Thompson 
1474eaf62fffSJeremy L Thompson /**
1475eaf62fffSJeremy L Thompson   @brief Assemble a linear CeedQFunction associated with a CeedOperator
1476eaf62fffSJeremy L Thompson 
1477eaf62fffSJeremy L Thompson   This returns a CeedVector containing a matrix at each quadrature point
1478eaf62fffSJeremy L Thompson     providing the action of the CeedQFunction associated with the CeedOperator.
1479eaf62fffSJeremy L Thompson     The vector 'assembled' is of shape
1480eaf62fffSJeremy L Thompson       [num_elements, num_input_fields, num_output_fields, num_quad_points]
1481eaf62fffSJeremy L Thompson     and contains column-major matrices representing the action of the
1482eaf62fffSJeremy L Thompson     CeedQFunction for a corresponding quadrature point on an element. Inputs and
1483eaf62fffSJeremy L Thompson     outputs are in the order provided by the user when adding CeedOperator fields.
1484eaf62fffSJeremy L Thompson     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
1485eaf62fffSJeremy L Thompson     'v', provided in that order, would result in an assembled QFunction that
1486eaf62fffSJeremy L Thompson     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
1487eaf62fffSJeremy L Thompson     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
1488eaf62fffSJeremy L Thompson 
1489f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1490f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1491f04ea552SJeremy L Thompson 
1492eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1493eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedQFunction at
1494eaf62fffSJeremy L Thompson                            quadrature points
1495eaf62fffSJeremy L Thompson   @param[out] rstr       CeedElemRestriction for CeedVector containing assembled
1496eaf62fffSJeremy L Thompson                            CeedQFunction
1497eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1498eaf62fffSJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
1499eaf62fffSJeremy L Thompson 
1500eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1501eaf62fffSJeremy L Thompson 
1502eaf62fffSJeremy L Thompson   @ref User
1503eaf62fffSJeremy L Thompson **/
1504eaf62fffSJeremy L Thompson int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled,
1505eaf62fffSJeremy L Thompson                                         CeedElemRestriction *rstr,
1506eaf62fffSJeremy L Thompson                                         CeedRequest *request) {
1507eaf62fffSJeremy L Thompson   int ierr;
1508eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1509eaf62fffSJeremy L Thompson 
1510eaf62fffSJeremy L Thompson   if (op->LinearAssembleQFunction) {
1511d04bbc78SJeremy L Thompson     // Backend version
151270a7ffb3SJeremy L Thompson     ierr = op->LinearAssembleQFunction(op, assembled, rstr, request);
151370a7ffb3SJeremy L Thompson     CeedChk(ierr);
1514eaf62fffSJeremy L Thompson   } else {
1515d04bbc78SJeremy L Thompson     // Operator fallback
1516d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1517d04bbc78SJeremy L Thompson 
1518d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1519d04bbc78SJeremy L Thompson     if (op_fallback) {
1520d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssembleQFunction(op_fallback, assembled,
1521eaf62fffSJeremy L Thompson              rstr, request); CeedChk(ierr);
1522d04bbc78SJeremy L Thompson     } else {
1523d04bbc78SJeremy L Thompson       // LCOV_EXCL_START
1524d04bbc78SJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
1525d04bbc78SJeremy L Thompson                        "Backend does not support CeedOperatorLinearAssembleQFunction");
1526d04bbc78SJeremy L Thompson       // LCOV_EXCL_STOP
1527d04bbc78SJeremy L Thompson     }
152870a7ffb3SJeremy L Thompson   }
1529eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1530eaf62fffSJeremy L Thompson }
153170a7ffb3SJeremy L Thompson 
153270a7ffb3SJeremy L Thompson /**
153370a7ffb3SJeremy L Thompson   @brief Assemble CeedQFunction and store result internall. Return copied
153470a7ffb3SJeremy L Thompson            references of stored data to the caller. Caller is responsible for
153570a7ffb3SJeremy L Thompson            ownership and destruction of the copied references. See also
153670a7ffb3SJeremy L Thompson            @ref CeedOperatorLinearAssembleQFunction
153770a7ffb3SJeremy L Thompson 
153870a7ffb3SJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
153970a7ffb3SJeremy L Thompson   @param assembled       CeedVector to store assembled CeedQFunction at
154070a7ffb3SJeremy L Thompson                            quadrature points
154170a7ffb3SJeremy L Thompson   @param rstr            CeedElemRestriction for CeedVector containing assembled
154270a7ffb3SJeremy L Thompson                            CeedQFunction
154370a7ffb3SJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
154470a7ffb3SJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
154570a7ffb3SJeremy L Thompson 
154670a7ffb3SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
154770a7ffb3SJeremy L Thompson 
154870a7ffb3SJeremy L Thompson   @ref User
154970a7ffb3SJeremy L Thompson **/
155070a7ffb3SJeremy L Thompson int CeedOperatorLinearAssembleQFunctionBuildOrUpdate(CeedOperator op,
155170a7ffb3SJeremy L Thompson     CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
155270a7ffb3SJeremy L Thompson   int ierr;
155370a7ffb3SJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
155470a7ffb3SJeremy L Thompson 
155570a7ffb3SJeremy L Thompson   if (op->LinearAssembleQFunctionUpdate) {
1556d04bbc78SJeremy L Thompson     // Backend version
1557480fae85SJeremy L Thompson     bool qf_assembled_is_setup;
15582efa2d85SJeremy L Thompson     CeedVector assembled_vec = NULL;
15592efa2d85SJeremy L Thompson     CeedElemRestriction assembled_rstr = NULL;
1560480fae85SJeremy L Thompson 
1561480fae85SJeremy L Thompson     ierr = CeedQFunctionAssemblyDataIsSetup(op->qf_assembled,
1562480fae85SJeremy L Thompson                                             &qf_assembled_is_setup); CeedChk(ierr);
1563480fae85SJeremy L Thompson     if (qf_assembled_is_setup) {
1564d04bbc78SJeremy L Thompson       bool update_needed;
1565d04bbc78SJeremy L Thompson 
1566480fae85SJeremy L Thompson       ierr = CeedQFunctionAssemblyDataGetObjects(op->qf_assembled, &assembled_vec,
1567480fae85SJeremy L Thompson              &assembled_rstr); CeedChk(ierr);
15688b919e6bSJeremy L Thompson       ierr = CeedQFunctionAssemblyDataIsUpdateNeeded(op->qf_assembled,
15698b919e6bSJeremy L Thompson              &update_needed); CeedChk(ierr);
15708b919e6bSJeremy L Thompson       if (update_needed) {
1571480fae85SJeremy L Thompson         ierr = op->LinearAssembleQFunctionUpdate(op, assembled_vec, assembled_rstr,
1572480fae85SJeremy L Thompson                request); CeedChk(ierr);
15738b919e6bSJeremy L Thompson       }
157470a7ffb3SJeremy L Thompson     } else {
1575480fae85SJeremy L Thompson       ierr = op->LinearAssembleQFunction(op, &assembled_vec, &assembled_rstr,
1576480fae85SJeremy L Thompson                                          request); CeedChk(ierr);
1577480fae85SJeremy L Thompson       ierr = CeedQFunctionAssemblyDataSetObjects(op->qf_assembled, assembled_vec,
1578480fae85SJeremy L Thompson              assembled_rstr); CeedChk(ierr);
157970a7ffb3SJeremy L Thompson     }
1580beecbf24SJeremy L Thompson     ierr = CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, false);
15818b919e6bSJeremy L Thompson     CeedChk(ierr);
15822efa2d85SJeremy L Thompson 
1583d04bbc78SJeremy L Thompson     // Copy reference from internally held copy
158470a7ffb3SJeremy L Thompson     *assembled = NULL;
158570a7ffb3SJeremy L Thompson     *rstr = NULL;
1586480fae85SJeremy L Thompson     ierr = CeedVectorReferenceCopy(assembled_vec, assembled); CeedChk(ierr);
15872efa2d85SJeremy L Thompson     ierr = CeedVectorDestroy(&assembled_vec); CeedChk(ierr);
1588480fae85SJeremy L Thompson     ierr = CeedElemRestrictionReferenceCopy(assembled_rstr, rstr); CeedChk(ierr);
15892efa2d85SJeremy L Thompson     ierr = CeedElemRestrictionDestroy(&assembled_rstr); CeedChk(ierr);
159070a7ffb3SJeremy L Thompson   } else {
1591d04bbc78SJeremy L Thompson     // Operator fallback
1592d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1593d04bbc78SJeremy L Thompson 
1594d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1595d04bbc78SJeremy L Thompson     if (op_fallback) {
1596d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op_fallback, assembled,
1597d04bbc78SJeremy L Thompson              rstr, request); CeedChk(ierr);
1598d04bbc78SJeremy L Thompson     } else {
1599d04bbc78SJeremy L Thompson       // LCOV_EXCL_START
1600d04bbc78SJeremy L Thompson       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
1601d04bbc78SJeremy L Thompson                        "Backend does not support CeedOperatorLinearAssembleQFunctionUpdate");
1602d04bbc78SJeremy L Thompson       // LCOV_EXCL_STOP
160370a7ffb3SJeremy L Thompson     }
160470a7ffb3SJeremy L Thompson   }
160570a7ffb3SJeremy L Thompson 
160670a7ffb3SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1607eaf62fffSJeremy L Thompson }
1608eaf62fffSJeremy L Thompson 
1609eaf62fffSJeremy L Thompson /**
1610eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1611eaf62fffSJeremy L Thompson 
1612eaf62fffSJeremy L Thompson   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1613eaf62fffSJeremy L Thompson 
1614eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1615eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1616eaf62fffSJeremy L Thompson 
1617f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1618f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1619f04ea552SJeremy L Thompson 
1620eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1621eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
1622eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1623eaf62fffSJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
1624eaf62fffSJeremy L Thompson 
1625eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1626eaf62fffSJeremy L Thompson 
1627eaf62fffSJeremy L Thompson   @ref User
1628eaf62fffSJeremy L Thompson **/
1629eaf62fffSJeremy L Thompson int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled,
1630eaf62fffSJeremy L Thompson                                        CeedRequest *request) {
1631eaf62fffSJeremy L Thompson   int ierr;
1632eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1633eaf62fffSJeremy L Thompson 
1634c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1635c9366a6bSJeremy L Thompson   ierr = CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size);
1636c9366a6bSJeremy L Thompson   CeedChk(ierr);
1637c9366a6bSJeremy L Thompson   if (input_size != output_size)
1638c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1639c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1640c9366a6bSJeremy L Thompson   // LCOV_EXCL_STOP
1641c9366a6bSJeremy L Thompson 
1642eaf62fffSJeremy L Thompson   if (op->LinearAssembleDiagonal) {
1643d04bbc78SJeremy L Thompson     // Backend version
1644eaf62fffSJeremy L Thompson     ierr = op->LinearAssembleDiagonal(op, assembled, request); CeedChk(ierr);
1645eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1646eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddDiagonal) {
1647d04bbc78SJeremy L Thompson     // Backend version with zeroing first
1648eaf62fffSJeremy L Thompson     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1649eaf62fffSJeremy L Thompson     ierr = op->LinearAssembleAddDiagonal(op, assembled, request); CeedChk(ierr);
1650eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1651eaf62fffSJeremy L Thompson   } else {
1652d04bbc78SJeremy L Thompson     // Operator fallback
1653d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1654d04bbc78SJeremy L Thompson 
1655d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1656d04bbc78SJeremy L Thompson     if (op_fallback) {
1657d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssembleDiagonal(op_fallback, assembled, request);
1658eaf62fffSJeremy L Thompson       CeedChk(ierr);
1659eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1660eaf62fffSJeremy L Thompson     }
1661eaf62fffSJeremy L Thompson   }
1662eaf62fffSJeremy L Thompson   // Default interface implementation
1663eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1664eaf62fffSJeremy L Thompson   ierr = CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1665eaf62fffSJeremy L Thompson   CeedChk(ierr);
1666d04bbc78SJeremy L Thompson 
1667eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1668eaf62fffSJeremy L Thompson }
1669eaf62fffSJeremy L Thompson 
1670eaf62fffSJeremy L Thompson /**
1671eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1672eaf62fffSJeremy L Thompson 
1673eaf62fffSJeremy L Thompson   This sums into a CeedVector the diagonal of a linear CeedOperator.
1674eaf62fffSJeremy L Thompson 
1675eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1676eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1677eaf62fffSJeremy L Thompson 
1678f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1679f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1680f04ea552SJeremy L Thompson 
1681eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1682eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator diagonal
1683eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1684eaf62fffSJeremy L Thompson                            @ref CEED_REQUEST_IMMEDIATE
1685eaf62fffSJeremy L Thompson 
1686eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1687eaf62fffSJeremy L Thompson 
1688eaf62fffSJeremy L Thompson   @ref User
1689eaf62fffSJeremy L Thompson **/
1690eaf62fffSJeremy L Thompson int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled,
1691eaf62fffSJeremy L Thompson     CeedRequest *request) {
1692eaf62fffSJeremy L Thompson   int ierr;
1693eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1694eaf62fffSJeremy L Thompson 
1695c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1696c9366a6bSJeremy L Thompson   ierr = CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size);
1697c9366a6bSJeremy L Thompson   CeedChk(ierr);
1698c9366a6bSJeremy L Thompson   if (input_size != output_size)
1699c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1700c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1701c9366a6bSJeremy L Thompson   // LCOV_EXCL_STOP
1702c9366a6bSJeremy L Thompson 
1703eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddDiagonal) {
1704d04bbc78SJeremy L Thompson     // Backend version
1705eaf62fffSJeremy L Thompson     ierr = op->LinearAssembleAddDiagonal(op, assembled, request); CeedChk(ierr);
1706eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1707eaf62fffSJeremy L Thompson   } else {
1708d04bbc78SJeremy L Thompson     // Operator fallback
1709d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1710d04bbc78SJeremy L Thompson 
1711d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1712d04bbc78SJeremy L Thompson     if (op_fallback) {
1713d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssembleAddDiagonal(op_fallback, assembled, request);
1714eaf62fffSJeremy L Thompson       CeedChk(ierr);
1715eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1716eaf62fffSJeremy L Thompson     }
1717eaf62fffSJeremy L Thompson   }
1718eaf62fffSJeremy L Thompson   // Default interface implementation
1719eaf62fffSJeremy L Thompson   bool is_composite;
1720eaf62fffSJeremy L Thompson   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1721eaf62fffSJeremy L Thompson   if (is_composite) {
1722eaf62fffSJeremy L Thompson     ierr = CeedCompositeOperatorLinearAssembleAddDiagonal(op, request,
1723eaf62fffSJeremy L Thompson            false, assembled); CeedChk(ierr);
1724eaf62fffSJeremy L Thompson   } else {
1725*0e455453SJeremy L Thompson     ierr = CeedSingleOperatorAssembleAddDiagonal_Core(op, request, false,
1726*0e455453SJeremy L Thompson            assembled); CeedChk(ierr);
1727eaf62fffSJeremy L Thompson   }
1728d04bbc78SJeremy L Thompson 
1729d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1730eaf62fffSJeremy L Thompson }
1731eaf62fffSJeremy L Thompson 
1732eaf62fffSJeremy L Thompson /**
1733eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1734eaf62fffSJeremy L Thompson 
1735eaf62fffSJeremy L Thompson   This overwrites a CeedVector with the point block diagonal of a linear
1736eaf62fffSJeremy L Thompson     CeedOperator.
1737eaf62fffSJeremy L Thompson 
1738eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1739eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1740eaf62fffSJeremy L Thompson 
1741f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1742f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1743f04ea552SJeremy L Thompson 
1744eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1745eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
1746eaf62fffSJeremy L Thompson                            diagonal, provided in row-major form with an
1747eaf62fffSJeremy L Thompson                            @a num_comp * @a num_comp block at each node. The dimensions
1748eaf62fffSJeremy L Thompson                            of this vector are derived from the active vector
1749eaf62fffSJeremy L Thompson                            for the CeedOperator. The array has shape
1750eaf62fffSJeremy L Thompson                            [nodes, component out, component in].
1751eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1752eaf62fffSJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
1753eaf62fffSJeremy L Thompson 
1754eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1755eaf62fffSJeremy L Thompson 
1756eaf62fffSJeremy L Thompson   @ref User
1757eaf62fffSJeremy L Thompson **/
1758eaf62fffSJeremy L Thompson int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op,
1759eaf62fffSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1760eaf62fffSJeremy L Thompson   int ierr;
1761eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1762eaf62fffSJeremy L Thompson 
1763c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1764c9366a6bSJeremy L Thompson   ierr = CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size);
1765c9366a6bSJeremy L Thompson   CeedChk(ierr);
1766c9366a6bSJeremy L Thompson   if (input_size != output_size)
1767c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1768c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1769c9366a6bSJeremy L Thompson   // LCOV_EXCL_STOP
1770c9366a6bSJeremy L Thompson 
1771eaf62fffSJeremy L Thompson   if (op->LinearAssemblePointBlockDiagonal) {
1772d04bbc78SJeremy L Thompson     // Backend version
1773eaf62fffSJeremy L Thompson     ierr = op->LinearAssemblePointBlockDiagonal(op, assembled, request);
1774eaf62fffSJeremy L Thompson     CeedChk(ierr);
1775eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1776eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddPointBlockDiagonal) {
1777d04bbc78SJeremy L Thompson     // Backend version with zeroing first
1778eaf62fffSJeremy L Thompson     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1779eaf62fffSJeremy L Thompson     ierr = CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1780eaf62fffSJeremy L Thompson            request); CeedChk(ierr);
1781eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1782eaf62fffSJeremy L Thompson   } else {
1783d04bbc78SJeremy L Thompson     // Operator fallback
1784d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1785d04bbc78SJeremy L Thompson 
1786d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1787d04bbc78SJeremy L Thompson     if (op_fallback) {
1788d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssemblePointBlockDiagonal(op_fallback, assembled,
1789d04bbc78SJeremy L Thompson              request); CeedChk(ierr);
1790eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1791eaf62fffSJeremy L Thompson     }
1792eaf62fffSJeremy L Thompson   }
1793eaf62fffSJeremy L Thompson   // Default interface implementation
1794eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1795eaf62fffSJeremy L Thompson   ierr = CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request);
1796eaf62fffSJeremy L Thompson   CeedChk(ierr);
1797d04bbc78SJeremy L Thompson 
1798eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1799eaf62fffSJeremy L Thompson }
1800eaf62fffSJeremy L Thompson 
1801eaf62fffSJeremy L Thompson /**
1802eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
1803eaf62fffSJeremy L Thompson 
1804eaf62fffSJeremy L Thompson   This sums into a CeedVector with the point block diagonal of a linear
1805eaf62fffSJeremy L Thompson     CeedOperator.
1806eaf62fffSJeremy L Thompson 
1807eaf62fffSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and
1808eaf62fffSJeremy L Thompson           composite CeedOperators with single field sub-operators are supported.
1809eaf62fffSJeremy L Thompson 
1810f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1811f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1812f04ea552SJeremy L Thompson 
1813eaf62fffSJeremy L Thompson   @param op              CeedOperator to assemble CeedQFunction
1814eaf62fffSJeremy L Thompson   @param[out] assembled  CeedVector to store assembled CeedOperator point block
1815eaf62fffSJeremy L Thompson                            diagonal, provided in row-major form with an
1816eaf62fffSJeremy L Thompson                            @a num_comp * @a num_comp block at each node. The dimensions
1817eaf62fffSJeremy L Thompson                            of this vector are derived from the active vector
1818eaf62fffSJeremy L Thompson                            for the CeedOperator. The array has shape
1819eaf62fffSJeremy L Thompson                            [nodes, component out, component in].
1820eaf62fffSJeremy L Thompson   @param request         Address of CeedRequest for non-blocking completion, else
1821eaf62fffSJeremy L Thompson                            CEED_REQUEST_IMMEDIATE
1822eaf62fffSJeremy L Thompson 
1823eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1824eaf62fffSJeremy L Thompson 
1825eaf62fffSJeremy L Thompson   @ref User
1826eaf62fffSJeremy L Thompson **/
1827eaf62fffSJeremy L Thompson int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op,
1828eaf62fffSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1829eaf62fffSJeremy L Thompson   int ierr;
1830eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1831eaf62fffSJeremy L Thompson 
1832c9366a6bSJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
1833c9366a6bSJeremy L Thompson   ierr = CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size);
1834c9366a6bSJeremy L Thompson   CeedChk(ierr);
1835c9366a6bSJeremy L Thompson   if (input_size != output_size)
1836c9366a6bSJeremy L Thompson     // LCOV_EXCL_START
1837c9366a6bSJeremy L Thompson     return CeedError(op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1838c9366a6bSJeremy L Thompson   // LCOV_EXCL_STOP
1839c9366a6bSJeremy L Thompson 
1840eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddPointBlockDiagonal) {
1841d04bbc78SJeremy L Thompson     // Backend version
1842eaf62fffSJeremy L Thompson     ierr = op->LinearAssembleAddPointBlockDiagonal(op, assembled, request);
1843eaf62fffSJeremy L Thompson     CeedChk(ierr);
1844eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1845eaf62fffSJeremy L Thompson   } else {
1846d04bbc78SJeremy L Thompson     // Operator fallback
1847d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1848d04bbc78SJeremy L Thompson 
1849d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1850d04bbc78SJeremy L Thompson     if (op_fallback) {
1851d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssembleAddPointBlockDiagonal(op_fallback, assembled,
1852d04bbc78SJeremy L Thompson              request); CeedChk(ierr);
1853eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1854eaf62fffSJeremy L Thompson     }
1855eaf62fffSJeremy L Thompson   }
1856eaf62fffSJeremy L Thompson   // Default interface implemenation
1857eaf62fffSJeremy L Thompson   bool is_composite;
1858eaf62fffSJeremy L Thompson   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1859eaf62fffSJeremy L Thompson   if (is_composite) {
1860eaf62fffSJeremy L Thompson     ierr = CeedCompositeOperatorLinearAssembleAddDiagonal(op, request,
1861eaf62fffSJeremy L Thompson            true, assembled); CeedChk(ierr);
1862eaf62fffSJeremy L Thompson   } else {
1863*0e455453SJeremy L Thompson     ierr = CeedSingleOperatorAssembleAddDiagonal_Core(op, request, true, assembled);
1864eaf62fffSJeremy L Thompson     CeedChk(ierr);
1865eaf62fffSJeremy L Thompson   }
1866d04bbc78SJeremy L Thompson 
1867d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1868eaf62fffSJeremy L Thompson }
1869eaf62fffSJeremy L Thompson 
1870eaf62fffSJeremy L Thompson /**
1871eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero pattern of a linear operator.
1872eaf62fffSJeremy L Thompson 
1873eaf62fffSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssemble()
1874eaf62fffSJeremy L Thompson 
1875eaf62fffSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the
1876eaf62fffSJeremy L Thompson    form (i, j, value) which indicate that value should be added to the matrix
1877eaf62fffSJeremy L Thompson    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1878eaf62fffSJeremy L Thompson    This function returns the number of entries and their (i, j) locations,
1879eaf62fffSJeremy L Thompson    while CeedOperatorLinearAssemble() provides the values in the same
1880eaf62fffSJeremy L Thompson    ordering.
1881eaf62fffSJeremy L Thompson 
1882eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
1883eaf62fffSJeremy L Thompson 
1884f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1885f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
1886f04ea552SJeremy L Thompson 
1887eaf62fffSJeremy L Thompson    @param[in]  op           CeedOperator to assemble
1888eaf62fffSJeremy L Thompson    @param[out] num_entries  Number of entries in coordinate nonzero pattern
1889eaf62fffSJeremy L Thompson    @param[out] rows         Row number for each entry
1890eaf62fffSJeremy L Thompson    @param[out] cols         Column number for each entry
1891eaf62fffSJeremy L Thompson 
1892eaf62fffSJeremy L Thompson    @ref User
1893eaf62fffSJeremy L Thompson **/
1894c30b7fbdSJeremy L Thompson int CeedOperatorLinearAssembleSymbolic(CeedOperator op, CeedSize *num_entries,
1895eaf62fffSJeremy L Thompson                                        CeedInt **rows, CeedInt **cols) {
1896eaf62fffSJeremy L Thompson   int ierr;
1897eaf62fffSJeremy L Thompson   CeedInt num_suboperators, single_entries;
1898eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
1899eaf62fffSJeremy L Thompson   bool is_composite;
1900eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1901eaf62fffSJeremy L Thompson 
1902eaf62fffSJeremy L Thompson   if (op->LinearAssembleSymbolic) {
1903d04bbc78SJeremy L Thompson     // Backend version
1904eaf62fffSJeremy L Thompson     ierr = op->LinearAssembleSymbolic(op, num_entries, rows, cols); CeedChk(ierr);
1905eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1906eaf62fffSJeremy L Thompson   } else {
1907d04bbc78SJeremy L Thompson     // Operator fallback
1908d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1909d04bbc78SJeremy L Thompson 
1910d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1911d04bbc78SJeremy L Thompson     if (op_fallback) {
1912d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssembleSymbolic(op_fallback, num_entries, rows, cols);
1913eaf62fffSJeremy L Thompson       CeedChk(ierr);
1914eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1915eaf62fffSJeremy L Thompson     }
1916eaf62fffSJeremy L Thompson   }
1917eaf62fffSJeremy L Thompson 
1918eaf62fffSJeremy L Thompson   // Default interface implementation
1919eaf62fffSJeremy L Thompson 
1920eaf62fffSJeremy L Thompson   // count entries and allocate rows, cols arrays
1921eaf62fffSJeremy L Thompson   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1922eaf62fffSJeremy L Thompson   *num_entries = 0;
1923eaf62fffSJeremy L Thompson   if (is_composite) {
1924eaf62fffSJeremy L Thompson     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1925eaf62fffSJeremy L Thompson     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
192692ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
1927eaf62fffSJeremy L Thompson       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1928eaf62fffSJeremy L Thompson              &single_entries); CeedChk(ierr);
1929eaf62fffSJeremy L Thompson       *num_entries += single_entries;
1930eaf62fffSJeremy L Thompson     }
1931eaf62fffSJeremy L Thompson   } else {
1932eaf62fffSJeremy L Thompson     ierr = CeedSingleOperatorAssemblyCountEntries(op,
1933eaf62fffSJeremy L Thompson            &single_entries); CeedChk(ierr);
1934eaf62fffSJeremy L Thompson     *num_entries += single_entries;
1935eaf62fffSJeremy L Thompson   }
1936eaf62fffSJeremy L Thompson   ierr = CeedCalloc(*num_entries, rows); CeedChk(ierr);
1937eaf62fffSJeremy L Thompson   ierr = CeedCalloc(*num_entries, cols); CeedChk(ierr);
1938eaf62fffSJeremy L Thompson 
1939eaf62fffSJeremy L Thompson   // assemble nonzero locations
1940eaf62fffSJeremy L Thompson   CeedInt offset = 0;
1941eaf62fffSJeremy L Thompson   if (is_composite) {
1942eaf62fffSJeremy L Thompson     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1943eaf62fffSJeremy L Thompson     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
194492ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
1945eaf62fffSJeremy L Thompson       ierr = CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows,
1946eaf62fffSJeremy L Thompson              *cols); CeedChk(ierr);
1947eaf62fffSJeremy L Thompson       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
1948eaf62fffSJeremy L Thompson              &single_entries);
1949eaf62fffSJeremy L Thompson       CeedChk(ierr);
1950eaf62fffSJeremy L Thompson       offset += single_entries;
1951eaf62fffSJeremy L Thompson     }
1952eaf62fffSJeremy L Thompson   } else {
1953eaf62fffSJeremy L Thompson     ierr = CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols);
1954eaf62fffSJeremy L Thompson     CeedChk(ierr);
1955eaf62fffSJeremy L Thompson   }
1956eaf62fffSJeremy L Thompson 
1957eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1958eaf62fffSJeremy L Thompson }
1959eaf62fffSJeremy L Thompson 
1960eaf62fffSJeremy L Thompson /**
1961eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero entries of a linear operator.
1962eaf62fffSJeremy L Thompson 
1963eaf62fffSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic()
1964eaf62fffSJeremy L Thompson 
1965eaf62fffSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the
1966eaf62fffSJeremy L Thompson    form (i, j, value) which indicate that value should be added to the matrix
1967eaf62fffSJeremy L Thompson    in entry (i, j). Note that the (i, j) pairs are not unique and may repeat.
1968eaf62fffSJeremy L Thompson    This function returns the values of the nonzero entries to be added, their
1969eaf62fffSJeremy L Thompson    (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic()
1970eaf62fffSJeremy L Thompson 
1971eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
1972eaf62fffSJeremy L Thompson 
1973f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
1974f04ea552SJeremy L Thompson           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   int ierr;
1983eaf62fffSJeremy L Thompson   CeedInt num_suboperators, single_entries = 0;
1984eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
1985eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1986eaf62fffSJeremy L Thompson 
1987eaf62fffSJeremy L Thompson   if (op->LinearAssemble) {
1988d04bbc78SJeremy L Thompson     // Backend version
1989eaf62fffSJeremy L Thompson     ierr = op->LinearAssemble(op, values); CeedChk(ierr);
1990eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1991eaf62fffSJeremy L Thompson   } else {
1992d04bbc78SJeremy L Thompson     // Operator fallback
1993d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1994d04bbc78SJeremy L Thompson 
1995d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
1996d04bbc78SJeremy L Thompson     if (op_fallback) {
1997d04bbc78SJeremy L Thompson       ierr = CeedOperatorLinearAssemble(op_fallback, values); CeedChk(ierr);
1998eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1999eaf62fffSJeremy L Thompson     }
2000eaf62fffSJeremy L Thompson   }
2001eaf62fffSJeremy L Thompson 
2002eaf62fffSJeremy L Thompson   // Default interface implementation
2003eaf62fffSJeremy L Thompson   bool is_composite;
2004eaf62fffSJeremy L Thompson   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
2005eaf62fffSJeremy L Thompson 
2006eaf62fffSJeremy L Thompson   CeedInt offset = 0;
2007eaf62fffSJeremy L Thompson   if (is_composite) {
2008eaf62fffSJeremy L Thompson     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
2009eaf62fffSJeremy L Thompson     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
2010cefa2673SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; k++) {
2011eaf62fffSJeremy L Thompson       ierr = CeedSingleOperatorAssemble(sub_operators[k], offset, values);
2012eaf62fffSJeremy L Thompson       CeedChk(ierr);
2013eaf62fffSJeremy L Thompson       ierr = CeedSingleOperatorAssemblyCountEntries(sub_operators[k],
2014eaf62fffSJeremy L Thompson              &single_entries);
2015eaf62fffSJeremy L Thompson       CeedChk(ierr);
2016eaf62fffSJeremy L Thompson       offset += single_entries;
2017eaf62fffSJeremy L Thompson     }
2018eaf62fffSJeremy L Thompson   } else {
2019eaf62fffSJeremy L Thompson     ierr = CeedSingleOperatorAssemble(op, offset, values); CeedChk(ierr);
2020eaf62fffSJeremy L Thompson   }
2021eaf62fffSJeremy L Thompson 
2022eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2023eaf62fffSJeremy L Thompson }
2024eaf62fffSJeremy L Thompson 
2025eaf62fffSJeremy L Thompson /**
2026eaf62fffSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
2027eaf62fffSJeremy L Thompson            for a CeedOperator, creating the prolongation basis from the
2028eaf62fffSJeremy L Thompson            fine and coarse grid interpolation
2029eaf62fffSJeremy L Thompson 
2030f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
2031f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
2032f04ea552SJeremy L Thompson 
2033eaf62fffSJeremy L Thompson   @param[in] op_fine       Fine grid operator
2034eaf62fffSJeremy L Thompson   @param[in] p_mult_fine   L-vector multiplicity in parallel gather/scatter
2035eaf62fffSJeremy L Thompson   @param[in] rstr_coarse   Coarse grid restriction
2036eaf62fffSJeremy L Thompson   @param[in] basis_coarse  Coarse grid active vector basis
2037eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
2038eaf62fffSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator
2039eaf62fffSJeremy L Thompson   @param[out] op_restrict  Fine to coarse operator
2040eaf62fffSJeremy L Thompson 
2041eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2042eaf62fffSJeremy L Thompson 
2043eaf62fffSJeremy L Thompson   @ref User
2044eaf62fffSJeremy L Thompson **/
2045eaf62fffSJeremy L Thompson int CeedOperatorMultigridLevelCreate(CeedOperator op_fine,
2046eaf62fffSJeremy L Thompson                                      CeedVector p_mult_fine,
2047eaf62fffSJeremy L Thompson                                      CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
2048eaf62fffSJeremy L Thompson                                      CeedOperator *op_coarse, CeedOperator *op_prolong,
2049eaf62fffSJeremy L Thompson                                      CeedOperator *op_restrict) {
2050eaf62fffSJeremy L Thompson   int ierr;
2051f04ea552SJeremy L Thompson   ierr = CeedOperatorCheckReady(op_fine); CeedChk(ierr);
2052eaf62fffSJeremy L Thompson   Ceed ceed;
2053eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2054eaf62fffSJeremy L Thompson 
2055eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
2056eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
2057eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2058eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
2059eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2060eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2061eaf62fffSJeremy L Thompson   if (Q_f != Q_c)
2062eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2063eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2064eaf62fffSJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2065eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
2066eaf62fffSJeremy L Thompson 
2067eaf62fffSJeremy L Thompson   // Coarse to fine basis
2068eaf62fffSJeremy L Thompson   CeedInt P_f, P_c, Q = Q_f;
2069eaf62fffSJeremy L Thompson   bool is_tensor_f, is_tensor_c;
2070eaf62fffSJeremy L Thompson   ierr = CeedBasisIsTensor(basis_fine, &is_tensor_f); CeedChk(ierr);
2071eaf62fffSJeremy L Thompson   ierr = CeedBasisIsTensor(basis_coarse, &is_tensor_c); CeedChk(ierr);
2072eaf62fffSJeremy L Thompson   CeedScalar *interp_c, *interp_f, *interp_c_to_f, *tau;
2073eaf62fffSJeremy L Thompson   if (is_tensor_f && is_tensor_c) {
2074eaf62fffSJeremy L Thompson     ierr = CeedBasisGetNumNodes1D(basis_fine, &P_f); CeedChk(ierr);
2075eaf62fffSJeremy L Thompson     ierr = CeedBasisGetNumNodes1D(basis_coarse, &P_c); CeedChk(ierr);
2076eaf62fffSJeremy L Thompson     ierr = CeedBasisGetNumQuadraturePoints1D(basis_coarse, &Q); CeedChk(ierr);
2077eaf62fffSJeremy L Thompson   } else if (!is_tensor_f && !is_tensor_c) {
2078eaf62fffSJeremy L Thompson     ierr = CeedBasisGetNumNodes(basis_fine, &P_f); CeedChk(ierr);
2079eaf62fffSJeremy L Thompson     ierr = CeedBasisGetNumNodes(basis_coarse, &P_c); CeedChk(ierr);
2080eaf62fffSJeremy L Thompson   } else {
2081eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2082eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_MINOR,
2083eaf62fffSJeremy L Thompson                      "Bases must both be tensor or non-tensor");
2084eaf62fffSJeremy L Thompson     // LCOV_EXCL_STOP
2085eaf62fffSJeremy L Thompson   }
2086eaf62fffSJeremy L Thompson 
2087eaf62fffSJeremy L Thompson   ierr = CeedMalloc(Q*P_f, &interp_f); CeedChk(ierr);
2088eaf62fffSJeremy L Thompson   ierr = CeedMalloc(Q*P_c, &interp_c); CeedChk(ierr);
2089eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_c*P_f, &interp_c_to_f); CeedChk(ierr);
2090eaf62fffSJeremy L Thompson   ierr = CeedMalloc(Q, &tau); CeedChk(ierr);
20918575dcacSJeremy L Thompson   const CeedScalar *interp_f_source = NULL, *interp_c_source = NULL;
2092eaf62fffSJeremy L Thompson   if (is_tensor_f) {
20938575dcacSJeremy L Thompson     ierr = CeedBasisGetInterp1D(basis_fine, &interp_f_source); CeedChk(ierr);
20948575dcacSJeremy L Thompson     ierr = CeedBasisGetInterp1D(basis_coarse, &interp_c_source); CeedChk(ierr);
2095eaf62fffSJeremy L Thompson   } else {
20968575dcacSJeremy L Thompson     ierr = CeedBasisGetInterp(basis_fine, &interp_f_source); CeedChk(ierr);
20978575dcacSJeremy L Thompson     ierr = CeedBasisGetInterp(basis_coarse, &interp_c_source); CeedChk(ierr);
2098eaf62fffSJeremy L Thompson   }
20998575dcacSJeremy L Thompson   memcpy(interp_f, interp_f_source, Q*P_f*sizeof interp_f_source[0]);
21008575dcacSJeremy L Thompson   memcpy(interp_c, interp_c_source, Q*P_c*sizeof interp_c_source[0]);
2101eaf62fffSJeremy L Thompson 
2102eaf62fffSJeremy L Thompson   // -- QR Factorization, interp_f = Q R
2103eaf62fffSJeremy L Thompson   ierr = CeedQRFactorization(ceed, interp_f, tau, Q, P_f); CeedChk(ierr);
2104eaf62fffSJeremy L Thompson 
2105eaf62fffSJeremy L Thompson   // -- Apply Qtranspose, interp_c = Qtranspose interp_c
2106eaf62fffSJeremy L Thompson   ierr = CeedHouseholderApplyQ(interp_c, interp_f, tau, CEED_TRANSPOSE,
2107eaf62fffSJeremy L Thompson                                Q, P_c, P_f, P_c, 1); CeedChk(ierr);
2108eaf62fffSJeremy L Thompson 
2109eaf62fffSJeremy L Thompson   // -- Apply Rinv, interp_c_to_f = Rinv interp_c
2110eaf62fffSJeremy L Thompson   for (CeedInt j=0; j<P_c; j++) { // Column j
2111eaf62fffSJeremy L Thompson     interp_c_to_f[j+P_c*(P_f-1)] = interp_c[j+P_c*(P_f-1)]/interp_f[P_f*P_f-1];
2112eaf62fffSJeremy L Thompson     for (CeedInt i=P_f-2; i>=0; i--) { // Row i
2113eaf62fffSJeremy L Thompson       interp_c_to_f[j+P_c*i] = interp_c[j+P_c*i];
2114eaf62fffSJeremy L Thompson       for (CeedInt k=i+1; k<P_f; k++)
2115eaf62fffSJeremy L Thompson         interp_c_to_f[j+P_c*i] -= interp_f[k+P_f*i]*interp_c_to_f[j+P_c*k];
2116eaf62fffSJeremy L Thompson       interp_c_to_f[j+P_c*i] /= interp_f[i+P_f*i];
2117eaf62fffSJeremy L Thompson     }
2118eaf62fffSJeremy L Thompson   }
2119eaf62fffSJeremy L Thompson   ierr = CeedFree(&tau); CeedChk(ierr);
2120eaf62fffSJeremy L Thompson   ierr = CeedFree(&interp_c); CeedChk(ierr);
2121eaf62fffSJeremy L Thompson   ierr = CeedFree(&interp_f); CeedChk(ierr);
2122eaf62fffSJeremy L Thompson 
2123eaf62fffSJeremy L Thompson   // Complete with interp_c_to_f versions of code
2124eaf62fffSJeremy L Thompson   if (is_tensor_f) {
2125eaf62fffSJeremy L Thompson     ierr = CeedOperatorMultigridLevelCreateTensorH1(op_fine, p_mult_fine,
2126eaf62fffSJeremy L Thompson            rstr_coarse, basis_coarse, interp_c_to_f, op_coarse, op_prolong, op_restrict);
2127eaf62fffSJeremy L Thompson     CeedChk(ierr);
2128eaf62fffSJeremy L Thompson   } else {
2129eaf62fffSJeremy L Thompson     ierr = CeedOperatorMultigridLevelCreateH1(op_fine, p_mult_fine,
2130eaf62fffSJeremy L Thompson            rstr_coarse, basis_coarse, interp_c_to_f, op_coarse, op_prolong, op_restrict);
2131eaf62fffSJeremy L Thompson     CeedChk(ierr);
2132eaf62fffSJeremy L Thompson   }
2133eaf62fffSJeremy L Thompson 
2134eaf62fffSJeremy L Thompson   // Cleanup
2135eaf62fffSJeremy L Thompson   ierr = CeedFree(&interp_c_to_f); CeedChk(ierr);
2136eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2137eaf62fffSJeremy L Thompson }
2138eaf62fffSJeremy L Thompson 
2139eaf62fffSJeremy L Thompson /**
2140eaf62fffSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
2141eaf62fffSJeremy L Thompson            for a CeedOperator with a tensor basis for the active basis
2142eaf62fffSJeremy L Thompson 
2143f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
2144f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
2145f04ea552SJeremy L Thompson 
2146eaf62fffSJeremy L Thompson   @param[in] op_fine        Fine grid operator
2147eaf62fffSJeremy L Thompson   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
2148eaf62fffSJeremy L Thompson   @param[in] rstr_coarse    Coarse grid restriction
2149eaf62fffSJeremy L Thompson   @param[in] basis_coarse   Coarse grid active vector basis
2150eaf62fffSJeremy L Thompson   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
2151eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
2152eaf62fffSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator
2153eaf62fffSJeremy L Thompson   @param[out] op_restrict   Fine to coarse operator
2154eaf62fffSJeremy L Thompson 
2155eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2156eaf62fffSJeremy L Thompson 
2157eaf62fffSJeremy L Thompson   @ref User
2158eaf62fffSJeremy L Thompson **/
2159eaf62fffSJeremy L Thompson int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine,
2160eaf62fffSJeremy L Thompson     CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
2161eaf62fffSJeremy L Thompson     const CeedScalar *interp_c_to_f, CeedOperator *op_coarse,
2162eaf62fffSJeremy L Thompson     CeedOperator *op_prolong, CeedOperator *op_restrict) {
2163eaf62fffSJeremy L Thompson   int ierr;
2164f04ea552SJeremy L Thompson   ierr = CeedOperatorCheckReady(op_fine); CeedChk(ierr);
2165eaf62fffSJeremy L Thompson   Ceed ceed;
2166eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2167eaf62fffSJeremy L Thompson 
2168eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
2169eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
2170eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2171eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
2172eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2173eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2174eaf62fffSJeremy L Thompson   if (Q_f != Q_c)
2175eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2176eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2177eaf62fffSJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2178eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
2179eaf62fffSJeremy L Thompson 
2180eaf62fffSJeremy L Thompson   // Coarse to fine basis
2181eaf62fffSJeremy L Thompson   CeedInt dim, num_comp, num_nodes_c, P_1d_f, P_1d_c;
2182eaf62fffSJeremy L Thompson   ierr = CeedBasisGetDimension(basis_fine, &dim); CeedChk(ierr);
2183eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumComponents(basis_fine, &num_comp); CeedChk(ierr);
2184eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumNodes1D(basis_fine, &P_1d_f); CeedChk(ierr);
2185eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c);
2186eaf62fffSJeremy L Thompson   CeedChk(ierr);
2187eaf62fffSJeremy L Thompson   P_1d_c = dim == 1 ? num_nodes_c :
2188eaf62fffSJeremy L Thompson            dim == 2 ? sqrt(num_nodes_c) :
2189eaf62fffSJeremy L Thompson            cbrt(num_nodes_c);
2190eaf62fffSJeremy L Thompson   CeedScalar *q_ref, *q_weight, *grad;
2191eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d_f, &q_ref); CeedChk(ierr);
2192eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d_f, &q_weight); CeedChk(ierr);
2193eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d_f*P_1d_c*dim, &grad); CeedChk(ierr);
2194eaf62fffSJeremy L Thompson   CeedBasis basis_c_to_f;
2195eaf62fffSJeremy L Thompson   ierr = CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d_c, P_1d_f,
2196eaf62fffSJeremy L Thompson                                  interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f);
2197eaf62fffSJeremy L Thompson   CeedChk(ierr);
2198eaf62fffSJeremy L Thompson   ierr = CeedFree(&q_ref); CeedChk(ierr);
2199eaf62fffSJeremy L Thompson   ierr = CeedFree(&q_weight); CeedChk(ierr);
2200eaf62fffSJeremy L Thompson   ierr = CeedFree(&grad); CeedChk(ierr);
2201eaf62fffSJeremy L Thompson 
2202eaf62fffSJeremy L Thompson   // Core code
2203eaf62fffSJeremy L Thompson   ierr = CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse,
2204eaf62fffSJeremy L Thompson                                           basis_coarse, basis_c_to_f, op_coarse,
2205eaf62fffSJeremy L Thompson                                           op_prolong, op_restrict);
2206eaf62fffSJeremy L Thompson   CeedChk(ierr);
2207eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2208eaf62fffSJeremy L Thompson }
2209eaf62fffSJeremy L Thompson 
2210eaf62fffSJeremy L Thompson /**
2211eaf62fffSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators
2212eaf62fffSJeremy L Thompson            for a CeedOperator with a non-tensor basis for the active vector
2213eaf62fffSJeremy L Thompson 
2214f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
2215f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
2216f04ea552SJeremy L Thompson 
2217eaf62fffSJeremy L Thompson   @param[in] op_fine        Fine grid operator
2218eaf62fffSJeremy L Thompson   @param[in] p_mult_fine    L-vector multiplicity in parallel gather/scatter
2219eaf62fffSJeremy L Thompson   @param[in] rstr_coarse    Coarse grid restriction
2220eaf62fffSJeremy L Thompson   @param[in] basis_coarse   Coarse grid active vector basis
2221eaf62fffSJeremy L Thompson   @param[in] interp_c_to_f  Matrix for coarse to fine interpolation
2222eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
2223eaf62fffSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator
2224eaf62fffSJeremy L Thompson   @param[out] op_restrict   Fine to coarse operator
2225eaf62fffSJeremy L Thompson 
2226eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2227eaf62fffSJeremy L Thompson 
2228eaf62fffSJeremy L Thompson   @ref User
2229eaf62fffSJeremy L Thompson **/
2230eaf62fffSJeremy L Thompson int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine,
2231eaf62fffSJeremy L Thompson                                        CeedVector p_mult_fine,
2232eaf62fffSJeremy L Thompson                                        CeedElemRestriction rstr_coarse,
2233eaf62fffSJeremy L Thompson                                        CeedBasis basis_coarse,
2234eaf62fffSJeremy L Thompson                                        const CeedScalar *interp_c_to_f,
2235eaf62fffSJeremy L Thompson                                        CeedOperator *op_coarse,
2236eaf62fffSJeremy L Thompson                                        CeedOperator *op_prolong,
2237eaf62fffSJeremy L Thompson                                        CeedOperator *op_restrict) {
2238eaf62fffSJeremy L Thompson   int ierr;
2239f04ea552SJeremy L Thompson   ierr = CeedOperatorCheckReady(op_fine); CeedChk(ierr);
2240eaf62fffSJeremy L Thompson   Ceed ceed;
2241eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetCeed(op_fine, &ceed); CeedChk(ierr);
2242eaf62fffSJeremy L Thompson 
2243eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
2244eaf62fffSJeremy L Thompson   CeedBasis basis_fine;
2245eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetActiveBasis(op_fine, &basis_fine); CeedChk(ierr);
2246eaf62fffSJeremy L Thompson   CeedInt Q_f, Q_c;
2247eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f); CeedChk(ierr);
2248eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c); CeedChk(ierr);
2249eaf62fffSJeremy L Thompson   if (Q_f != Q_c)
2250eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2251eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_DIMENSION,
2252eaf62fffSJeremy L Thompson                      "Bases must have compatible quadrature spaces");
2253eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
2254eaf62fffSJeremy L Thompson 
2255eaf62fffSJeremy L Thompson   // Coarse to fine basis
2256eaf62fffSJeremy L Thompson   CeedElemTopology topo;
2257eaf62fffSJeremy L Thompson   ierr = CeedBasisGetTopology(basis_fine, &topo); CeedChk(ierr);
2258eaf62fffSJeremy L Thompson   CeedInt dim, num_comp, num_nodes_c, num_nodes_f;
2259eaf62fffSJeremy L Thompson   ierr = CeedBasisGetDimension(basis_fine, &dim); CeedChk(ierr);
2260eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumComponents(basis_fine, &num_comp); CeedChk(ierr);
2261eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumNodes(basis_fine, &num_nodes_f); CeedChk(ierr);
2262eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c);
2263eaf62fffSJeremy L Thompson   CeedChk(ierr);
2264eaf62fffSJeremy L Thompson   CeedScalar *q_ref, *q_weight, *grad;
2265f9e0419eSJeremy L Thompson   ierr = CeedCalloc(num_nodes_f*dim, &q_ref); CeedChk(ierr);
2266eaf62fffSJeremy L Thompson   ierr = CeedCalloc(num_nodes_f, &q_weight); CeedChk(ierr);
2267eaf62fffSJeremy L Thompson   ierr = CeedCalloc(num_nodes_f*num_nodes_c*dim, &grad); CeedChk(ierr);
2268eaf62fffSJeremy L Thompson   CeedBasis basis_c_to_f;
2269eaf62fffSJeremy L Thompson   ierr = CeedBasisCreateH1(ceed, topo, num_comp, num_nodes_c, num_nodes_f,
2270eaf62fffSJeremy L Thompson                            interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f);
2271eaf62fffSJeremy L Thompson   CeedChk(ierr);
2272eaf62fffSJeremy L Thompson   ierr = CeedFree(&q_ref); CeedChk(ierr);
2273eaf62fffSJeremy L Thompson   ierr = CeedFree(&q_weight); CeedChk(ierr);
2274eaf62fffSJeremy L Thompson   ierr = CeedFree(&grad); CeedChk(ierr);
2275eaf62fffSJeremy L Thompson 
2276eaf62fffSJeremy L Thompson   // Core code
2277eaf62fffSJeremy L Thompson   ierr = CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse,
2278eaf62fffSJeremy L Thompson                                           basis_coarse, basis_c_to_f, op_coarse,
2279eaf62fffSJeremy L Thompson                                           op_prolong, op_restrict);
2280eaf62fffSJeremy L Thompson   CeedChk(ierr);
2281eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2282eaf62fffSJeremy L Thompson }
2283eaf62fffSJeremy L Thompson 
2284eaf62fffSJeremy L Thompson /**
2285eaf62fffSJeremy L Thompson   @brief Build a FDM based approximate inverse for each element for a
2286eaf62fffSJeremy L Thompson            CeedOperator
2287eaf62fffSJeremy L Thompson 
2288eaf62fffSJeremy L Thompson   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
2289eaf62fffSJeremy L Thompson     Method based approximate inverse. This function obtains the simultaneous
2290eaf62fffSJeremy L Thompson     diagonalization for the 1D mass and Laplacian operators,
2291eaf62fffSJeremy L Thompson       M = V^T V, K = V^T S V.
2292eaf62fffSJeremy L Thompson     The assembled QFunction is used to modify the eigenvalues from simultaneous
2293eaf62fffSJeremy L Thompson     diagonalization and obtain an approximate inverse of the form
2294eaf62fffSJeremy L Thompson       V^T S^hat V. The CeedOperator must be linear and non-composite. The
2295eaf62fffSJeremy L Thompson     associated CeedQFunction must therefore also be linear.
2296eaf62fffSJeremy L Thompson 
2297f04ea552SJeremy L Thompson   Note: Calling this function asserts that setup is complete
2298f04ea552SJeremy L Thompson           and sets the CeedOperator as immutable.
2299f04ea552SJeremy L Thompson 
2300eaf62fffSJeremy L Thompson   @param op            CeedOperator to create element inverses
2301eaf62fffSJeremy L Thompson   @param[out] fdm_inv  CeedOperator to apply the action of a FDM based inverse
2302eaf62fffSJeremy L Thompson                          for each element
2303eaf62fffSJeremy L Thompson   @param request       Address of CeedRequest for non-blocking completion, else
2304eaf62fffSJeremy L Thompson                          @ref CEED_REQUEST_IMMEDIATE
2305eaf62fffSJeremy L Thompson 
2306eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2307eaf62fffSJeremy L Thompson 
2308480fae85SJeremy L Thompson   @ref User
2309eaf62fffSJeremy L Thompson **/
2310eaf62fffSJeremy L Thompson int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv,
2311eaf62fffSJeremy L Thompson                                         CeedRequest *request) {
2312eaf62fffSJeremy L Thompson   int ierr;
2313eaf62fffSJeremy L Thompson   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
2314eaf62fffSJeremy L Thompson 
2315eaf62fffSJeremy L Thompson   if (op->CreateFDMElementInverse) {
2316d04bbc78SJeremy L Thompson     // Backend version
2317eaf62fffSJeremy L Thompson     ierr = op->CreateFDMElementInverse(op, fdm_inv, request); CeedChk(ierr);
2318eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2319eaf62fffSJeremy L Thompson   } else {
2320d04bbc78SJeremy L Thompson     // Operator fallback
2321d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2322d04bbc78SJeremy L Thompson 
2323d04bbc78SJeremy L Thompson     ierr = CeedOperatorGetFallback(op, &op_fallback); CeedChk(ierr);
2324d04bbc78SJeremy L Thompson     if (op_fallback) {
2325d04bbc78SJeremy L Thompson       ierr = CeedOperatorCreateFDMElementInverse(op_fallback, fdm_inv, request);
2326eaf62fffSJeremy L Thompson       CeedChk(ierr);
2327eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2328eaf62fffSJeremy L Thompson     }
2329eaf62fffSJeremy L Thompson   }
2330eaf62fffSJeremy L Thompson 
2331d04bbc78SJeremy L Thompson   // Default interface implementation
2332eaf62fffSJeremy L Thompson   Ceed ceed, ceed_parent;
2333eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
2334eaf62fffSJeremy L Thompson   ierr = CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent); CeedChk(ierr);
2335eaf62fffSJeremy L Thompson   ceed_parent = ceed_parent ? ceed_parent : ceed;
2336eaf62fffSJeremy L Thompson   CeedQFunction qf;
2337eaf62fffSJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChk(ierr);
2338eaf62fffSJeremy L Thompson 
2339eaf62fffSJeremy L Thompson   // Determine active input basis
2340eaf62fffSJeremy L Thompson   bool interp = false, grad = false;
2341eaf62fffSJeremy L Thompson   CeedBasis basis = NULL;
2342eaf62fffSJeremy L Thompson   CeedElemRestriction rstr = NULL;
2343eaf62fffSJeremy L Thompson   CeedOperatorField *op_fields;
2344eaf62fffSJeremy L Thompson   CeedQFunctionField *qf_fields;
2345eaf62fffSJeremy L Thompson   CeedInt num_input_fields;
23467e7773b5SJeremy L Thompson   ierr = CeedOperatorGetFields(op, &num_input_fields, &op_fields, NULL, NULL);
23477e7773b5SJeremy L Thompson   CeedChk(ierr);
23487e7773b5SJeremy L Thompson   ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields, NULL, NULL); CeedChk(ierr);
2349eaf62fffSJeremy L Thompson   for (CeedInt i=0; i<num_input_fields; i++) {
2350eaf62fffSJeremy L Thompson     CeedVector vec;
2351eaf62fffSJeremy L Thompson     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChk(ierr);
2352eaf62fffSJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
2353eaf62fffSJeremy L Thompson       CeedEvalMode eval_mode;
2354eaf62fffSJeremy L Thompson       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode); CeedChk(ierr);
2355eaf62fffSJeremy L Thompson       interp = interp || eval_mode == CEED_EVAL_INTERP;
2356eaf62fffSJeremy L Thompson       grad = grad || eval_mode == CEED_EVAL_GRAD;
2357eaf62fffSJeremy L Thompson       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChk(ierr);
2358eaf62fffSJeremy L Thompson       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr); CeedChk(ierr);
2359eaf62fffSJeremy L Thompson     }
2360eaf62fffSJeremy L Thompson   }
2361eaf62fffSJeremy L Thompson   if (!basis)
2362eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2363eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "No active field set");
2364eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
2365e79b91d9SJeremy L Thompson   CeedSize l_size = 1;
2366e79b91d9SJeremy L Thompson   CeedInt P_1d, Q_1d, elem_size, num_qpts, dim, num_comp = 1, num_elem = 1;
2367eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumNodes1D(basis, &P_1d); CeedChk(ierr);
2368eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumNodes(basis, &elem_size); CeedChk(ierr);
2369eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d); CeedChk(ierr);
2370eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumQuadraturePoints(basis, &num_qpts); CeedChk(ierr);
2371eaf62fffSJeremy L Thompson   ierr = CeedBasisGetDimension(basis, &dim); CeedChk(ierr);
2372eaf62fffSJeremy L Thompson   ierr = CeedBasisGetNumComponents(basis, &num_comp); CeedChk(ierr);
2373eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChk(ierr);
2374eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetLVectorSize(rstr, &l_size); CeedChk(ierr);
2375eaf62fffSJeremy L Thompson 
2376eaf62fffSJeremy L Thompson   // Build and diagonalize 1D Mass and Laplacian
2377eaf62fffSJeremy L Thompson   bool tensor_basis;
2378eaf62fffSJeremy L Thompson   ierr = CeedBasisIsTensor(basis, &tensor_basis); CeedChk(ierr);
2379eaf62fffSJeremy L Thompson   if (!tensor_basis)
2380eaf62fffSJeremy L Thompson     // LCOV_EXCL_START
2381eaf62fffSJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
2382eaf62fffSJeremy L Thompson                      "FDMElementInverse only supported for tensor "
2383eaf62fffSJeremy L Thompson                      "bases");
2384eaf62fffSJeremy L Thompson   // LCOV_EXCL_STOP
2385eaf62fffSJeremy L Thompson   CeedScalar *mass, *laplace, *x, *fdm_interp, *lambda;
2386eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d*P_1d, &mass); CeedChk(ierr);
2387eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d*P_1d, &laplace); CeedChk(ierr);
2388eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d*P_1d, &x); CeedChk(ierr);
2389eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d*P_1d, &fdm_interp); CeedChk(ierr);
2390eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d, &lambda); CeedChk(ierr);
2391eaf62fffSJeremy L Thompson   // -- Build matrices
2392eaf62fffSJeremy L Thompson   const CeedScalar *interp_1d, *grad_1d, *q_weight_1d;
2393eaf62fffSJeremy L Thompson   ierr = CeedBasisGetInterp1D(basis, &interp_1d); CeedChk(ierr);
2394eaf62fffSJeremy L Thompson   ierr = CeedBasisGetGrad1D(basis, &grad_1d); CeedChk(ierr);
2395eaf62fffSJeremy L Thompson   ierr = CeedBasisGetQWeights(basis, &q_weight_1d); CeedChk(ierr);
2396eaf62fffSJeremy L Thompson   ierr = CeedBuildMassLaplace(interp_1d, grad_1d, q_weight_1d, P_1d, Q_1d, dim,
2397eaf62fffSJeremy L Thompson                               mass, laplace); CeedChk(ierr);
2398eaf62fffSJeremy L Thompson 
2399eaf62fffSJeremy L Thompson   // -- Diagonalize
2400eaf62fffSJeremy L Thompson   ierr = CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d);
2401eaf62fffSJeremy L Thompson   CeedChk(ierr);
2402eaf62fffSJeremy L Thompson   ierr = CeedFree(&mass); CeedChk(ierr);
2403eaf62fffSJeremy L Thompson   ierr = CeedFree(&laplace); CeedChk(ierr);
2404eaf62fffSJeremy L Thompson   for (CeedInt i=0; i<P_1d; i++)
2405eaf62fffSJeremy L Thompson     for (CeedInt j=0; j<P_1d; j++)
2406eaf62fffSJeremy L Thompson       fdm_interp[i+j*P_1d] = x[j+i*P_1d];
2407eaf62fffSJeremy L Thompson   ierr = CeedFree(&x); CeedChk(ierr);
2408eaf62fffSJeremy L Thompson 
2409eaf62fffSJeremy L Thompson   // Assemble QFunction
2410eaf62fffSJeremy L Thompson   CeedVector assembled;
2411eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_qf;
241270a7ffb3SJeremy L Thompson   ierr =  CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled,
241370a7ffb3SJeremy L Thompson           &rstr_qf, request); CeedChk(ierr);
2414eaf62fffSJeremy L Thompson   CeedInt layout[3];
2415eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionGetELayout(rstr_qf, &layout); CeedChk(ierr);
2416eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_qf); CeedChk(ierr);
2417eaf62fffSJeremy L Thompson   CeedScalar max_norm = 0;
2418eaf62fffSJeremy L Thompson   ierr = CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm); CeedChk(ierr);
2419eaf62fffSJeremy L Thompson 
2420eaf62fffSJeremy L Thompson   // Calculate element averages
2421eaf62fffSJeremy L Thompson   CeedInt num_modes = (interp?1:0) + (grad?dim:0);
2422eaf62fffSJeremy L Thompson   CeedScalar *elem_avg;
2423eaf62fffSJeremy L Thompson   const CeedScalar *assembled_array, *q_weight_array;
2424eaf62fffSJeremy L Thompson   CeedVector q_weight;
2425eaf62fffSJeremy L Thompson   ierr = CeedVectorCreate(ceed_parent, num_qpts, &q_weight); CeedChk(ierr);
2426eaf62fffSJeremy L Thompson   ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
2427eaf62fffSJeremy L Thompson                         CEED_VECTOR_NONE, q_weight); CeedChk(ierr);
2428eaf62fffSJeremy L Thompson   ierr = CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembled_array);
2429eaf62fffSJeremy L Thompson   CeedChk(ierr);
2430eaf62fffSJeremy L Thompson   ierr = CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array);
2431eaf62fffSJeremy L Thompson   CeedChk(ierr);
2432eaf62fffSJeremy L Thompson   ierr = CeedCalloc(num_elem, &elem_avg); CeedChk(ierr);
2433eaf62fffSJeremy L Thompson   const CeedScalar qf_value_bound = max_norm*100*CEED_EPSILON;
2434eaf62fffSJeremy L Thompson   for (CeedInt e=0; e<num_elem; e++) {
2435eaf62fffSJeremy L Thompson     CeedInt count = 0;
2436eaf62fffSJeremy L Thompson     for (CeedInt q=0; q<num_qpts; q++)
2437eaf62fffSJeremy L Thompson       for (CeedInt i=0; i<num_comp*num_comp*num_modes*num_modes; i++)
2438eaf62fffSJeremy L Thompson         if (fabs(assembled_array[q*layout[0] + i*layout[1] + e*layout[2]]) >
2439eaf62fffSJeremy L Thompson             qf_value_bound) {
2440eaf62fffSJeremy L Thompson           elem_avg[e] += assembled_array[q*layout[0] + i*layout[1] + e*layout[2]] /
2441eaf62fffSJeremy L Thompson                          q_weight_array[q];
2442eaf62fffSJeremy L Thompson           count++;
2443eaf62fffSJeremy L Thompson         }
2444eaf62fffSJeremy L Thompson     if (count) {
2445eaf62fffSJeremy L Thompson       elem_avg[e] /= count;
2446eaf62fffSJeremy L Thompson     } else {
2447eaf62fffSJeremy L Thompson       elem_avg[e] = 1.0;
2448eaf62fffSJeremy L Thompson     }
2449eaf62fffSJeremy L Thompson   }
2450eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(assembled, &assembled_array); CeedChk(ierr);
2451eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&assembled); CeedChk(ierr);
2452eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(q_weight, &q_weight_array); CeedChk(ierr);
2453eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&q_weight); CeedChk(ierr);
2454eaf62fffSJeremy L Thompson 
2455eaf62fffSJeremy L Thompson   // Build FDM diagonal
2456eaf62fffSJeremy L Thompson   CeedVector q_data;
2457eaf62fffSJeremy L Thompson   CeedScalar *q_data_array, *fdm_diagonal;
2458eaf62fffSJeremy L Thompson   ierr = CeedCalloc(num_comp*elem_size, &fdm_diagonal); CeedChk(ierr);
2459eaf62fffSJeremy L Thompson   const CeedScalar fdm_diagonal_bound = elem_size*CEED_EPSILON;
2460eaf62fffSJeremy L Thompson   for (CeedInt c=0; c<num_comp; c++)
2461eaf62fffSJeremy L Thompson     for (CeedInt n=0; n<elem_size; n++) {
2462eaf62fffSJeremy L Thompson       if (interp)
2463eaf62fffSJeremy L Thompson         fdm_diagonal[c*elem_size + n] = 1.0;
2464eaf62fffSJeremy L Thompson       if (grad)
2465eaf62fffSJeremy L Thompson         for (CeedInt d=0; d<dim; d++) {
2466eaf62fffSJeremy L Thompson           CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d;
2467eaf62fffSJeremy L Thompson           fdm_diagonal[c*elem_size + n] += lambda[i];
2468eaf62fffSJeremy L Thompson         }
2469eaf62fffSJeremy L Thompson       if (fabs(fdm_diagonal[c*elem_size + n]) < fdm_diagonal_bound)
2470eaf62fffSJeremy L Thompson         fdm_diagonal[c*elem_size + n] = fdm_diagonal_bound;
2471eaf62fffSJeremy L Thompson     }
2472eaf62fffSJeremy L Thompson   ierr = CeedVectorCreate(ceed_parent, num_elem*num_comp*elem_size, &q_data);
2473eaf62fffSJeremy L Thompson   CeedChk(ierr);
2474eaf62fffSJeremy L Thompson   ierr = CeedVectorSetValue(q_data, 0.0); CeedChk(ierr);
24759c774eddSJeremy L Thompson   ierr = CeedVectorGetArrayWrite(q_data, CEED_MEM_HOST, &q_data_array);
24769c774eddSJeremy L Thompson   CeedChk(ierr);
2477eaf62fffSJeremy L Thompson   for (CeedInt e=0; e<num_elem; e++)
2478eaf62fffSJeremy L Thompson     for (CeedInt c=0; c<num_comp; c++)
2479eaf62fffSJeremy L Thompson       for (CeedInt n=0; n<elem_size; n++)
2480eaf62fffSJeremy L Thompson         q_data_array[(e*num_comp+c)*elem_size+n] = 1. / (elem_avg[e] *
2481eaf62fffSJeremy L Thompson             fdm_diagonal[c*elem_size + n]);
2482eaf62fffSJeremy L Thompson   ierr = CeedFree(&elem_avg); CeedChk(ierr);
2483eaf62fffSJeremy L Thompson   ierr = CeedFree(&fdm_diagonal); CeedChk(ierr);
2484eaf62fffSJeremy L Thompson   ierr = CeedVectorRestoreArray(q_data, &q_data_array); CeedChk(ierr);
2485eaf62fffSJeremy L Thompson 
2486eaf62fffSJeremy L Thompson   // Setup FDM operator
2487eaf62fffSJeremy L Thompson   // -- Basis
2488eaf62fffSJeremy L Thompson   CeedBasis fdm_basis;
2489eaf62fffSJeremy L Thompson   CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy;
2490eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d*P_1d, &grad_dummy); CeedChk(ierr);
2491eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d, &q_ref_dummy); CeedChk(ierr);
2492eaf62fffSJeremy L Thompson   ierr = CeedCalloc(P_1d, &q_weight_dummy); CeedChk(ierr);
2493eaf62fffSJeremy L Thompson   ierr = CeedBasisCreateTensorH1(ceed_parent, dim, num_comp, P_1d, P_1d,
2494eaf62fffSJeremy L Thompson                                  fdm_interp, grad_dummy, q_ref_dummy,
2495eaf62fffSJeremy L Thompson                                  q_weight_dummy, &fdm_basis); CeedChk(ierr);
2496eaf62fffSJeremy L Thompson   ierr = CeedFree(&fdm_interp); CeedChk(ierr);
2497eaf62fffSJeremy L Thompson   ierr = CeedFree(&grad_dummy); CeedChk(ierr);
2498eaf62fffSJeremy L Thompson   ierr = CeedFree(&q_ref_dummy); CeedChk(ierr);
2499eaf62fffSJeremy L Thompson   ierr = CeedFree(&q_weight_dummy); CeedChk(ierr);
2500eaf62fffSJeremy L Thompson   ierr = CeedFree(&lambda); CeedChk(ierr);
2501eaf62fffSJeremy L Thompson 
2502eaf62fffSJeremy L Thompson   // -- Restriction
2503eaf62fffSJeremy L Thompson   CeedElemRestriction rstr_qd_i;
2504eaf62fffSJeremy L Thompson   CeedInt strides[3] = {1, elem_size, elem_size*num_comp};
2505eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionCreateStrided(ceed_parent, num_elem, elem_size,
2506eaf62fffSJeremy L Thompson                                           num_comp, num_elem*num_comp*elem_size,
2507eaf62fffSJeremy L Thompson                                           strides, &rstr_qd_i); CeedChk(ierr);
2508eaf62fffSJeremy L Thompson   // -- QFunction
2509eaf62fffSJeremy L Thompson   CeedQFunction qf_fdm;
2510eaf62fffSJeremy L Thompson   ierr = CeedQFunctionCreateInteriorByName(ceed_parent, "Scale", &qf_fdm);
2511eaf62fffSJeremy L Thompson   CeedChk(ierr);
2512eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddInput(qf_fdm, "input", num_comp, CEED_EVAL_INTERP);
2513eaf62fffSJeremy L Thompson   CeedChk(ierr);
2514eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddInput(qf_fdm, "scale", num_comp, CEED_EVAL_NONE);
2515eaf62fffSJeremy L Thompson   CeedChk(ierr);
2516eaf62fffSJeremy L Thompson   ierr = CeedQFunctionAddOutput(qf_fdm, "output", num_comp, CEED_EVAL_INTERP);
2517eaf62fffSJeremy L Thompson   CeedChk(ierr);
25186e15d496SJeremy L Thompson   ierr = CeedQFunctionSetUserFlopsEstimate(qf_fdm, num_comp); CeedChk(ierr);
2519eaf62fffSJeremy L Thompson   // -- QFunction context
2520eaf62fffSJeremy L Thompson   CeedInt *num_comp_data;
2521eaf62fffSJeremy L Thompson   ierr = CeedCalloc(1, &num_comp_data); CeedChk(ierr);
2522eaf62fffSJeremy L Thompson   num_comp_data[0] = num_comp;
2523eaf62fffSJeremy L Thompson   CeedQFunctionContext ctx_fdm;
2524eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextCreate(ceed, &ctx_fdm); CeedChk(ierr);
2525eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextSetData(ctx_fdm, CEED_MEM_HOST, CEED_OWN_POINTER,
2526eaf62fffSJeremy L Thompson                                      sizeof(*num_comp_data), num_comp_data);
2527eaf62fffSJeremy L Thompson   CeedChk(ierr);
2528eaf62fffSJeremy L Thompson   ierr = CeedQFunctionSetContext(qf_fdm, ctx_fdm); CeedChk(ierr);
2529eaf62fffSJeremy L Thompson   ierr = CeedQFunctionContextDestroy(&ctx_fdm); CeedChk(ierr);
2530eaf62fffSJeremy L Thompson   // -- Operator
2531eaf62fffSJeremy L Thompson   ierr = CeedOperatorCreate(ceed_parent, qf_fdm, NULL, NULL, fdm_inv);
2532eaf62fffSJeremy L Thompson   CeedChk(ierr);
2533eaf62fffSJeremy L Thompson   CeedOperatorSetField(*fdm_inv, "input", rstr, fdm_basis, CEED_VECTOR_ACTIVE);
2534eaf62fffSJeremy L Thompson   CeedChk(ierr);
2535eaf62fffSJeremy L Thompson   CeedOperatorSetField(*fdm_inv, "scale", rstr_qd_i, CEED_BASIS_COLLOCATED,
2536eaf62fffSJeremy L Thompson                        q_data); CeedChk(ierr);
2537eaf62fffSJeremy L Thompson   CeedOperatorSetField(*fdm_inv, "output", rstr, fdm_basis, CEED_VECTOR_ACTIVE);
2538eaf62fffSJeremy L Thompson   CeedChk(ierr);
2539eaf62fffSJeremy L Thompson 
2540eaf62fffSJeremy L Thompson   // Cleanup
2541eaf62fffSJeremy L Thompson   ierr = CeedVectorDestroy(&q_data); CeedChk(ierr);
2542eaf62fffSJeremy L Thompson   ierr = CeedBasisDestroy(&fdm_basis); CeedChk(ierr);
2543eaf62fffSJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_qd_i); CeedChk(ierr);
2544eaf62fffSJeremy L Thompson   ierr = CeedQFunctionDestroy(&qf_fdm); CeedChk(ierr);
2545eaf62fffSJeremy L Thompson 
2546eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2547eaf62fffSJeremy L Thompson }
2548eaf62fffSJeremy L Thompson 
2549eaf62fffSJeremy L Thompson /// @}
2550