xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-preconditioning.c (revision 4dd1a9d2ed83e0c7750f521ad012b0dd9503bf69)
13d8e8822SJeremy L Thompson // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
23d8e8822SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3eaf62fffSJeremy L Thompson //
43d8e8822SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
5eaf62fffSJeremy L Thompson //
63d8e8822SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
7eaf62fffSJeremy L Thompson 
82b730f8bSJeremy L Thompson #include <ceed-impl.h>
949aac155SJeremy L Thompson #include <ceed.h>
102b730f8bSJeremy L Thompson #include <ceed/backend.h>
11c85e8640SSebastian Grimberg #include <assert.h>
122b730f8bSJeremy L Thompson #include <math.h>
13eaf62fffSJeremy L Thompson #include <stdbool.h>
14eaf62fffSJeremy L Thompson #include <stdio.h>
15eaf62fffSJeremy L Thompson #include <string.h>
16eaf62fffSJeremy L Thompson 
17eaf62fffSJeremy L Thompson /// @file
18eaf62fffSJeremy L Thompson /// Implementation of CeedOperator preconditioning interfaces
19eaf62fffSJeremy L Thompson 
20eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
21eaf62fffSJeremy L Thompson /// CeedOperator Library Internal Preconditioning Functions
22eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
23eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorDeveloper
24eaf62fffSJeremy L Thompson /// @{
25eaf62fffSJeremy L Thompson 
26eaf62fffSJeremy L Thompson /**
27ea61e9acSJeremy L Thompson   @brief Duplicate a CeedQFunction with a reference Ceed to fallback for advanced CeedOperator functionality
289e77b9c8SJeremy L Thompson 
2901ea9c81SJed Brown   @param[in]  fallback_ceed Ceed on which to create fallback CeedQFunction
309e77b9c8SJeremy L Thompson   @param[in]  qf            CeedQFunction to create fallback for
3101ea9c81SJed Brown   @param[out] qf_fallback   fallback CeedQFunction
329e77b9c8SJeremy L Thompson 
339e77b9c8SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
349e77b9c8SJeremy L Thompson 
359e77b9c8SJeremy L Thompson   @ref Developer
369e77b9c8SJeremy L Thompson **/
372b730f8bSJeremy L Thompson static int CeedQFunctionCreateFallback(Ceed fallback_ceed, CeedQFunction qf, CeedQFunction *qf_fallback) {
381c66c397SJeremy L Thompson   char *source_path_with_name = NULL;
391c66c397SJeremy L Thompson 
409e77b9c8SJeremy L Thompson   // Check if NULL qf passed in
419e77b9c8SJeremy L Thompson   if (!qf) return CEED_ERROR_SUCCESS;
429e77b9c8SJeremy L Thompson 
43d04bbc78SJeremy L Thompson   CeedDebug256(qf->ceed, 1, "---------- CeedOperator Fallback ----------\n");
4413f886e9SJeremy L Thompson   CeedDebug(qf->ceed, "Creating fallback CeedQFunction\n");
45d04bbc78SJeremy L Thompson 
469e77b9c8SJeremy L Thompson   if (qf->source_path) {
472b730f8bSJeremy L Thompson     size_t path_len = strlen(qf->source_path), name_len = strlen(qf->kernel_name);
482b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(path_len + name_len + 2, &source_path_with_name));
499e77b9c8SJeremy L Thompson     memcpy(source_path_with_name, qf->source_path, path_len);
509e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len], ":", 1);
519e77b9c8SJeremy L Thompson     memcpy(&source_path_with_name[path_len + 1], qf->kernel_name, name_len);
529e77b9c8SJeremy L Thompson   } else {
532b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &source_path_with_name));
549e77b9c8SJeremy L Thompson   }
559e77b9c8SJeremy L Thompson 
562b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInterior(fallback_ceed, qf->vec_length, qf->function, source_path_with_name, qf_fallback));
579e77b9c8SJeremy L Thompson   {
589e77b9c8SJeremy L Thompson     CeedQFunctionContext ctx;
599e77b9c8SJeremy L Thompson 
602b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionGetContext(qf, &ctx));
612b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetContext(*qf_fallback, ctx));
629e77b9c8SJeremy L Thompson   }
639e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_input_fields; i++) {
642b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(*qf_fallback, qf->input_fields[i]->field_name, qf->input_fields[i]->size, qf->input_fields[i]->eval_mode));
659e77b9c8SJeremy L Thompson   }
669e77b9c8SJeremy L Thompson   for (CeedInt i = 0; i < qf->num_output_fields; i++) {
672b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddOutput(*qf_fallback, qf->output_fields[i]->field_name, qf->output_fields[i]->size, qf->output_fields[i]->eval_mode));
689e77b9c8SJeremy L Thompson   }
692b730f8bSJeremy L Thompson   CeedCall(CeedFree(&source_path_with_name));
709e77b9c8SJeremy L Thompson   return CEED_ERROR_SUCCESS;
719e77b9c8SJeremy L Thompson }
729e77b9c8SJeremy L Thompson 
739e77b9c8SJeremy L Thompson /**
74ea61e9acSJeremy L Thompson   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced CeedOperator functionality
75eaf62fffSJeremy L Thompson 
76ea61e9acSJeremy L Thompson   @param[in,out] op CeedOperator to create fallback for
77eaf62fffSJeremy L Thompson 
78eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
79eaf62fffSJeremy L Thompson 
80eaf62fffSJeremy L Thompson   @ref Developer
81eaf62fffSJeremy L Thompson **/
82d04bbc78SJeremy L Thompson static int CeedOperatorCreateFallback(CeedOperator op) {
839e77b9c8SJeremy L Thompson   Ceed         ceed_fallback;
841c66c397SJeremy L Thompson   bool         is_composite;
851c66c397SJeremy L Thompson   CeedOperator op_fallback;
86eaf62fffSJeremy L Thompson 
87805fe78eSJeremy L Thompson   // Check not already created
88805fe78eSJeremy L Thompson   if (op->op_fallback) return CEED_ERROR_SUCCESS;
89805fe78eSJeremy L Thompson 
90eaf62fffSJeremy L Thompson   // Fallback Ceed
912b730f8bSJeremy L Thompson   CeedCall(CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback));
92d04bbc78SJeremy L Thompson   if (!ceed_fallback) return CEED_ERROR_SUCCESS;
93d04bbc78SJeremy L Thompson 
94d04bbc78SJeremy L Thompson   CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n");
9513f886e9SJeremy L Thompson   CeedDebug(op->ceed, "Creating fallback CeedOperator\n");
96eaf62fffSJeremy L Thompson 
97eaf62fffSJeremy L Thompson   // Clone Op
98b275c451SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
99b275c451SJeremy L Thompson   if (is_composite) {
100b275c451SJeremy L Thompson     CeedInt       num_suboperators;
101b275c451SJeremy L Thompson     CeedOperator *sub_operators;
102b275c451SJeremy L Thompson 
1032b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorCreate(ceed_fallback, &op_fallback));
104b275c451SJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
105b275c451SJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
106b275c451SJeremy L Thompson     for (CeedInt i = 0; i < num_suboperators; i++) {
107d04bbc78SJeremy L Thompson       CeedOperator op_sub_fallback;
108d04bbc78SJeremy L Thompson 
109b275c451SJeremy L Thompson       CeedCall(CeedOperatorGetFallback(sub_operators[i], &op_sub_fallback));
1102b730f8bSJeremy L Thompson       CeedCall(CeedCompositeOperatorAddSub(op_fallback, op_sub_fallback));
111805fe78eSJeremy L Thompson     }
112805fe78eSJeremy L Thompson   } else {
1139e77b9c8SJeremy L Thompson     CeedQFunction qf_fallback = NULL, dqf_fallback = NULL, dqfT_fallback = NULL;
1141c66c397SJeremy L Thompson 
1152b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->qf, &qf_fallback));
1162b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqf, &dqf_fallback));
1172b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqfT, &dqfT_fallback));
1182b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreate(ceed_fallback, qf_fallback, dqf_fallback, dqfT_fallback, &op_fallback));
119805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
120437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(op_fallback, op->input_fields[i]->field_name, op->input_fields[i]->elem_rstr, op->input_fields[i]->basis,
1212b730f8bSJeremy L Thompson                                     op->input_fields[i]->vec));
122805fe78eSJeremy L Thompson     }
123805fe78eSJeremy L Thompson     for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
124437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(op_fallback, op->output_fields[i]->field_name, op->output_fields[i]->elem_rstr, op->output_fields[i]->basis,
1252b730f8bSJeremy L Thompson                                     op->output_fields[i]->vec));
126805fe78eSJeremy L Thompson     }
1272b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op->qf_assembled, &op_fallback->qf_assembled));
1289e77b9c8SJeremy L Thompson     // Cleanup
1292b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&qf_fallback));
1302b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&dqf_fallback));
1312b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionDestroy(&dqfT_fallback));
132805fe78eSJeremy L Thompson   }
1332b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetName(op_fallback, op->name));
1342b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fallback));
135b05f7e9fSJeremy L Thompson   // Note: No ref-counting here so we don't get caught in a reference loop.
136b05f7e9fSJeremy L Thompson   //       The op holds the only reference to op_fallback and is responsible for deleting itself and op_fallback.
137805fe78eSJeremy L Thompson   op->op_fallback                 = op_fallback;
138b05f7e9fSJeremy L Thompson   op_fallback->op_fallback_parent = op;
139eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
140eaf62fffSJeremy L Thompson }
141eaf62fffSJeremy L Thompson 
142eaf62fffSJeremy L Thompson /**
143eaf62fffSJeremy L Thompson   @brief Select correct basis matrix pointer based on CeedEvalMode
144eaf62fffSJeremy L Thompson 
145352a5e7cSSebastian Grimberg   @param[in]  basis     CeedBasis from which to get the basis matrix
146eaf62fffSJeremy L Thompson   @param[in]  eval_mode Current basis evaluation mode
147eaf62fffSJeremy L Thompson   @param[in]  identity  Pointer to identity matrix
148eaf62fffSJeremy L Thompson   @param[out] basis_ptr Basis pointer to set
149eaf62fffSJeremy L Thompson 
150eaf62fffSJeremy L Thompson   @ref Developer
151eaf62fffSJeremy L Thompson **/
152352a5e7cSSebastian Grimberg static inline int CeedOperatorGetBasisPointer(CeedBasis basis, CeedEvalMode eval_mode, const CeedScalar *identity, const CeedScalar **basis_ptr) {
153eaf62fffSJeremy L Thompson   switch (eval_mode) {
154eaf62fffSJeremy L Thompson     case CEED_EVAL_NONE:
155eaf62fffSJeremy L Thompson       *basis_ptr = identity;
156eaf62fffSJeremy L Thompson       break;
157eaf62fffSJeremy L Thompson     case CEED_EVAL_INTERP:
158352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetInterp(basis, basis_ptr));
159eaf62fffSJeremy L Thompson       break;
160eaf62fffSJeremy L Thompson     case CEED_EVAL_GRAD:
161352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetGrad(basis, basis_ptr));
162352a5e7cSSebastian Grimberg       break;
163352a5e7cSSebastian Grimberg     case CEED_EVAL_DIV:
164352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetDiv(basis, basis_ptr));
165352a5e7cSSebastian Grimberg       break;
166352a5e7cSSebastian Grimberg     case CEED_EVAL_CURL:
167352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetCurl(basis, basis_ptr));
168eaf62fffSJeremy L Thompson       break;
169eaf62fffSJeremy L Thompson     case CEED_EVAL_WEIGHT:
170eaf62fffSJeremy L Thompson       break;  // Caught by QF Assembly
171eaf62fffSJeremy L Thompson   }
172ed9e99e6SJeremy L Thompson   assert(*basis_ptr != NULL);
173352a5e7cSSebastian Grimberg   return CEED_ERROR_SUCCESS;
174eaf62fffSJeremy L Thompson }
175eaf62fffSJeremy L Thompson 
176eaf62fffSJeremy L Thompson /**
177eaf62fffSJeremy L Thompson   @brief Core logic for assembling operator diagonal or point block diagonal
178eaf62fffSJeremy L Thompson 
179eaf62fffSJeremy L Thompson   @param[in]  op             CeedOperator to assemble point block diagonal
180ea61e9acSJeremy L Thompson   @param[in]  request        Address of CeedRequest for non-blocking completion, else CEED_REQUEST_IMMEDIATE
181bd83916cSSebastian Grimberg   @param[in]  is_point_block Boolean flag to assemble diagonal or point block diagonal
182eaf62fffSJeremy L Thompson   @param[out] assembled      CeedVector to store assembled diagonal
183eaf62fffSJeremy L Thompson 
184eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
185eaf62fffSJeremy L Thompson 
186eaf62fffSJeremy L Thompson   @ref Developer
187eaf62fffSJeremy L Thompson **/
188bd83916cSSebastian Grimberg static inline int CeedSingleOperatorAssembleAddDiagonal_Core(CeedOperator op, CeedRequest *request, const bool is_point_block, CeedVector assembled) {
189eaf62fffSJeremy L Thompson   Ceed ceed;
190506b1a0cSSebastian Grimberg   bool is_composite;
191506b1a0cSSebastian Grimberg 
192506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorGetCeed(op, &ceed));
193506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorIsComposite(op, &is_composite));
194506b1a0cSSebastian Grimberg   CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
195506b1a0cSSebastian Grimberg 
196506b1a0cSSebastian Grimberg   // Assemble QFunction
197506b1a0cSSebastian Grimberg   CeedInt             layout_qf[3];
198437c7c90SJeremy L Thompson   const CeedScalar   *assembled_qf_array;
199c5f45aeaSJeremy L Thompson   CeedVector          assembled_qf        = NULL;
200c5f45aeaSJeremy L Thompson   CeedElemRestriction assembled_elem_rstr = NULL;
201437c7c90SJeremy L Thompson 
202437c7c90SJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &assembled_elem_rstr, request));
203506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetELayout(assembled_elem_rstr, &layout_qf));
204437c7c90SJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&assembled_elem_rstr));
205437c7c90SJeremy L Thompson   CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array));
206eaf62fffSJeremy L Thompson 
207ed9e99e6SJeremy L Thompson   // Get assembly data
208437c7c90SJeremy L Thompson   const CeedEvalMode     **eval_modes_in, **eval_modes_out;
209506b1a0cSSebastian Grimberg   CeedInt                  num_active_bases_in, *num_eval_modes_in, num_active_bases_out, *num_eval_modes_out;
210437c7c90SJeremy L Thompson   CeedSize               **eval_mode_offsets_in, **eval_mode_offsets_out, num_output_components;
211506b1a0cSSebastian Grimberg   CeedBasis               *active_bases_in, *active_bases_out;
212506b1a0cSSebastian Grimberg   CeedElemRestriction     *active_elem_rstrs_in, *active_elem_rstrs_out;
2131c66c397SJeremy L Thompson   CeedOperatorAssemblyData data;
2141c66c397SJeremy L Thompson 
215437c7c90SJeremy L Thompson   CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data));
216506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_active_bases_in, &num_eval_modes_in, &eval_modes_in, &eval_mode_offsets_in,
217506b1a0cSSebastian Grimberg                                                 &num_active_bases_out, &num_eval_modes_out, &eval_modes_out, &eval_mode_offsets_out,
218506b1a0cSSebastian Grimberg                                                 &num_output_components));
219506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &active_bases_in, NULL, NULL, &active_bases_out, NULL));
220506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, NULL, &active_elem_rstrs_in, NULL, &active_elem_rstrs_out));
221506b1a0cSSebastian Grimberg 
222934a29f5SSebastian Grimberg   // Loop over all active bases (find matching input/output pairs)
223934a29f5SSebastian Grimberg   for (CeedInt b = 0; b < CeedIntMin(num_active_bases_in, num_active_bases_out); b++) {
224934a29f5SSebastian Grimberg     CeedInt             b_in, b_out, num_elem, num_nodes, num_qpts, num_comp;
2251c66c397SJeremy L Thompson     bool                has_eval_none = false;
2261c66c397SJeremy L Thompson     CeedScalar         *elem_diag_array, *identity = NULL;
2271c66c397SJeremy L Thompson     CeedVector          elem_diag;
2287c1dbaffSSebastian Grimberg     CeedElemRestriction diag_elem_rstr;
2291c66c397SJeremy L Thompson 
230934a29f5SSebastian Grimberg     if (num_active_bases_in <= num_active_bases_out) {
231934a29f5SSebastian Grimberg       b_in = b;
232934a29f5SSebastian Grimberg       for (b_out = 0; b_out < num_active_bases_out; b_out++) {
233934a29f5SSebastian Grimberg         if (active_bases_in[b_in] == active_bases_out[b_out]) {
234934a29f5SSebastian Grimberg           break;
235934a29f5SSebastian Grimberg         }
236934a29f5SSebastian Grimberg       }
237934a29f5SSebastian Grimberg       if (b_out == num_active_bases_out) {
238934a29f5SSebastian Grimberg         continue;
239934a29f5SSebastian Grimberg       }  // No matching output basis found
240934a29f5SSebastian Grimberg     } else {
241934a29f5SSebastian Grimberg       b_out = b;
242934a29f5SSebastian Grimberg       for (b_in = 0; b_in < num_active_bases_in; b_in++) {
243934a29f5SSebastian Grimberg         if (active_bases_in[b_in] == active_bases_out[b_out]) {
244934a29f5SSebastian Grimberg           break;
245934a29f5SSebastian Grimberg         }
246934a29f5SSebastian Grimberg       }
247934a29f5SSebastian Grimberg       if (b_in == num_active_bases_in) {
248934a29f5SSebastian Grimberg         continue;
249934a29f5SSebastian Grimberg       }  // No matching output basis found
250934a29f5SSebastian Grimberg     }
251934a29f5SSebastian Grimberg     CeedCheck(active_elem_rstrs_in[b_in] == active_elem_rstrs_out[b_out], ceed, CEED_ERROR_UNSUPPORTED,
252506b1a0cSSebastian Grimberg               "Cannot assemble operator diagonal with different input and output active element restrictions");
253506b1a0cSSebastian Grimberg 
2541c66c397SJeremy L Thompson     // Assemble point block diagonal restriction, if needed
255bd83916cSSebastian Grimberg     if (is_point_block) {
256934a29f5SSebastian Grimberg       CeedCall(CeedOperatorCreateActivePointBlockRestriction(active_elem_rstrs_in[b_in], &diag_elem_rstr));
2577c1dbaffSSebastian Grimberg     } else {
258934a29f5SSebastian Grimberg       CeedCall(CeedElemRestrictionCreateUnsignedCopy(active_elem_rstrs_in[b_in], &diag_elem_rstr));
259eaf62fffSJeremy L Thompson     }
260eaf62fffSJeremy L Thompson 
261eaf62fffSJeremy L Thompson     // Create diagonal vector
262437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionCreateVector(diag_elem_rstr, NULL, &elem_diag));
263eaf62fffSJeremy L Thompson 
264eaf62fffSJeremy L Thompson     // Assemble element operator diagonals
2652b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(elem_diag, 0.0));
2662b730f8bSJeremy L Thompson     CeedCall(CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array));
267437c7c90SJeremy L Thompson     CeedCall(CeedElemRestrictionGetNumElements(diag_elem_rstr, &num_elem));
268934a29f5SSebastian Grimberg     CeedCall(CeedBasisGetNumNodes(active_bases_in[b_in], &num_nodes));
269934a29f5SSebastian Grimberg     CeedCall(CeedBasisGetNumComponents(active_bases_in[b_in], &num_comp));
270934a29f5SSebastian Grimberg     if (active_bases_in[b_in] == CEED_BASIS_NONE) num_qpts = num_nodes;
271934a29f5SSebastian Grimberg     else CeedCall(CeedBasisGetNumQuadraturePoints(active_bases_in[b_in], &num_qpts));
272ed9e99e6SJeremy L Thompson 
273352a5e7cSSebastian Grimberg     // Construct identity matrix for basis if required
274934a29f5SSebastian Grimberg     for (CeedInt i = 0; i < num_eval_modes_in[b_in]; i++) {
275934a29f5SSebastian Grimberg       has_eval_none = has_eval_none || (eval_modes_in[b_in][i] == CEED_EVAL_NONE);
276ed9e99e6SJeremy L Thompson     }
277934a29f5SSebastian Grimberg     for (CeedInt i = 0; i < num_eval_modes_out[b_out]; i++) {
278934a29f5SSebastian Grimberg       has_eval_none = has_eval_none || (eval_modes_out[b_out][i] == CEED_EVAL_NONE);
279ed9e99e6SJeremy L Thompson     }
280ed9e99e6SJeremy L Thompson     if (has_eval_none) {
2812b730f8bSJeremy L Thompson       CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
2822b730f8bSJeremy L Thompson       for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) identity[i * num_nodes + i] = 1.0;
283eaf62fffSJeremy L Thompson     }
284352a5e7cSSebastian Grimberg 
285eaf62fffSJeremy L Thompson     // Compute the diagonal of B^T D B
286eaf62fffSJeremy L Thompson     // Each element
287b94338b9SJed Brown     for (CeedSize e = 0; e < num_elem; e++) {
288eaf62fffSJeremy L Thompson       // Each basis eval mode pair
289352a5e7cSSebastian Grimberg       CeedInt      d_out              = 0, q_comp_out;
290352a5e7cSSebastian Grimberg       CeedEvalMode eval_mode_out_prev = CEED_EVAL_NONE;
2911c66c397SJeremy L Thompson 
292934a29f5SSebastian Grimberg       for (CeedInt e_out = 0; e_out < num_eval_modes_out[b_out]; e_out++) {
2931c66c397SJeremy L Thompson         CeedInt           d_in              = 0, q_comp_in;
294437c7c90SJeremy L Thompson         const CeedScalar *B_t               = NULL;
2951c66c397SJeremy L Thompson         CeedEvalMode      eval_mode_in_prev = CEED_EVAL_NONE;
2961c66c397SJeremy L Thompson 
297934a29f5SSebastian Grimberg         CeedCall(CeedOperatorGetBasisPointer(active_bases_out[b_out], eval_modes_out[b_out][e_out], identity, &B_t));
298934a29f5SSebastian Grimberg         CeedCall(CeedBasisGetNumQuadratureComponents(active_bases_out[b_out], eval_modes_out[b_out][e_out], &q_comp_out));
299352a5e7cSSebastian Grimberg         if (q_comp_out > 1) {
300934a29f5SSebastian Grimberg           if (e_out == 0 || eval_modes_out[b_out][e_out] != eval_mode_out_prev) d_out = 0;
301352a5e7cSSebastian Grimberg           else B_t = &B_t[(++d_out) * num_qpts * num_nodes];
302352a5e7cSSebastian Grimberg         }
303934a29f5SSebastian Grimberg         eval_mode_out_prev = eval_modes_out[b_out][e_out];
304352a5e7cSSebastian Grimberg 
305934a29f5SSebastian Grimberg         for (CeedInt e_in = 0; e_in < num_eval_modes_in[b_in]; e_in++) {
306437c7c90SJeremy L Thompson           const CeedScalar *B = NULL;
3071c66c397SJeremy L Thompson 
308934a29f5SSebastian Grimberg           CeedCall(CeedOperatorGetBasisPointer(active_bases_in[b_in], eval_modes_in[b_in][e_in], identity, &B));
309934a29f5SSebastian Grimberg           CeedCall(CeedBasisGetNumQuadratureComponents(active_bases_in[b_in], eval_modes_in[b_in][e_in], &q_comp_in));
310352a5e7cSSebastian Grimberg           if (q_comp_in > 1) {
311934a29f5SSebastian Grimberg             if (e_in == 0 || eval_modes_in[b_in][e_in] != eval_mode_in_prev) d_in = 0;
312352a5e7cSSebastian Grimberg             else B = &B[(++d_in) * num_qpts * num_nodes];
313352a5e7cSSebastian Grimberg           }
314934a29f5SSebastian Grimberg           eval_mode_in_prev = eval_modes_in[b_in][e_in];
315352a5e7cSSebastian Grimberg 
316eaf62fffSJeremy L Thompson           // Each component
317506b1a0cSSebastian Grimberg           for (CeedInt c_out = 0; c_out < num_comp; c_out++) {
318437c7c90SJeremy L Thompson             // Each qpt/node pair
3192b730f8bSJeremy L Thompson             for (CeedInt q = 0; q < num_qpts; q++) {
320bd83916cSSebastian Grimberg               if (is_point_block) {
321eaf62fffSJeremy L Thompson                 // Point Block Diagonal
322506b1a0cSSebastian Grimberg                 for (CeedInt c_in = 0; c_in < num_comp; c_in++) {
323934a29f5SSebastian Grimberg                   const CeedSize c_offset =
324934a29f5SSebastian Grimberg                       (eval_mode_offsets_in[b_in][e_in] + c_in) * num_output_components + eval_mode_offsets_out[b_out][e_out] + c_out;
325506b1a0cSSebastian Grimberg                   const CeedScalar qf_value = assembled_qf_array[q * layout_qf[0] + c_offset * layout_qf[1] + e * layout_qf[2]];
3261c66c397SJeremy L Thompson 
3272b730f8bSJeremy L Thompson                   for (CeedInt n = 0; n < num_nodes; n++) {
328506b1a0cSSebastian Grimberg                     elem_diag_array[((e * num_comp + c_out) * num_comp + c_in) * num_nodes + n] +=
329437c7c90SJeremy L Thompson                         B_t[q * num_nodes + n] * qf_value * B[q * num_nodes + n];
330eaf62fffSJeremy L Thompson                   }
3312b730f8bSJeremy L Thompson                 }
332eaf62fffSJeremy L Thompson               } else {
333eaf62fffSJeremy L Thompson                 // Diagonal Only
334934a29f5SSebastian Grimberg                 const CeedInt c_offset =
335934a29f5SSebastian Grimberg                     (eval_mode_offsets_in[b_in][e_in] + c_out) * num_output_components + eval_mode_offsets_out[b_out][e_out] + c_out;
336506b1a0cSSebastian Grimberg                 const CeedScalar qf_value = assembled_qf_array[q * layout_qf[0] + c_offset * layout_qf[1] + e * layout_qf[2]];
3371c66c397SJeremy L Thompson 
3382b730f8bSJeremy L Thompson                 for (CeedInt n = 0; n < num_nodes; n++) {
339506b1a0cSSebastian Grimberg                   elem_diag_array[(e * num_comp + c_out) * num_nodes + n] += B_t[q * num_nodes + n] * qf_value * B[q * num_nodes + n];
340eaf62fffSJeremy L Thompson                 }
341eaf62fffSJeremy L Thompson               }
342eaf62fffSJeremy L Thompson             }
343eaf62fffSJeremy L Thompson           }
3442b730f8bSJeremy L Thompson         }
3452b730f8bSJeremy L Thompson       }
3462b730f8bSJeremy L Thompson     }
3472b730f8bSJeremy L Thompson     CeedCall(CeedVectorRestoreArray(elem_diag, &elem_diag_array));
348eaf62fffSJeremy L Thompson 
349eaf62fffSJeremy L Thompson     // Assemble local operator diagonal
3507c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionApply(diag_elem_rstr, CEED_TRANSPOSE, elem_diag, assembled, request));
351eaf62fffSJeremy L Thompson 
352eaf62fffSJeremy L Thompson     // Cleanup
3537c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionDestroy(&diag_elem_rstr));
3542b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&elem_diag));
3552b730f8bSJeremy L Thompson     CeedCall(CeedFree(&identity));
356437c7c90SJeremy L Thompson   }
357437c7c90SJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array));
358437c7c90SJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled_qf));
359eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
360eaf62fffSJeremy L Thompson }
361eaf62fffSJeremy L Thompson 
362eaf62fffSJeremy L Thompson /**
363eaf62fffSJeremy L Thompson   @brief Core logic for assembling composite operator diagonal
364eaf62fffSJeremy L Thompson 
365eaf62fffSJeremy L Thompson   @param[in]  op             CeedOperator to assemble point block diagonal
366ea61e9acSJeremy L Thompson   @param[in]  request        Address of CeedRequest for non-blocking completion, else CEED_REQUEST_IMMEDIATE
367bd83916cSSebastian Grimberg   @param[in]  is_point_block Boolean flag to assemble diagonal or point block diagonal
368eaf62fffSJeremy L Thompson   @param[out] assembled      CeedVector to store assembled diagonal
369eaf62fffSJeremy L Thompson 
370eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
371eaf62fffSJeremy L Thompson 
372eaf62fffSJeremy L Thompson   @ref Developer
373eaf62fffSJeremy L Thompson **/
374bd83916cSSebastian Grimberg static inline int CeedCompositeOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedRequest *request, const bool is_point_block,
375eaf62fffSJeremy L Thompson                                                                  CeedVector assembled) {
376eaf62fffSJeremy L Thompson   CeedInt       num_sub;
377eaf62fffSJeremy L Thompson   CeedOperator *suboperators;
3781c66c397SJeremy L Thompson 
379c6ebc35dSJeremy L Thompson   CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub));
380c6ebc35dSJeremy L Thompson   CeedCall(CeedCompositeOperatorGetSubList(op, &suboperators));
381eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_sub; i++) {
382bd83916cSSebastian Grimberg     if (is_point_block) {
3832b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(suboperators[i], assembled, request));
3846aa95790SJeremy L Thompson     } else {
3852b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddDiagonal(suboperators[i], assembled, request));
3866aa95790SJeremy L Thompson     }
387eaf62fffSJeremy L Thompson   }
388eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
389eaf62fffSJeremy L Thompson }
390eaf62fffSJeremy L Thompson 
391eaf62fffSJeremy L Thompson /**
392eaf62fffSJeremy L Thompson   @brief Build nonzero pattern for non-composite operator
393eaf62fffSJeremy L Thompson 
394eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssembleSymbolic()
395eaf62fffSJeremy L Thompson 
396eaf62fffSJeremy L Thompson   @param[in]  op     CeedOperator to assemble nonzero pattern
397eaf62fffSJeremy L Thompson   @param[in]  offset Offset for number of entries
398eaf62fffSJeremy L Thompson   @param[out] rows   Row number for each entry
399eaf62fffSJeremy L Thompson   @param[out] cols   Column number for each entry
400eaf62fffSJeremy L Thompson 
401eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
402eaf62fffSJeremy L Thompson 
403eaf62fffSJeremy L Thompson   @ref Developer
404eaf62fffSJeremy L Thompson **/
4052b730f8bSJeremy L Thompson static int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset, CeedInt *rows, CeedInt *cols) {
406f3d47e36SJeremy L Thompson   Ceed                ceed;
407f3d47e36SJeremy L Thompson   bool                is_composite;
408506b1a0cSSebastian Grimberg   CeedSize            num_nodes_in, num_nodes_out, count = 0;
409506b1a0cSSebastian Grimberg   CeedInt             num_elem_in, elem_size_in, num_comp_in, layout_er_in[3];
410506b1a0cSSebastian Grimberg   CeedInt             num_elem_out, elem_size_out, num_comp_out, layout_er_out[3], local_num_entries;
4111c66c397SJeremy L Thompson   CeedScalar         *array;
412506b1a0cSSebastian Grimberg   const CeedScalar   *elem_dof_a_in, *elem_dof_a_out;
413506b1a0cSSebastian Grimberg   CeedVector          index_vec_in, index_vec_out, elem_dof_in, elem_dof_out;
414506b1a0cSSebastian Grimberg   CeedElemRestriction elem_rstr_in, elem_rstr_out, index_elem_rstr_in, index_elem_rstr_out;
4151c66c397SJeremy L Thompson 
416f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
417f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
4186574a04fSJeremy L Thompson   CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
419eaf62fffSJeremy L Thompson 
420506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorGetActiveVectorLengths(op, &num_nodes_in, &num_nodes_out));
421506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorGetActiveElemRestrictions(op, &elem_rstr_in, &elem_rstr_out));
422506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumElements(elem_rstr_in, &num_elem_in));
423506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetElementSize(elem_rstr_in, &elem_size_in));
424506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumComponents(elem_rstr_in, &num_comp_in));
425506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetELayout(elem_rstr_in, &layout_er_in));
426eaf62fffSJeremy L Thompson 
427506b1a0cSSebastian Grimberg   // Determine elem_dof relation for input
428506b1a0cSSebastian Grimberg   CeedCall(CeedVectorCreate(ceed, num_nodes_in, &index_vec_in));
429506b1a0cSSebastian Grimberg   CeedCall(CeedVectorGetArrayWrite(index_vec_in, CEED_MEM_HOST, &array));
430506b1a0cSSebastian Grimberg   for (CeedInt i = 0; i < num_nodes_in; i++) array[i] = i;
431506b1a0cSSebastian Grimberg   CeedCall(CeedVectorRestoreArray(index_vec_in, &array));
432506b1a0cSSebastian Grimberg   CeedCall(CeedVectorCreate(ceed, num_elem_in * elem_size_in * num_comp_in, &elem_dof_in));
433506b1a0cSSebastian Grimberg   CeedCall(CeedVectorSetValue(elem_dof_in, 0.0));
434506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionCreateUnorientedCopy(elem_rstr_in, &index_elem_rstr_in));
435506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionApply(index_elem_rstr_in, CEED_NOTRANSPOSE, index_vec_in, elem_dof_in, CEED_REQUEST_IMMEDIATE));
436506b1a0cSSebastian Grimberg   CeedCall(CeedVectorGetArrayRead(elem_dof_in, CEED_MEM_HOST, &elem_dof_a_in));
437506b1a0cSSebastian Grimberg   CeedCall(CeedVectorDestroy(&index_vec_in));
438506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionDestroy(&index_elem_rstr_in));
439506b1a0cSSebastian Grimberg 
440506b1a0cSSebastian Grimberg   if (elem_rstr_in != elem_rstr_out) {
441506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetNumElements(elem_rstr_out, &num_elem_out));
442506b1a0cSSebastian Grimberg     CeedCheck(num_elem_in == num_elem_out, ceed, CEED_ERROR_UNSUPPORTED,
443506b1a0cSSebastian Grimberg               "Active input and output operator restrictions must have the same number of elements");
444506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetElementSize(elem_rstr_out, &elem_size_out));
445506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetNumComponents(elem_rstr_out, &num_comp_out));
446506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetELayout(elem_rstr_out, &layout_er_out));
447506b1a0cSSebastian Grimberg 
448506b1a0cSSebastian Grimberg     // Determine elem_dof relation for output
449506b1a0cSSebastian Grimberg     CeedCall(CeedVectorCreate(ceed, num_nodes_out, &index_vec_out));
450506b1a0cSSebastian Grimberg     CeedCall(CeedVectorGetArrayWrite(index_vec_out, CEED_MEM_HOST, &array));
451506b1a0cSSebastian Grimberg     for (CeedInt i = 0; i < num_nodes_out; i++) array[i] = i;
452506b1a0cSSebastian Grimberg     CeedCall(CeedVectorRestoreArray(index_vec_out, &array));
453506b1a0cSSebastian Grimberg     CeedCall(CeedVectorCreate(ceed, num_elem_out * elem_size_out * num_comp_out, &elem_dof_out));
454506b1a0cSSebastian Grimberg     CeedCall(CeedVectorSetValue(elem_dof_out, 0.0));
455506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionCreateUnorientedCopy(elem_rstr_out, &index_elem_rstr_out));
456506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionApply(index_elem_rstr_out, CEED_NOTRANSPOSE, index_vec_out, elem_dof_out, CEED_REQUEST_IMMEDIATE));
457506b1a0cSSebastian Grimberg     CeedCall(CeedVectorGetArrayRead(elem_dof_out, CEED_MEM_HOST, &elem_dof_a_out));
458506b1a0cSSebastian Grimberg     CeedCall(CeedVectorDestroy(&index_vec_out));
459506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionDestroy(&index_elem_rstr_out));
460506b1a0cSSebastian Grimberg   } else {
461506b1a0cSSebastian Grimberg     num_elem_out     = num_elem_in;
462506b1a0cSSebastian Grimberg     elem_size_out    = elem_size_in;
463506b1a0cSSebastian Grimberg     num_comp_out     = num_comp_in;
464506b1a0cSSebastian Grimberg     layout_er_out[0] = layout_er_in[0];
465506b1a0cSSebastian Grimberg     layout_er_out[1] = layout_er_in[1];
466506b1a0cSSebastian Grimberg     layout_er_out[2] = layout_er_in[2];
467506b1a0cSSebastian Grimberg     elem_dof_a_out   = elem_dof_a_in;
468506b1a0cSSebastian Grimberg   }
469506b1a0cSSebastian Grimberg   local_num_entries = elem_size_out * num_comp_out * elem_size_in * num_comp_in * num_elem_in;
470eaf62fffSJeremy L Thompson 
471eaf62fffSJeremy L Thompson   // Determine i, j locations for element matrices
472506b1a0cSSebastian Grimberg   for (CeedInt e = 0; e < num_elem_in; e++) {
473506b1a0cSSebastian Grimberg     for (CeedInt comp_in = 0; comp_in < num_comp_in; comp_in++) {
474506b1a0cSSebastian Grimberg       for (CeedInt comp_out = 0; comp_out < num_comp_out; comp_out++) {
475506b1a0cSSebastian Grimberg         for (CeedInt i = 0; i < elem_size_out; i++) {
476506b1a0cSSebastian Grimberg           for (CeedInt j = 0; j < elem_size_in; j++) {
477506b1a0cSSebastian Grimberg             const CeedInt elem_dof_index_row = i * layout_er_out[0] + comp_out * layout_er_out[1] + e * layout_er_out[2];
478506b1a0cSSebastian Grimberg             const CeedInt elem_dof_index_col = j * layout_er_in[0] + comp_in * layout_er_in[1] + e * layout_er_in[2];
479506b1a0cSSebastian Grimberg             const CeedInt row                = elem_dof_a_out[elem_dof_index_row];
480506b1a0cSSebastian Grimberg             const CeedInt col                = elem_dof_a_in[elem_dof_index_col];
481eaf62fffSJeremy L Thompson 
482eaf62fffSJeremy L Thompson             rows[offset + count] = row;
483eaf62fffSJeremy L Thompson             cols[offset + count] = col;
484eaf62fffSJeremy L Thompson             count++;
485eaf62fffSJeremy L Thompson           }
486eaf62fffSJeremy L Thompson         }
487eaf62fffSJeremy L Thompson       }
488eaf62fffSJeremy L Thompson     }
489eaf62fffSJeremy L Thompson   }
4906574a04fSJeremy L Thompson   CeedCheck(count == local_num_entries, ceed, CEED_ERROR_MAJOR, "Error computing assembled entries");
491506b1a0cSSebastian Grimberg   CeedCall(CeedVectorRestoreArrayRead(elem_dof_in, &elem_dof_a_in));
492506b1a0cSSebastian Grimberg   CeedCall(CeedVectorDestroy(&elem_dof_in));
493506b1a0cSSebastian Grimberg   if (elem_rstr_in != elem_rstr_out) {
494506b1a0cSSebastian Grimberg     CeedCall(CeedVectorRestoreArrayRead(elem_dof_out, &elem_dof_a_out));
495506b1a0cSSebastian Grimberg     CeedCall(CeedVectorDestroy(&elem_dof_out));
496506b1a0cSSebastian Grimberg   }
497eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
498eaf62fffSJeremy L Thompson }
499eaf62fffSJeremy L Thompson 
500eaf62fffSJeremy L Thompson /**
501eaf62fffSJeremy L Thompson   @brief Assemble nonzero entries for non-composite operator
502eaf62fffSJeremy L Thompson 
503eaf62fffSJeremy L Thompson   Users should generally use CeedOperatorLinearAssemble()
504eaf62fffSJeremy L Thompson 
505eaf62fffSJeremy L Thompson   @param[in]  op     CeedOperator to assemble
506ea61e9acSJeremy L Thompson   @param[in]  offset Offset for number of entries
507eaf62fffSJeremy L Thompson   @param[out] values Values to assemble into matrix
508eaf62fffSJeremy L Thompson 
509eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
510eaf62fffSJeremy L Thompson 
511eaf62fffSJeremy L Thompson   @ref Developer
512eaf62fffSJeremy L Thompson **/
5132b730f8bSJeremy L Thompson static int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset, CeedVector values) {
514f3d47e36SJeremy L Thompson   Ceed ceed;
515f3d47e36SJeremy L Thompson   bool is_composite;
5161c66c397SJeremy L Thompson 
517f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
518f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
5196574a04fSJeremy L Thompson   CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
520f3d47e36SJeremy L Thompson 
521f3d47e36SJeremy L Thompson   // Early exit for empty operator
522f3d47e36SJeremy L Thompson   {
523f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
524f3d47e36SJeremy L Thompson 
525f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
526f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
527f3d47e36SJeremy L Thompson   }
528eaf62fffSJeremy L Thompson 
529cefa2673SJeremy L Thompson   if (op->LinearAssembleSingle) {
530cefa2673SJeremy L Thompson     // Backend version
5312b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleSingle(op, offset, values));
532cefa2673SJeremy L Thompson     return CEED_ERROR_SUCCESS;
533cefa2673SJeremy L Thompson   } else {
534cefa2673SJeremy L Thompson     // Operator fallback
535cefa2673SJeremy L Thompson     CeedOperator op_fallback;
536cefa2673SJeremy L Thompson 
5372b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
538cefa2673SJeremy L Thompson     if (op_fallback) {
5392b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemble(op_fallback, offset, values));
540cefa2673SJeremy L Thompson       return CEED_ERROR_SUCCESS;
541cefa2673SJeremy L Thompson     }
542cefa2673SJeremy L Thompson   }
543cefa2673SJeremy L Thompson 
544eaf62fffSJeremy L Thompson   // Assemble QFunction
545506b1a0cSSebastian Grimberg   CeedInt             layout_qf[3];
5461c66c397SJeremy L Thompson   const CeedScalar   *assembled_qf_array;
547c5f45aeaSJeremy L Thompson   CeedVector          assembled_qf        = NULL;
548506b1a0cSSebastian Grimberg   CeedElemRestriction assembled_elem_rstr = NULL;
549eaf62fffSJeremy L Thompson 
550506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &assembled_elem_rstr, CEED_REQUEST_IMMEDIATE));
551506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetELayout(assembled_elem_rstr, &layout_qf));
552506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionDestroy(&assembled_elem_rstr));
553506b1a0cSSebastian Grimberg   CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array));
554eaf62fffSJeremy L Thompson 
555ed9e99e6SJeremy L Thompson   // Get assembly data
556506b1a0cSSebastian Grimberg   CeedInt                  num_elem_in, elem_size_in, num_comp_in, num_qpts_in;
557506b1a0cSSebastian Grimberg   CeedInt                  num_elem_out, elem_size_out, num_comp_out, num_qpts_out, local_num_entries;
558506b1a0cSSebastian Grimberg   const CeedEvalMode     **eval_modes_in, **eval_modes_out;
559506b1a0cSSebastian Grimberg   CeedInt                  num_active_bases_in, *num_eval_modes_in, num_active_bases_out, *num_eval_modes_out;
560506b1a0cSSebastian Grimberg   CeedBasis               *active_bases_in, *active_bases_out, basis_in, basis_out;
561506b1a0cSSebastian Grimberg   const CeedScalar       **B_mats_in, **B_mats_out, *B_mat_in, *B_mat_out;
562506b1a0cSSebastian Grimberg   CeedElemRestriction      elem_rstr_in, elem_rstr_out;
563506b1a0cSSebastian Grimberg   CeedRestrictionType      elem_rstr_type_in, elem_rstr_type_out;
564506b1a0cSSebastian Grimberg   const bool              *elem_rstr_orients_in = NULL, *elem_rstr_orients_out = NULL;
565506b1a0cSSebastian Grimberg   const CeedInt8          *elem_rstr_curl_orients_in = NULL, *elem_rstr_curl_orients_out = NULL;
566506b1a0cSSebastian Grimberg   CeedOperatorAssemblyData data;
567eaf62fffSJeremy L Thompson 
568506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data));
569506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_active_bases_in, &num_eval_modes_in, &eval_modes_in, NULL, &num_active_bases_out,
570506b1a0cSSebastian Grimberg                                                 &num_eval_modes_out, &eval_modes_out, NULL, NULL));
571506b1a0cSSebastian Grimberg 
572506b1a0cSSebastian Grimberg   CeedCheck(num_active_bases_in == num_active_bases_out && num_active_bases_in == 1, ceed, CEED_ERROR_UNSUPPORTED,
573506b1a0cSSebastian Grimberg             "Cannot assemble operator with multiple active bases");
5746574a04fSJeremy L Thompson   CeedCheck(num_eval_modes_in[0] > 0 && num_eval_modes_out[0] > 0, ceed, CEED_ERROR_UNSUPPORTED, "Cannot assemble operator without inputs/outputs");
575eaf62fffSJeremy L Thompson 
576506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &active_bases_in, &B_mats_in, NULL, &active_bases_out, &B_mats_out));
577506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorGetActiveElemRestrictions(op, &elem_rstr_in, &elem_rstr_out));
578506b1a0cSSebastian Grimberg   basis_in  = active_bases_in[0];
579506b1a0cSSebastian Grimberg   basis_out = active_bases_out[0];
580506b1a0cSSebastian Grimberg   B_mat_in  = B_mats_in[0];
581506b1a0cSSebastian Grimberg   B_mat_out = B_mats_out[0];
582eaf62fffSJeremy L Thompson 
583506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumElements(elem_rstr_in, &num_elem_in));
584506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetElementSize(elem_rstr_in, &elem_size_in));
585506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumComponents(elem_rstr_in, &num_comp_in));
586506b1a0cSSebastian Grimberg   if (basis_in == CEED_BASIS_NONE) num_qpts_in = elem_size_in;
587506b1a0cSSebastian Grimberg   else CeedCall(CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts_in));
588506b1a0cSSebastian Grimberg 
589506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetType(elem_rstr_in, &elem_rstr_type_in));
590506b1a0cSSebastian Grimberg   if (elem_rstr_type_in == CEED_RESTRICTION_ORIENTED) {
591506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetOrientations(elem_rstr_in, CEED_MEM_HOST, &elem_rstr_orients_in));
592506b1a0cSSebastian Grimberg   } else if (elem_rstr_type_in == CEED_RESTRICTION_CURL_ORIENTED) {
593506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetCurlOrientations(elem_rstr_in, CEED_MEM_HOST, &elem_rstr_curl_orients_in));
5947c1dbaffSSebastian Grimberg   }
5957c1dbaffSSebastian Grimberg 
596506b1a0cSSebastian Grimberg   if (elem_rstr_in != elem_rstr_out) {
597506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetNumElements(elem_rstr_out, &num_elem_out));
598506b1a0cSSebastian Grimberg     CeedCheck(num_elem_in == num_elem_out, ceed, CEED_ERROR_UNSUPPORTED,
599506b1a0cSSebastian Grimberg               "Active input and output operator restrictions must have the same number of elements");
600506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetElementSize(elem_rstr_out, &elem_size_out));
601506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetNumComponents(elem_rstr_out, &num_comp_out));
602506b1a0cSSebastian Grimberg     if (basis_out == CEED_BASIS_NONE) num_qpts_out = elem_size_out;
603506b1a0cSSebastian Grimberg     else CeedCall(CeedBasisGetNumQuadraturePoints(basis_out, &num_qpts_out));
604506b1a0cSSebastian Grimberg     CeedCheck(num_qpts_in == num_qpts_out, ceed, CEED_ERROR_UNSUPPORTED,
605506b1a0cSSebastian Grimberg               "Active input and output bases must have the same number of quadrature points");
606eaf62fffSJeremy L Thompson 
607506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetType(elem_rstr_out, &elem_rstr_type_out));
608506b1a0cSSebastian Grimberg     if (elem_rstr_type_out == CEED_RESTRICTION_ORIENTED) {
609506b1a0cSSebastian Grimberg       CeedCall(CeedElemRestrictionGetOrientations(elem_rstr_out, CEED_MEM_HOST, &elem_rstr_orients_out));
610506b1a0cSSebastian Grimberg     } else if (elem_rstr_type_out == CEED_RESTRICTION_CURL_ORIENTED) {
611506b1a0cSSebastian Grimberg       CeedCall(CeedElemRestrictionGetCurlOrientations(elem_rstr_out, CEED_MEM_HOST, &elem_rstr_curl_orients_out));
612506b1a0cSSebastian Grimberg     }
613506b1a0cSSebastian Grimberg   } else {
614506b1a0cSSebastian Grimberg     num_elem_out  = num_elem_in;
615506b1a0cSSebastian Grimberg     elem_size_out = elem_size_in;
616506b1a0cSSebastian Grimberg     num_comp_out  = num_comp_in;
617506b1a0cSSebastian Grimberg     num_qpts_out  = num_qpts_in;
618506b1a0cSSebastian Grimberg 
619506b1a0cSSebastian Grimberg     elem_rstr_orients_out      = elem_rstr_orients_in;
620506b1a0cSSebastian Grimberg     elem_rstr_curl_orients_out = elem_rstr_curl_orients_in;
621506b1a0cSSebastian Grimberg   }
622506b1a0cSSebastian Grimberg   local_num_entries = elem_size_out * num_comp_out * elem_size_in * num_comp_in * num_elem_in;
623506b1a0cSSebastian Grimberg 
624506b1a0cSSebastian Grimberg   // Loop over elements and put in data structure
6257c1dbaffSSebastian Grimberg   // We store B_mat_in, B_mat_out, BTD, elem_mat in row-major order
6261c66c397SJeremy L Thompson   CeedSize    count = 0;
627123d890dSSebastian Grimberg   CeedScalar *vals, *BTD_mat = NULL, *elem_mat = NULL, *elem_mat_b = NULL;
628506b1a0cSSebastian Grimberg 
629123d890dSSebastian Grimberg   CeedCall(CeedCalloc(elem_size_out * num_qpts_in * num_eval_modes_in[0], &BTD_mat));
630123d890dSSebastian Grimberg   CeedCall(CeedCalloc(elem_size_out * elem_size_in, &elem_mat));
631506b1a0cSSebastian Grimberg   if (elem_rstr_curl_orients_in || elem_rstr_curl_orients_out) CeedCall(CeedCalloc(elem_size_out * elem_size_in, &elem_mat_b));
6321c66c397SJeremy L Thompson 
63328ec399dSJeremy L Thompson   CeedCall(CeedVectorGetArray(values, CEED_MEM_HOST, &vals));
634506b1a0cSSebastian Grimberg   for (CeedSize e = 0; e < num_elem_in; e++) {
635506b1a0cSSebastian Grimberg     for (CeedInt comp_in = 0; comp_in < num_comp_in; comp_in++) {
636506b1a0cSSebastian Grimberg       for (CeedInt comp_out = 0; comp_out < num_comp_out; comp_out++) {
637ed9e99e6SJeremy L Thompson         // Compute B^T*D
638506b1a0cSSebastian Grimberg         for (CeedSize n = 0; n < elem_size_out; n++) {
639506b1a0cSSebastian Grimberg           for (CeedSize q = 0; q < num_qpts_in; q++) {
640437c7c90SJeremy L Thompson             for (CeedInt e_in = 0; e_in < num_eval_modes_in[0]; e_in++) {
641506b1a0cSSebastian Grimberg               const CeedSize btd_index = n * (num_qpts_in * num_eval_modes_in[0]) + q * num_eval_modes_in[0] + e_in;
642067fd99fSJeremy L Thompson               CeedScalar     sum       = 0.0;
6431c66c397SJeremy L Thompson 
644437c7c90SJeremy L Thompson               for (CeedInt e_out = 0; e_out < num_eval_modes_out[0]; e_out++) {
645506b1a0cSSebastian Grimberg                 const CeedSize b_out_index     = (q * num_eval_modes_out[0] + e_out) * elem_size_out + n;
646506b1a0cSSebastian Grimberg                 const CeedSize eval_mode_index = ((e_in * num_comp_in + comp_in) * num_eval_modes_out[0] + e_out) * num_comp_out + comp_out;
647b94338b9SJed Brown                 const CeedSize qf_index        = q * layout_qf[0] + eval_mode_index * layout_qf[1] + e * layout_qf[2];
6481c66c397SJeremy L Thompson 
649067fd99fSJeremy L Thompson                 sum += B_mat_out[b_out_index] * assembled_qf_array[qf_index];
650eaf62fffSJeremy L Thompson               }
651067fd99fSJeremy L Thompson               BTD_mat[btd_index] = sum;
652ed9e99e6SJeremy L Thompson             }
653ed9e99e6SJeremy L Thompson           }
654eaf62fffSJeremy L Thompson         }
6557c1dbaffSSebastian Grimberg 
6567c1dbaffSSebastian Grimberg         // Form element matrix itself (for each block component)
657506b1a0cSSebastian Grimberg         CeedCall(CeedMatrixMatrixMultiply(ceed, BTD_mat, B_mat_in, elem_mat, elem_size_out, elem_size_in, num_qpts_in * num_eval_modes_in[0]));
658eaf62fffSJeremy L Thompson 
6597c1dbaffSSebastian Grimberg         // Transform the element matrix if required
660506b1a0cSSebastian Grimberg         if (elem_rstr_orients_out) {
661506b1a0cSSebastian Grimberg           const bool *elem_orients = &elem_rstr_orients_out[e * elem_size_out];
6621c66c397SJeremy L Thompson 
663506b1a0cSSebastian Grimberg           for (CeedInt i = 0; i < elem_size_out; i++) {
664506b1a0cSSebastian Grimberg             const double orient = elem_orients[i] ? -1.0 : 1.0;
665506b1a0cSSebastian Grimberg 
666506b1a0cSSebastian Grimberg             for (CeedInt j = 0; j < elem_size_in; j++) {
667506b1a0cSSebastian Grimberg               elem_mat[i * elem_size_in + j] *= orient;
6687c1dbaffSSebastian Grimberg             }
6697c1dbaffSSebastian Grimberg           }
670506b1a0cSSebastian Grimberg         } else if (elem_rstr_curl_orients_out) {
671506b1a0cSSebastian Grimberg           const CeedInt8 *elem_curl_orients = &elem_rstr_curl_orients_out[e * 3 * elem_size_out];
6721c66c397SJeremy L Thompson 
6737c1dbaffSSebastian Grimberg           // T^T*(B^T*D*B)
674506b1a0cSSebastian Grimberg           memcpy(elem_mat_b, elem_mat, elem_size_out * elem_size_in * sizeof(CeedScalar));
675506b1a0cSSebastian Grimberg           for (CeedInt i = 0; i < elem_size_out; i++) {
676506b1a0cSSebastian Grimberg             for (CeedInt j = 0; j < elem_size_in; j++) {
677506b1a0cSSebastian Grimberg               elem_mat[i * elem_size_in + j] = elem_mat_b[i * elem_size_in + j] * elem_curl_orients[3 * i + 1] +
678506b1a0cSSebastian Grimberg                                                (i > 0 ? elem_mat_b[(i - 1) * elem_size_in + j] * elem_curl_orients[3 * i - 1] : 0.0) +
679506b1a0cSSebastian Grimberg                                                (i < elem_size_out - 1 ? elem_mat_b[(i + 1) * elem_size_in + j] * elem_curl_orients[3 * i + 3] : 0.0);
6807c1dbaffSSebastian Grimberg             }
6817c1dbaffSSebastian Grimberg           }
682506b1a0cSSebastian Grimberg         }
683506b1a0cSSebastian Grimberg         if (elem_rstr_orients_in) {
684506b1a0cSSebastian Grimberg           const bool *elem_orients = &elem_rstr_orients_in[e * elem_size_in];
685506b1a0cSSebastian Grimberg 
686506b1a0cSSebastian Grimberg           for (CeedInt i = 0; i < elem_size_out; i++) {
687506b1a0cSSebastian Grimberg             for (CeedInt j = 0; j < elem_size_in; j++) {
688506b1a0cSSebastian Grimberg               elem_mat[i * elem_size_in + j] *= elem_orients[j] ? -1.0 : 1.0;
689506b1a0cSSebastian Grimberg             }
690506b1a0cSSebastian Grimberg           }
691506b1a0cSSebastian Grimberg         } else if (elem_rstr_curl_orients_in) {
692506b1a0cSSebastian Grimberg           const CeedInt8 *elem_curl_orients = &elem_rstr_curl_orients_in[e * 3 * elem_size_in];
693506b1a0cSSebastian Grimberg 
694506b1a0cSSebastian Grimberg           // (B^T*D*B)*T
695506b1a0cSSebastian Grimberg           memcpy(elem_mat_b, elem_mat, elem_size_out * elem_size_in * sizeof(CeedScalar));
696506b1a0cSSebastian Grimberg           for (CeedInt i = 0; i < elem_size_out; i++) {
697506b1a0cSSebastian Grimberg             for (CeedInt j = 0; j < elem_size_in; j++) {
698506b1a0cSSebastian Grimberg               elem_mat[i * elem_size_in + j] = elem_mat_b[i * elem_size_in + j] * elem_curl_orients[3 * j + 1] +
699506b1a0cSSebastian Grimberg                                                (j > 0 ? elem_mat_b[i * elem_size_in + j - 1] * elem_curl_orients[3 * j - 1] : 0.0) +
700506b1a0cSSebastian Grimberg                                                (j < elem_size_in - 1 ? elem_mat_b[i * elem_size_in + j + 1] * elem_curl_orients[3 * j + 3] : 0.0);
7017c1dbaffSSebastian Grimberg             }
7027c1dbaffSSebastian Grimberg           }
7037c1dbaffSSebastian Grimberg         }
7047c1dbaffSSebastian Grimberg 
7057c1dbaffSSebastian Grimberg         // Put element matrix in coordinate data structure
706506b1a0cSSebastian Grimberg         for (CeedInt i = 0; i < elem_size_out; i++) {
707506b1a0cSSebastian Grimberg           for (CeedInt j = 0; j < elem_size_in; j++) {
708506b1a0cSSebastian Grimberg             vals[offset + count] = elem_mat[i * elem_size_in + j];
709eaf62fffSJeremy L Thompson             count++;
710eaf62fffSJeremy L Thompson           }
711eaf62fffSJeremy L Thompson         }
712eaf62fffSJeremy L Thompson       }
713eaf62fffSJeremy L Thompson     }
714eaf62fffSJeremy L Thompson   }
7156574a04fSJeremy L Thompson   CeedCheck(count == local_num_entries, ceed, CEED_ERROR_MAJOR, "Error computing entries");
7162b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArray(values, &vals));
717eaf62fffSJeremy L Thompson 
718506b1a0cSSebastian Grimberg   // Cleanup
719123d890dSSebastian Grimberg   CeedCall(CeedFree(&BTD_mat));
720123d890dSSebastian Grimberg   CeedCall(CeedFree(&elem_mat));
721506b1a0cSSebastian Grimberg   CeedCall(CeedFree(&elem_mat_b));
722506b1a0cSSebastian Grimberg   if (elem_rstr_type_in == CEED_RESTRICTION_ORIENTED) {
723506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionRestoreOrientations(elem_rstr_in, &elem_rstr_orients_in));
724506b1a0cSSebastian Grimberg   } else if (elem_rstr_type_in == CEED_RESTRICTION_CURL_ORIENTED) {
725506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionRestoreCurlOrientations(elem_rstr_in, &elem_rstr_curl_orients_in));
726506b1a0cSSebastian Grimberg   }
727506b1a0cSSebastian Grimberg   if (elem_rstr_in != elem_rstr_out) {
728506b1a0cSSebastian Grimberg     if (elem_rstr_type_out == CEED_RESTRICTION_ORIENTED) {
729506b1a0cSSebastian Grimberg       CeedCall(CeedElemRestrictionRestoreOrientations(elem_rstr_out, &elem_rstr_orients_out));
730506b1a0cSSebastian Grimberg     } else if (elem_rstr_type_out == CEED_RESTRICTION_CURL_ORIENTED) {
731506b1a0cSSebastian Grimberg       CeedCall(CeedElemRestrictionRestoreCurlOrientations(elem_rstr_out, &elem_rstr_curl_orients_out));
732506b1a0cSSebastian Grimberg     }
733506b1a0cSSebastian Grimberg   }
7342b730f8bSJeremy L Thompson   CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array));
7352b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&assembled_qf));
736eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
737eaf62fffSJeremy L Thompson }
738eaf62fffSJeremy L Thompson 
739eaf62fffSJeremy L Thompson /**
740eaf62fffSJeremy L Thompson   @brief Count number of entries for assembled CeedOperator
741eaf62fffSJeremy L Thompson 
742eaf62fffSJeremy L Thompson   @param[in]  op          CeedOperator to assemble
743eaf62fffSJeremy L Thompson   @param[out] num_entries Number of entries in assembled representation
744eaf62fffSJeremy L Thompson 
745eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
746eaf62fffSJeremy L Thompson 
747eaf62fffSJeremy L Thompson   @ref Utility
748eaf62fffSJeremy L Thompson **/
749b94338b9SJed Brown static int CeedSingleOperatorAssemblyCountEntries(CeedOperator op, CeedSize *num_entries) {
750b275c451SJeremy L Thompson   bool                is_composite;
751506b1a0cSSebastian Grimberg   CeedInt             num_elem_in, elem_size_in, num_comp_in, num_elem_out, elem_size_out, num_comp_out;
752506b1a0cSSebastian Grimberg   CeedElemRestriction rstr_in, rstr_out;
753eaf62fffSJeremy L Thompson 
754b275c451SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
7556574a04fSJeremy L Thompson   CeedCheck(!is_composite, op->ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported");
756506b1a0cSSebastian Grimberg 
757506b1a0cSSebastian Grimberg   CeedCall(CeedOperatorGetActiveElemRestrictions(op, &rstr_in, &rstr_out));
758506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumElements(rstr_in, &num_elem_in));
759506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetElementSize(rstr_in, &elem_size_in));
760506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumComponents(rstr_in, &num_comp_in));
761506b1a0cSSebastian Grimberg   if (rstr_in != rstr_out) {
762506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetNumElements(rstr_out, &num_elem_out));
763506b1a0cSSebastian Grimberg     CeedCheck(num_elem_in == num_elem_out, op->ceed, CEED_ERROR_UNSUPPORTED,
764506b1a0cSSebastian Grimberg               "Active input and output operator restrictions must have the same number of elements");
765506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetElementSize(rstr_out, &elem_size_out));
766506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetNumComponents(rstr_out, &num_comp_out));
767506b1a0cSSebastian Grimberg   } else {
768506b1a0cSSebastian Grimberg     num_elem_out  = num_elem_in;
769506b1a0cSSebastian Grimberg     elem_size_out = elem_size_in;
770506b1a0cSSebastian Grimberg     num_comp_out  = num_comp_in;
771506b1a0cSSebastian Grimberg   }
772506b1a0cSSebastian Grimberg   *num_entries = (CeedSize)elem_size_in * num_comp_in * elem_size_out * num_comp_out * num_elem_in;
773eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
774eaf62fffSJeremy L Thompson }
775eaf62fffSJeremy L Thompson 
776eaf62fffSJeremy L Thompson /**
777ea61e9acSJeremy L Thompson   @brief Common code for creating a multigrid coarse operator and level transfer operators for a CeedOperator
778eaf62fffSJeremy L Thompson 
779eaf62fffSJeremy L Thompson   @param[in]  op_fine      Fine grid operator
78085bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine  L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
781eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse  Coarse grid restriction
782eaf62fffSJeremy L Thompson   @param[in]  basis_coarse Coarse grid active vector basis
78385bb9dcfSJeremy L Thompson   @param[in]  basis_c_to_f Basis for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators
784eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
78585bb9dcfSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator, or NULL
7867758292fSSebastian Grimberg   @param[out] op_restrict  Fine to coarse operator, or NULL
787eaf62fffSJeremy L Thompson 
788eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
789eaf62fffSJeremy L Thompson 
790eaf62fffSJeremy L Thompson   @ref Developer
791eaf62fffSJeremy L Thompson **/
7922b730f8bSJeremy L Thompson static int CeedSingleOperatorMultigridLevel(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
7937758292fSSebastian Grimberg                                             CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_restrict) {
7941c66c397SJeremy L Thompson   bool                is_composite;
795eaf62fffSJeremy L Thompson   Ceed                ceed;
7961c66c397SJeremy L Thompson   CeedInt             num_comp;
79785bb9dcfSJeremy L Thompson   CeedVector          mult_vec         = NULL;
7981c66c397SJeremy L Thompson   CeedElemRestriction rstr_p_mult_fine = NULL, rstr_fine = NULL;
7991c66c397SJeremy L Thompson 
8002b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
801eaf62fffSJeremy L Thompson 
802eaf62fffSJeremy L Thompson   // Check for composite operator
8032b730f8bSJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op_fine, &is_composite));
8046574a04fSJeremy L Thompson   CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Automatic multigrid setup for composite operators not supported");
805eaf62fffSJeremy L Thompson 
806eaf62fffSJeremy L Thompson   // Coarse Grid
8072b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT, op_coarse));
808eaf62fffSJeremy L Thompson   // -- Clone input fields
80992ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_input_fields; i++) {
810eaf62fffSJeremy L Thompson     if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
811437c7c90SJeremy L Thompson       rstr_fine = op_fine->input_fields[i]->elem_rstr;
8122b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE));
813eaf62fffSJeremy L Thompson     } else {
814437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, op_fine->input_fields[i]->elem_rstr,
8152b730f8bSJeremy L Thompson                                     op_fine->input_fields[i]->basis, op_fine->input_fields[i]->vec));
816eaf62fffSJeremy L Thompson     }
817eaf62fffSJeremy L Thompson   }
818eaf62fffSJeremy L Thompson   // -- Clone output fields
81992ae7e47SJeremy L Thompson   for (CeedInt i = 0; i < op_fine->qf->num_output_fields; i++) {
820eaf62fffSJeremy L Thompson     if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
8212b730f8bSJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE));
822eaf62fffSJeremy L Thompson     } else {
823437c7c90SJeremy L Thompson       CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, op_fine->output_fields[i]->elem_rstr,
8242b730f8bSJeremy L Thompson                                     op_fine->output_fields[i]->basis, op_fine->output_fields[i]->vec));
825eaf62fffSJeremy L Thompson     }
826eaf62fffSJeremy L Thompson   }
827af99e877SJeremy L Thompson   // -- Clone QFunctionAssemblyData
8282b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op_fine->qf_assembled, &(*op_coarse)->qf_assembled));
829eaf62fffSJeremy L Thompson 
830eaf62fffSJeremy L Thompson   // Multiplicity vector
8317758292fSSebastian Grimberg   if (op_restrict || op_prolong) {
83285bb9dcfSJeremy L Thompson     CeedVector          mult_e_vec;
8331c66c397SJeremy L Thompson     CeedRestrictionType rstr_type;
83485bb9dcfSJeremy L Thompson 
8357c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionGetType(rstr_fine, &rstr_type));
8367c1dbaffSSebastian Grimberg     CeedCheck(rstr_type != CEED_RESTRICTION_CURL_ORIENTED, ceed, CEED_ERROR_UNSUPPORTED,
8377c1dbaffSSebastian Grimberg               "Element restrictions created with CeedElemRestrictionCreateCurlOriented are not supported");
8386574a04fSJeremy L Thompson     CeedCheck(p_mult_fine, ceed, CEED_ERROR_INCOMPATIBLE, "Prolongation or restriction operator creation requires fine grid multiplicity vector");
8397c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionCreateUnsignedCopy(rstr_fine, &rstr_p_mult_fine));
8402b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec));
8412b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(mult_e_vec, 0.0));
842c17ec2beSJeremy L Thompson     CeedCall(CeedElemRestrictionApply(rstr_p_mult_fine, CEED_NOTRANSPOSE, p_mult_fine, mult_e_vec, CEED_REQUEST_IMMEDIATE));
8432b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(mult_vec, 0.0));
844c17ec2beSJeremy L Thompson     CeedCall(CeedElemRestrictionApply(rstr_p_mult_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec, CEED_REQUEST_IMMEDIATE));
8452b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&mult_e_vec));
8462b730f8bSJeremy L Thompson     CeedCall(CeedVectorReciprocal(mult_vec));
84785bb9dcfSJeremy L Thompson   }
848eaf62fffSJeremy L Thompson 
849addd79feSZach Atkins   // Clone name
850addd79feSZach Atkins   bool   has_name = op_fine->name;
851addd79feSZach Atkins   size_t name_len = op_fine->name ? strlen(op_fine->name) : 0;
852addd79feSZach Atkins   CeedCall(CeedOperatorSetName(*op_coarse, op_fine->name));
853addd79feSZach Atkins 
8547758292fSSebastian Grimberg   // Check that coarse to fine basis is provided if prolong/restrict operators are requested
8557758292fSSebastian Grimberg   CeedCheck(basis_c_to_f || (!op_restrict && !op_prolong), ceed, CEED_ERROR_INCOMPATIBLE,
8566574a04fSJeremy L Thompson             "Prolongation or restriction operator creation requires coarse-to-fine basis");
85783d6adf3SZach Atkins 
85885bb9dcfSJeremy L Thompson   // Restriction/Prolongation Operators
8592b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis_coarse, &num_comp));
860addd79feSZach Atkins 
861addd79feSZach Atkins   // Restriction
8627758292fSSebastian Grimberg   if (op_restrict) {
863eaf62fffSJeremy L Thompson     CeedInt             *num_comp_r_data;
86485bb9dcfSJeremy L Thompson     CeedQFunctionContext ctx_r;
8657758292fSSebastian Grimberg     CeedQFunction        qf_restrict;
86685bb9dcfSJeremy L Thompson 
8677758292fSSebastian Grimberg     CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_restrict));
8682b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &num_comp_r_data));
869eaf62fffSJeremy L Thompson     num_comp_r_data[0] = num_comp;
8702b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextCreate(ceed, &ctx_r));
8712b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_r_data), num_comp_r_data));
8727758292fSSebastian Grimberg     CeedCall(CeedQFunctionSetContext(qf_restrict, ctx_r));
8732b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextDestroy(&ctx_r));
8747758292fSSebastian Grimberg     CeedCall(CeedQFunctionAddInput(qf_restrict, "input", num_comp, CEED_EVAL_NONE));
8757758292fSSebastian Grimberg     CeedCall(CeedQFunctionAddInput(qf_restrict, "scale", num_comp, CEED_EVAL_NONE));
8767758292fSSebastian Grimberg     CeedCall(CeedQFunctionAddOutput(qf_restrict, "output", num_comp, CEED_EVAL_INTERP));
8777758292fSSebastian Grimberg     CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_restrict, num_comp));
878eaf62fffSJeremy L Thompson 
8797758292fSSebastian Grimberg     CeedCall(CeedOperatorCreate(ceed, qf_restrict, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_restrict));
8807758292fSSebastian Grimberg     CeedCall(CeedOperatorSetField(*op_restrict, "input", rstr_fine, CEED_BASIS_NONE, CEED_VECTOR_ACTIVE));
8817758292fSSebastian Grimberg     CeedCall(CeedOperatorSetField(*op_restrict, "scale", rstr_p_mult_fine, CEED_BASIS_NONE, mult_vec));
8827758292fSSebastian Grimberg     CeedCall(CeedOperatorSetField(*op_restrict, "output", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE));
883eaf62fffSJeremy L Thompson 
884addd79feSZach Atkins     // Set name
885addd79feSZach Atkins     char *restriction_name;
8861c66c397SJeremy L Thompson 
887addd79feSZach Atkins     CeedCall(CeedCalloc(17 + name_len, &restriction_name));
888addd79feSZach Atkins     sprintf(restriction_name, "restriction%s%s", has_name ? " for " : "", has_name ? op_fine->name : "");
8897758292fSSebastian Grimberg     CeedCall(CeedOperatorSetName(*op_restrict, restriction_name));
890addd79feSZach Atkins     CeedCall(CeedFree(&restriction_name));
891addd79feSZach Atkins 
892addd79feSZach Atkins     // Check
8937758292fSSebastian Grimberg     CeedCall(CeedOperatorCheckReady(*op_restrict));
894addd79feSZach Atkins 
895addd79feSZach Atkins     // Cleanup
8967758292fSSebastian Grimberg     CeedCall(CeedQFunctionDestroy(&qf_restrict));
897addd79feSZach Atkins   }
898addd79feSZach Atkins 
899eaf62fffSJeremy L Thompson   // Prolongation
900addd79feSZach Atkins   if (op_prolong) {
901eaf62fffSJeremy L Thompson     CeedInt             *num_comp_p_data;
90285bb9dcfSJeremy L Thompson     CeedQFunctionContext ctx_p;
9031c66c397SJeremy L Thompson     CeedQFunction        qf_prolong;
90485bb9dcfSJeremy L Thompson 
90585bb9dcfSJeremy L Thompson     CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong));
9062b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &num_comp_p_data));
907eaf62fffSJeremy L Thompson     num_comp_p_data[0] = num_comp;
9082b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextCreate(ceed, &ctx_p));
9092b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_p_data), num_comp_p_data));
9102b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetContext(qf_prolong, ctx_p));
9112b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextDestroy(&ctx_p));
9122b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP));
9132b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE));
9142b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE));
9152b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_prolong, num_comp));
916eaf62fffSJeremy L Thompson 
9172b730f8bSJeremy L Thompson     CeedCall(CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_prolong));
9182b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE));
919356036faSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_prolong, "scale", rstr_p_mult_fine, CEED_BASIS_NONE, mult_vec));
920356036faSJeremy L Thompson     CeedCall(CeedOperatorSetField(*op_prolong, "output", rstr_fine, CEED_BASIS_NONE, CEED_VECTOR_ACTIVE));
921eaf62fffSJeremy L Thompson 
922addd79feSZach Atkins     // Set name
923ea6b5821SJeremy L Thompson     char *prolongation_name;
9241c66c397SJeremy L Thompson 
9252b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(18 + name_len, &prolongation_name));
9262b730f8bSJeremy L Thompson     sprintf(prolongation_name, "prolongation%s%s", has_name ? " for " : "", has_name ? op_fine->name : "");
9272b730f8bSJeremy L Thompson     CeedCall(CeedOperatorSetName(*op_prolong, prolongation_name));
9282b730f8bSJeremy L Thompson     CeedCall(CeedFree(&prolongation_name));
929addd79feSZach Atkins 
930addd79feSZach Atkins     // Check
931addd79feSZach Atkins     CeedCall(CeedOperatorCheckReady(*op_prolong));
932addd79feSZach Atkins 
933addd79feSZach Atkins     // Cleanup
934addd79feSZach Atkins     CeedCall(CeedQFunctionDestroy(&qf_prolong));
935ea6b5821SJeremy L Thompson   }
936ea6b5821SJeremy L Thompson 
93758e4b056SJeremy L Thompson   // Check
93858e4b056SJeremy L Thompson   CeedCall(CeedOperatorCheckReady(*op_coarse));
93958e4b056SJeremy L Thompson 
940eaf62fffSJeremy L Thompson   // Cleanup
9412b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&mult_vec));
942c17ec2beSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_p_mult_fine));
9432b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&basis_c_to_f));
944eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
945eaf62fffSJeremy L Thompson }
946eaf62fffSJeremy L Thompson 
947eaf62fffSJeremy L Thompson /**
948eaf62fffSJeremy L Thompson   @brief Build 1D mass matrix and Laplacian with perturbation
949eaf62fffSJeremy L Thompson 
950eaf62fffSJeremy L Thompson   @param[in]  interp_1d   Interpolation matrix in one dimension
951eaf62fffSJeremy L Thompson   @param[in]  grad_1d     Gradient matrix in one dimension
952eaf62fffSJeremy L Thompson   @param[in]  q_weight_1d Quadrature weights in one dimension
953eaf62fffSJeremy L Thompson   @param[in]  P_1d        Number of basis nodes in one dimension
954eaf62fffSJeremy L Thompson   @param[in]  Q_1d        Number of quadrature points in one dimension
955eaf62fffSJeremy L Thompson   @param[in]  dim         Dimension of basis
956eaf62fffSJeremy L Thompson   @param[out] mass        Assembled mass matrix in one dimension
957eaf62fffSJeremy L Thompson   @param[out] laplace     Assembled perturbed Laplacian in one dimension
958eaf62fffSJeremy L Thompson 
959eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
960eaf62fffSJeremy L Thompson 
961eaf62fffSJeremy L Thompson   @ref Developer
962eaf62fffSJeremy L Thompson **/
9632c2ea1dbSJeremy L Thompson CeedPragmaOptimizeOff
9642c2ea1dbSJeremy L Thompson static int CeedBuildMassLaplace(const CeedScalar *interp_1d, const CeedScalar *grad_1d, const CeedScalar *q_weight_1d, CeedInt P_1d, CeedInt Q_1d,
9652c2ea1dbSJeremy L Thompson                                 CeedInt dim, CeedScalar *mass, CeedScalar *laplace) {
9662b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
967eaf62fffSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) {
968eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
9692b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < Q_1d; k++) sum += interp_1d[k * P_1d + i] * q_weight_1d[k] * interp_1d[k * P_1d + j];
970eaf62fffSJeremy L Thompson       mass[i + j * P_1d] = sum;
971eaf62fffSJeremy L Thompson     }
9722b730f8bSJeremy L Thompson   }
973eaf62fffSJeremy L Thompson   // -- Laplacian
9742b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
975eaf62fffSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) {
976eaf62fffSJeremy L Thompson       CeedScalar sum = 0.0;
9771c66c397SJeremy L Thompson 
9782b730f8bSJeremy L Thompson       for (CeedInt k = 0; k < Q_1d; k++) sum += grad_1d[k * P_1d + i] * q_weight_1d[k] * grad_1d[k * P_1d + j];
979eaf62fffSJeremy L Thompson       laplace[i + j * P_1d] = sum;
980eaf62fffSJeremy L Thompson     }
9812b730f8bSJeremy L Thompson   }
982eaf62fffSJeremy L Thompson   CeedScalar perturbation = dim > 2 ? 1e-6 : 1e-4;
9832b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) laplace[i + P_1d * i] += perturbation;
984eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
985eaf62fffSJeremy L Thompson }
9862c2ea1dbSJeremy L Thompson CeedPragmaOptimizeOn
987eaf62fffSJeremy L Thompson 
988eaf62fffSJeremy L Thompson /// @}
989eaf62fffSJeremy L Thompson 
990eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
991480fae85SJeremy L Thompson /// CeedOperator Backend API
992480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
993480fae85SJeremy L Thompson /// @addtogroup CeedOperatorBackend
994480fae85SJeremy L Thompson /// @{
995480fae85SJeremy L Thompson 
996480fae85SJeremy L Thompson /**
997506b1a0cSSebastian Grimberg   @brief Create point block restriction for active operator field
998506b1a0cSSebastian Grimberg 
999506b1a0cSSebastian Grimberg   @param[in]  rstr             Original CeedElemRestriction for active field
1000506b1a0cSSebastian Grimberg   @param[out] point_block_rstr Address of the variable where the newly created CeedElemRestriction will be stored
1001506b1a0cSSebastian Grimberg 
1002506b1a0cSSebastian Grimberg   @return An error code: 0 - success, otherwise - failure
1003506b1a0cSSebastian Grimberg 
1004506b1a0cSSebastian Grimberg   @ref Backend
1005506b1a0cSSebastian Grimberg **/
1006506b1a0cSSebastian Grimberg int CeedOperatorCreateActivePointBlockRestriction(CeedElemRestriction rstr, CeedElemRestriction *point_block_rstr) {
1007506b1a0cSSebastian Grimberg   Ceed           ceed;
1008506b1a0cSSebastian Grimberg   CeedInt        num_elem, num_comp, shift, elem_size, comp_stride, *point_block_offsets;
1009506b1a0cSSebastian Grimberg   CeedSize       l_size;
1010506b1a0cSSebastian Grimberg   const CeedInt *offsets;
1011506b1a0cSSebastian Grimberg 
1012506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed));
1013506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets));
1014506b1a0cSSebastian Grimberg 
1015506b1a0cSSebastian Grimberg   // Expand offsets
1016506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
1017506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp));
1018506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size));
1019506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetCompStride(rstr, &comp_stride));
1020506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
1021506b1a0cSSebastian Grimberg   shift = num_comp;
1022506b1a0cSSebastian Grimberg   if (comp_stride != 1) shift *= num_comp;
1023506b1a0cSSebastian Grimberg   CeedCall(CeedCalloc(num_elem * elem_size, &point_block_offsets));
1024506b1a0cSSebastian Grimberg   for (CeedInt i = 0; i < num_elem * elem_size; i++) {
1025506b1a0cSSebastian Grimberg     point_block_offsets[i] = offsets[i] * shift;
1026506b1a0cSSebastian Grimberg   }
1027506b1a0cSSebastian Grimberg 
1028506b1a0cSSebastian Grimberg   // Create new restriction
1029506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp * num_comp, 1, l_size * num_comp, CEED_MEM_HOST, CEED_OWN_POINTER,
1030506b1a0cSSebastian Grimberg                                      point_block_offsets, point_block_rstr));
1031506b1a0cSSebastian Grimberg 
1032506b1a0cSSebastian Grimberg   // Cleanup
1033506b1a0cSSebastian Grimberg   CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets));
1034506b1a0cSSebastian Grimberg   return CEED_ERROR_SUCCESS;
1035506b1a0cSSebastian Grimberg }
1036506b1a0cSSebastian Grimberg 
1037506b1a0cSSebastian Grimberg /**
1038480fae85SJeremy L Thompson   @brief Create object holding CeedQFunction assembly data for CeedOperator
1039480fae85SJeremy L Thompson 
1040480fae85SJeremy L Thompson   @param[in]  ceed A Ceed object where the CeedQFunctionAssemblyData will be created
1041ea61e9acSJeremy L Thompson   @param[out] data Address of the variable where the newly created CeedQFunctionAssemblyData will be stored
1042480fae85SJeremy L Thompson 
1043480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1044480fae85SJeremy L Thompson 
1045480fae85SJeremy L Thompson   @ref Backend
1046480fae85SJeremy L Thompson **/
1047ea61e9acSJeremy L Thompson int CeedQFunctionAssemblyDataCreate(Ceed ceed, CeedQFunctionAssemblyData *data) {
10482b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, data));
1049480fae85SJeremy L Thompson   (*data)->ref_count = 1;
1050480fae85SJeremy L Thompson   (*data)->ceed      = ceed;
10512b730f8bSJeremy L Thompson   CeedCall(CeedReference(ceed));
1052480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1053480fae85SJeremy L Thompson }
1054480fae85SJeremy L Thompson 
1055480fae85SJeremy L Thompson /**
1056480fae85SJeremy L Thompson   @brief Increment the reference counter for a CeedQFunctionAssemblyData
1057480fae85SJeremy L Thompson 
1058ea61e9acSJeremy L Thompson   @param[in,out] data CeedQFunctionAssemblyData to increment the reference counter
1059480fae85SJeremy L Thompson 
1060480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1061480fae85SJeremy L Thompson 
1062480fae85SJeremy L Thompson   @ref Backend
1063480fae85SJeremy L Thompson **/
1064480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataReference(CeedQFunctionAssemblyData data) {
1065480fae85SJeremy L Thompson   data->ref_count++;
1066480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1067480fae85SJeremy L Thompson }
1068480fae85SJeremy L Thompson 
1069480fae85SJeremy L Thompson /**
1070beecbf24SJeremy L Thompson   @brief Set re-use of CeedQFunctionAssemblyData
10718b919e6bSJeremy L Thompson 
1072ea61e9acSJeremy L Thompson   @param[in,out] data       CeedQFunctionAssemblyData to mark for reuse
1073ea61e9acSJeremy L Thompson   @param[in]     reuse_data Boolean flag indicating data re-use
10748b919e6bSJeremy L Thompson 
10758b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
10768b919e6bSJeremy L Thompson 
10778b919e6bSJeremy L Thompson   @ref Backend
10788b919e6bSJeremy L Thompson **/
10792b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetReuse(CeedQFunctionAssemblyData data, bool reuse_data) {
1080beecbf24SJeremy L Thompson   data->reuse_data        = reuse_data;
1081beecbf24SJeremy L Thompson   data->needs_data_update = true;
1082beecbf24SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1083beecbf24SJeremy L Thompson }
1084beecbf24SJeremy L Thompson 
1085beecbf24SJeremy L Thompson /**
1086beecbf24SJeremy L Thompson   @brief Mark QFunctionAssemblyData as stale
1087beecbf24SJeremy L Thompson 
1088ea61e9acSJeremy L Thompson   @param[in,out] data              CeedQFunctionAssemblyData to mark as stale
1089ea61e9acSJeremy L Thompson   @param[in]     needs_data_update Boolean flag indicating if update is needed or completed
1090beecbf24SJeremy L Thompson 
1091beecbf24SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1092beecbf24SJeremy L Thompson 
1093beecbf24SJeremy L Thompson   @ref Backend
1094beecbf24SJeremy L Thompson **/
10952b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetUpdateNeeded(CeedQFunctionAssemblyData data, bool needs_data_update) {
1096beecbf24SJeremy L Thompson   data->needs_data_update = needs_data_update;
10978b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
10988b919e6bSJeremy L Thompson }
10998b919e6bSJeremy L Thompson 
11008b919e6bSJeremy L Thompson /**
11018b919e6bSJeremy L Thompson   @brief Determine if QFunctionAssemblyData needs update
11028b919e6bSJeremy L Thompson 
11038b919e6bSJeremy L Thompson   @param[in]  data             CeedQFunctionAssemblyData to mark as stale
11048b919e6bSJeremy L Thompson   @param[out] is_update_needed Boolean flag indicating if re-assembly is required
11058b919e6bSJeremy L Thompson 
11068b919e6bSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
11078b919e6bSJeremy L Thompson 
11088b919e6bSJeremy L Thompson   @ref Backend
11098b919e6bSJeremy L Thompson **/
11102b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataIsUpdateNeeded(CeedQFunctionAssemblyData data, bool *is_update_needed) {
1111beecbf24SJeremy L Thompson   *is_update_needed = !data->reuse_data || data->needs_data_update;
11128b919e6bSJeremy L Thompson   return CEED_ERROR_SUCCESS;
11138b919e6bSJeremy L Thompson }
11148b919e6bSJeremy L Thompson 
11158b919e6bSJeremy L Thompson /**
1116ea61e9acSJeremy L Thompson   @brief Copy the pointer to a CeedQFunctionAssemblyData.
11174385fb7fSSebastian Grimberg 
1118ea61e9acSJeremy L Thompson   Both pointers should be destroyed with `CeedCeedQFunctionAssemblyDataDestroy()`.
1119512bb800SJeremy L Thompson 
1120512bb800SJeremy L Thompson   Note: If the value of `data_copy` passed to this function is non-NULL, then it is assumed that `*data_copy` is a pointer to a
1121512bb800SJeremy L Thompson         CeedQFunctionAssemblyData. This CeedQFunctionAssemblyData will be destroyed if `data_copy` is the only reference to this
1122512bb800SJeremy L Thompson         CeedQFunctionAssemblyData.
1123480fae85SJeremy L Thompson 
1124ea61e9acSJeremy L Thompson   @param[in]     data      CeedQFunctionAssemblyData to copy reference to
1125ea61e9acSJeremy L Thompson   @param[in,out] data_copy Variable to store copied reference
1126480fae85SJeremy L Thompson 
1127480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1128480fae85SJeremy L Thompson 
1129480fae85SJeremy L Thompson   @ref Backend
1130480fae85SJeremy L Thompson **/
11312b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataReferenceCopy(CeedQFunctionAssemblyData data, CeedQFunctionAssemblyData *data_copy) {
11322b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataReference(data));
11332b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAssemblyDataDestroy(data_copy));
1134480fae85SJeremy L Thompson   *data_copy = data;
1135480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1136480fae85SJeremy L Thompson }
1137480fae85SJeremy L Thompson 
1138480fae85SJeremy L Thompson /**
1139480fae85SJeremy L Thompson   @brief Get setup status for internal objects for CeedQFunctionAssemblyData
1140480fae85SJeremy L Thompson 
1141ea61e9acSJeremy L Thompson   @param[in]  data     CeedQFunctionAssemblyData to retrieve status
1142480fae85SJeremy L Thompson   @param[out] is_setup Boolean flag for setup status
1143480fae85SJeremy L Thompson 
1144480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1145480fae85SJeremy L Thompson 
1146480fae85SJeremy L Thompson   @ref Backend
1147480fae85SJeremy L Thompson **/
11482b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataIsSetup(CeedQFunctionAssemblyData data, bool *is_setup) {
1149480fae85SJeremy L Thompson   *is_setup = data->is_setup;
1150480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1151480fae85SJeremy L Thompson }
1152480fae85SJeremy L Thompson 
1153480fae85SJeremy L Thompson /**
1154480fae85SJeremy L Thompson   @brief Set internal objects for CeedQFunctionAssemblyData
1155480fae85SJeremy L Thompson 
1156ea61e9acSJeremy L Thompson   @param[in,out] data CeedQFunctionAssemblyData to set objects
1157480fae85SJeremy L Thompson   @param[in]     vec  CeedVector to store assembled CeedQFunction at quadrature points
1158480fae85SJeremy L Thompson   @param[in]     rstr CeedElemRestriction for CeedVector containing assembled CeedQFunction
1159480fae85SJeremy L Thompson 
1160480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1161480fae85SJeremy L Thompson 
1162480fae85SJeremy L Thompson   @ref Backend
1163480fae85SJeremy L Thompson **/
11642b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataSetObjects(CeedQFunctionAssemblyData data, CeedVector vec, CeedElemRestriction rstr) {
11652b730f8bSJeremy L Thompson   CeedCall(CeedVectorReferenceCopy(vec, &data->vec));
11662b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionReferenceCopy(rstr, &data->rstr));
1167480fae85SJeremy L Thompson 
1168480fae85SJeremy L Thompson   data->is_setup = true;
1169480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1170480fae85SJeremy L Thompson }
1171480fae85SJeremy L Thompson 
1172*4dd1a9d2SSebastian Grimberg /**
1173*4dd1a9d2SSebastian Grimberg   @brief Get internal objects for CeedQFunctionAssemblyData
1174*4dd1a9d2SSebastian Grimberg 
1175*4dd1a9d2SSebastian Grimberg   @param[in,out] data CeedQFunctionAssemblyData to set objects
1176*4dd1a9d2SSebastian Grimberg   @param[out]    vec  CeedVector to store assembled CeedQFunction at quadrature points
1177*4dd1a9d2SSebastian Grimberg   @param[out]    rstr CeedElemRestriction for CeedVector containing assembled CeedQFunction
1178*4dd1a9d2SSebastian Grimberg 
1179*4dd1a9d2SSebastian Grimberg   @return An error code: 0 - success, otherwise - failure
1180*4dd1a9d2SSebastian Grimberg 
1181*4dd1a9d2SSebastian Grimberg   @ref Backend
1182*4dd1a9d2SSebastian Grimberg **/
11832b730f8bSJeremy L Thompson int CeedQFunctionAssemblyDataGetObjects(CeedQFunctionAssemblyData data, CeedVector *vec, CeedElemRestriction *rstr) {
11846574a04fSJeremy L Thompson   CeedCheck(data->is_setup, data->ceed, CEED_ERROR_INCOMPLETE, "Internal objects not set; must call CeedQFunctionAssemblyDataSetObjects first.");
1185480fae85SJeremy L Thompson 
11862b730f8bSJeremy L Thompson   CeedCall(CeedVectorReferenceCopy(data->vec, vec));
11872b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionReferenceCopy(data->rstr, rstr));
1188480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1189480fae85SJeremy L Thompson }
1190480fae85SJeremy L Thompson 
1191480fae85SJeremy L Thompson /**
1192480fae85SJeremy L Thompson   @brief Destroy CeedQFunctionAssemblyData
1193480fae85SJeremy L Thompson 
1194ea61e9acSJeremy L Thompson   @param[in,out] data  CeedQFunctionAssemblyData to destroy
1195480fae85SJeremy L Thompson 
1196480fae85SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1197480fae85SJeremy L Thompson 
1198480fae85SJeremy L Thompson   @ref Backend
1199480fae85SJeremy L Thompson **/
1200480fae85SJeremy L Thompson int CeedQFunctionAssemblyDataDestroy(CeedQFunctionAssemblyData *data) {
1201ad6481ceSJeremy L Thompson   if (!*data || --(*data)->ref_count > 0) {
1202ad6481ceSJeremy L Thompson     *data = NULL;
1203ad6481ceSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1204ad6481ceSJeremy L Thompson   }
12052b730f8bSJeremy L Thompson   CeedCall(CeedDestroy(&(*data)->ceed));
12062b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&(*data)->vec));
12072b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&(*data)->rstr));
1208480fae85SJeremy L Thompson 
12092b730f8bSJeremy L Thompson   CeedCall(CeedFree(data));
1210480fae85SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1211480fae85SJeremy L Thompson }
1212480fae85SJeremy L Thompson 
1213ed9e99e6SJeremy L Thompson /**
1214ed9e99e6SJeremy L Thompson   @brief Get CeedOperatorAssemblyData
1215ed9e99e6SJeremy L Thompson 
1216ed9e99e6SJeremy L Thompson   @param[in]  op   CeedOperator to assemble
1217ed9e99e6SJeremy L Thompson   @param[out] data CeedQFunctionAssemblyData
1218ed9e99e6SJeremy L Thompson 
1219ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1220ed9e99e6SJeremy L Thompson 
1221ed9e99e6SJeremy L Thompson   @ref Backend
1222ed9e99e6SJeremy L Thompson **/
12232b730f8bSJeremy L Thompson int CeedOperatorGetOperatorAssemblyData(CeedOperator op, CeedOperatorAssemblyData *data) {
1224ed9e99e6SJeremy L Thompson   if (!op->op_assembled) {
1225ed9e99e6SJeremy L Thompson     CeedOperatorAssemblyData data;
1226ed9e99e6SJeremy L Thompson 
12272b730f8bSJeremy L Thompson     CeedCall(CeedOperatorAssemblyDataCreate(op->ceed, op, &data));
1228ed9e99e6SJeremy L Thompson     op->op_assembled = data;
1229ed9e99e6SJeremy L Thompson   }
1230ed9e99e6SJeremy L Thompson   *data = op->op_assembled;
1231ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1232ed9e99e6SJeremy L Thompson }
1233ed9e99e6SJeremy L Thompson 
1234ed9e99e6SJeremy L Thompson /**
1235ba746a46SJeremy L Thompson   @brief Create object holding CeedOperator assembly data.
1236ba746a46SJeremy L Thompson 
1237ba746a46SJeremy L Thompson   The CeedOperatorAssemblyData holds an array with references to every active CeedBasis used in the CeedOperator.
1238ba746a46SJeremy L Thompson   An array with references to the corresponding active CeedElemRestrictions is also stored.
1239ba746a46SJeremy L Thompson   For each active CeedBasis, the CeedOperatorAssemblyData holds an array of all input and output CeedEvalModes for this CeedBasis.
1240ba746a46SJeremy L Thompson   The CeedOperatorAssemblyData holds an array of offsets for indexing into the assembled CeedQFunction arrays to the row representing each
1241ba746a46SJeremy L Thompson CeedEvalMode.
1242ba746a46SJeremy L Thompson   The number of input columns across all active bases for the assembled CeedQFunction is also stored.
1243ba746a46SJeremy L Thompson   Lastly, the CeedOperatorAssembly data holds assembled matrices representing the full action of the CeedBasis for all CeedEvalModes.
1244ed9e99e6SJeremy L Thompson 
1245ea61e9acSJeremy L Thompson   @param[in]  ceed Ceed object where the CeedOperatorAssemblyData will be created
1246ed9e99e6SJeremy L Thompson   @param[in]  op   CeedOperator to be assembled
1247ea61e9acSJeremy L Thompson   @param[out] data Address of the variable where the newly created CeedOperatorAssemblyData will be stored
1248ed9e99e6SJeremy L Thompson 
1249ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1250ed9e99e6SJeremy L Thompson 
1251ed9e99e6SJeremy L Thompson   @ref Backend
1252ed9e99e6SJeremy L Thompson **/
12532b730f8bSJeremy L Thompson int CeedOperatorAssemblyDataCreate(Ceed ceed, CeedOperator op, CeedOperatorAssemblyData *data) {
1254506b1a0cSSebastian Grimberg   CeedInt             num_active_bases_in = 0, num_active_bases_out = 0, offset = 0;
1255506b1a0cSSebastian Grimberg   CeedInt             num_input_fields, *num_eval_modes_in = NULL, num_output_fields, *num_eval_modes_out = NULL;
12561c66c397SJeremy L Thompson   CeedSize          **eval_mode_offsets_in = NULL, **eval_mode_offsets_out = NULL;
12571c66c397SJeremy L Thompson   CeedEvalMode      **eval_modes_in = NULL, **eval_modes_out = NULL;
12581c66c397SJeremy L Thompson   CeedQFunctionField *qf_fields;
12591c66c397SJeremy L Thompson   CeedQFunction       qf;
12601c66c397SJeremy L Thompson   CeedOperatorField  *op_fields;
126101f0e615SJames Wright   bool                is_composite;
126201f0e615SJames Wright 
126301f0e615SJames Wright   CeedCall(CeedOperatorIsComposite(op, &is_composite));
126401f0e615SJames Wright   CeedCheck(!is_composite, ceed, CEED_ERROR_INCOMPATIBLE, "Can only create CeedOperator assembly data for non-composite operators.");
1265437c7c90SJeremy L Thompson 
1266437c7c90SJeremy L Thompson   // Allocate
12672b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(1, data));
1268ed9e99e6SJeremy L Thompson   (*data)->ceed = ceed;
12692b730f8bSJeremy L Thompson   CeedCall(CeedReference(ceed));
1270ed9e99e6SJeremy L Thompson 
1271ed9e99e6SJeremy L Thompson   // Build OperatorAssembly data
12722b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
12732b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, &num_input_fields, &qf_fields, NULL, NULL));
12742b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, NULL, &op_fields, NULL, NULL));
1275ed9e99e6SJeremy L Thompson 
1276ed9e99e6SJeremy L Thompson   // Determine active input basis
1277ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_input_fields; i++) {
1278ed9e99e6SJeremy L Thompson     CeedVector vec;
12791c66c397SJeremy L Thompson 
12802b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
1281ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
12827c1dbaffSSebastian Grimberg       CeedInt      index = -1, num_comp, q_comp;
12831c66c397SJeremy L Thompson       CeedEvalMode eval_mode;
12841c66c397SJeremy L Thompson       CeedBasis    basis_in = NULL;
12851c66c397SJeremy L Thompson 
12862b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_in));
12872b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
1288352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumComponents(basis_in, &num_comp));
1289352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumQuadratureComponents(basis_in, eval_mode, &q_comp));
1290506b1a0cSSebastian Grimberg       for (CeedInt i = 0; i < num_active_bases_in; i++) {
1291506b1a0cSSebastian Grimberg         if ((*data)->active_bases_in[i] == basis_in) index = i;
1292437c7c90SJeremy L Thompson       }
1293437c7c90SJeremy L Thompson       if (index == -1) {
1294437c7c90SJeremy L Thompson         CeedElemRestriction elem_rstr_in;
12951c66c397SJeremy L Thompson 
1296506b1a0cSSebastian Grimberg         index = num_active_bases_in;
1297506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_in + 1, &(*data)->active_bases_in));
1298506b1a0cSSebastian Grimberg         (*data)->active_bases_in[num_active_bases_in] = NULL;
1299506b1a0cSSebastian Grimberg         CeedCall(CeedBasisReferenceCopy(basis_in, &(*data)->active_bases_in[num_active_bases_in]));
1300506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_in + 1, &(*data)->active_elem_rstrs_in));
1301506b1a0cSSebastian Grimberg         (*data)->active_elem_rstrs_in[num_active_bases_in] = NULL;
1302437c7c90SJeremy L Thompson         CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_rstr_in));
1303506b1a0cSSebastian Grimberg         CeedCall(CeedElemRestrictionReferenceCopy(elem_rstr_in, &(*data)->active_elem_rstrs_in[num_active_bases_in]));
1304506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_in + 1, &num_eval_modes_in));
1305437c7c90SJeremy L Thompson         num_eval_modes_in[index] = 0;
1306506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_in + 1, &eval_modes_in));
1307437c7c90SJeremy L Thompson         eval_modes_in[index] = NULL;
1308506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_in + 1, &eval_mode_offsets_in));
1309437c7c90SJeremy L Thompson         eval_mode_offsets_in[index] = NULL;
1310506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_in + 1, &(*data)->assembled_bases_in));
1311437c7c90SJeremy L Thompson         (*data)->assembled_bases_in[index] = NULL;
1312506b1a0cSSebastian Grimberg         num_active_bases_in++;
1313437c7c90SJeremy L Thompson       }
1314352a5e7cSSebastian Grimberg       if (eval_mode != CEED_EVAL_WEIGHT) {
1315352a5e7cSSebastian Grimberg         // q_comp = 1 if CEED_EVAL_NONE, CEED_EVAL_WEIGHT caught by QF Assembly
1316352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_in[index] + q_comp, &eval_modes_in[index]));
1317352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_in[index] + q_comp, &eval_mode_offsets_in[index]));
1318352a5e7cSSebastian Grimberg         for (CeedInt d = 0; d < q_comp; d++) {
1319437c7c90SJeremy L Thompson           eval_modes_in[index][num_eval_modes_in[index] + d]        = eval_mode;
1320437c7c90SJeremy L Thompson           eval_mode_offsets_in[index][num_eval_modes_in[index] + d] = offset;
1321352a5e7cSSebastian Grimberg           offset += num_comp;
1322ed9e99e6SJeremy L Thompson         }
1323352a5e7cSSebastian Grimberg         num_eval_modes_in[index] += q_comp;
1324ed9e99e6SJeremy L Thompson       }
1325ed9e99e6SJeremy L Thompson     }
1326ed9e99e6SJeremy L Thompson   }
1327ed9e99e6SJeremy L Thompson 
1328ed9e99e6SJeremy L Thompson   // Determine active output basis
13292b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, NULL, NULL, &num_output_fields, &qf_fields));
13302b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, NULL, NULL, NULL, &op_fields));
1331437c7c90SJeremy L Thompson   offset = 0;
1332ed9e99e6SJeremy L Thompson   for (CeedInt i = 0; i < num_output_fields; i++) {
1333ed9e99e6SJeremy L Thompson     CeedVector vec;
13341c66c397SJeremy L Thompson 
13352b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
1336ed9e99e6SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
13377c1dbaffSSebastian Grimberg       CeedInt      index = -1, num_comp, q_comp;
13381c66c397SJeremy L Thompson       CeedEvalMode eval_mode;
13391c66c397SJeremy L Thompson       CeedBasis    basis_out = NULL;
13401c66c397SJeremy L Thompson 
1341437c7c90SJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_out));
13422b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
1343352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumComponents(basis_out, &num_comp));
1344352a5e7cSSebastian Grimberg       CeedCall(CeedBasisGetNumQuadratureComponents(basis_out, eval_mode, &q_comp));
1345506b1a0cSSebastian Grimberg       for (CeedInt i = 0; i < num_active_bases_out; i++) {
1346506b1a0cSSebastian Grimberg         if ((*data)->active_bases_out[i] == basis_out) index = i;
1347437c7c90SJeremy L Thompson       }
1348437c7c90SJeremy L Thompson       if (index == -1) {
1349437c7c90SJeremy L Thompson         CeedElemRestriction elem_rstr_out;
13501c66c397SJeremy L Thompson 
1351506b1a0cSSebastian Grimberg         index = num_active_bases_out;
1352506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_out + 1, &(*data)->active_bases_out));
1353506b1a0cSSebastian Grimberg         (*data)->active_bases_out[num_active_bases_out] = NULL;
1354506b1a0cSSebastian Grimberg         CeedCall(CeedBasisReferenceCopy(basis_out, &(*data)->active_bases_out[num_active_bases_out]));
1355506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_out + 1, &(*data)->active_elem_rstrs_out));
1356506b1a0cSSebastian Grimberg         (*data)->active_elem_rstrs_out[num_active_bases_out] = NULL;
1357437c7c90SJeremy L Thompson         CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_rstr_out));
1358506b1a0cSSebastian Grimberg         CeedCall(CeedElemRestrictionReferenceCopy(elem_rstr_out, &(*data)->active_elem_rstrs_out[num_active_bases_out]));
1359506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_out + 1, &num_eval_modes_out));
1360437c7c90SJeremy L Thompson         num_eval_modes_out[index] = 0;
1361506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_out + 1, &eval_modes_out));
1362437c7c90SJeremy L Thompson         eval_modes_out[index] = NULL;
1363506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_out + 1, &eval_mode_offsets_out));
1364437c7c90SJeremy L Thompson         eval_mode_offsets_out[index] = NULL;
1365506b1a0cSSebastian Grimberg         CeedCall(CeedRealloc(num_active_bases_out + 1, &(*data)->assembled_bases_out));
1366437c7c90SJeremy L Thompson         (*data)->assembled_bases_out[index] = NULL;
1367506b1a0cSSebastian Grimberg         num_active_bases_out++;
1368437c7c90SJeremy L Thompson       }
1369352a5e7cSSebastian Grimberg       if (eval_mode != CEED_EVAL_WEIGHT) {
1370352a5e7cSSebastian Grimberg         // q_comp = 1 if CEED_EVAL_NONE, CEED_EVAL_WEIGHT caught by QF Assembly
1371352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_out[index] + q_comp, &eval_modes_out[index]));
1372352a5e7cSSebastian Grimberg         CeedCall(CeedRealloc(num_eval_modes_out[index] + q_comp, &eval_mode_offsets_out[index]));
1373352a5e7cSSebastian Grimberg         for (CeedInt d = 0; d < q_comp; d++) {
1374437c7c90SJeremy L Thompson           eval_modes_out[index][num_eval_modes_out[index] + d]        = eval_mode;
1375437c7c90SJeremy L Thompson           eval_mode_offsets_out[index][num_eval_modes_out[index] + d] = offset;
1376352a5e7cSSebastian Grimberg           offset += num_comp;
1377ed9e99e6SJeremy L Thompson         }
1378352a5e7cSSebastian Grimberg         num_eval_modes_out[index] += q_comp;
1379ed9e99e6SJeremy L Thompson       }
1380ed9e99e6SJeremy L Thompson     }
1381ed9e99e6SJeremy L Thompson   }
1382506b1a0cSSebastian Grimberg   (*data)->num_active_bases_in   = num_active_bases_in;
138327789c4aSJed Brown   (*data)->num_eval_modes_in     = num_eval_modes_in;
138427789c4aSJed Brown   (*data)->eval_modes_in         = eval_modes_in;
138527789c4aSJed Brown   (*data)->eval_mode_offsets_in  = eval_mode_offsets_in;
1386506b1a0cSSebastian Grimberg   (*data)->num_active_bases_out  = num_active_bases_out;
1387437c7c90SJeremy L Thompson   (*data)->num_eval_modes_out    = num_eval_modes_out;
1388437c7c90SJeremy L Thompson   (*data)->eval_modes_out        = eval_modes_out;
1389437c7c90SJeremy L Thompson   (*data)->eval_mode_offsets_out = eval_mode_offsets_out;
1390506b1a0cSSebastian Grimberg   (*data)->num_output_components = offset;
1391ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1392ed9e99e6SJeremy L Thompson }
1393ed9e99e6SJeremy L Thompson 
1394ed9e99e6SJeremy L Thompson /**
1395ba746a46SJeremy L Thompson   @brief Get CeedOperator CeedEvalModes for assembly.
1396ba746a46SJeremy L Thompson 
1397ba746a46SJeremy L Thompson   Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object.
1398ed9e99e6SJeremy L Thompson 
1399ed9e99e6SJeremy L Thompson   @param[in]  data                  CeedOperatorAssemblyData
1400506b1a0cSSebastian Grimberg   @param[out] num_active_bases_in   Total number of active bases for input
1401c5d0f995SJed Brown   @param[out] num_eval_modes_in     Pointer to hold array of numbers of input CeedEvalModes, or NULL.
1402ba746a46SJeremy L Thompson                                       `eval_modes_in[0]` holds an array of eval modes for the first active basis.
1403c5d0f995SJed Brown   @param[out] eval_modes_in         Pointer to hold arrays of input CeedEvalModes, or NULL.
1404ba746a46SJeremy L Thompson   @param[out] eval_mode_offsets_in  Pointer to hold arrays of input offsets at each quadrature point.
1405506b1a0cSSebastian Grimberg   @param[out] num_active_bases_out  Total number of active bases for output
1406c5d0f995SJed Brown   @param[out] num_eval_modes_out    Pointer to hold array of numbers of output CeedEvalModes, or NULL
1407c5d0f995SJed Brown   @param[out] eval_modes_out        Pointer to hold arrays of output CeedEvalModes, or NULL.
1408437c7c90SJeremy L Thompson   @param[out] eval_mode_offsets_out Pointer to hold arrays of output offsets at each quadrature point
1409ba746a46SJeremy L Thompson   @param[out] num_output_components The number of columns in the assembled CeedQFunction matrix for each quadrature point,
1410ba746a46SJeremy L Thompson                                       including contributions of all active bases
1411ed9e99e6SJeremy L Thompson 
1412ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1413ed9e99e6SJeremy L Thompson 
1414ed9e99e6SJeremy L Thompson   @ref Backend
1415ed9e99e6SJeremy L Thompson **/
1416506b1a0cSSebastian Grimberg int CeedOperatorAssemblyDataGetEvalModes(CeedOperatorAssemblyData data, CeedInt *num_active_bases_in, CeedInt **num_eval_modes_in,
1417506b1a0cSSebastian Grimberg                                          const CeedEvalMode ***eval_modes_in, CeedSize ***eval_mode_offsets_in, CeedInt *num_active_bases_out,
1418506b1a0cSSebastian Grimberg                                          CeedInt **num_eval_modes_out, const CeedEvalMode ***eval_modes_out, CeedSize ***eval_mode_offsets_out,
1419506b1a0cSSebastian Grimberg                                          CeedSize *num_output_components) {
1420506b1a0cSSebastian Grimberg   if (num_active_bases_in) *num_active_bases_in = data->num_active_bases_in;
1421437c7c90SJeremy L Thompson   if (num_eval_modes_in) *num_eval_modes_in = data->num_eval_modes_in;
1422437c7c90SJeremy L Thompson   if (eval_modes_in) *eval_modes_in = (const CeedEvalMode **)data->eval_modes_in;
1423437c7c90SJeremy L Thompson   if (eval_mode_offsets_in) *eval_mode_offsets_in = data->eval_mode_offsets_in;
1424506b1a0cSSebastian Grimberg   if (num_active_bases_out) *num_active_bases_out = data->num_active_bases_out;
1425437c7c90SJeremy L Thompson   if (num_eval_modes_out) *num_eval_modes_out = data->num_eval_modes_out;
1426437c7c90SJeremy L Thompson   if (eval_modes_out) *eval_modes_out = (const CeedEvalMode **)data->eval_modes_out;
1427437c7c90SJeremy L Thompson   if (eval_mode_offsets_out) *eval_mode_offsets_out = data->eval_mode_offsets_out;
1428437c7c90SJeremy L Thompson   if (num_output_components) *num_output_components = data->num_output_components;
1429ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1430ed9e99e6SJeremy L Thompson }
1431ed9e99e6SJeremy L Thompson 
1432ed9e99e6SJeremy L Thompson /**
1433ba746a46SJeremy L Thompson   @brief Get CeedOperator CeedBasis data for assembly.
1434ba746a46SJeremy L Thompson 
1435ba746a46SJeremy L Thompson   Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object.
1436ed9e99e6SJeremy L Thompson 
1437ed9e99e6SJeremy L Thompson   @param[in]  data                 CeedOperatorAssemblyData
1438506b1a0cSSebastian Grimberg   @param[out] num_active_bases_in  Number of active input bases, or NULL
1439506b1a0cSSebastian Grimberg   @param[out] active_bases_in      Pointer to hold active input CeedBasis, or NULL
1440437c7c90SJeremy L Thompson   @param[out] assembled_bases_in   Pointer to hold assembled active input B, or NULL
1441506b1a0cSSebastian Grimberg   @param[out] num_active_bases_out Number of active output bases, or NULL
1442506b1a0cSSebastian Grimberg   @param[out] active_bases_out     Pointer to hold active output CeedBasis, or NULL
1443437c7c90SJeremy L Thompson   @param[out] assembled_bases_out  Pointer to hold assembled active output B, or NULL
1444ed9e99e6SJeremy L Thompson 
1445ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1446ed9e99e6SJeremy L Thompson 
1447ed9e99e6SJeremy L Thompson   @ref Backend
1448ed9e99e6SJeremy L Thompson **/
1449506b1a0cSSebastian Grimberg int CeedOperatorAssemblyDataGetBases(CeedOperatorAssemblyData data, CeedInt *num_active_bases_in, CeedBasis **active_bases_in,
1450506b1a0cSSebastian Grimberg                                      const CeedScalar ***assembled_bases_in, CeedInt *num_active_bases_out, CeedBasis **active_bases_out,
1451506b1a0cSSebastian Grimberg                                      const CeedScalar ***assembled_bases_out) {
1452ed9e99e6SJeremy L Thompson   // Assemble B_in, B_out if needed
1453437c7c90SJeremy L Thompson   if (assembled_bases_in && !data->assembled_bases_in[0]) {
1454437c7c90SJeremy L Thompson     CeedInt num_qpts;
1455437c7c90SJeremy L Thompson 
1456506b1a0cSSebastian Grimberg     if (data->active_bases_in[0] == CEED_BASIS_NONE) CeedCall(CeedElemRestrictionGetElementSize(data->active_elem_rstrs_in[0], &num_qpts));
1457506b1a0cSSebastian Grimberg     else CeedCall(CeedBasisGetNumQuadraturePoints(data->active_bases_in[0], &num_qpts));
1458506b1a0cSSebastian Grimberg     for (CeedInt b = 0; b < data->num_active_bases_in; b++) {
14591c66c397SJeremy L Thompson       bool        has_eval_none = false;
1460352a5e7cSSebastian Grimberg       CeedInt     num_nodes;
1461437c7c90SJeremy L Thompson       CeedScalar *B_in = NULL, *identity = NULL;
1462ed9e99e6SJeremy L Thompson 
1463506b1a0cSSebastian Grimberg       CeedCall(CeedElemRestrictionGetElementSize(data->active_elem_rstrs_in[b], &num_nodes));
1464352a5e7cSSebastian Grimberg       CeedCall(CeedCalloc(num_qpts * num_nodes * data->num_eval_modes_in[b], &B_in));
1465ed9e99e6SJeremy L Thompson 
1466437c7c90SJeremy L Thompson       for (CeedInt i = 0; i < data->num_eval_modes_in[b]; i++) {
1467437c7c90SJeremy L Thompson         has_eval_none = has_eval_none || (data->eval_modes_in[b][i] == CEED_EVAL_NONE);
1468ed9e99e6SJeremy L Thompson       }
1469ed9e99e6SJeremy L Thompson       if (has_eval_none) {
1470352a5e7cSSebastian Grimberg         CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
1471352a5e7cSSebastian Grimberg         for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) {
1472352a5e7cSSebastian Grimberg           identity[i * num_nodes + i] = 1.0;
1473ed9e99e6SJeremy L Thompson         }
1474ed9e99e6SJeremy L Thompson       }
1475ed9e99e6SJeremy L Thompson 
1476ed9e99e6SJeremy L Thompson       for (CeedInt q = 0; q < num_qpts; q++) {
1477352a5e7cSSebastian Grimberg         for (CeedInt n = 0; n < num_nodes; n++) {
1478352a5e7cSSebastian Grimberg           CeedInt      d_in              = 0, q_comp_in;
1479352a5e7cSSebastian Grimberg           CeedEvalMode eval_mode_in_prev = CEED_EVAL_NONE;
14801c66c397SJeremy L Thompson 
1481437c7c90SJeremy L Thompson           for (CeedInt e_in = 0; e_in < data->num_eval_modes_in[b]; e_in++) {
1482437c7c90SJeremy L Thompson             const CeedInt     qq = data->num_eval_modes_in[b] * q;
1483437c7c90SJeremy L Thompson             const CeedScalar *B  = NULL;
14841c66c397SJeremy L Thompson 
1485506b1a0cSSebastian Grimberg             CeedCall(CeedOperatorGetBasisPointer(data->active_bases_in[b], data->eval_modes_in[b][e_in], identity, &B));
1486506b1a0cSSebastian Grimberg             CeedCall(CeedBasisGetNumQuadratureComponents(data->active_bases_in[b], data->eval_modes_in[b][e_in], &q_comp_in));
1487352a5e7cSSebastian Grimberg             if (q_comp_in > 1) {
1488352a5e7cSSebastian Grimberg               if (e_in == 0 || data->eval_modes_in[b][e_in] != eval_mode_in_prev) d_in = 0;
1489352a5e7cSSebastian Grimberg               else B = &B[(++d_in) * num_qpts * num_nodes];
1490352a5e7cSSebastian Grimberg             }
1491352a5e7cSSebastian Grimberg             eval_mode_in_prev                 = data->eval_modes_in[b][e_in];
1492352a5e7cSSebastian Grimberg             B_in[(qq + e_in) * num_nodes + n] = B[q * num_nodes + n];
1493ed9e99e6SJeremy L Thompson           }
1494ed9e99e6SJeremy L Thompson         }
1495ed9e99e6SJeremy L Thompson       }
14967c1dbaffSSebastian Grimberg       if (identity) CeedCall(CeedFree(&identity));
1497437c7c90SJeremy L Thompson       data->assembled_bases_in[b] = B_in;
1498437c7c90SJeremy L Thompson     }
1499ed9e99e6SJeremy L Thompson   }
1500ed9e99e6SJeremy L Thompson 
1501437c7c90SJeremy L Thompson   if (assembled_bases_out && !data->assembled_bases_out[0]) {
1502437c7c90SJeremy L Thompson     CeedInt num_qpts;
1503437c7c90SJeremy L Thompson 
1504506b1a0cSSebastian Grimberg     if (data->active_bases_out[0] == CEED_BASIS_NONE) CeedCall(CeedElemRestrictionGetElementSize(data->active_elem_rstrs_out[0], &num_qpts));
1505506b1a0cSSebastian Grimberg     else CeedCall(CeedBasisGetNumQuadraturePoints(data->active_bases_out[0], &num_qpts));
1506506b1a0cSSebastian Grimberg     for (CeedInt b = 0; b < data->num_active_bases_out; b++) {
1507ed9e99e6SJeremy L Thompson       bool        has_eval_none = false;
15081c66c397SJeremy L Thompson       CeedInt     num_nodes;
1509437c7c90SJeremy L Thompson       CeedScalar *B_out = NULL, *identity = NULL;
1510ed9e99e6SJeremy L Thompson 
1511506b1a0cSSebastian Grimberg       CeedCall(CeedElemRestrictionGetElementSize(data->active_elem_rstrs_out[b], &num_nodes));
1512352a5e7cSSebastian Grimberg       CeedCall(CeedCalloc(num_qpts * num_nodes * data->num_eval_modes_out[b], &B_out));
1513ed9e99e6SJeremy L Thompson 
1514437c7c90SJeremy L Thompson       for (CeedInt i = 0; i < data->num_eval_modes_out[b]; i++) {
1515437c7c90SJeremy L Thompson         has_eval_none = has_eval_none || (data->eval_modes_out[b][i] == CEED_EVAL_NONE);
1516ed9e99e6SJeremy L Thompson       }
1517ed9e99e6SJeremy L Thompson       if (has_eval_none) {
1518352a5e7cSSebastian Grimberg         CeedCall(CeedCalloc(num_qpts * num_nodes, &identity));
1519352a5e7cSSebastian Grimberg         for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) {
1520352a5e7cSSebastian Grimberg           identity[i * num_nodes + i] = 1.0;
1521ed9e99e6SJeremy L Thompson         }
1522ed9e99e6SJeremy L Thompson       }
1523ed9e99e6SJeremy L Thompson 
1524ed9e99e6SJeremy L Thompson       for (CeedInt q = 0; q < num_qpts; q++) {
1525352a5e7cSSebastian Grimberg         for (CeedInt n = 0; n < num_nodes; n++) {
1526352a5e7cSSebastian Grimberg           CeedInt      d_out              = 0, q_comp_out;
1527352a5e7cSSebastian Grimberg           CeedEvalMode eval_mode_out_prev = CEED_EVAL_NONE;
15281c66c397SJeremy L Thompson 
1529437c7c90SJeremy L Thompson           for (CeedInt e_out = 0; e_out < data->num_eval_modes_out[b]; e_out++) {
1530437c7c90SJeremy L Thompson             const CeedInt     qq = data->num_eval_modes_out[b] * q;
1531437c7c90SJeremy L Thompson             const CeedScalar *B  = NULL;
15321c66c397SJeremy L Thompson 
1533506b1a0cSSebastian Grimberg             CeedCall(CeedOperatorGetBasisPointer(data->active_bases_out[b], data->eval_modes_out[b][e_out], identity, &B));
1534506b1a0cSSebastian Grimberg             CeedCall(CeedBasisGetNumQuadratureComponents(data->active_bases_out[b], data->eval_modes_out[b][e_out], &q_comp_out));
1535352a5e7cSSebastian Grimberg             if (q_comp_out > 1) {
1536352a5e7cSSebastian Grimberg               if (e_out == 0 || data->eval_modes_out[b][e_out] != eval_mode_out_prev) d_out = 0;
1537352a5e7cSSebastian Grimberg               else B = &B[(++d_out) * num_qpts * num_nodes];
1538352a5e7cSSebastian Grimberg             }
1539352a5e7cSSebastian Grimberg             eval_mode_out_prev                  = data->eval_modes_out[b][e_out];
1540352a5e7cSSebastian Grimberg             B_out[(qq + e_out) * num_nodes + n] = B[q * num_nodes + n];
1541ed9e99e6SJeremy L Thompson           }
1542ed9e99e6SJeremy L Thompson         }
1543ed9e99e6SJeremy L Thompson       }
15447c1dbaffSSebastian Grimberg       if (identity) CeedCall(CeedFree(&identity));
1545437c7c90SJeremy L Thompson       data->assembled_bases_out[b] = B_out;
1546437c7c90SJeremy L Thompson     }
1547ed9e99e6SJeremy L Thompson   }
1548ed9e99e6SJeremy L Thompson 
1549437c7c90SJeremy L Thompson   // Pass out assembled data
1550506b1a0cSSebastian Grimberg   if (num_active_bases_in) *num_active_bases_in = data->num_active_bases_in;
1551506b1a0cSSebastian Grimberg   if (active_bases_in) *active_bases_in = data->active_bases_in;
1552437c7c90SJeremy L Thompson   if (assembled_bases_in) *assembled_bases_in = (const CeedScalar **)data->assembled_bases_in;
1553506b1a0cSSebastian Grimberg   if (num_active_bases_out) *num_active_bases_out = data->num_active_bases_out;
1554506b1a0cSSebastian Grimberg   if (active_bases_out) *active_bases_out = data->active_bases_out;
1555437c7c90SJeremy L Thompson   if (assembled_bases_out) *assembled_bases_out = (const CeedScalar **)data->assembled_bases_out;
1556437c7c90SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1557437c7c90SJeremy L Thompson }
1558437c7c90SJeremy L Thompson 
1559437c7c90SJeremy L Thompson /**
1560ba746a46SJeremy L Thompson   @brief Get CeedOperator CeedBasis data for assembly.
1561ba746a46SJeremy L Thompson 
1562ba746a46SJeremy L Thompson   Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object.
1563437c7c90SJeremy L Thompson 
1564437c7c90SJeremy L Thompson   @param[in]  data                      CeedOperatorAssemblyData
1565506b1a0cSSebastian Grimberg   @param[out] num_active_elem_rstrs_in  Number of active input element restrictions, or NULL
1566506b1a0cSSebastian Grimberg   @param[out] active_elem_rstrs_in      Pointer to hold active input CeedElemRestrictions, or NULL
1567506b1a0cSSebastian Grimberg   @param[out] num_active_elem_rstrs_out Number of active output element restrictions, or NULL
1568506b1a0cSSebastian Grimberg   @param[out] active_elem_rstrs_out     Pointer to hold active output CeedElemRestrictions, or NULL
1569437c7c90SJeremy L Thompson 
1570437c7c90SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1571437c7c90SJeremy L Thompson 
1572437c7c90SJeremy L Thompson   @ref Backend
1573437c7c90SJeremy L Thompson **/
1574506b1a0cSSebastian Grimberg int CeedOperatorAssemblyDataGetElemRestrictions(CeedOperatorAssemblyData data, CeedInt *num_active_elem_rstrs_in,
1575506b1a0cSSebastian Grimberg                                                 CeedElemRestriction **active_elem_rstrs_in, CeedInt *num_active_elem_rstrs_out,
1576506b1a0cSSebastian Grimberg                                                 CeedElemRestriction **active_elem_rstrs_out) {
1577506b1a0cSSebastian Grimberg   if (num_active_elem_rstrs_in) *num_active_elem_rstrs_in = data->num_active_bases_in;
1578506b1a0cSSebastian Grimberg   if (active_elem_rstrs_in) *active_elem_rstrs_in = data->active_elem_rstrs_in;
1579506b1a0cSSebastian Grimberg   if (num_active_elem_rstrs_out) *num_active_elem_rstrs_out = data->num_active_bases_out;
1580506b1a0cSSebastian Grimberg   if (active_elem_rstrs_out) *active_elem_rstrs_out = data->active_elem_rstrs_out;
1581ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1582ed9e99e6SJeremy L Thompson }
1583ed9e99e6SJeremy L Thompson 
1584ed9e99e6SJeremy L Thompson /**
1585ed9e99e6SJeremy L Thompson   @brief Destroy CeedOperatorAssemblyData
1586ed9e99e6SJeremy L Thompson 
1587ea61e9acSJeremy L Thompson   @param[in,out] data CeedOperatorAssemblyData to destroy
1588ed9e99e6SJeremy L Thompson 
1589ed9e99e6SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1590ed9e99e6SJeremy L Thompson 
1591ed9e99e6SJeremy L Thompson   @ref Backend
1592ed9e99e6SJeremy L Thompson **/
1593ed9e99e6SJeremy L Thompson int CeedOperatorAssemblyDataDestroy(CeedOperatorAssemblyData *data) {
1594ad6481ceSJeremy L Thompson   if (!*data) {
1595ad6481ceSJeremy L Thompson     *data = NULL;
1596ad6481ceSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1597ad6481ceSJeremy L Thompson   }
15982b730f8bSJeremy L Thompson   CeedCall(CeedDestroy(&(*data)->ceed));
1599506b1a0cSSebastian Grimberg   for (CeedInt b = 0; b < (*data)->num_active_bases_in; b++) {
1600506b1a0cSSebastian Grimberg     CeedCall(CeedBasisDestroy(&(*data)->active_bases_in[b]));
1601506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionDestroy(&(*data)->active_elem_rstrs_in[b]));
1602437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->eval_modes_in[b]));
1603437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->eval_mode_offsets_in[b]));
1604437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->assembled_bases_in[b]));
1605506b1a0cSSebastian Grimberg   }
1606506b1a0cSSebastian Grimberg   for (CeedInt b = 0; b < (*data)->num_active_bases_out; b++) {
1607506b1a0cSSebastian Grimberg     CeedCall(CeedBasisDestroy(&(*data)->active_bases_out[b]));
1608506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionDestroy(&(*data)->active_elem_rstrs_out[b]));
1609506b1a0cSSebastian Grimberg     CeedCall(CeedFree(&(*data)->eval_modes_out[b]));
1610506b1a0cSSebastian Grimberg     CeedCall(CeedFree(&(*data)->eval_mode_offsets_out[b]));
1611437c7c90SJeremy L Thompson     CeedCall(CeedFree(&(*data)->assembled_bases_out[b]));
1612437c7c90SJeremy L Thompson   }
1613506b1a0cSSebastian Grimberg   CeedCall(CeedFree(&(*data)->active_bases_in));
1614506b1a0cSSebastian Grimberg   CeedCall(CeedFree(&(*data)->active_bases_out));
1615506b1a0cSSebastian Grimberg   CeedCall(CeedFree(&(*data)->active_elem_rstrs_in));
1616506b1a0cSSebastian Grimberg   CeedCall(CeedFree(&(*data)->active_elem_rstrs_out));
1617437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->num_eval_modes_in));
1618437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->num_eval_modes_out));
1619437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_modes_in));
1620437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_modes_out));
1621437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_mode_offsets_in));
1622437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->eval_mode_offsets_out));
1623437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->assembled_bases_in));
1624437c7c90SJeremy L Thompson   CeedCall(CeedFree(&(*data)->assembled_bases_out));
1625ed9e99e6SJeremy L Thompson 
16262b730f8bSJeremy L Thompson   CeedCall(CeedFree(data));
1627ed9e99e6SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1628ed9e99e6SJeremy L Thompson }
1629ed9e99e6SJeremy L Thompson 
1630*4dd1a9d2SSebastian Grimberg /**
1631*4dd1a9d2SSebastian Grimberg   @brief Retrieve fallback CeedOperator with a reference Ceed for advanced CeedOperator functionality
1632*4dd1a9d2SSebastian Grimberg 
1633*4dd1a9d2SSebastian Grimberg   @param[in]  op          CeedOperator to retrieve fallback for
1634*4dd1a9d2SSebastian Grimberg   @param[out] op_fallback Fallback CeedOperator
1635*4dd1a9d2SSebastian Grimberg 
1636*4dd1a9d2SSebastian Grimberg   @return An error code: 0 - success, otherwise - failure
1637*4dd1a9d2SSebastian Grimberg 
1638*4dd1a9d2SSebastian Grimberg   @ref Backend
1639*4dd1a9d2SSebastian Grimberg **/
1640*4dd1a9d2SSebastian Grimberg int CeedOperatorGetFallback(CeedOperator op, CeedOperator *op_fallback) {
1641*4dd1a9d2SSebastian Grimberg   // Create if needed
1642*4dd1a9d2SSebastian Grimberg   if (!op->op_fallback) CeedCall(CeedOperatorCreateFallback(op));
1643*4dd1a9d2SSebastian Grimberg   if (op->op_fallback) {
1644*4dd1a9d2SSebastian Grimberg     bool is_debug;
1645*4dd1a9d2SSebastian Grimberg 
1646*4dd1a9d2SSebastian Grimberg     CeedCall(CeedIsDebug(op->ceed, &is_debug));
1647*4dd1a9d2SSebastian Grimberg     if (is_debug) {
1648*4dd1a9d2SSebastian Grimberg       Ceed        ceed, ceed_fallback;
1649*4dd1a9d2SSebastian Grimberg       const char *resource, *resource_fallback;
1650*4dd1a9d2SSebastian Grimberg 
1651*4dd1a9d2SSebastian Grimberg       CeedCall(CeedOperatorGetCeed(op, &ceed));
1652*4dd1a9d2SSebastian Grimberg       CeedCall(CeedGetOperatorFallbackCeed(ceed, &ceed_fallback));
1653*4dd1a9d2SSebastian Grimberg       CeedCall(CeedGetResource(ceed, &resource));
1654*4dd1a9d2SSebastian Grimberg       CeedCall(CeedGetResource(ceed_fallback, &resource_fallback));
1655*4dd1a9d2SSebastian Grimberg 
1656*4dd1a9d2SSebastian Grimberg       CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- CeedOperator Fallback ----------\n");
1657*4dd1a9d2SSebastian Grimberg       CeedDebug(ceed, "Falling back from %s operator at address %ld to %s operator at address %ld\n", resource, op, resource_fallback,
1658*4dd1a9d2SSebastian Grimberg                 op->op_fallback);
1659*4dd1a9d2SSebastian Grimberg     }
1660*4dd1a9d2SSebastian Grimberg   }
1661*4dd1a9d2SSebastian Grimberg   *op_fallback = op->op_fallback;
1662*4dd1a9d2SSebastian Grimberg   return CEED_ERROR_SUCCESS;
1663*4dd1a9d2SSebastian Grimberg }
1664*4dd1a9d2SSebastian Grimberg 
1665*4dd1a9d2SSebastian Grimberg /**
1666*4dd1a9d2SSebastian Grimberg   @brief Get the parent CeedOperator for a fallback CeedOperator
1667*4dd1a9d2SSebastian Grimberg 
1668*4dd1a9d2SSebastian Grimberg   @param[in]  op     CeedOperator context
1669*4dd1a9d2SSebastian Grimberg   @param[out] parent Variable to store parent CeedOperator context
1670*4dd1a9d2SSebastian Grimberg 
1671*4dd1a9d2SSebastian Grimberg   @return An error code: 0 - success, otherwise - failure
1672*4dd1a9d2SSebastian Grimberg 
1673*4dd1a9d2SSebastian Grimberg   @ref Backend
1674*4dd1a9d2SSebastian Grimberg **/
1675*4dd1a9d2SSebastian Grimberg int CeedOperatorGetFallbackParent(CeedOperator op, CeedOperator *parent) {
1676*4dd1a9d2SSebastian Grimberg   *parent = op->op_fallback_parent ? op->op_fallback_parent : NULL;
1677*4dd1a9d2SSebastian Grimberg   return CEED_ERROR_SUCCESS;
1678*4dd1a9d2SSebastian Grimberg }
1679*4dd1a9d2SSebastian Grimberg 
1680*4dd1a9d2SSebastian Grimberg /**
1681*4dd1a9d2SSebastian Grimberg   @brief Get the Ceed context of the parent CeedOperator for a fallback CeedOperator
1682*4dd1a9d2SSebastian Grimberg 
1683*4dd1a9d2SSebastian Grimberg   @param[in]  op     CeedOperator context
1684*4dd1a9d2SSebastian Grimberg   @param[out] parent Variable to store parent Ceed context
1685*4dd1a9d2SSebastian Grimberg 
1686*4dd1a9d2SSebastian Grimberg   @return An error code: 0 - success, otherwise - failure
1687*4dd1a9d2SSebastian Grimberg 
1688*4dd1a9d2SSebastian Grimberg   @ref Backend
1689*4dd1a9d2SSebastian Grimberg **/
1690*4dd1a9d2SSebastian Grimberg int CeedOperatorGetFallbackParentCeed(CeedOperator op, Ceed *parent) {
1691*4dd1a9d2SSebastian Grimberg   *parent = op->op_fallback_parent ? op->op_fallback_parent->ceed : op->ceed;
1692*4dd1a9d2SSebastian Grimberg   return CEED_ERROR_SUCCESS;
1693*4dd1a9d2SSebastian Grimberg }
1694*4dd1a9d2SSebastian Grimberg 
1695480fae85SJeremy L Thompson /// @}
1696480fae85SJeremy L Thompson 
1697480fae85SJeremy L Thompson /// ----------------------------------------------------------------------------
1698eaf62fffSJeremy L Thompson /// CeedOperator Public API
1699eaf62fffSJeremy L Thompson /// ----------------------------------------------------------------------------
1700eaf62fffSJeremy L Thompson /// @addtogroup CeedOperatorUser
1701eaf62fffSJeremy L Thompson /// @{
1702eaf62fffSJeremy L Thompson 
1703eaf62fffSJeremy L Thompson /**
1704eaf62fffSJeremy L Thompson   @brief Assemble a linear CeedQFunction associated with a CeedOperator
1705eaf62fffSJeremy L Thompson 
1706ea61e9acSJeremy L Thompson   This returns a CeedVector containing a matrix at each quadrature point providing the action of the CeedQFunction associated with the CeedOperator.
1707859c15bbSJames Wright   The vector `assembled` is of shape `[num_elements, num_input_fields, num_output_fields, num_quad_points]` and contains column-major matrices
1708859c15bbSJames Wright representing the action of the CeedQFunction for a corresponding quadrature point on an element.
1709859c15bbSJames Wright 
17109fd66db6SSebastian Grimberg   Inputs and outputs are in the order provided by the user when adding CeedOperator fields.
17119fd66db6SSebastian Grimberg   For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and 'v', provided in that order, would result in an assembled QFunction
17129fd66db6SSebastian Grimberg that consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
1713eaf62fffSJeremy L Thompson 
1714ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1715f04ea552SJeremy L Thompson 
1716ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1717ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedQFunction at quadrature points
1718ea61e9acSJeremy L Thompson   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled CeedQFunction
1719ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1720eaf62fffSJeremy L Thompson 
1721eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1722eaf62fffSJeremy L Thompson 
1723eaf62fffSJeremy L Thompson   @ref User
1724eaf62fffSJeremy L Thompson **/
17252b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
17262b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1727eaf62fffSJeremy L Thompson 
1728eaf62fffSJeremy L Thompson   if (op->LinearAssembleQFunction) {
1729d04bbc78SJeremy L Thompson     // Backend version
17302b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleQFunction(op, assembled, rstr, request));
1731eaf62fffSJeremy L Thompson   } else {
1732d04bbc78SJeremy L Thompson     // Operator fallback
1733d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1734d04bbc78SJeremy L Thompson 
17352b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
17366574a04fSJeremy L Thompson     if (op_fallback) CeedCall(CeedOperatorLinearAssembleQFunction(op_fallback, assembled, rstr, request));
17376574a04fSJeremy L Thompson     else return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunction");
173870a7ffb3SJeremy L Thompson   }
1739eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1740eaf62fffSJeremy L Thompson }
174170a7ffb3SJeremy L Thompson 
174270a7ffb3SJeremy L Thompson /**
1743ea61e9acSJeremy L Thompson   @brief Assemble CeedQFunction and store result internally.
17444385fb7fSSebastian Grimberg 
1745ea61e9acSJeremy L Thompson   Return copied references of stored data to the caller.
1746ea61e9acSJeremy L Thompson   Caller is responsible for ownership and destruction of the copied references.
1747ea61e9acSJeremy L Thompson   See also @ref CeedOperatorLinearAssembleQFunction
174870a7ffb3SJeremy L Thompson 
1749c5f45aeaSJeremy L Thompson   Note: If the value of `assembled` or `rstr` passed to this function are non-NULL, then it is assumed that they hold valid pointers.
1750c5f45aeaSJeremy L Thompson         These objects will be destroyed if `*assembled` or `*rstr` is the only reference to the object.
1751c5f45aeaSJeremy L Thompson 
1752ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1753ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedQFunction at quadrature points
1754ea61e9acSJeremy L Thompson   @param[out] rstr      CeedElemRestriction for CeedVector containing assembledCeedQFunction
1755ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
175670a7ffb3SJeremy L Thompson 
175770a7ffb3SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
175870a7ffb3SJeremy L Thompson 
175970a7ffb3SJeremy L Thompson   @ref User
176070a7ffb3SJeremy L Thompson **/
17612b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleQFunctionBuildOrUpdate(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
1762b05f7e9fSJeremy L Thompson   int (*LinearAssembleQFunctionUpdate)(CeedOperator, CeedVector, CeedElemRestriction, CeedRequest *) = NULL;
1763b05f7e9fSJeremy L Thompson   CeedOperator op_assemble                                                                           = NULL;
1764bb229da9SJeremy L Thompson   CeedOperator op_fallback_parent                                                                    = NULL;
1765b05f7e9fSJeremy L Thompson 
17662b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
176770a7ffb3SJeremy L Thompson 
1768b05f7e9fSJeremy L Thompson   // Determine if fallback parent or operator has implementation
1769bb229da9SJeremy L Thompson   CeedCall(CeedOperatorGetFallbackParent(op, &op_fallback_parent));
1770bb229da9SJeremy L Thompson   if (op_fallback_parent && op_fallback_parent->LinearAssembleQFunctionUpdate) {
1771b05f7e9fSJeremy L Thompson     // -- Backend version for op fallback parent is faster, if it exists
1772bb229da9SJeremy L Thompson     LinearAssembleQFunctionUpdate = op_fallback_parent->LinearAssembleQFunctionUpdate;
1773bb229da9SJeremy L Thompson     op_assemble                   = op_fallback_parent;
1774b05f7e9fSJeremy L Thompson   } else if (op->LinearAssembleQFunctionUpdate) {
1775b05f7e9fSJeremy L Thompson     // -- Backend version for op
1776b05f7e9fSJeremy L Thompson     LinearAssembleQFunctionUpdate = op->LinearAssembleQFunctionUpdate;
1777b05f7e9fSJeremy L Thompson     op_assemble                   = op;
1778b05f7e9fSJeremy L Thompson   }
1779b05f7e9fSJeremy L Thompson 
1780b05f7e9fSJeremy L Thompson   // Assemble QFunction
1781b05f7e9fSJeremy L Thompson   if (LinearAssembleQFunctionUpdate) {
1782b05f7e9fSJeremy L Thompson     // Backend or fallback parent version
1783480fae85SJeremy L Thompson     bool                qf_assembled_is_setup;
17842efa2d85SJeremy L Thompson     CeedVector          assembled_vec  = NULL;
17852efa2d85SJeremy L Thompson     CeedElemRestriction assembled_rstr = NULL;
1786480fae85SJeremy L Thompson 
17872b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataIsSetup(op->qf_assembled, &qf_assembled_is_setup));
1788480fae85SJeremy L Thompson     if (qf_assembled_is_setup) {
1789d04bbc78SJeremy L Thompson       bool update_needed;
1790d04bbc78SJeremy L Thompson 
17912b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataGetObjects(op->qf_assembled, &assembled_vec, &assembled_rstr));
17922b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataIsUpdateNeeded(op->qf_assembled, &update_needed));
1793b05f7e9fSJeremy L Thompson       if (update_needed) CeedCall(LinearAssembleQFunctionUpdate(op_assemble, assembled_vec, assembled_rstr, request));
179470a7ffb3SJeremy L Thompson     } else {
1795b05f7e9fSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleQFunction(op_assemble, &assembled_vec, &assembled_rstr, request));
17962b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionAssemblyDataSetObjects(op->qf_assembled, assembled_vec, assembled_rstr));
179770a7ffb3SJeremy L Thompson     }
17982b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, false));
17992efa2d85SJeremy L Thompson 
1800d04bbc78SJeremy L Thompson     // Copy reference from internally held copy
18012b730f8bSJeremy L Thompson     CeedCall(CeedVectorReferenceCopy(assembled_vec, assembled));
18022b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionReferenceCopy(assembled_rstr, rstr));
1803c5f45aeaSJeremy L Thompson     CeedCall(CeedVectorDestroy(&assembled_vec));
18042b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionDestroy(&assembled_rstr));
180570a7ffb3SJeremy L Thompson   } else {
1806d04bbc78SJeremy L Thompson     // Operator fallback
1807d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1808d04bbc78SJeremy L Thompson 
18092b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
18106574a04fSJeremy L Thompson     if (op_fallback) CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op_fallback, assembled, rstr, request));
18116574a04fSJeremy L Thompson     else return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunctionUpdate");
181270a7ffb3SJeremy L Thompson   }
181370a7ffb3SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1814eaf62fffSJeremy L Thompson }
1815eaf62fffSJeremy L Thompson 
1816eaf62fffSJeremy L Thompson /**
1817eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1818eaf62fffSJeremy L Thompson 
1819eaf62fffSJeremy L Thompson   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1820eaf62fffSJeremy L Thompson 
1821ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
1822eaf62fffSJeremy L Thompson 
1823ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1824f04ea552SJeremy L Thompson 
1825ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1826eaf62fffSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1827ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1828eaf62fffSJeremy L Thompson 
1829eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1830eaf62fffSJeremy L Thompson 
1831eaf62fffSJeremy L Thompson   @ref User
1832eaf62fffSJeremy L Thompson **/
18332b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1834f3d47e36SJeremy L Thompson   bool     is_composite;
18351c66c397SJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
18361c66c397SJeremy L Thompson 
18372b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1838f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1839eaf62fffSJeremy L Thompson 
18402b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
18416574a04fSJeremy L Thompson   CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1842c9366a6bSJeremy L Thompson 
1843f3d47e36SJeremy L Thompson   // Early exit for empty operator
1844f3d47e36SJeremy L Thompson   if (!is_composite) {
1845f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1846f3d47e36SJeremy L Thompson 
1847f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1848f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1849f3d47e36SJeremy L Thompson   }
1850f3d47e36SJeremy L Thompson 
1851eaf62fffSJeremy L Thompson   if (op->LinearAssembleDiagonal) {
1852d04bbc78SJeremy L Thompson     // Backend version
18532b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleDiagonal(op, assembled, request));
1854eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1855eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddDiagonal) {
1856d04bbc78SJeremy L Thompson     // Backend version with zeroing first
18572b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(assembled, 0.0));
18582b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request));
1859eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1860eaf62fffSJeremy L Thompson   } else {
1861d04bbc78SJeremy L Thompson     // Operator fallback
1862d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1863d04bbc78SJeremy L Thompson 
18642b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1865d04bbc78SJeremy L Thompson     if (op_fallback) {
18662b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleDiagonal(op_fallback, assembled, request));
1867eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1868eaf62fffSJeremy L Thompson     }
1869eaf62fffSJeremy L Thompson   }
1870eaf62fffSJeremy L Thompson   // Default interface implementation
18712b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(assembled, 0.0));
18722b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleAddDiagonal(op, assembled, request));
1873eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
1874eaf62fffSJeremy L Thompson }
1875eaf62fffSJeremy L Thompson 
1876eaf62fffSJeremy L Thompson /**
1877eaf62fffSJeremy L Thompson   @brief Assemble the diagonal of a square linear CeedOperator
1878eaf62fffSJeremy L Thompson 
1879eaf62fffSJeremy L Thompson   This sums into a CeedVector the diagonal of a linear CeedOperator.
1880eaf62fffSJeremy L Thompson 
1881ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
1882eaf62fffSJeremy L Thompson 
1883ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
1884f04ea552SJeremy L Thompson 
1885ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
1886eaf62fffSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1887ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
1888eaf62fffSJeremy L Thompson 
1889eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
1890eaf62fffSJeremy L Thompson 
1891eaf62fffSJeremy L Thompson   @ref User
1892eaf62fffSJeremy L Thompson **/
18932b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
1894f3d47e36SJeremy L Thompson   bool     is_composite;
18951c66c397SJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
18961c66c397SJeremy L Thompson 
18972b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
1898f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
1899eaf62fffSJeremy L Thompson 
19002b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
19016574a04fSJeremy L Thompson   CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
1902c9366a6bSJeremy L Thompson 
1903f3d47e36SJeremy L Thompson   // Early exit for empty operator
1904f3d47e36SJeremy L Thompson   if (!is_composite) {
1905f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
1906f3d47e36SJeremy L Thompson 
1907f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
1908f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
1909f3d47e36SJeremy L Thompson   }
1910f3d47e36SJeremy L Thompson 
1911eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddDiagonal) {
1912d04bbc78SJeremy L Thompson     // Backend version
19132b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request));
1914eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
1915eaf62fffSJeremy L Thompson   } else {
1916d04bbc78SJeremy L Thompson     // Operator fallback
1917d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
1918d04bbc78SJeremy L Thompson 
19192b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
1920d04bbc78SJeremy L Thompson     if (op_fallback) {
19212b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddDiagonal(op_fallback, assembled, request));
1922eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
1923eaf62fffSJeremy L Thompson     }
1924eaf62fffSJeremy L Thompson   }
1925eaf62fffSJeremy L Thompson   // Default interface implementation
1926eaf62fffSJeremy L Thompson   if (is_composite) {
19272b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, false, assembled));
1928eaf62fffSJeremy L Thompson   } else {
19292b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, false, assembled));
1930eaf62fffSJeremy L Thompson   }
1931d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1932eaf62fffSJeremy L Thompson }
1933eaf62fffSJeremy L Thompson 
1934eaf62fffSJeremy L Thompson /**
193501f0e615SJames Wright    @brief Fully assemble the point-block diagonal pattern of a linear operator.
193601f0e615SJames Wright 
193701f0e615SJames Wright    Expected to be used in conjunction with CeedOperatorLinearAssemblePointBlockDiagonal().
193801f0e615SJames Wright 
193901f0e615SJames Wright    The assembly routines use coordinate format, with `num_entries` tuples of the form (i, j, value) which indicate that value should be added to the
194001f0e615SJames Wright matrix in entry (i, j).
194101f0e615SJames Wright   Note that the (i, j) pairs are unique.
194201f0e615SJames Wright   This function returns the number of entries and their (i, j) locations, while CeedOperatorLinearAssemblePointBlockDiagonal() provides the values in
194301f0e615SJames Wright the same ordering.
194401f0e615SJames Wright 
194501f0e615SJames Wright    This will generally be slow unless your operator is low-order.
194601f0e615SJames Wright 
194701f0e615SJames Wright    Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
194801f0e615SJames Wright 
194901f0e615SJames Wright    @param[in]  op          CeedOperator to assemble
195001f0e615SJames Wright    @param[out] num_entries Number of entries in coordinate nonzero pattern
195101f0e615SJames Wright    @param[out] rows        Row number for each entry
195201f0e615SJames Wright    @param[out] cols        Column number for each entry
195301f0e615SJames Wright 
195401f0e615SJames Wright    @ref User
195501f0e615SJames Wright **/
195601f0e615SJames Wright int CeedOperatorLinearAssemblePointBlockDiagonalSymbolic(CeedOperator op, CeedSize *num_entries, CeedInt **rows, CeedInt **cols) {
195701f0e615SJames Wright   Ceed          ceed;
195801f0e615SJames Wright   bool          is_composite;
195901f0e615SJames Wright   CeedInt       num_active_components, num_sub_operators;
196001f0e615SJames Wright   CeedOperator *sub_operators;
196101f0e615SJames Wright 
196201f0e615SJames Wright   CeedCall(CeedOperatorGetCeed(op, &ceed));
196301f0e615SJames Wright   CeedCall(CeedOperatorIsComposite(op, &is_composite));
196401f0e615SJames Wright 
196501f0e615SJames Wright   CeedSize input_size = 0, output_size = 0;
196601f0e615SJames Wright   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
196701f0e615SJames Wright   CeedCheck(input_size == output_size, ceed, CEED_ERROR_DIMENSION, "Operator must be square");
196801f0e615SJames Wright 
196901f0e615SJames Wright   if (is_composite) {
197001f0e615SJames Wright     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub_operators));
197101f0e615SJames Wright     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
197201f0e615SJames Wright   } else {
197301f0e615SJames Wright     sub_operators     = &op;
197401f0e615SJames Wright     num_sub_operators = 1;
197501f0e615SJames Wright   }
197601f0e615SJames Wright 
1977506b1a0cSSebastian Grimberg   // Verify operator can be assembled correctly
1978506b1a0cSSebastian Grimberg   {
197901f0e615SJames Wright     CeedOperatorAssemblyData data;
1980506b1a0cSSebastian Grimberg     CeedInt                  num_active_elem_rstrs, comp_stride;
198101f0e615SJames Wright     CeedElemRestriction     *active_elem_rstrs;
198201f0e615SJames Wright 
198301f0e615SJames Wright     // Get initial values to check against
198401f0e615SJames Wright     CeedCall(CeedOperatorGetOperatorAssemblyData(sub_operators[0], &data));
1985506b1a0cSSebastian Grimberg     CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, &num_active_elem_rstrs, &active_elem_rstrs, NULL, NULL));
198601f0e615SJames Wright     CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstrs[0], &comp_stride));
198701f0e615SJames Wright     CeedCall(CeedElemRestrictionGetNumComponents(active_elem_rstrs[0], &num_active_components));
198801f0e615SJames Wright 
1989506b1a0cSSebastian Grimberg     // Verify that all active element restrictions have same component stride and number of components
199001f0e615SJames Wright     for (CeedInt k = 0; k < num_sub_operators; k++) {
199101f0e615SJames Wright       CeedCall(CeedOperatorGetOperatorAssemblyData(sub_operators[k], &data));
1992506b1a0cSSebastian Grimberg       CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, &num_active_elem_rstrs, &active_elem_rstrs, NULL, NULL));
199301f0e615SJames Wright       for (CeedInt i = 0; i < num_active_elem_rstrs; i++) {
1994506b1a0cSSebastian Grimberg         CeedInt comp_stride_sub, num_active_components_sub;
1995506b1a0cSSebastian Grimberg 
199601f0e615SJames Wright         CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstrs[i], &comp_stride_sub));
199701f0e615SJames Wright         CeedCheck(comp_stride == comp_stride_sub, ceed, CEED_ERROR_DIMENSION,
199801f0e615SJames Wright                   "Active element restrictions must have the same component stride: %d vs %d", comp_stride, comp_stride_sub);
199901f0e615SJames Wright         CeedCall(CeedElemRestrictionGetNumComponents(active_elem_rstrs[i], &num_active_components_sub));
200001f0e615SJames Wright         CeedCheck(num_active_components == num_active_components_sub, ceed, CEED_ERROR_INCOMPATIBLE,
200101f0e615SJames Wright                   "All suboperators must have the same number of output components");
200201f0e615SJames Wright       }
200301f0e615SJames Wright     }
200401f0e615SJames Wright   }
200501f0e615SJames Wright   *num_entries = input_size * num_active_components;
200601f0e615SJames Wright   CeedCall(CeedCalloc(*num_entries, rows));
200701f0e615SJames Wright   CeedCall(CeedCalloc(*num_entries, cols));
200801f0e615SJames Wright 
200901f0e615SJames Wright   for (CeedInt o = 0; o < num_sub_operators; o++) {
2010506b1a0cSSebastian Grimberg     CeedElemRestriction active_elem_rstr, point_block_active_elem_rstr;
201101f0e615SJames Wright     CeedInt             comp_stride, num_elem, elem_size;
2012506b1a0cSSebastian Grimberg     const CeedInt      *offsets, *point_block_offsets;
201301f0e615SJames Wright 
201401f0e615SJames Wright     CeedCall(CeedOperatorGetActiveElemRestriction(sub_operators[o], &active_elem_rstr));
201501f0e615SJames Wright     CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstr, &comp_stride));
201601f0e615SJames Wright     CeedCall(CeedElemRestrictionGetNumElements(active_elem_rstr, &num_elem));
201701f0e615SJames Wright     CeedCall(CeedElemRestrictionGetElementSize(active_elem_rstr, &elem_size));
201801f0e615SJames Wright     CeedCall(CeedElemRestrictionGetOffsets(active_elem_rstr, CEED_MEM_HOST, &offsets));
201901f0e615SJames Wright 
2020506b1a0cSSebastian Grimberg     CeedCall(CeedOperatorCreateActivePointBlockRestriction(active_elem_rstr, &point_block_active_elem_rstr));
2021506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionGetOffsets(point_block_active_elem_rstr, CEED_MEM_HOST, &point_block_offsets));
202201f0e615SJames Wright 
202301f0e615SJames Wright     for (CeedSize i = 0; i < num_elem * elem_size; i++) {
202401f0e615SJames Wright       for (CeedInt c_out = 0; c_out < num_active_components; c_out++) {
202501f0e615SJames Wright         for (CeedInt c_in = 0; c_in < num_active_components; c_in++) {
2026506b1a0cSSebastian Grimberg           (*rows)[point_block_offsets[i] + c_out * num_active_components + c_in] = offsets[i] + c_out * comp_stride;
2027506b1a0cSSebastian Grimberg           (*cols)[point_block_offsets[i] + c_out * num_active_components + c_in] = offsets[i] + c_in * comp_stride;
202801f0e615SJames Wright         }
202901f0e615SJames Wright       }
203001f0e615SJames Wright     }
203101f0e615SJames Wright 
203201f0e615SJames Wright     CeedCall(CeedElemRestrictionRestoreOffsets(active_elem_rstr, &offsets));
2033506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionRestoreOffsets(point_block_active_elem_rstr, &point_block_offsets));
2034506b1a0cSSebastian Grimberg     CeedCall(CeedElemRestrictionDestroy(&point_block_active_elem_rstr));
203501f0e615SJames Wright   }
203601f0e615SJames Wright   return CEED_ERROR_SUCCESS;
203701f0e615SJames Wright }
203801f0e615SJames Wright 
203901f0e615SJames Wright /**
2040eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
2041eaf62fffSJeremy L Thompson 
2042ea61e9acSJeremy L Thompson   This overwrites a CeedVector with the point block diagonal of a linear CeedOperator.
2043eaf62fffSJeremy L Thompson 
2044ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
2045eaf62fffSJeremy L Thompson 
2046ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
2047f04ea552SJeremy L Thompson 
2048ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
2049ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator point block diagonal, provided in row-major form with an @a num_comp * @a num_comp
2050ea61e9acSJeremy L Thompson block at each node. The dimensions of this vector are derived from the active vector for the CeedOperator. The array has shape [nodes, component out,
2051ea61e9acSJeremy L Thompson component in].
2052ea61e9acSJeremy L Thompson   @param[in]  request   Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
2053eaf62fffSJeremy L Thompson 
2054eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2055eaf62fffSJeremy L Thompson 
2056eaf62fffSJeremy L Thompson   @ref User
2057eaf62fffSJeremy L Thompson **/
20582b730f8bSJeremy L Thompson int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
2059f3d47e36SJeremy L Thompson   bool     is_composite;
20601c66c397SJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
20611c66c397SJeremy L Thompson 
20622b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
2063f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
2064eaf62fffSJeremy L Thompson 
20652b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
20666574a04fSJeremy L Thompson   CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
2067c9366a6bSJeremy L Thompson 
2068f3d47e36SJeremy L Thompson   // Early exit for empty operator
2069f3d47e36SJeremy L Thompson   if (!is_composite) {
2070f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
2071f3d47e36SJeremy L Thompson 
2072f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
2073f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
2074f3d47e36SJeremy L Thompson   }
2075f3d47e36SJeremy L Thompson 
2076eaf62fffSJeremy L Thompson   if (op->LinearAssemblePointBlockDiagonal) {
2077d04bbc78SJeremy L Thompson     // Backend version
20782b730f8bSJeremy L Thompson     CeedCall(op->LinearAssemblePointBlockDiagonal(op, assembled, request));
2079eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2080eaf62fffSJeremy L Thompson   } else if (op->LinearAssembleAddPointBlockDiagonal) {
2081d04bbc78SJeremy L Thompson     // Backend version with zeroing first
20822b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(assembled, 0.0));
20832b730f8bSJeremy L Thompson     CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request));
2084eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2085eaf62fffSJeremy L Thompson   } else {
2086d04bbc78SJeremy L Thompson     // Operator fallback
2087d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2088d04bbc78SJeremy L Thompson 
20892b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2090d04bbc78SJeremy L Thompson     if (op_fallback) {
20912b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssemblePointBlockDiagonal(op_fallback, assembled, request));
2092eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2093eaf62fffSJeremy L Thompson     }
2094eaf62fffSJeremy L Thompson   }
2095eaf62fffSJeremy L Thompson   // Default interface implementation
20962b730f8bSJeremy L Thompson   CeedCall(CeedVectorSetValue(assembled, 0.0));
20972b730f8bSJeremy L Thompson   CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request));
2098eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2099eaf62fffSJeremy L Thompson }
2100eaf62fffSJeremy L Thompson 
2101eaf62fffSJeremy L Thompson /**
2102eaf62fffSJeremy L Thompson   @brief Assemble the point block diagonal of a square linear CeedOperator
2103eaf62fffSJeremy L Thompson 
2104ea61e9acSJeremy L Thompson   This sums into a CeedVector with the point block diagonal of a linear CeedOperator.
2105eaf62fffSJeremy L Thompson 
2106ea61e9acSJeremy L Thompson   Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported.
2107eaf62fffSJeremy L Thompson 
2108ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
2109f04ea552SJeremy L Thompson 
2110ea61e9acSJeremy L Thompson   @param[in]  op        CeedOperator to assemble CeedQFunction
2111ea61e9acSJeremy L Thompson   @param[out] assembled CeedVector to store assembled CeedOperator point block diagonal, provided in row-major form with an @a num_comp * @a num_comp
2112ea61e9acSJeremy L Thompson block at each node. The dimensions of this vector are derived from the active vector for the CeedOperator. The array has shape [nodes, component out,
2113ea61e9acSJeremy L Thompson component in].
2114ea61e9acSJeremy L Thompson   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
2115eaf62fffSJeremy L Thompson 
2116eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2117eaf62fffSJeremy L Thompson 
2118eaf62fffSJeremy L Thompson   @ref User
2119eaf62fffSJeremy L Thompson **/
21202b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) {
2121f3d47e36SJeremy L Thompson   bool     is_composite;
21221c66c397SJeremy L Thompson   CeedSize input_size = 0, output_size = 0;
21231c66c397SJeremy L Thompson 
21242b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
2125f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
2126eaf62fffSJeremy L Thompson 
21272b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size));
21286574a04fSJeremy L Thompson   CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square");
2129c9366a6bSJeremy L Thompson 
2130f3d47e36SJeremy L Thompson   // Early exit for empty operator
2131f3d47e36SJeremy L Thompson   if (!is_composite) {
2132f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
2133f3d47e36SJeremy L Thompson 
2134f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
2135f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
2136f3d47e36SJeremy L Thompson   }
2137f3d47e36SJeremy L Thompson 
2138eaf62fffSJeremy L Thompson   if (op->LinearAssembleAddPointBlockDiagonal) {
2139d04bbc78SJeremy L Thompson     // Backend version
21402b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleAddPointBlockDiagonal(op, assembled, request));
2141eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2142eaf62fffSJeremy L Thompson   } else {
2143d04bbc78SJeremy L Thompson     // Operator fallback
2144d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2145d04bbc78SJeremy L Thompson 
21462b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2147d04bbc78SJeremy L Thompson     if (op_fallback) {
21482b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op_fallback, assembled, request));
2149eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2150eaf62fffSJeremy L Thompson     }
2151eaf62fffSJeremy L Thompson   }
2152ea61e9acSJeremy L Thompson   // Default interface implementation
2153eaf62fffSJeremy L Thompson   if (is_composite) {
21542b730f8bSJeremy L Thompson     CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, true, assembled));
2155eaf62fffSJeremy L Thompson   } else {
21562b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, true, assembled));
2157eaf62fffSJeremy L Thompson   }
2158d04bbc78SJeremy L Thompson   return CEED_ERROR_SUCCESS;
2159eaf62fffSJeremy L Thompson }
2160eaf62fffSJeremy L Thompson 
2161eaf62fffSJeremy L Thompson /**
2162eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero pattern of a linear operator.
2163eaf62fffSJeremy L Thompson 
2164ea61e9acSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssemble().
2165eaf62fffSJeremy L Thompson 
2166ea61e9acSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the form (i, j, value) which indicate that value should be added to the
21679fd66db6SSebastian Grimberg matrix in entry (i, j).
21689fd66db6SSebastian Grimberg   Note that the (i, j) pairs are not unique and may repeat.
21699fd66db6SSebastian Grimberg   This function returns the number of entries and their (i, j) locations, while CeedOperatorLinearAssemble() provides the values in the same ordering.
2170eaf62fffSJeremy L Thompson 
2171eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
2172eaf62fffSJeremy L Thompson 
2173ea61e9acSJeremy L Thompson    Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
2174f04ea552SJeremy L Thompson 
2175eaf62fffSJeremy L Thompson    @param[in]  op          CeedOperator to assemble
2176eaf62fffSJeremy L Thompson    @param[out] num_entries Number of entries in coordinate nonzero pattern
2177eaf62fffSJeremy L Thompson    @param[out] rows        Row number for each entry
2178eaf62fffSJeremy L Thompson    @param[out] cols        Column number for each entry
2179eaf62fffSJeremy L Thompson 
2180eaf62fffSJeremy L Thompson    @ref User
2181eaf62fffSJeremy L Thompson **/
21822b730f8bSJeremy L Thompson int CeedOperatorLinearAssembleSymbolic(CeedOperator op, CeedSize *num_entries, CeedInt **rows, CeedInt **cols) {
21831c66c397SJeremy L Thompson   bool          is_composite;
21841c66c397SJeremy L Thompson   CeedInt       num_suboperators, offset = 0;
2185b94338b9SJed Brown   CeedSize      single_entries;
2186eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
21871c66c397SJeremy L Thompson 
21882b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
2189f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
2190eaf62fffSJeremy L Thompson 
2191eaf62fffSJeremy L Thompson   if (op->LinearAssembleSymbolic) {
2192d04bbc78SJeremy L Thompson     // Backend version
21932b730f8bSJeremy L Thompson     CeedCall(op->LinearAssembleSymbolic(op, num_entries, rows, cols));
2194eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2195eaf62fffSJeremy L Thompson   } else {
2196d04bbc78SJeremy L Thompson     // Operator fallback
2197d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2198d04bbc78SJeremy L Thompson 
21992b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2200d04bbc78SJeremy L Thompson     if (op_fallback) {
22012b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssembleSymbolic(op_fallback, num_entries, rows, cols));
2202eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2203eaf62fffSJeremy L Thompson     }
2204eaf62fffSJeremy L Thompson   }
2205eaf62fffSJeremy L Thompson 
2206eaf62fffSJeremy L Thompson   // Default interface implementation
2207eaf62fffSJeremy L Thompson 
2208506b1a0cSSebastian Grimberg   // Count entries and allocate rows, cols arrays
2209eaf62fffSJeremy L Thompson   *num_entries = 0;
2210eaf62fffSJeremy L Thompson   if (is_composite) {
2211c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
2212c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
221392ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
22142b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
2215eaf62fffSJeremy L Thompson       *num_entries += single_entries;
2216eaf62fffSJeremy L Thompson     }
2217eaf62fffSJeremy L Thompson   } else {
22182b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssemblyCountEntries(op, &single_entries));
2219eaf62fffSJeremy L Thompson     *num_entries += single_entries;
2220eaf62fffSJeremy L Thompson   }
22212b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(*num_entries, rows));
22222b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(*num_entries, cols));
2223eaf62fffSJeremy L Thompson 
2224506b1a0cSSebastian Grimberg   // Assemble nonzero locations
2225eaf62fffSJeremy L Thompson   if (is_composite) {
2226c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
2227c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
222892ae7e47SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; ++k) {
22292b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows, *cols));
22302b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
2231eaf62fffSJeremy L Thompson       offset += single_entries;
2232eaf62fffSJeremy L Thompson     }
2233eaf62fffSJeremy L Thompson   } else {
22342b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols));
2235eaf62fffSJeremy L Thompson   }
2236eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2237eaf62fffSJeremy L Thompson }
2238eaf62fffSJeremy L Thompson 
2239eaf62fffSJeremy L Thompson /**
2240eaf62fffSJeremy L Thompson    @brief Fully assemble the nonzero entries of a linear operator.
2241eaf62fffSJeremy L Thompson 
2242ea61e9acSJeremy L Thompson    Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic().
2243eaf62fffSJeremy L Thompson 
2244ea61e9acSJeremy L Thompson    The assembly routines use coordinate format, with num_entries tuples of the form (i, j, value) which indicate that value should be added to the
22459fd66db6SSebastian Grimberg matrix in entry (i, j).
22469fd66db6SSebastian Grimberg   Note that the (i, j) pairs are not unique and may repeat.
22479fd66db6SSebastian Grimberg   This function returns the values of the nonzero entries to be added, their (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic()
2248eaf62fffSJeremy L Thompson 
2249eaf62fffSJeremy L Thompson    This will generally be slow unless your operator is low-order.
2250eaf62fffSJeremy L Thompson 
2251ea61e9acSJeremy L Thompson    Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
2252f04ea552SJeremy L Thompson 
2253eaf62fffSJeremy L Thompson    @param[in]  op     CeedOperator to assemble
2254eaf62fffSJeremy L Thompson    @param[out] values Values to assemble into matrix
2255eaf62fffSJeremy L Thompson 
2256eaf62fffSJeremy L Thompson    @ref User
2257eaf62fffSJeremy L Thompson **/
2258eaf62fffSJeremy L Thompson int CeedOperatorLinearAssemble(CeedOperator op, CeedVector values) {
22591c66c397SJeremy L Thompson   bool          is_composite;
22601c66c397SJeremy L Thompson   CeedInt       num_suboperators, offset = 0;
2261b94338b9SJed Brown   CeedSize      single_entries = 0;
2262eaf62fffSJeremy L Thompson   CeedOperator *sub_operators;
22631c66c397SJeremy L Thompson 
22642b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
2265f3d47e36SJeremy L Thompson   CeedCall(CeedOperatorIsComposite(op, &is_composite));
2266f3d47e36SJeremy L Thompson 
2267f3d47e36SJeremy L Thompson   // Early exit for empty operator
2268f3d47e36SJeremy L Thompson   if (!is_composite) {
2269f3d47e36SJeremy L Thompson     CeedInt num_elem = 0;
2270f3d47e36SJeremy L Thompson 
2271f3d47e36SJeremy L Thompson     CeedCall(CeedOperatorGetNumElements(op, &num_elem));
2272f3d47e36SJeremy L Thompson     if (num_elem == 0) return CEED_ERROR_SUCCESS;
2273f3d47e36SJeremy L Thompson   }
2274eaf62fffSJeremy L Thompson 
2275eaf62fffSJeremy L Thompson   if (op->LinearAssemble) {
2276d04bbc78SJeremy L Thompson     // Backend version
22772b730f8bSJeremy L Thompson     CeedCall(op->LinearAssemble(op, values));
2278eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2279eaf62fffSJeremy L Thompson   } else {
2280d04bbc78SJeremy L Thompson     // Operator fallback
2281d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2282d04bbc78SJeremy L Thompson 
22832b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2284d04bbc78SJeremy L Thompson     if (op_fallback) {
22852b730f8bSJeremy L Thompson       CeedCall(CeedOperatorLinearAssemble(op_fallback, values));
2286eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2287eaf62fffSJeremy L Thompson     }
2288eaf62fffSJeremy L Thompson   }
2289eaf62fffSJeremy L Thompson 
2290eaf62fffSJeremy L Thompson   // Default interface implementation
229128ec399dSJeremy L Thompson   CeedCall(CeedVectorSetValue(values, 0.0));
2292eaf62fffSJeremy L Thompson   if (is_composite) {
2293c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
2294c6ebc35dSJeremy L Thompson     CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
2295cefa2673SJeremy L Thompson     for (CeedInt k = 0; k < num_suboperators; k++) {
22962b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemble(sub_operators[k], offset, values));
22972b730f8bSJeremy L Thompson       CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries));
2298eaf62fffSJeremy L Thompson       offset += single_entries;
2299eaf62fffSJeremy L Thompson     }
2300eaf62fffSJeremy L Thompson   } else {
23012b730f8bSJeremy L Thompson     CeedCall(CeedSingleOperatorAssemble(op, offset, values));
2302eaf62fffSJeremy L Thompson   }
2303eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2304eaf62fffSJeremy L Thompson }
2305eaf62fffSJeremy L Thompson 
2306eaf62fffSJeremy L Thompson /**
230775f0d5a4SJeremy L Thompson   @brief Get the multiplicity of nodes across suboperators in a composite CeedOperator
230875f0d5a4SJeremy L Thompson 
230975f0d5a4SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
231075f0d5a4SJeremy L Thompson 
231175f0d5a4SJeremy L Thompson   @param[in]  op               Composite CeedOperator
231275f0d5a4SJeremy L Thompson   @param[in]  num_skip_indices Number of suboperators to skip
231375f0d5a4SJeremy L Thompson   @param[in]  skip_indices     Array of indices of suboperators to skip
231475f0d5a4SJeremy L Thompson   @param[out] mult             Vector to store multiplicity (of size l_size)
231575f0d5a4SJeremy L Thompson 
231675f0d5a4SJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
231775f0d5a4SJeremy L Thompson 
231875f0d5a4SJeremy L Thompson   @ref User
231975f0d5a4SJeremy L Thompson **/
232075f0d5a4SJeremy L Thompson int CeedCompositeOperatorGetMultiplicity(CeedOperator op, CeedInt num_skip_indices, CeedInt *skip_indices, CeedVector mult) {
232175f0d5a4SJeremy L Thompson   Ceed                ceed;
2322b275c451SJeremy L Thompson   CeedInt             num_suboperators;
232375f0d5a4SJeremy L Thompson   CeedSize            l_vec_len;
232475f0d5a4SJeremy L Thompson   CeedScalar         *mult_array;
232575f0d5a4SJeremy L Thompson   CeedVector          ones_l_vec;
23267c1dbaffSSebastian Grimberg   CeedElemRestriction elem_rstr, mult_elem_rstr;
2327b275c451SJeremy L Thompson   CeedOperator       *sub_operators;
232875f0d5a4SJeremy L Thompson 
23291c66c397SJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
23301c66c397SJeremy L Thompson 
233175f0d5a4SJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
233275f0d5a4SJeremy L Thompson 
233375f0d5a4SJeremy L Thompson   // Zero mult vector
233475f0d5a4SJeremy L Thompson   CeedCall(CeedVectorSetValue(mult, 0.0));
233575f0d5a4SJeremy L Thompson 
233675f0d5a4SJeremy L Thompson   // Get suboperators
2337b275c451SJeremy L Thompson   CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators));
2338b275c451SJeremy L Thompson   CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators));
2339b275c451SJeremy L Thompson   if (num_suboperators == 0) return CEED_ERROR_SUCCESS;
234075f0d5a4SJeremy L Thompson 
234175f0d5a4SJeremy L Thompson   // Work vector
234275f0d5a4SJeremy L Thompson   CeedCall(CeedVectorGetLength(mult, &l_vec_len));
234375f0d5a4SJeremy L Thompson   CeedCall(CeedVectorCreate(ceed, l_vec_len, &ones_l_vec));
234475f0d5a4SJeremy L Thompson   CeedCall(CeedVectorSetValue(ones_l_vec, 1.0));
234575f0d5a4SJeremy L Thompson   CeedCall(CeedVectorGetArray(mult, CEED_MEM_HOST, &mult_array));
234675f0d5a4SJeremy L Thompson 
234775f0d5a4SJeremy L Thompson   // Compute multiplicity across suboperators
2348b275c451SJeremy L Thompson   for (CeedInt i = 0; i < num_suboperators; i++) {
234975f0d5a4SJeremy L Thompson     const CeedScalar *sub_mult_array;
235075f0d5a4SJeremy L Thompson     CeedVector        sub_mult_l_vec, ones_e_vec;
235175f0d5a4SJeremy L Thompson 
235275f0d5a4SJeremy L Thompson     // -- Check for suboperator to skip
235375f0d5a4SJeremy L Thompson     for (CeedInt j = 0; j < num_skip_indices; j++) {
235475f0d5a4SJeremy L Thompson       if (skip_indices[j] == i) continue;
235575f0d5a4SJeremy L Thompson     }
235675f0d5a4SJeremy L Thompson 
235775f0d5a4SJeremy L Thompson     // -- Sub operator multiplicity
2358437c7c90SJeremy L Thompson     CeedCall(CeedOperatorGetActiveElemRestriction(sub_operators[i], &elem_rstr));
23597c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionCreateUnorientedCopy(elem_rstr, &mult_elem_rstr));
23607c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionCreateVector(mult_elem_rstr, &sub_mult_l_vec, &ones_e_vec));
236175f0d5a4SJeremy L Thompson     CeedCall(CeedVectorSetValue(sub_mult_l_vec, 0.0));
23627c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionApply(mult_elem_rstr, CEED_NOTRANSPOSE, ones_l_vec, ones_e_vec, CEED_REQUEST_IMMEDIATE));
23637c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionApply(mult_elem_rstr, CEED_TRANSPOSE, ones_e_vec, sub_mult_l_vec, CEED_REQUEST_IMMEDIATE));
236475f0d5a4SJeremy L Thompson     CeedCall(CeedVectorGetArrayRead(sub_mult_l_vec, CEED_MEM_HOST, &sub_mult_array));
236575f0d5a4SJeremy L Thompson     // ---- Flag every node present in the current suboperator
236675f0d5a4SJeremy L Thompson     for (CeedInt j = 0; j < l_vec_len; j++) {
236775f0d5a4SJeremy L Thompson       if (sub_mult_array[j] > 0.0) mult_array[j] += 1.0;
236875f0d5a4SJeremy L Thompson     }
236975f0d5a4SJeremy L Thompson     CeedCall(CeedVectorRestoreArrayRead(sub_mult_l_vec, &sub_mult_array));
237075f0d5a4SJeremy L Thompson     CeedCall(CeedVectorDestroy(&sub_mult_l_vec));
237175f0d5a4SJeremy L Thompson     CeedCall(CeedVectorDestroy(&ones_e_vec));
23727c1dbaffSSebastian Grimberg     CeedCall(CeedElemRestrictionDestroy(&mult_elem_rstr));
237375f0d5a4SJeremy L Thompson   }
237475f0d5a4SJeremy L Thompson   CeedCall(CeedVectorRestoreArray(mult, &mult_array));
2375811d0ccfSJeremy L Thompson   CeedCall(CeedVectorDestroy(&ones_l_vec));
237675f0d5a4SJeremy L Thompson   return CEED_ERROR_SUCCESS;
237775f0d5a4SJeremy L Thompson }
237875f0d5a4SJeremy L Thompson 
237975f0d5a4SJeremy L Thompson /**
2380ea61e9acSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator, creating the prolongation basis from the fine and coarse
2381ea61e9acSJeremy L Thompson grid interpolation
2382eaf62fffSJeremy L Thompson 
238358e4b056SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable.
2384f04ea552SJeremy L Thompson 
2385eaf62fffSJeremy L Thompson   @param[in]  op_fine      Fine grid operator
238685bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine  L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
2387eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse  Coarse grid restriction
2388eaf62fffSJeremy L Thompson   @param[in]  basis_coarse Coarse grid active vector basis
2389eaf62fffSJeremy L Thompson   @param[out] op_coarse    Coarse grid operator
239085bb9dcfSJeremy L Thompson   @param[out] op_prolong   Coarse to fine operator, or NULL
23917758292fSSebastian Grimberg   @param[out] op_restrict  Fine to coarse operator, or NULL
2392eaf62fffSJeremy L Thompson 
2393eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2394eaf62fffSJeremy L Thompson 
2395eaf62fffSJeremy L Thompson   @ref User
2396eaf62fffSJeremy L Thompson **/
23972b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreate(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
23987758292fSSebastian Grimberg                                      CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_restrict) {
23991c66c397SJeremy L Thompson   CeedBasis basis_c_to_f = NULL;
24001c66c397SJeremy L Thompson 
24012b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
2402eaf62fffSJeremy L Thompson 
240383d6adf3SZach Atkins   // Build prolongation matrix, if required
24047758292fSSebastian Grimberg   if (op_prolong || op_restrict) {
240583d6adf3SZach Atkins     CeedBasis basis_fine;
24061c66c397SJeremy L Thompson 
24072b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
24082b730f8bSJeremy L Thompson     CeedCall(CeedBasisCreateProjection(basis_coarse, basis_fine, &basis_c_to_f));
240983d6adf3SZach Atkins   }
2410eaf62fffSJeremy L Thompson 
2411f113e5dcSJeremy L Thompson   // Core code
24127758292fSSebastian Grimberg   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
2413eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2414eaf62fffSJeremy L Thompson }
2415eaf62fffSJeremy L Thompson 
2416eaf62fffSJeremy L Thompson /**
2417ea61e9acSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator with a tensor basis for the active basis
2418eaf62fffSJeremy L Thompson 
241958e4b056SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable.
2420f04ea552SJeremy L Thompson 
2421eaf62fffSJeremy L Thompson   @param[in]  op_fine       Fine grid operator
242285bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine   L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
2423eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse   Coarse grid restriction
2424eaf62fffSJeremy L Thompson   @param[in]  basis_coarse  Coarse grid active vector basis
242585bb9dcfSJeremy L Thompson   @param[in]  interp_c_to_f Matrix for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators
2426eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
242785bb9dcfSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator, or NULL
24287758292fSSebastian Grimberg   @param[out] op_restrict   Fine to coarse operator, or NULL
2429eaf62fffSJeremy L Thompson 
2430eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2431eaf62fffSJeremy L Thompson 
2432eaf62fffSJeremy L Thompson   @ref User
2433eaf62fffSJeremy L Thompson **/
24342b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
24352b730f8bSJeremy L Thompson                                              const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
24367758292fSSebastian Grimberg                                              CeedOperator *op_restrict) {
2437eaf62fffSJeremy L Thompson   Ceed      ceed;
24381c66c397SJeremy L Thompson   CeedInt   Q_f, Q_c;
24391c66c397SJeremy L Thompson   CeedBasis basis_fine, basis_c_to_f = NULL;
24401c66c397SJeremy L Thompson 
24411c66c397SJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
24422b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
2443eaf62fffSJeremy L Thompson 
2444eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
24452b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
24462b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f));
24472b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c));
24486574a04fSJeremy L Thompson   CeedCheck(Q_f == Q_c, ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces");
2449eaf62fffSJeremy L Thompson 
245083d6adf3SZach Atkins   // Create coarse to fine basis, if required
24517758292fSSebastian Grimberg   if (op_prolong || op_restrict) {
24521c66c397SJeremy L Thompson     CeedInt     dim, num_comp, num_nodes_c, P_1d_f, P_1d_c;
24531c66c397SJeremy L Thompson     CeedScalar *q_ref, *q_weight, *grad;
24541c66c397SJeremy L Thompson 
245583d6adf3SZach Atkins     // Check if interpolation matrix is provided
24566574a04fSJeremy L Thompson     CeedCheck(interp_c_to_f, ceed, CEED_ERROR_INCOMPATIBLE,
24576574a04fSJeremy L Thompson               "Prolongation or restriction operator creation requires coarse-to-fine interpolation matrix");
24582b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetDimension(basis_fine, &dim));
24592b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp));
24602b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumNodes1D(basis_fine, &P_1d_f));
24612b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c));
24622b730f8bSJeremy L Thompson     P_1d_c = dim == 1 ? num_nodes_c : dim == 2 ? sqrt(num_nodes_c) : cbrt(num_nodes_c);
24632b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d_f, &q_ref));
24642b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d_f, &q_weight));
24652b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d_f * P_1d_c * dim, &grad));
24662b730f8bSJeremy L Thompson     CeedCall(CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d_c, P_1d_f, interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f));
24672b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_ref));
24682b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_weight));
24692b730f8bSJeremy L Thompson     CeedCall(CeedFree(&grad));
247083d6adf3SZach Atkins   }
2471eaf62fffSJeremy L Thompson 
2472eaf62fffSJeremy L Thompson   // Core code
24737758292fSSebastian Grimberg   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
2474eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2475eaf62fffSJeremy L Thompson }
2476eaf62fffSJeremy L Thompson 
2477eaf62fffSJeremy L Thompson /**
2478ea61e9acSJeremy L Thompson   @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator with a non-tensor basis for the active vector
2479eaf62fffSJeremy L Thompson 
248058e4b056SJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable.
2481f04ea552SJeremy L Thompson 
2482eaf62fffSJeremy L Thompson   @param[in]  op_fine       Fine grid operator
248385bb9dcfSJeremy L Thompson   @param[in]  p_mult_fine   L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators
2484eaf62fffSJeremy L Thompson   @param[in]  rstr_coarse   Coarse grid restriction
2485eaf62fffSJeremy L Thompson   @param[in]  basis_coarse  Coarse grid active vector basis
248685bb9dcfSJeremy L Thompson   @param[in]  interp_c_to_f Matrix for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators
2487eaf62fffSJeremy L Thompson   @param[out] op_coarse     Coarse grid operator
248885bb9dcfSJeremy L Thompson   @param[out] op_prolong    Coarse to fine operator, or NULL
24897758292fSSebastian Grimberg   @param[out] op_restrict   Fine to coarse operator, or NULL
2490eaf62fffSJeremy L Thompson 
2491eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2492eaf62fffSJeremy L Thompson 
2493eaf62fffSJeremy L Thompson   @ref User
2494eaf62fffSJeremy L Thompson **/
24952b730f8bSJeremy L Thompson int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse,
24967758292fSSebastian Grimberg                                        const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong,
24977758292fSSebastian Grimberg                                        CeedOperator *op_restrict) {
2498eaf62fffSJeremy L Thompson   Ceed      ceed;
24991c66c397SJeremy L Thompson   CeedInt   Q_f, Q_c;
25001c66c397SJeremy L Thompson   CeedBasis basis_fine, basis_c_to_f = NULL;
25011c66c397SJeremy L Thompson 
25021c66c397SJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op_fine));
25032b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op_fine, &ceed));
2504eaf62fffSJeremy L Thompson 
2505eaf62fffSJeremy L Thompson   // Check for compatible quadrature spaces
25062b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine));
25072b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f));
25082b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c));
25096574a04fSJeremy L Thompson   CeedCheck(Q_f == Q_c, ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces");
2510eaf62fffSJeremy L Thompson 
2511eaf62fffSJeremy L Thompson   // Coarse to fine basis
25127758292fSSebastian Grimberg   if (op_prolong || op_restrict) {
25131c66c397SJeremy L Thompson     CeedInt          dim, num_comp, num_nodes_c, num_nodes_f;
25141c66c397SJeremy L Thompson     CeedScalar      *q_ref, *q_weight, *grad;
25151c66c397SJeremy L Thompson     CeedElemTopology topo;
25161c66c397SJeremy L Thompson 
251783d6adf3SZach Atkins     // Check if interpolation matrix is provided
25186574a04fSJeremy L Thompson     CeedCheck(interp_c_to_f, ceed, CEED_ERROR_INCOMPATIBLE,
25196574a04fSJeremy L Thompson               "Prolongation or restriction operator creation requires coarse-to-fine interpolation matrix");
25202b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetTopology(basis_fine, &topo));
25212b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetDimension(basis_fine, &dim));
25222b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp));
25232b730f8bSJeremy L Thompson     CeedCall(CeedBasisGetNumNodes(basis_fine, &num_nodes_f));
25242b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c));
25252b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_nodes_f * dim, &q_ref));
25262b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_nodes_f, &q_weight));
25272b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_nodes_f * num_nodes_c * dim, &grad));
25282b730f8bSJeremy L Thompson     CeedCall(CeedBasisCreateH1(ceed, topo, num_comp, num_nodes_c, num_nodes_f, interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f));
25292b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_ref));
25302b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_weight));
25312b730f8bSJeremy L Thompson     CeedCall(CeedFree(&grad));
253283d6adf3SZach Atkins   }
2533eaf62fffSJeremy L Thompson 
2534eaf62fffSJeremy L Thompson   // Core code
25357758292fSSebastian Grimberg   CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_restrict));
2536eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2537eaf62fffSJeremy L Thompson }
2538eaf62fffSJeremy L Thompson 
2539eaf62fffSJeremy L Thompson /**
2540ea61e9acSJeremy L Thompson   @brief Build a FDM based approximate inverse for each element for a CeedOperator
2541eaf62fffSJeremy L Thompson 
2542ea61e9acSJeremy L Thompson   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization Method based approximate inverse.
2543859c15bbSJames Wright   This function obtains the simultaneous diagonalization for the 1D mass and Laplacian operators, \f$M = V^T V, K = V^T S V\f$.
2544859c15bbSJames Wright   The assembled QFunction is used to modify the eigenvalues from simultaneous diagonalization and obtain an approximate inverse of the form \f$V^T
25459fd66db6SSebastian Grimberg \hat S V\f$.
25469fd66db6SSebastian Grimberg   The CeedOperator must be linear and non-composite.
25479fd66db6SSebastian Grimberg   The associated CeedQFunction must therefore also be linear.
2548eaf62fffSJeremy L Thompson 
2549ea61e9acSJeremy L Thompson   Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable.
2550f04ea552SJeremy L Thompson 
2551ea61e9acSJeremy L Thompson   @param[in]  op      CeedOperator to create element inverses
2552ea61e9acSJeremy L Thompson   @param[out] fdm_inv CeedOperator to apply the action of a FDM based inverse for each element
2553ea61e9acSJeremy L Thompson   @param[in]  request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE
2554eaf62fffSJeremy L Thompson 
2555eaf62fffSJeremy L Thompson   @return An error code: 0 - success, otherwise - failure
2556eaf62fffSJeremy L Thompson 
2557480fae85SJeremy L Thompson   @ref User
2558eaf62fffSJeremy L Thompson **/
25592b730f8bSJeremy L Thompson int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv, CeedRequest *request) {
25601c66c397SJeremy L Thompson   Ceed                 ceed, ceed_parent;
25611c66c397SJeremy L Thompson   bool                 interp = false, grad = false, is_tensor_basis = true;
25621c66c397SJeremy L Thompson   CeedInt              num_input_fields, P_1d, Q_1d, num_nodes, num_qpts, dim, num_comp = 1, num_elem = 1;
25631c66c397SJeremy L Thompson   CeedSize             l_size = 1;
25641c66c397SJeremy L Thompson   CeedScalar          *mass, *laplace, *x, *fdm_interp, *lambda, *elem_avg;
25651c66c397SJeremy L Thompson   const CeedScalar    *interp_1d, *grad_1d, *q_weight_1d;
25661c66c397SJeremy L Thompson   CeedVector           q_data;
25671c66c397SJeremy L Thompson   CeedElemRestriction  rstr  = NULL, rstr_qd_i;
25681c66c397SJeremy L Thompson   CeedBasis            basis = NULL, fdm_basis;
25691c66c397SJeremy L Thompson   CeedQFunctionContext ctx_fdm;
25701c66c397SJeremy L Thompson   CeedQFunctionField  *qf_fields;
25711c66c397SJeremy L Thompson   CeedQFunction        qf, qf_fdm;
25721c66c397SJeremy L Thompson   CeedOperatorField   *op_fields;
25731c66c397SJeremy L Thompson 
25742b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCheckReady(op));
2575eaf62fffSJeremy L Thompson 
2576eaf62fffSJeremy L Thompson   if (op->CreateFDMElementInverse) {
2577d04bbc78SJeremy L Thompson     // Backend version
25782b730f8bSJeremy L Thompson     CeedCall(op->CreateFDMElementInverse(op, fdm_inv, request));
2579eaf62fffSJeremy L Thompson     return CEED_ERROR_SUCCESS;
2580eaf62fffSJeremy L Thompson   } else {
2581d04bbc78SJeremy L Thompson     // Operator fallback
2582d04bbc78SJeremy L Thompson     CeedOperator op_fallback;
2583d04bbc78SJeremy L Thompson 
25842b730f8bSJeremy L Thompson     CeedCall(CeedOperatorGetFallback(op, &op_fallback));
2585d04bbc78SJeremy L Thompson     if (op_fallback) {
25862b730f8bSJeremy L Thompson       CeedCall(CeedOperatorCreateFDMElementInverse(op_fallback, fdm_inv, request));
2587eaf62fffSJeremy L Thompson       return CEED_ERROR_SUCCESS;
2588eaf62fffSJeremy L Thompson     }
2589eaf62fffSJeremy L Thompson   }
2590eaf62fffSJeremy L Thompson 
2591d04bbc78SJeremy L Thompson   // Default interface implementation
25922b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetCeed(op, &ceed));
2593bb229da9SJeremy L Thompson   CeedCall(CeedOperatorGetFallbackParentCeed(op, &ceed_parent));
25942b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetQFunction(op, &qf));
2595eaf62fffSJeremy L Thompson 
2596eaf62fffSJeremy L Thompson   // Determine active input basis
25972b730f8bSJeremy L Thompson   CeedCall(CeedOperatorGetFields(op, &num_input_fields, &op_fields, NULL, NULL));
25982b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionGetFields(qf, NULL, &qf_fields, NULL, NULL));
2599eaf62fffSJeremy L Thompson   for (CeedInt i = 0; i < num_input_fields; i++) {
2600eaf62fffSJeremy L Thompson     CeedVector vec;
26011c66c397SJeremy L Thompson 
26022b730f8bSJeremy L Thompson     CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec));
2603eaf62fffSJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
2604eaf62fffSJeremy L Thompson       CeedEvalMode eval_mode;
26051c66c397SJeremy L Thompson 
26062b730f8bSJeremy L Thompson       CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode));
2607eaf62fffSJeremy L Thompson       interp = interp || eval_mode == CEED_EVAL_INTERP;
2608eaf62fffSJeremy L Thompson       grad   = grad || eval_mode == CEED_EVAL_GRAD;
26092b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis));
26102b730f8bSJeremy L Thompson       CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr));
2611eaf62fffSJeremy L Thompson     }
2612eaf62fffSJeremy L Thompson   }
26136574a04fSJeremy L Thompson   CeedCheck(basis, ceed, CEED_ERROR_BACKEND, "No active field set");
26142b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumNodes1D(basis, &P_1d));
2615352a5e7cSSebastian Grimberg   CeedCall(CeedBasisGetNumNodes(basis, &num_nodes));
26162b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d));
26172b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumQuadraturePoints(basis, &num_qpts));
26182b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetDimension(basis, &dim));
26192b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetNumComponents(basis, &num_comp));
26202b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem));
26212b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size));
2622eaf62fffSJeremy L Thompson 
2623eaf62fffSJeremy L Thompson   // Build and diagonalize 1D Mass and Laplacian
26246574a04fSJeremy L Thompson   CeedCall(CeedBasisIsTensor(basis, &is_tensor_basis));
26256574a04fSJeremy L Thompson   CeedCheck(is_tensor_basis, ceed, CEED_ERROR_BACKEND, "FDMElementInverse only supported for tensor bases");
26262b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &mass));
26272b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &laplace));
26282b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &x));
26292b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d * P_1d, &fdm_interp));
26302b730f8bSJeremy L Thompson   CeedCall(CeedCalloc(P_1d, &lambda));
2631eaf62fffSJeremy L Thompson   // -- Build matrices
26322b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetInterp1D(basis, &interp_1d));
26332b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetGrad1D(basis, &grad_1d));
26342b730f8bSJeremy L Thompson   CeedCall(CeedBasisGetQWeights(basis, &q_weight_1d));
26352b730f8bSJeremy L Thompson   CeedCall(CeedBuildMassLaplace(interp_1d, grad_1d, q_weight_1d, P_1d, Q_1d, dim, mass, laplace));
2636eaf62fffSJeremy L Thompson 
2637eaf62fffSJeremy L Thompson   // -- Diagonalize
26382b730f8bSJeremy L Thompson   CeedCall(CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d));
26392b730f8bSJeremy L Thompson   CeedCall(CeedFree(&mass));
26402b730f8bSJeremy L Thompson   CeedCall(CeedFree(&laplace));
26412b730f8bSJeremy L Thompson   for (CeedInt i = 0; i < P_1d; i++) {
26422b730f8bSJeremy L Thompson     for (CeedInt j = 0; j < P_1d; j++) fdm_interp[i + j * P_1d] = x[j + i * P_1d];
26432b730f8bSJeremy L Thompson   }
26442b730f8bSJeremy L Thompson   CeedCall(CeedFree(&x));
2645eaf62fffSJeremy L Thompson 
26461c66c397SJeremy L Thompson   {
26471c66c397SJeremy L Thompson     CeedInt             layout[3], num_modes = (interp ? 1 : 0) + (grad ? dim : 0);
26481c66c397SJeremy L Thompson     CeedScalar          max_norm = 0;
26491c66c397SJeremy L Thompson     const CeedScalar   *assembled_array, *q_weight_array;
26501c66c397SJeremy L Thompson     CeedVector          assembled = NULL, q_weight;
2651c5f45aeaSJeremy L Thompson     CeedElemRestriction rstr_qf   = NULL;
26521c66c397SJeremy L Thompson 
26531c66c397SJeremy L Thompson     // Assemble QFunction
26542b730f8bSJeremy L Thompson     CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled, &rstr_qf, request));
26552b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionGetELayout(rstr_qf, &layout));
26562b730f8bSJeremy L Thompson     CeedCall(CeedElemRestrictionDestroy(&rstr_qf));
26572b730f8bSJeremy L Thompson     CeedCall(CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm));
2658eaf62fffSJeremy L Thompson 
2659eaf62fffSJeremy L Thompson     // Calculate element averages
26602b730f8bSJeremy L Thompson     CeedCall(CeedVectorCreate(ceed_parent, num_qpts, &q_weight));
26612b730f8bSJeremy L Thompson     CeedCall(CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT, CEED_VECTOR_NONE, q_weight));
26622b730f8bSJeremy L Thompson     CeedCall(CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembled_array));
26632b730f8bSJeremy L Thompson     CeedCall(CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array));
26642b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(num_elem, &elem_avg));
2665eaf62fffSJeremy L Thompson     const CeedScalar qf_value_bound = max_norm * 100 * CEED_EPSILON;
26661c66c397SJeremy L Thompson 
2667eaf62fffSJeremy L Thompson     for (CeedInt e = 0; e < num_elem; e++) {
2668eaf62fffSJeremy L Thompson       CeedInt count = 0;
26691c66c397SJeremy L Thompson 
26702b730f8bSJeremy L Thompson       for (CeedInt q = 0; q < num_qpts; q++) {
26712b730f8bSJeremy L Thompson         for (CeedInt i = 0; i < num_comp * num_comp * num_modes * num_modes; i++) {
26722b730f8bSJeremy L Thompson           if (fabs(assembled_array[q * layout[0] + i * layout[1] + e * layout[2]]) > qf_value_bound) {
26732b730f8bSJeremy L Thompson             elem_avg[e] += assembled_array[q * layout[0] + i * layout[1] + e * layout[2]] / q_weight_array[q];
2674eaf62fffSJeremy L Thompson             count++;
2675eaf62fffSJeremy L Thompson           }
26762b730f8bSJeremy L Thompson         }
26772b730f8bSJeremy L Thompson       }
2678eaf62fffSJeremy L Thompson       if (count) {
2679eaf62fffSJeremy L Thompson         elem_avg[e] /= count;
2680eaf62fffSJeremy L Thompson       } else {
2681eaf62fffSJeremy L Thompson         elem_avg[e] = 1.0;
2682eaf62fffSJeremy L Thompson       }
2683eaf62fffSJeremy L Thompson     }
26842b730f8bSJeremy L Thompson     CeedCall(CeedVectorRestoreArrayRead(assembled, &assembled_array));
26852b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&assembled));
26862b730f8bSJeremy L Thompson     CeedCall(CeedVectorRestoreArrayRead(q_weight, &q_weight_array));
26872b730f8bSJeremy L Thompson     CeedCall(CeedVectorDestroy(&q_weight));
26881c66c397SJeremy L Thompson   }
2689eaf62fffSJeremy L Thompson 
2690eaf62fffSJeremy L Thompson   // Build FDM diagonal
26911c66c397SJeremy L Thompson   {
2692eaf62fffSJeremy L Thompson     CeedScalar *q_data_array, *fdm_diagonal;
26931c66c397SJeremy L Thompson 
2694352a5e7cSSebastian Grimberg     CeedCall(CeedCalloc(num_comp * num_nodes, &fdm_diagonal));
2695352a5e7cSSebastian Grimberg     const CeedScalar fdm_diagonal_bound = num_nodes * CEED_EPSILON;
26962b730f8bSJeremy L Thompson     for (CeedInt c = 0; c < num_comp; c++) {
2697352a5e7cSSebastian Grimberg       for (CeedInt n = 0; n < num_nodes; n++) {
2698352a5e7cSSebastian Grimberg         if (interp) fdm_diagonal[c * num_nodes + n] = 1.0;
26992b730f8bSJeremy L Thompson         if (grad) {
2700eaf62fffSJeremy L Thompson           for (CeedInt d = 0; d < dim; d++) {
2701eaf62fffSJeremy L Thompson             CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d;
2702352a5e7cSSebastian Grimberg             fdm_diagonal[c * num_nodes + n] += lambda[i];
2703eaf62fffSJeremy L Thompson           }
2704eaf62fffSJeremy L Thompson         }
2705352a5e7cSSebastian Grimberg         if (fabs(fdm_diagonal[c * num_nodes + n]) < fdm_diagonal_bound) fdm_diagonal[c * num_nodes + n] = fdm_diagonal_bound;
27062b730f8bSJeremy L Thompson       }
27072b730f8bSJeremy L Thompson     }
2708352a5e7cSSebastian Grimberg     CeedCall(CeedVectorCreate(ceed_parent, num_elem * num_comp * num_nodes, &q_data));
27092b730f8bSJeremy L Thompson     CeedCall(CeedVectorSetValue(q_data, 0.0));
27102b730f8bSJeremy L Thompson     CeedCall(CeedVectorGetArrayWrite(q_data, CEED_MEM_HOST, &q_data_array));
27112b730f8bSJeremy L Thompson     for (CeedInt e = 0; e < num_elem; e++) {
27122b730f8bSJeremy L Thompson       for (CeedInt c = 0; c < num_comp; c++) {
27131c66c397SJeremy L Thompson         for (CeedInt n = 0; n < num_nodes; n++)
27141c66c397SJeremy L Thompson           q_data_array[(e * num_comp + c) * num_nodes + n] = 1. / (elem_avg[e] * fdm_diagonal[c * num_nodes + n]);
27152b730f8bSJeremy L Thompson       }
27162b730f8bSJeremy L Thompson     }
27172b730f8bSJeremy L Thompson     CeedCall(CeedFree(&elem_avg));
27182b730f8bSJeremy L Thompson     CeedCall(CeedFree(&fdm_diagonal));
27192b730f8bSJeremy L Thompson     CeedCall(CeedVectorRestoreArray(q_data, &q_data_array));
27201c66c397SJeremy L Thompson   }
2721eaf62fffSJeremy L Thompson 
2722eaf62fffSJeremy L Thompson   // Setup FDM operator
2723eaf62fffSJeremy L Thompson   // -- Basis
27241c66c397SJeremy L Thompson   {
2725eaf62fffSJeremy L Thompson     CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy;
27261c66c397SJeremy L Thompson 
27272b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d * P_1d, &grad_dummy));
27282b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d, &q_ref_dummy));
27292b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(P_1d, &q_weight_dummy));
27302b730f8bSJeremy L Thompson     CeedCall(CeedBasisCreateTensorH1(ceed_parent, dim, num_comp, P_1d, P_1d, fdm_interp, grad_dummy, q_ref_dummy, q_weight_dummy, &fdm_basis));
27312b730f8bSJeremy L Thompson     CeedCall(CeedFree(&fdm_interp));
27322b730f8bSJeremy L Thompson     CeedCall(CeedFree(&grad_dummy));
27332b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_ref_dummy));
27342b730f8bSJeremy L Thompson     CeedCall(CeedFree(&q_weight_dummy));
27352b730f8bSJeremy L Thompson     CeedCall(CeedFree(&lambda));
27361c66c397SJeremy L Thompson   }
2737eaf62fffSJeremy L Thompson 
2738eaf62fffSJeremy L Thompson   // -- Restriction
27391c66c397SJeremy L Thompson   {
2740352a5e7cSSebastian Grimberg     CeedInt strides[3] = {1, num_nodes, num_nodes * num_comp};
2741352a5e7cSSebastian Grimberg     CeedCall(CeedElemRestrictionCreateStrided(ceed_parent, num_elem, num_nodes, num_comp, num_elem * num_comp * num_nodes, strides, &rstr_qd_i));
27421c66c397SJeremy L Thompson   }
27431c66c397SJeremy L Thompson 
2744eaf62fffSJeremy L Thompson   // -- QFunction
27452b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionCreateInteriorByName(ceed_parent, "Scale", &qf_fdm));
27462b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_fdm, "input", num_comp, CEED_EVAL_INTERP));
27472b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddInput(qf_fdm, "scale", num_comp, CEED_EVAL_NONE));
27482b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionAddOutput(qf_fdm, "output", num_comp, CEED_EVAL_INTERP));
27492b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_fdm, num_comp));
27501c66c397SJeremy L Thompson 
2751eaf62fffSJeremy L Thompson   // -- QFunction context
27521c66c397SJeremy L Thompson   {
2753eaf62fffSJeremy L Thompson     CeedInt *num_comp_data;
27541c66c397SJeremy L Thompson 
27552b730f8bSJeremy L Thompson     CeedCall(CeedCalloc(1, &num_comp_data));
2756eaf62fffSJeremy L Thompson     num_comp_data[0] = num_comp;
27572b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextCreate(ceed, &ctx_fdm));
27582b730f8bSJeremy L Thompson     CeedCall(CeedQFunctionContextSetData(ctx_fdm, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_data), num_comp_data));
27591c66c397SJeremy L Thompson   }
27602b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionSetContext(qf_fdm, ctx_fdm));
27612b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionContextDestroy(&ctx_fdm));
27621c66c397SJeremy L Thompson 
2763eaf62fffSJeremy L Thompson   // -- Operator
27642b730f8bSJeremy L Thompson   CeedCall(CeedOperatorCreate(ceed_parent, qf_fdm, NULL, NULL, fdm_inv));
27652b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "input", rstr, fdm_basis, CEED_VECTOR_ACTIVE));
2766356036faSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "scale", rstr_qd_i, CEED_BASIS_NONE, q_data));
27672b730f8bSJeremy L Thompson   CeedCall(CeedOperatorSetField(*fdm_inv, "output", rstr, fdm_basis, CEED_VECTOR_ACTIVE));
2768eaf62fffSJeremy L Thompson 
2769eaf62fffSJeremy L Thompson   // Cleanup
27702b730f8bSJeremy L Thompson   CeedCall(CeedVectorDestroy(&q_data));
27712b730f8bSJeremy L Thompson   CeedCall(CeedBasisDestroy(&fdm_basis));
27722b730f8bSJeremy L Thompson   CeedCall(CeedElemRestrictionDestroy(&rstr_qd_i));
27732b730f8bSJeremy L Thompson   CeedCall(CeedQFunctionDestroy(&qf_fdm));
2774eaf62fffSJeremy L Thompson   return CEED_ERROR_SUCCESS;
2775eaf62fffSJeremy L Thompson }
2776eaf62fffSJeremy L Thompson 
2777eaf62fffSJeremy L Thompson /// @}
2778