xref: /libCEED/backends/ref/ceed-ref-operator.c (revision 0b454692e1b44187f5e3492150f1dd8bba6f5f17)
121617c04Sjeremylt // Copyright (c) 2017-2018, Lawrence Livermore National Security, LLC.
221617c04Sjeremylt // Produced at the Lawrence Livermore National Laboratory. LLNL-CODE-734707.
321617c04Sjeremylt // All Rights reserved. See files LICENSE and NOTICE for details.
421617c04Sjeremylt //
521617c04Sjeremylt // This file is part of CEED, a collection of benchmarks, miniapps, software
621617c04Sjeremylt // libraries and APIs for efficient high-order finite element and spectral
721617c04Sjeremylt // element discretizations for exascale applications. For more information and
821617c04Sjeremylt // source code availability see http://github.com/ceed.
921617c04Sjeremylt //
1021617c04Sjeremylt // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
1121617c04Sjeremylt // a collaborative effort of two U.S. Department of Energy organizations (Office
1221617c04Sjeremylt // of Science and the National Nuclear Security Administration) responsible for
1321617c04Sjeremylt // the planning and preparation of a capable exascale ecosystem, including
1421617c04Sjeremylt // software, applications, hardware, advanced system engineering and early
1521617c04Sjeremylt // testbed platforms, in support of the nation's exascale computing imperative.
1621617c04Sjeremylt 
17ec3da8bcSJed Brown #include <ceed/ceed.h>
18ec3da8bcSJed Brown #include <ceed/backend.h>
193d576824SJeremy L Thompson #include <math.h>
203d576824SJeremy L Thompson #include <stdbool.h>
213d576824SJeremy L Thompson #include <stddef.h>
223d576824SJeremy L Thompson #include <stdint.h>
2321617c04Sjeremylt #include "ceed-ref.h"
2421617c04Sjeremylt 
25f10650afSjeremylt //------------------------------------------------------------------------------
26f10650afSjeremylt // Setup Input/Output Fields
27f10650afSjeremylt //------------------------------------------------------------------------------
28fe2413ffSjeremylt static int CeedOperatorSetupFields_Ref(CeedQFunction qf, CeedOperator op,
2991703d3fSjeremylt                                        bool inOrOut,
30d1d35e2fSjeremylt                                        CeedVector *full_evecs, CeedVector *e_vecs,
31d1d35e2fSjeremylt                                        CeedVector *q_vecs, CeedInt starte,
32d1d35e2fSjeremylt                                        CeedInt num_fields, CeedInt Q) {
334d537eeaSYohann   CeedInt dim, ierr, size, P;
34aedaa0e5Sjeremylt   Ceed ceed;
35e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
36d1bcdac9Sjeremylt   CeedBasis basis;
37d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
38d1d35e2fSjeremylt   CeedOperatorField *op_fields;
39d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
40fe2413ffSjeremylt   if (inOrOut) {
41d1d35e2fSjeremylt     ierr = CeedOperatorGetFields(op, NULL, &op_fields); CeedChkBackend(ierr);
42d1d35e2fSjeremylt     ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields); CeedChkBackend(ierr);
43fe2413ffSjeremylt   } else {
44d1d35e2fSjeremylt     ierr = CeedOperatorGetFields(op, &op_fields, NULL); CeedChkBackend(ierr);
45d1d35e2fSjeremylt     ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChkBackend(ierr);
46fe2413ffSjeremylt   }
4721617c04Sjeremylt 
48885ac19cSjeremylt   // Loop over fields
49d1d35e2fSjeremylt   for (CeedInt i=0; i<num_fields; i++) {
50d1d35e2fSjeremylt     CeedEvalMode eval_mode;
51d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
52e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
53d1d35e2fSjeremylt 
54d1d35e2fSjeremylt     if (eval_mode != CEED_EVAL_WEIGHT) {
55d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_restr);
56d1d35e2fSjeremylt       CeedChkBackend(ierr);
57d1d35e2fSjeremylt       ierr = CeedElemRestrictionCreateVector(elem_restr, NULL,
58d1d35e2fSjeremylt                                              &full_evecs[i+starte]);
59e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
60135a076eSjeremylt     }
61135a076eSjeremylt 
62d1d35e2fSjeremylt     switch(eval_mode) {
63885ac19cSjeremylt     case CEED_EVAL_NONE:
64d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_fields[i], &size); CeedChkBackend(ierr);
65d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q*size, &q_vecs[i]); CeedChkBackend(ierr);
66aedaa0e5Sjeremylt       break;
67aedaa0e5Sjeremylt     case CEED_EVAL_INTERP:
68d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_fields[i], &size); CeedChkBackend(ierr);
69d1d35e2fSjeremylt       ierr = CeedElemRestrictionGetElementSize(elem_restr, &P);
70e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
71d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, P*size, &e_vecs[i]); CeedChkBackend(ierr);
72d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q*size, &q_vecs[i]); CeedChkBackend(ierr);
73885ac19cSjeremylt       break;
74885ac19cSjeremylt     case CEED_EVAL_GRAD:
75d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChkBackend(ierr);
76d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_fields[i], &size); CeedChkBackend(ierr);
77e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
78d1d35e2fSjeremylt       ierr = CeedElemRestrictionGetElementSize(elem_restr, &P);
79e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
80d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, P*size/dim, &e_vecs[i]); CeedChkBackend(ierr);
81d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q*size, &q_vecs[i]); CeedChkBackend(ierr);
82885ac19cSjeremylt       break;
83885ac19cSjeremylt     case CEED_EVAL_WEIGHT: // Only on input fields
84d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChkBackend(ierr);
85d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q, &q_vecs[i]); CeedChkBackend(ierr);
86d1bcdac9Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
87d1d35e2fSjeremylt                             CEED_VECTOR_NONE, q_vecs[i]); CeedChkBackend(ierr);
88885ac19cSjeremylt       break;
89885ac19cSjeremylt     case CEED_EVAL_DIV:
904d537eeaSYohann       break; // Not implemented
91885ac19cSjeremylt     case CEED_EVAL_CURL:
924d537eeaSYohann       break; // Not implemented
9321617c04Sjeremylt     }
94885ac19cSjeremylt   }
95e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
9621617c04Sjeremylt }
9721617c04Sjeremylt 
98f10650afSjeremylt //------------------------------------------------------------------------------
99f10650afSjeremylt // Setup Operator
100f10650afSjeremylt //------------------------------------------------------------------------------/*
101885ac19cSjeremylt static int CeedOperatorSetup_Ref(CeedOperator op) {
10221617c04Sjeremylt   int ierr;
103d1d35e2fSjeremylt   bool setup_done;
104d1d35e2fSjeremylt   ierr = CeedOperatorIsSetupDone(op, &setup_done); CeedChkBackend(ierr);
105d1d35e2fSjeremylt   if (setup_done) return CEED_ERROR_SUCCESS;
106aedaa0e5Sjeremylt   Ceed ceed;
107e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
1084ce2993fSjeremylt   CeedOperator_Ref *impl;
109e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
1104ce2993fSjeremylt   CeedQFunction qf;
111e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
112d1d35e2fSjeremylt   CeedInt Q, num_input_fields, num_output_fields;
113e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetNumQuadraturePoints(op, &Q); CeedChkBackend(ierr);
114*0b454692Sjeremylt   ierr = CeedQFunctionIsIdentity(qf, &impl->is_identity_qf); CeedChkBackend(ierr);
115d1d35e2fSjeremylt   ierr = CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
116e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
117d1d35e2fSjeremylt   CeedOperatorField *op_input_fields, *op_output_fields;
118d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_input_fields, &op_output_fields);
119e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
120d1d35e2fSjeremylt   CeedQFunctionField *qf_input_fields, *qf_output_fields;
121d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_input_fields, &qf_output_fields);
122e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
123885ac19cSjeremylt 
124885ac19cSjeremylt   // Allocate
125d1d35e2fSjeremylt   ierr = CeedCalloc(num_input_fields + num_output_fields, &impl->e_vecs);
126e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
127d1d35e2fSjeremylt   ierr = CeedCalloc(num_input_fields + num_output_fields, &impl->e_data);
128e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
129885ac19cSjeremylt 
130d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->input_state); CeedChkBackend(ierr);
131d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->e_vecs_in); CeedChkBackend(ierr);
132d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->e_vecs_out); CeedChkBackend(ierr);
133d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->q_vecs_in); CeedChkBackend(ierr);
134d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->q_vecs_out); CeedChkBackend(ierr);
135885ac19cSjeremylt 
136d1d35e2fSjeremylt   impl->num_e_vecs_in = num_input_fields;
137d1d35e2fSjeremylt   impl->num_e_vecs_out = num_output_fields;
138885ac19cSjeremylt 
139d1d35e2fSjeremylt   // Set up infield and outfield e_vecs and q_vecs
140885ac19cSjeremylt   // Infields
141d1d35e2fSjeremylt   ierr = CeedOperatorSetupFields_Ref(qf, op, 0, impl->e_vecs,
142d1d35e2fSjeremylt                                      impl->e_vecs_in, impl->q_vecs_in, 0,
143d1d35e2fSjeremylt                                      num_input_fields, Q);
144e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
145885ac19cSjeremylt   // Outfields
146d1d35e2fSjeremylt   ierr = CeedOperatorSetupFields_Ref(qf, op, 1, impl->e_vecs,
147d1d35e2fSjeremylt                                      impl->e_vecs_out, impl->q_vecs_out,
148d1d35e2fSjeremylt                                      num_input_fields, num_output_fields, Q);
149e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
150885ac19cSjeremylt 
15116911fdaSjeremylt   // Identity QFunctions
152*0b454692Sjeremylt   if (impl->is_identity_qf) {
153d1d35e2fSjeremylt     CeedEvalMode in_mode, out_mode;
154d1d35e2fSjeremylt     CeedQFunctionField *in_fields, *out_fields;
155d1d35e2fSjeremylt     ierr = CeedQFunctionGetFields(qf, &in_fields, &out_fields);
156e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
157*0b454692Sjeremylt     ierr = CeedQFunctionFieldGetEvalMode(in_fields[0], &in_mode);
158d1d35e2fSjeremylt     CeedChkBackend(ierr);
159*0b454692Sjeremylt     ierr = CeedQFunctionFieldGetEvalMode(out_fields[0], &out_mode);
160d1d35e2fSjeremylt     CeedChkBackend(ierr);
161d1d35e2fSjeremylt 
162*0b454692Sjeremylt     if (in_mode == CEED_EVAL_NONE && out_mode == CEED_EVAL_NONE) {
163*0b454692Sjeremylt       impl->is_identity_restr_op = true;
164*0b454692Sjeremylt     } else {
165*0b454692Sjeremylt       ierr = CeedVectorDestroy(&impl->q_vecs_out[0]); CeedChkBackend(ierr);
166*0b454692Sjeremylt       impl->q_vecs_out[0] = impl->q_vecs_in[0];
167*0b454692Sjeremylt       ierr = CeedVectorAddReference(impl->q_vecs_in[0]); CeedChkBackend(ierr);
16816911fdaSjeremylt     }
16916911fdaSjeremylt   }
17016911fdaSjeremylt 
171e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetSetupDone(op); CeedChkBackend(ierr);
172885ac19cSjeremylt 
173e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
174885ac19cSjeremylt }
175885ac19cSjeremylt 
176f10650afSjeremylt //------------------------------------------------------------------------------
177f10650afSjeremylt // Setup Operator Inputs
178f10650afSjeremylt //------------------------------------------------------------------------------
179d1d35e2fSjeremylt static inline int CeedOperatorSetupInputs_Ref(CeedInt num_input_fields,
180d1d35e2fSjeremylt     CeedQFunctionField *qf_input_fields, CeedOperatorField *op_input_fields,
181d1d35e2fSjeremylt     CeedVector in_vec, const bool skip_active, CeedOperator_Ref *impl,
1821d102b48SJeremy L Thompson     CeedRequest *request) {
1831d102b48SJeremy L Thompson   CeedInt ierr;
184d1d35e2fSjeremylt   CeedEvalMode eval_mode;
185d1bcdac9Sjeremylt   CeedVector vec;
186d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
1878d713cf6Sjeremylt   uint64_t state;
188885ac19cSjeremylt 
189d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
190d1bcdac9Sjeremylt     // Get input vector
191d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
192d1d35e2fSjeremylt     CeedChkBackend(ierr);
1931d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
194d1d35e2fSjeremylt       if (skip_active)
1951d102b48SJeremy L Thompson         continue;
1961d102b48SJeremy L Thompson       else
197d1d35e2fSjeremylt         vec = in_vec;
1981d102b48SJeremy L Thompson     }
1991d102b48SJeremy L Thompson 
200d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_input_fields[i], &eval_mode);
201e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
2021d102b48SJeremy L Thompson     // Restrict and Evec
203d1d35e2fSjeremylt     if (eval_mode == CEED_EVAL_WEIGHT) { // Skip
2041d102b48SJeremy L Thompson     } else {
205668048e2SJed Brown       // Restrict
206e15f9bd0SJeremy L Thompson       ierr = CeedVectorGetState(vec, &state); CeedChkBackend(ierr);
2078d713cf6Sjeremylt       // Skip restriction if input is unchanged
208d1d35e2fSjeremylt       if (state != impl->input_state[i] || vec == in_vec) {
209d1d35e2fSjeremylt         ierr = CeedOperatorFieldGetElemRestriction(op_input_fields[i], &elem_restr);
210e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
211d1d35e2fSjeremylt         ierr = CeedElemRestrictionApply(elem_restr, CEED_NOTRANSPOSE, vec,
212d1d35e2fSjeremylt                                         impl->e_vecs[i], request); CeedChkBackend(ierr);
213d1d35e2fSjeremylt         impl->input_state[i] = state;
2148d713cf6Sjeremylt       }
215668048e2SJed Brown       // Get evec
216d1d35e2fSjeremylt       ierr = CeedVectorGetArrayRead(impl->e_vecs[i], CEED_MEM_HOST,
217d1d35e2fSjeremylt                                     (const CeedScalar **) &impl->e_data[i]);
218e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
219885ac19cSjeremylt     }
220885ac19cSjeremylt   }
221e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
222885ac19cSjeremylt }
223885ac19cSjeremylt 
224f10650afSjeremylt //------------------------------------------------------------------------------
225f10650afSjeremylt // Input Basis Action
226f10650afSjeremylt //------------------------------------------------------------------------------
2271d102b48SJeremy L Thompson static inline int CeedOperatorInputBasis_Ref(CeedInt e, CeedInt Q,
228d1d35e2fSjeremylt     CeedQFunctionField *qf_input_fields, CeedOperatorField *op_input_fields,
229d1d35e2fSjeremylt     CeedInt num_input_fields, const bool skip_active, CeedOperator_Ref *impl) {
2301d102b48SJeremy L Thompson   CeedInt ierr;
231d1d35e2fSjeremylt   CeedInt dim, elem_size, size;
232d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
233d1d35e2fSjeremylt   CeedEvalMode eval_mode;
2341d102b48SJeremy L Thompson   CeedBasis basis;
2351d102b48SJeremy L Thompson 
236d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
2371d102b48SJeremy L Thompson     // Skip active input
238d1d35e2fSjeremylt     if (skip_active) {
2391d102b48SJeremy L Thompson       CeedVector vec;
240d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
241d1d35e2fSjeremylt       CeedChkBackend(ierr);
2421d102b48SJeremy L Thompson       if (vec == CEED_VECTOR_ACTIVE)
2431d102b48SJeremy L Thompson         continue;
2441d102b48SJeremy L Thompson     }
245d1d35e2fSjeremylt     // Get elem_size, eval_mode, size
246d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_input_fields[i], &elem_restr);
247e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
248d1d35e2fSjeremylt     ierr = CeedElemRestrictionGetElementSize(elem_restr, &elem_size);
249e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
250d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_input_fields[i], &eval_mode);
251e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
252d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetSize(qf_input_fields[i], &size);
253d1d35e2fSjeremylt     CeedChkBackend(ierr);
254885ac19cSjeremylt     // Basis action
255d1d35e2fSjeremylt     switch(eval_mode) {
256885ac19cSjeremylt     case CEED_EVAL_NONE:
257d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->q_vecs_in[i], CEED_MEM_HOST,
258d1d35e2fSjeremylt                                 CEED_USE_POINTER, &impl->e_data[i][e*Q*size]);
259d1d35e2fSjeremylt       CeedChkBackend(ierr);
260885ac19cSjeremylt       break;
261885ac19cSjeremylt     case CEED_EVAL_INTERP:
262d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_input_fields[i], &basis);
263e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
264d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_in[i], CEED_MEM_HOST,
265d1d35e2fSjeremylt                                 CEED_USE_POINTER, &impl->e_data[i][e*elem_size*size]);
266e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
267d1d35e2fSjeremylt       ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_INTERP,
268d1d35e2fSjeremylt                             impl->e_vecs_in[i], impl->q_vecs_in[i]); CeedChkBackend(ierr);
269885ac19cSjeremylt       break;
270885ac19cSjeremylt     case CEED_EVAL_GRAD:
271d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_input_fields[i], &basis);
272e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
273e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
274d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_in[i], CEED_MEM_HOST,
275d1d35e2fSjeremylt                                 CEED_USE_POINTER, &impl->e_data[i][e*elem_size*size/dim]);
276e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
277d1bcdac9Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE,
278d1d35e2fSjeremylt                             CEED_EVAL_GRAD, impl->e_vecs_in[i],
279d1d35e2fSjeremylt                             impl->q_vecs_in[i]); CeedChkBackend(ierr);
280885ac19cSjeremylt       break;
281885ac19cSjeremylt     case CEED_EVAL_WEIGHT:
282885ac19cSjeremylt       break;  // No action
283bbfacfcdSjeremylt     // LCOV_EXCL_START
284885ac19cSjeremylt     case CEED_EVAL_DIV:
2851d102b48SJeremy L Thompson     case CEED_EVAL_CURL: {
286d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_input_fields[i], &basis);
287e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
2881d102b48SJeremy L Thompson       Ceed ceed;
289e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetCeed(basis, &ceed); CeedChkBackend(ierr);
290e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_BACKEND,
291e15f9bd0SJeremy L Thompson                        "Ceed evaluation mode not implemented");
292bbfacfcdSjeremylt       // LCOV_EXCL_STOP
293885ac19cSjeremylt     }
294885ac19cSjeremylt     }
295885ac19cSjeremylt   }
296e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
297885ac19cSjeremylt }
298885ac19cSjeremylt 
299f10650afSjeremylt //------------------------------------------------------------------------------
300f10650afSjeremylt // Output Basis Action
301f10650afSjeremylt //------------------------------------------------------------------------------
3021d102b48SJeremy L Thompson static inline int CeedOperatorOutputBasis_Ref(CeedInt e, CeedInt Q,
303d1d35e2fSjeremylt     CeedQFunctionField *qf_output_fields, CeedOperatorField *op_output_fields,
304d1d35e2fSjeremylt     CeedInt num_input_fields, CeedInt num_output_fields, CeedOperator op,
3051d102b48SJeremy L Thompson     CeedOperator_Ref *impl) {
3061d102b48SJeremy L Thompson   CeedInt ierr;
307d1d35e2fSjeremylt   CeedInt dim, elem_size, size;
308d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
309d1d35e2fSjeremylt   CeedEvalMode eval_mode;
3101d102b48SJeremy L Thompson   CeedBasis basis;
3111d102b48SJeremy L Thompson 
312d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
313d1d35e2fSjeremylt     // Get elem_size, eval_mode, size
314d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_output_fields[i], &elem_restr);
315e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
316d1d35e2fSjeremylt     ierr = CeedElemRestrictionGetElementSize(elem_restr, &elem_size);
317e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
318d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_output_fields[i], &eval_mode);
319e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
320d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetSize(qf_output_fields[i], &size);
321e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
322885ac19cSjeremylt     // Basis action
323d1d35e2fSjeremylt     switch(eval_mode) {
324885ac19cSjeremylt     case CEED_EVAL_NONE:
325885ac19cSjeremylt       break; // No action
326885ac19cSjeremylt     case CEED_EVAL_INTERP:
327d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_output_fields[i], &basis);
328e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
329d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_out[i], CEED_MEM_HOST,
330aedaa0e5Sjeremylt                                 CEED_USE_POINTER,
331d1d35e2fSjeremylt                                 &impl->e_data[i + num_input_fields][e*elem_size*size]);
332e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
333aedaa0e5Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_TRANSPOSE,
334d1d35e2fSjeremylt                             CEED_EVAL_INTERP, impl->q_vecs_out[i],
335d1d35e2fSjeremylt                             impl->e_vecs_out[i]); CeedChkBackend(ierr);
336885ac19cSjeremylt       break;
337885ac19cSjeremylt     case CEED_EVAL_GRAD:
338d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_output_fields[i], &basis);
339e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
340e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
341d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_out[i], CEED_MEM_HOST,
342aedaa0e5Sjeremylt                                 CEED_USE_POINTER,
343d1d35e2fSjeremylt                                 &impl->e_data[i + num_input_fields][e*elem_size*size/dim]);
344e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
345aedaa0e5Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_TRANSPOSE,
346d1d35e2fSjeremylt                             CEED_EVAL_GRAD, impl->q_vecs_out[i],
347d1d35e2fSjeremylt                             impl->e_vecs_out[i]); CeedChkBackend(ierr);
348885ac19cSjeremylt       break;
349c042f62fSJeremy L Thompson     // LCOV_EXCL_START
350bbfacfcdSjeremylt     case CEED_EVAL_WEIGHT: {
3514ce2993fSjeremylt       Ceed ceed;
352e15f9bd0SJeremy L Thompson       ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
353e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_BACKEND,
354e15f9bd0SJeremy L Thompson                        "CEED_EVAL_WEIGHT cannot be an output "
3551d102b48SJeremy L Thompson                        "evaluation mode");
3564ce2993fSjeremylt     }
357885ac19cSjeremylt     case CEED_EVAL_DIV:
3581d102b48SJeremy L Thompson     case CEED_EVAL_CURL: {
3591d102b48SJeremy L Thompson       Ceed ceed;
360e15f9bd0SJeremy L Thompson       ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
361e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_BACKEND,
362e15f9bd0SJeremy L Thompson                        "Ceed evaluation mode not implemented");
3631d102b48SJeremy L Thompson       // LCOV_EXCL_STOP
364885ac19cSjeremylt     }
365885ac19cSjeremylt     }
366885ac19cSjeremylt   }
367e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
3681d102b48SJeremy L Thompson }
3691d102b48SJeremy L Thompson 
370f10650afSjeremylt //------------------------------------------------------------------------------
371f10650afSjeremylt // Restore Input Vectors
372f10650afSjeremylt //------------------------------------------------------------------------------
373d1d35e2fSjeremylt static inline int CeedOperatorRestoreInputs_Ref(CeedInt num_input_fields,
374d1d35e2fSjeremylt     CeedQFunctionField *qf_input_fields, CeedOperatorField *op_input_fields,
375d1d35e2fSjeremylt     const bool skip_active, CeedOperator_Ref *impl) {
3761d102b48SJeremy L Thompson   CeedInt ierr;
377d1d35e2fSjeremylt   CeedEvalMode eval_mode;
3781d102b48SJeremy L Thompson 
379d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
3801d102b48SJeremy L Thompson     // Skip active inputs
381d1d35e2fSjeremylt     if (skip_active) {
3821d102b48SJeremy L Thompson       CeedVector vec;
383d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
384d1d35e2fSjeremylt       CeedChkBackend(ierr);
3851d102b48SJeremy L Thompson       if (vec == CEED_VECTOR_ACTIVE)
3861d102b48SJeremy L Thompson         continue;
3871d102b48SJeremy L Thompson     }
3881d102b48SJeremy L Thompson     // Restore input
389d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_input_fields[i], &eval_mode);
390e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
391d1d35e2fSjeremylt     if (eval_mode == CEED_EVAL_WEIGHT) { // Skip
3921d102b48SJeremy L Thompson     } else {
393d1d35e2fSjeremylt       ierr = CeedVectorRestoreArrayRead(impl->e_vecs[i],
394d1d35e2fSjeremylt                                         (const CeedScalar **) &impl->e_data[i]);
395e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
3961d102b48SJeremy L Thompson     }
3971d102b48SJeremy L Thompson   }
398e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
3991d102b48SJeremy L Thompson }
4001d102b48SJeremy L Thompson 
401f10650afSjeremylt //------------------------------------------------------------------------------
402f10650afSjeremylt // Operator Apply
403f10650afSjeremylt //------------------------------------------------------------------------------
404d1d35e2fSjeremylt static int CeedOperatorApplyAdd_Ref(CeedOperator op, CeedVector in_vec,
405d1d35e2fSjeremylt                                     CeedVector out_vec, CeedRequest *request) {
4061d102b48SJeremy L Thompson   int ierr;
4071d102b48SJeremy L Thompson   CeedOperator_Ref *impl;
408e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
4091d102b48SJeremy L Thompson   CeedQFunction qf;
410e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
411d1d35e2fSjeremylt   CeedInt Q, num_elem, num_input_fields, num_output_fields, size;
412e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetNumQuadraturePoints(op, &Q); CeedChkBackend(ierr);
413d1d35e2fSjeremylt   ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChkBackend(ierr);
414d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
415e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
416d1d35e2fSjeremylt   CeedOperatorField *op_input_fields, *op_output_fields;
417d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_input_fields, &op_output_fields);
418e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
419d1d35e2fSjeremylt   CeedQFunctionField *qf_input_fields, *qf_output_fields;
420d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_input_fields, &qf_output_fields);
421e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
422d1d35e2fSjeremylt   CeedEvalMode eval_mode;
4231d102b48SJeremy L Thompson   CeedVector vec;
424d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
4251d102b48SJeremy L Thompson 
4261d102b48SJeremy L Thompson   // Setup
427e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetup_Ref(op); CeedChkBackend(ierr);
4281d102b48SJeremy L Thompson 
429*0b454692Sjeremylt   // Restriction only operator
430*0b454692Sjeremylt   if (impl->is_identity_restr_op) {
431*0b454692Sjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_input_fields[0], &elem_restr);
432*0b454692Sjeremylt     CeedChkBackend(ierr);
433*0b454692Sjeremylt     ierr = CeedElemRestrictionApply(elem_restr, CEED_NOTRANSPOSE, in_vec,
434*0b454692Sjeremylt                                     impl->e_vecs[0], request); CeedChkBackend(ierr);
435*0b454692Sjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_output_fields[0], &elem_restr);
436*0b454692Sjeremylt     CeedChkBackend(ierr);
437*0b454692Sjeremylt     ierr = CeedElemRestrictionApply(elem_restr, CEED_TRANSPOSE, impl->e_vecs[0],
438*0b454692Sjeremylt                                     out_vec, request); CeedChkBackend(ierr);
439*0b454692Sjeremylt     return CEED_ERROR_SUCCESS;
440*0b454692Sjeremylt   }
441*0b454692Sjeremylt 
4421d102b48SJeremy L Thompson   // Input Evecs and Restriction
443d1d35e2fSjeremylt   ierr = CeedOperatorSetupInputs_Ref(num_input_fields, qf_input_fields,
444d1d35e2fSjeremylt                                      op_input_fields, in_vec, false, impl,
445e15f9bd0SJeremy L Thompson                                      request); CeedChkBackend(ierr);
4461d102b48SJeremy L Thompson 
4471d102b48SJeremy L Thompson   // Output Evecs
448d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
449d1d35e2fSjeremylt     ierr = CeedVectorGetArray(impl->e_vecs[i+impl->num_e_vecs_in], CEED_MEM_HOST,
450d1d35e2fSjeremylt                               &impl->e_data[i + num_input_fields]); CeedChkBackend(ierr);
4511d102b48SJeremy L Thompson   }
4521d102b48SJeremy L Thompson 
4531d102b48SJeremy L Thompson   // Loop through elements
454d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
4551d102b48SJeremy L Thompson     // Output pointers
456d1d35e2fSjeremylt     for (CeedInt i=0; i<num_output_fields; i++) {
457d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_output_fields[i], &eval_mode);
458e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
459d1d35e2fSjeremylt       if (eval_mode == CEED_EVAL_NONE) {
460d1d35e2fSjeremylt         ierr = CeedQFunctionFieldGetSize(qf_output_fields[i], &size);
461e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
462d1d35e2fSjeremylt         ierr = CeedVectorSetArray(impl->q_vecs_out[i], CEED_MEM_HOST,
4631d102b48SJeremy L Thompson                                   CEED_USE_POINTER,
464d1d35e2fSjeremylt                                   &impl->e_data[i + num_input_fields][e*Q*size]);
465e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
4661d102b48SJeremy L Thompson       }
4671d102b48SJeremy L Thompson     }
4681d102b48SJeremy L Thompson 
46916911fdaSjeremylt     // Input basis apply
470d1d35e2fSjeremylt     ierr = CeedOperatorInputBasis_Ref(e, Q, qf_input_fields, op_input_fields,
471d1d35e2fSjeremylt                                       num_input_fields, false, impl);
472e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
47316911fdaSjeremylt 
4741d102b48SJeremy L Thompson     // Q function
475*0b454692Sjeremylt     if (!impl->is_identity_qf) {
476d1d35e2fSjeremylt       ierr = CeedQFunctionApply(qf, Q, impl->q_vecs_in, impl->q_vecs_out);
477e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
47816911fdaSjeremylt     }
4791d102b48SJeremy L Thompson 
4801d102b48SJeremy L Thompson     // Output basis apply
481d1d35e2fSjeremylt     ierr = CeedOperatorOutputBasis_Ref(e, Q, qf_output_fields, op_output_fields,
482d1d35e2fSjeremylt                                        num_input_fields, num_output_fields, op, impl);
483e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
4841d102b48SJeremy L Thompson   }
485885ac19cSjeremylt 
486885ac19cSjeremylt   // Output restriction
487d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
488d1d35e2fSjeremylt     // Restore Evec
489d1d35e2fSjeremylt     ierr = CeedVectorRestoreArray(impl->e_vecs[i+impl->num_e_vecs_in],
490d1d35e2fSjeremylt                                   &impl->e_data[i + num_input_fields]);
491e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
492d1bcdac9Sjeremylt     // Get output vector
493d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_output_fields[i], &vec);
494e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
495668048e2SJed Brown     // Active
496d1bcdac9Sjeremylt     if (vec == CEED_VECTOR_ACTIVE)
497d1d35e2fSjeremylt       vec = out_vec;
4987ca8db16Sjeremylt     // Restrict
499d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_output_fields[i], &elem_restr);
500e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
501d1d35e2fSjeremylt     ierr = CeedElemRestrictionApply(elem_restr, CEED_TRANSPOSE,
502d1d35e2fSjeremylt                                     impl->e_vecs[i+impl->num_e_vecs_in], vec, request);
503e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
504885ac19cSjeremylt   }
505885ac19cSjeremylt 
5067ca8db16Sjeremylt   // Restore input arrays
507d1d35e2fSjeremylt   ierr = CeedOperatorRestoreInputs_Ref(num_input_fields, qf_input_fields,
508d1d35e2fSjeremylt                                        op_input_fields, false, impl);
509e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
5107ca8db16Sjeremylt 
511e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
51221617c04Sjeremylt }
51321617c04Sjeremylt 
514f10650afSjeremylt //------------------------------------------------------------------------------
5151d102b48SJeremy L Thompson // Assemble Linear QFunction
516f10650afSjeremylt //------------------------------------------------------------------------------
51780ac2e43SJeremy L Thompson static int CeedOperatorLinearAssembleQFunction_Ref(CeedOperator op,
5181d102b48SJeremy L Thompson     CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
5191d102b48SJeremy L Thompson   int ierr;
5201d102b48SJeremy L Thompson   CeedOperator_Ref *impl;
521e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
5221d102b48SJeremy L Thompson   CeedQFunction qf;
523e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
524d1d35e2fSjeremylt   CeedInt Q, num_elem, num_input_fields, num_output_fields, size;
525e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetNumQuadraturePoints(op, &Q); CeedChkBackend(ierr);
526d1d35e2fSjeremylt   ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChkBackend(ierr);
527d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
528e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
529d1d35e2fSjeremylt   CeedOperatorField *op_input_fields, *op_output_fields;
530d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_input_fields, &op_output_fields);
531e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
532d1d35e2fSjeremylt   CeedQFunctionField *qf_input_fields, *qf_output_fields;
533d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_input_fields, &qf_output_fields);
534e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
5351d102b48SJeremy L Thompson   CeedVector vec;
536d1d35e2fSjeremylt   CeedInt num_active_in = 0, num_active_out = 0;
537d1d35e2fSjeremylt   CeedVector *active_in = NULL;
5381d102b48SJeremy L Thompson   CeedScalar *a, *tmp;
539d1d35e2fSjeremylt   Ceed ceed, ceed_parent;
540e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
541d1d35e2fSjeremylt   ierr = CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent);
542e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
543d1d35e2fSjeremylt   ceed_parent = ceed_parent ? ceed_parent : ceed;
5441d102b48SJeremy L Thompson 
5451d102b48SJeremy L Thompson   // Setup
546e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetup_Ref(op); CeedChkBackend(ierr);
5471d102b48SJeremy L Thompson 
54816911fdaSjeremylt   // Check for identity
549*0b454692Sjeremylt   if (impl->is_identity_qf)
55016911fdaSjeremylt     // LCOV_EXCL_START
551e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
552e15f9bd0SJeremy L Thompson                      "Assembling identity QFunctions not supported");
55316911fdaSjeremylt   // LCOV_EXCL_STOP
55416911fdaSjeremylt 
5551d102b48SJeremy L Thompson   // Input Evecs and Restriction
556d1d35e2fSjeremylt   ierr = CeedOperatorSetupInputs_Ref(num_input_fields, qf_input_fields,
557d1d35e2fSjeremylt                                      op_input_fields, NULL, true, impl, request);
558e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
5591d102b48SJeremy L Thompson 
5601d102b48SJeremy L Thompson   // Count number of active input fields
561d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
5621d102b48SJeremy L Thompson     // Get input vector
563d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
564d1d35e2fSjeremylt     CeedChkBackend(ierr);
5651d102b48SJeremy L Thompson     // Check if active input
5661d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
567d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_input_fields[i], &size);
568e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
569d1d35e2fSjeremylt       ierr = CeedVectorSetValue(impl->q_vecs_in[i], 0.0); CeedChkBackend(ierr);
570d1d35e2fSjeremylt       ierr = CeedVectorGetArray(impl->q_vecs_in[i], CEED_MEM_HOST, &tmp);
571d1d35e2fSjeremylt       CeedChkBackend(ierr);
572d1d35e2fSjeremylt       ierr = CeedRealloc(num_active_in + size, &active_in); CeedChkBackend(ierr);
5731d102b48SJeremy L Thompson       for (CeedInt field=0; field<size; field++) {
574d1d35e2fSjeremylt         ierr = CeedVectorCreate(ceed, Q, &active_in[num_active_in+field]);
575e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
576d1d35e2fSjeremylt         ierr = CeedVectorSetArray(active_in[num_active_in+field], CEED_MEM_HOST,
57742ea3801Sjeremylt                                   CEED_USE_POINTER, &tmp[field*Q]);
578e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
5791d102b48SJeremy L Thompson       }
580d1d35e2fSjeremylt       num_active_in += size;
581d1d35e2fSjeremylt       ierr = CeedVectorRestoreArray(impl->q_vecs_in[i], &tmp); CeedChkBackend(ierr);
5821d102b48SJeremy L Thompson     }
5831d102b48SJeremy L Thompson   }
5841d102b48SJeremy L Thompson 
5851d102b48SJeremy L Thompson   // Count number of active output fields
586d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
5871d102b48SJeremy L Thompson     // Get output vector
588d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_output_fields[i], &vec);
589e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
5901d102b48SJeremy L Thompson     // Check if active output
5911d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
592d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_output_fields[i], &size);
593e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
594d1d35e2fSjeremylt       num_active_out += size;
5951d102b48SJeremy L Thompson     }
5961d102b48SJeremy L Thompson   }
5971d102b48SJeremy L Thompson 
5981d102b48SJeremy L Thompson   // Check sizes
599d1d35e2fSjeremylt   if (!num_active_in || !num_active_out)
600138d4072Sjeremylt     // LCOV_EXCL_START
601e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
602e15f9bd0SJeremy L Thompson                      "Cannot assemble QFunction without active inputs "
603138d4072Sjeremylt                      "and outputs");
604138d4072Sjeremylt   // LCOV_EXCL_STOP
6051d102b48SJeremy L Thompson 
6061d102b48SJeremy L Thompson   // Create output restriction
607d1d35e2fSjeremylt   CeedInt strides[3] = {1, Q, num_active_in*num_active_out*Q}; /* *NOPAD* */
608d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateStrided(ceed_parent, num_elem, Q,
609d1d35e2fSjeremylt                                           num_active_in*num_active_out,
610d1d35e2fSjeremylt                                           num_active_in*num_active_out*num_elem*Q,
611e15f9bd0SJeremy L Thompson                                           strides, rstr); CeedChkBackend(ierr);
6121d102b48SJeremy L Thompson   // Create assembled vector
613d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed_parent, num_elem*Q*num_active_in*num_active_out,
614e15f9bd0SJeremy L Thompson                           assembled); CeedChkBackend(ierr);
615e15f9bd0SJeremy L Thompson   ierr = CeedVectorSetValue(*assembled, 0.0); CeedChkBackend(ierr);
616e15f9bd0SJeremy L Thompson   ierr = CeedVectorGetArray(*assembled, CEED_MEM_HOST, &a); CeedChkBackend(ierr);
6171d102b48SJeremy L Thompson 
6181d102b48SJeremy L Thompson   // Loop through elements
619d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
6201d102b48SJeremy L Thompson     // Input basis apply
621d1d35e2fSjeremylt     ierr = CeedOperatorInputBasis_Ref(e, Q, qf_input_fields, op_input_fields,
622d1d35e2fSjeremylt                                       num_input_fields, true, impl);
623e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
6241d102b48SJeremy L Thompson 
6251d102b48SJeremy L Thompson     // Assemble QFunction
626d1d35e2fSjeremylt     for (CeedInt in=0; in<num_active_in; in++) {
6271d102b48SJeremy L Thompson       // Set Inputs
628d1d35e2fSjeremylt       ierr = CeedVectorSetValue(active_in[in], 1.0); CeedChkBackend(ierr);
629d1d35e2fSjeremylt       if (num_active_in > 1) {
630d1d35e2fSjeremylt         ierr = CeedVectorSetValue(active_in[(in+num_active_in-1)%num_active_in],
631e15f9bd0SJeremy L Thompson                                   0.0); CeedChkBackend(ierr);
63242ea3801Sjeremylt       }
6331d102b48SJeremy L Thompson       // Set Outputs
634d1d35e2fSjeremylt       for (CeedInt out=0; out<num_output_fields; out++) {
6351d102b48SJeremy L Thompson         // Get output vector
636d1d35e2fSjeremylt         ierr = CeedOperatorFieldGetVector(op_output_fields[out], &vec);
637e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
6381d102b48SJeremy L Thompson         // Check if active output
6391d102b48SJeremy L Thompson         if (vec == CEED_VECTOR_ACTIVE) {
640d1d35e2fSjeremylt           CeedVectorSetArray(impl->q_vecs_out[out], CEED_MEM_HOST,
641e15f9bd0SJeremy L Thompson                              CEED_USE_POINTER, a); CeedChkBackend(ierr);
642d1d35e2fSjeremylt           ierr = CeedQFunctionFieldGetSize(qf_output_fields[out], &size);
643e15f9bd0SJeremy L Thompson           CeedChkBackend(ierr);
6441d102b48SJeremy L Thompson           a += size*Q; // Advance the pointer by the size of the output
6451d102b48SJeremy L Thompson         }
6461d102b48SJeremy L Thompson       }
6471d102b48SJeremy L Thompson       // Apply QFunction
648d1d35e2fSjeremylt       ierr = CeedQFunctionApply(qf, Q, impl->q_vecs_in, impl->q_vecs_out);
649e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
6501d102b48SJeremy L Thompson     }
6511d102b48SJeremy L Thompson   }
6521d102b48SJeremy L Thompson 
6531d102b48SJeremy L Thompson   // Un-set output Qvecs to prevent accidental overwrite of Assembled
654d1d35e2fSjeremylt   for (CeedInt out=0; out<num_output_fields; out++) {
6551d102b48SJeremy L Thompson     // Get output vector
656d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_output_fields[out], &vec);
657e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
6581d102b48SJeremy L Thompson     // Check if active output
6591d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
660d1d35e2fSjeremylt       CeedVectorTakeArray(impl->q_vecs_out[out], CEED_MEM_HOST, NULL);
661e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
6621d102b48SJeremy L Thompson     }
6631d102b48SJeremy L Thompson   }
6641d102b48SJeremy L Thompson 
6651d102b48SJeremy L Thompson   // Restore input arrays
666d1d35e2fSjeremylt   ierr = CeedOperatorRestoreInputs_Ref(num_input_fields, qf_input_fields,
667d1d35e2fSjeremylt                                        op_input_fields, true, impl);
668e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
6691d102b48SJeremy L Thompson 
6701d102b48SJeremy L Thompson   // Restore output
671e15f9bd0SJeremy L Thompson   ierr = CeedVectorRestoreArray(*assembled, &a); CeedChkBackend(ierr);
6721d102b48SJeremy L Thompson 
6731d102b48SJeremy L Thompson   // Cleanup
674d1d35e2fSjeremylt   for (CeedInt i=0; i<num_active_in; i++) {
675d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&active_in[i]); CeedChkBackend(ierr);
67642ea3801Sjeremylt   }
677d1d35e2fSjeremylt   ierr = CeedFree(&active_in); CeedChkBackend(ierr);
6781d102b48SJeremy L Thompson 
679e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6801d102b48SJeremy L Thompson }
6811d102b48SJeremy L Thompson 
682f10650afSjeremylt //------------------------------------------------------------------------------
683dfffd467Sjeremylt // Get Basis Emode Pointer
684dfffd467Sjeremylt //------------------------------------------------------------------------------
685d1d35e2fSjeremylt static inline void CeedOperatorGetBasisPointer_Ref(const CeedScalar **basis_ptr,
686d1d35e2fSjeremylt     CeedEvalMode eval_mode, const CeedScalar *identity, const CeedScalar *interp,
6876c58de82SJeremy L Thompson     const CeedScalar *grad) {
688d1d35e2fSjeremylt   switch (eval_mode) {
689dfffd467Sjeremylt   case CEED_EVAL_NONE:
690d1d35e2fSjeremylt     *basis_ptr = identity;
691dfffd467Sjeremylt     break;
692dfffd467Sjeremylt   case CEED_EVAL_INTERP:
693d1d35e2fSjeremylt     *basis_ptr = interp;
694dfffd467Sjeremylt     break;
695dfffd467Sjeremylt   case CEED_EVAL_GRAD:
696d1d35e2fSjeremylt     *basis_ptr = grad;
697dfffd467Sjeremylt     break;
698dfffd467Sjeremylt   case CEED_EVAL_WEIGHT:
699dfffd467Sjeremylt   case CEED_EVAL_DIV:
700dfffd467Sjeremylt   case CEED_EVAL_CURL:
701dfffd467Sjeremylt     break; // Caught by QF Assembly
702dfffd467Sjeremylt   }
703dfffd467Sjeremylt }
704dfffd467Sjeremylt 
705dfffd467Sjeremylt //------------------------------------------------------------------------------
706d965c7a7SJeremy L Thompson // Create point block restriction
707d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
708868092e3SJeremy L Thompson static int CreatePBRestriction_Ref(CeedElemRestriction rstr,
709d1d35e2fSjeremylt                                    CeedElemRestriction *pb_rstr) {
710d965c7a7SJeremy L Thompson   int ierr;
711d965c7a7SJeremy L Thompson   Ceed ceed;
712e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionGetCeed(rstr, &ceed); CeedChkBackend(ierr);
713d965c7a7SJeremy L Thompson   const CeedInt *offsets;
714d965c7a7SJeremy L Thompson   ierr = CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets);
715e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
716d965c7a7SJeremy L Thompson 
717d965c7a7SJeremy L Thompson   // Expand offsets
718d1d35e2fSjeremylt   CeedInt num_elem, num_comp, elem_size, comp_stride, max = 1, *pbOffsets;
719d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChkBackend(ierr);
720d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr, &num_comp);
721e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
722d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr, &elem_size);
723d1d35e2fSjeremylt   CeedChkBackend(ierr);
724d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetCompStride(rstr, &comp_stride);
725d1d35e2fSjeremylt   CeedChkBackend(ierr);
726d1d35e2fSjeremylt   CeedInt shift = num_comp;
727d1d35e2fSjeremylt   if (comp_stride != 1)
728d1d35e2fSjeremylt     shift *= num_comp;
729d1d35e2fSjeremylt   ierr = CeedCalloc(num_elem*elem_size, &pbOffsets); CeedChkBackend(ierr);
730d1d35e2fSjeremylt   for (CeedInt i = 0; i < num_elem*elem_size; i++) {
731d965c7a7SJeremy L Thompson     pbOffsets[i] = offsets[i]*shift;
732d965c7a7SJeremy L Thompson     if (pbOffsets[i] > max)
733d965c7a7SJeremy L Thompson       max = pbOffsets[i];
734d965c7a7SJeremy L Thompson   }
735d965c7a7SJeremy L Thompson 
736d965c7a7SJeremy L Thompson   // Create new restriction
737d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp*num_comp,
738d1d35e2fSjeremylt                                    1,
739d1d35e2fSjeremylt                                    max + num_comp*num_comp, CEED_MEM_HOST,
740d1d35e2fSjeremylt                                    CEED_OWN_POINTER, pbOffsets, pb_rstr);
741e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
742d965c7a7SJeremy L Thompson 
743d965c7a7SJeremy L Thompson   // Cleanup
744e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionRestoreOffsets(rstr, &offsets); CeedChkBackend(ierr);
745d965c7a7SJeremy L Thompson 
746e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
747d965c7a7SJeremy L Thompson }
748d965c7a7SJeremy L Thompson 
749d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
750c04a41a7SJeremy L Thompson // Assemble diagonal common code
751f10650afSjeremylt //------------------------------------------------------------------------------
75269af5e5fSJeremy L Thompson static inline int CeedOperatorAssembleAddDiagonalCore_Ref(CeedOperator op,
753d1d35e2fSjeremylt     CeedVector assembled, CeedRequest *request, const bool is_pointblock) {
7547172caadSjeremylt   int ierr;
755c04a41a7SJeremy L Thompson   Ceed ceed;
756e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
7577172caadSjeremylt 
7587172caadSjeremylt   // Assemble QFunction
7597172caadSjeremylt   CeedQFunction qf;
760e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
761d1d35e2fSjeremylt   CeedInt num_input_fields, num_output_fields;
762d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
763e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
764d1d35e2fSjeremylt   CeedVector assembled_qf;
7657172caadSjeremylt   CeedElemRestriction rstr;
766d1d35e2fSjeremylt   ierr = CeedOperatorLinearAssembleQFunction(op,  &assembled_qf, &rstr, request);
767e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
768e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr); CeedChkBackend(ierr);
769d1d35e2fSjeremylt   CeedScalar max_norm = 0;
770d1d35e2fSjeremylt   ierr = CeedVectorNorm(assembled_qf, CEED_NORM_MAX, &max_norm);
771e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
7727172caadSjeremylt 
7737172caadSjeremylt   // Determine active input basis
774d1d35e2fSjeremylt   CeedOperatorField *op_fields;
775d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
776d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_fields, NULL); CeedChkBackend(ierr);
777d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChkBackend(ierr);
778d1d35e2fSjeremylt   CeedInt num_eval_mode_in = 0, num_comp, dim = 1;
779d1d35e2fSjeremylt   CeedEvalMode *eval_mode_in = NULL;
780d1d35e2fSjeremylt   CeedBasis basis_in = NULL;
781d1d35e2fSjeremylt   CeedElemRestriction rstr_in = NULL;
782d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
7837172caadSjeremylt     CeedVector vec;
784d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChkBackend(ierr);
7857172caadSjeremylt     if (vec == CEED_VECTOR_ACTIVE) {
786c04a41a7SJeremy L Thompson       CeedElemRestriction rstr;
787d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis_in); CeedChkBackend(ierr);
788d1d35e2fSjeremylt       ierr = CeedBasisGetNumComponents(basis_in, &num_comp); CeedChkBackend(ierr);
789d1d35e2fSjeremylt       ierr = CeedBasisGetDimension(basis_in, &dim); CeedChkBackend(ierr);
790d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr);
791e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
792d1d35e2fSjeremylt       if (rstr_in && rstr_in != rstr)
793c04a41a7SJeremy L Thompson         // LCOV_EXCL_START
794e15f9bd0SJeremy L Thompson         return CeedError(ceed, CEED_ERROR_BACKEND,
795c04a41a7SJeremy L Thompson                          "Multi-field non-composite operator diagonal assembly not supported");
796c04a41a7SJeremy L Thompson       // LCOV_EXCL_STOP
797d1d35e2fSjeremylt       rstr_in = rstr;
798d1d35e2fSjeremylt       CeedEvalMode eval_mode;
799d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
800e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
801d1d35e2fSjeremylt       switch (eval_mode) {
8027172caadSjeremylt       case CEED_EVAL_NONE:
8037172caadSjeremylt       case CEED_EVAL_INTERP:
804d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + 1, &eval_mode_in); CeedChkBackend(ierr);
805d1d35e2fSjeremylt         eval_mode_in[num_eval_mode_in] = eval_mode;
806d1d35e2fSjeremylt         num_eval_mode_in += 1;
8077172caadSjeremylt         break;
8087172caadSjeremylt       case CEED_EVAL_GRAD:
809d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + dim, &eval_mode_in); CeedChkBackend(ierr);
8107172caadSjeremylt         for (CeedInt d=0; d<dim; d++)
811d1d35e2fSjeremylt           eval_mode_in[num_eval_mode_in+d] = eval_mode;
812d1d35e2fSjeremylt         num_eval_mode_in += dim;
8137172caadSjeremylt         break;
8147172caadSjeremylt       case CEED_EVAL_WEIGHT:
8157172caadSjeremylt       case CEED_EVAL_DIV:
8167172caadSjeremylt       case CEED_EVAL_CURL:
8177172caadSjeremylt         break; // Caught by QF Assembly
8187172caadSjeremylt       }
8197172caadSjeremylt     }
8207172caadSjeremylt   }
8217172caadSjeremylt 
8227172caadSjeremylt   // Determine active output basis
823d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, NULL, &op_fields); CeedChkBackend(ierr);
824d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields); CeedChkBackend(ierr);
825d1d35e2fSjeremylt   CeedInt num_eval_mode_out = 0;
826d1d35e2fSjeremylt   CeedEvalMode *eval_mode_out = NULL;
827d1d35e2fSjeremylt   CeedBasis basis_out = NULL;
828d1d35e2fSjeremylt   CeedElemRestriction rstr_out = NULL;
829d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
8307172caadSjeremylt     CeedVector vec;
831d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChkBackend(ierr);
8327172caadSjeremylt     if (vec == CEED_VECTOR_ACTIVE) {
833c04a41a7SJeremy L Thompson       CeedElemRestriction rstr;
834d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis_out);
835e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
836d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr);
837d1d35e2fSjeremylt       CeedChkBackend(ierr);
838d1d35e2fSjeremylt       if (rstr_out && rstr_out != rstr)
839c04a41a7SJeremy L Thompson         // LCOV_EXCL_START
840e15f9bd0SJeremy L Thompson         return CeedError(ceed, CEED_ERROR_BACKEND,
841c04a41a7SJeremy L Thompson                          "Multi-field non-composite operator diagonal assembly not supported");
842c04a41a7SJeremy L Thompson       // LCOV_EXCL_STOP
843d1d35e2fSjeremylt       rstr_out = rstr;
844d1d35e2fSjeremylt       CeedEvalMode eval_mode;
845d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
846d1d35e2fSjeremylt       CeedChkBackend(ierr);
847d1d35e2fSjeremylt       switch (eval_mode) {
8487172caadSjeremylt       case CEED_EVAL_NONE:
8497172caadSjeremylt       case CEED_EVAL_INTERP:
850d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + 1, &eval_mode_out); CeedChkBackend(ierr);
851d1d35e2fSjeremylt         eval_mode_out[num_eval_mode_out] = eval_mode;
852d1d35e2fSjeremylt         num_eval_mode_out += 1;
8537172caadSjeremylt         break;
8547172caadSjeremylt       case CEED_EVAL_GRAD:
855d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + dim, &eval_mode_out);
856d1d35e2fSjeremylt         CeedChkBackend(ierr);
8577172caadSjeremylt         for (CeedInt d=0; d<dim; d++)
858d1d35e2fSjeremylt           eval_mode_out[num_eval_mode_out+d] = eval_mode;
859d1d35e2fSjeremylt         num_eval_mode_out += dim;
8607172caadSjeremylt         break;
8617172caadSjeremylt       case CEED_EVAL_WEIGHT:
8627172caadSjeremylt       case CEED_EVAL_DIV:
8637172caadSjeremylt       case CEED_EVAL_CURL:
8647172caadSjeremylt         break; // Caught by QF Assembly
8657172caadSjeremylt       }
8667172caadSjeremylt     }
8677172caadSjeremylt   }
8687172caadSjeremylt 
869d965c7a7SJeremy L Thompson   // Assemble point-block diagonal restriction, if needed
870d1d35e2fSjeremylt   CeedElemRestriction diag_rstr = rstr_out;
871d1d35e2fSjeremylt   if (is_pointblock) {
872d1d35e2fSjeremylt     ierr = CreatePBRestriction_Ref(rstr_out, &diag_rstr); CeedChkBackend(ierr);
873d965c7a7SJeremy L Thompson   }
874d965c7a7SJeremy L Thompson 
8757172caadSjeremylt   // Create diagonal vector
876d1d35e2fSjeremylt   CeedVector elem_diag;
877d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateVector(diag_rstr, NULL, &elem_diag);
878e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
8797172caadSjeremylt 
8807172caadSjeremylt   // Assemble element operator diagonals
881d1d35e2fSjeremylt   CeedScalar *elem_diag_array, *assembled_qf_array;
882d1d35e2fSjeremylt   ierr = CeedVectorSetValue(elem_diag, 0.0); CeedChkBackend(ierr);
883d1d35e2fSjeremylt   ierr = CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array);
884e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
885d1d35e2fSjeremylt   ierr = CeedVectorGetArray(assembled_qf, CEED_MEM_HOST, &assembled_qf_array);
886e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
887d1d35e2fSjeremylt   CeedInt num_elem, num_nodes, num_qpts;
888d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(diag_rstr, &num_elem);
889e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
890d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes(basis_in, &num_nodes); CeedChkBackend(ierr);
891d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts);
892d1d35e2fSjeremylt   CeedChkBackend(ierr);
893dfffd467Sjeremylt   // Basis matrices
894d1d35e2fSjeremylt   const CeedScalar *interp_in, *interp_out, *grad_in, *grad_out;
8956c58de82SJeremy L Thompson   CeedScalar *identity = NULL;
896dfffd467Sjeremylt   bool evalNone = false;
897d1d35e2fSjeremylt   for (CeedInt i=0; i<num_eval_mode_in; i++)
898d1d35e2fSjeremylt     evalNone = evalNone || (eval_mode_in[i] == CEED_EVAL_NONE);
899d1d35e2fSjeremylt   for (CeedInt i=0; i<num_eval_mode_out; i++)
900d1d35e2fSjeremylt     evalNone = evalNone || (eval_mode_out[i] == CEED_EVAL_NONE);
901dfffd467Sjeremylt   if (evalNone) {
902d1d35e2fSjeremylt     ierr = CeedCalloc(num_qpts*num_nodes, &identity); CeedChkBackend(ierr);
903d1d35e2fSjeremylt     for (CeedInt i=0; i<(num_nodes<num_qpts?num_nodes:num_qpts); i++)
904d1d35e2fSjeremylt       identity[i*num_nodes+i] = 1.0;
905dfffd467Sjeremylt   }
906d1d35e2fSjeremylt   ierr = CeedBasisGetInterp(basis_in, &interp_in); CeedChkBackend(ierr);
907d1d35e2fSjeremylt   ierr = CeedBasisGetInterp(basis_out, &interp_out); CeedChkBackend(ierr);
908d1d35e2fSjeremylt   ierr = CeedBasisGetGrad(basis_in, &grad_in); CeedChkBackend(ierr);
909d1d35e2fSjeremylt   ierr = CeedBasisGetGrad(basis_out, &grad_out); CeedChkBackend(ierr);
910dfffd467Sjeremylt   // Compute the diagonal of B^T D B
911dfffd467Sjeremylt   // Each element
912d1d35e2fSjeremylt   const CeedScalar qf_value_bound = max_norm*1e-12;
913d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
914d1d35e2fSjeremylt     CeedInt d_out = -1;
9157172caadSjeremylt     // Each basis eval mode pair
916d1d35e2fSjeremylt     for (CeedInt e_out=0; e_out<num_eval_mode_out; e_out++) {
9176c58de82SJeremy L Thompson       const CeedScalar *bt = NULL;
918d1d35e2fSjeremylt       if (eval_mode_out[e_out] == CEED_EVAL_GRAD)
919d1d35e2fSjeremylt         d_out += 1;
920d1d35e2fSjeremylt       CeedOperatorGetBasisPointer_Ref(&bt, eval_mode_out[e_out], identity, interp_out,
921d1d35e2fSjeremylt                                       &grad_out[d_out*num_qpts*num_nodes]);
922d1d35e2fSjeremylt       CeedInt d_in = -1;
923d1d35e2fSjeremylt       for (CeedInt e_in=0; e_in<num_eval_mode_in; e_in++) {
9246c58de82SJeremy L Thompson         const CeedScalar *b = NULL;
925d1d35e2fSjeremylt         if (eval_mode_in[e_in] == CEED_EVAL_GRAD)
926d1d35e2fSjeremylt           d_in += 1;
927d1d35e2fSjeremylt         CeedOperatorGetBasisPointer_Ref(&b, eval_mode_in[e_in], identity, interp_in,
928d1d35e2fSjeremylt                                         &grad_in[d_in*num_qpts*num_nodes]);
929efcf4563Sjeremylt         // Each component
930d1d35e2fSjeremylt         for (CeedInt c_out=0; c_out<num_comp; c_out++)
931dfffd467Sjeremylt           // Each qpoint/node pair
932d1d35e2fSjeremylt           for (CeedInt q=0; q<num_qpts; q++)
933d1d35e2fSjeremylt             if (is_pointblock) {
934d965c7a7SJeremy L Thompson               // Point Block Diagonal
935d1d35e2fSjeremylt               for (CeedInt c_in=0; c_in<num_comp; c_in++) {
936d1d35e2fSjeremylt                 const CeedScalar qf_value =
937d1d35e2fSjeremylt                   assembled_qf_array[((((e*num_eval_mode_in+e_in)*num_comp+c_in)*
938d1d35e2fSjeremylt                                        num_eval_mode_out+e_out)*num_comp+c_out)*num_qpts+q];
939d1d35e2fSjeremylt                 if (fabs(qf_value) > qf_value_bound)
940d1d35e2fSjeremylt                   for (CeedInt n=0; n<num_nodes; n++)
941d1d35e2fSjeremylt                     elem_diag_array[((e*num_comp+c_out)*num_comp+c_in)*num_nodes+n] +=
942d1d35e2fSjeremylt                       bt[q*num_nodes+n] * qf_value * b[q*num_nodes+n];
943d965c7a7SJeremy L Thompson               }
944d965c7a7SJeremy L Thompson             } else {
945d965c7a7SJeremy L Thompson               // Diagonal Only
946d1d35e2fSjeremylt               const CeedScalar qf_value =
947d1d35e2fSjeremylt                 assembled_qf_array[((((e*num_eval_mode_in+e_in)*num_comp+c_out)*
948d1d35e2fSjeremylt                                      num_eval_mode_out+e_out)*num_comp+c_out)*num_qpts+q];
949d1d35e2fSjeremylt               if (fabs(qf_value) > qf_value_bound)
950d1d35e2fSjeremylt                 for (CeedInt n=0; n<num_nodes; n++)
951d1d35e2fSjeremylt                   elem_diag_array[(e*num_comp+c_out)*num_nodes+n] +=
952d1d35e2fSjeremylt                     bt[q*num_nodes+n] * qf_value * b[q*num_nodes+n];
9534ff7492eSjeremylt             }
9547172caadSjeremylt       }
9557172caadSjeremylt     }
9567172caadSjeremylt   }
957d1d35e2fSjeremylt   ierr = CeedVectorRestoreArray(elem_diag, &elem_diag_array);
958d1d35e2fSjeremylt   CeedChkBackend(ierr);
959d1d35e2fSjeremylt   ierr = CeedVectorRestoreArray(assembled_qf, &assembled_qf_array);
960e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
9617172caadSjeremylt 
9627172caadSjeremylt   // Assemble local operator diagonal
963d1d35e2fSjeremylt   ierr = CeedElemRestrictionApply(diag_rstr, CEED_TRANSPOSE, elem_diag,
964e15f9bd0SJeremy L Thompson                                   assembled, request); CeedChkBackend(ierr);
9657172caadSjeremylt 
9667172caadSjeremylt   // Cleanup
967d1d35e2fSjeremylt   if (is_pointblock) {
968d1d35e2fSjeremylt     ierr = CeedElemRestrictionDestroy(&diag_rstr); CeedChkBackend(ierr);
969d965c7a7SJeremy L Thompson   }
970d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&assembled_qf); CeedChkBackend(ierr);
971d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&elem_diag); CeedChkBackend(ierr);
972d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_in); CeedChkBackend(ierr);
973d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_out); CeedChkBackend(ierr);
974e15f9bd0SJeremy L Thompson   ierr = CeedFree(&identity); CeedChkBackend(ierr);
9757172caadSjeremylt 
976e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
9777172caadSjeremylt }
9787172caadSjeremylt 
979f10650afSjeremylt //------------------------------------------------------------------------------
980c04a41a7SJeremy L Thompson // Assemble composite diagonal common code
981c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
98269af5e5fSJeremy L Thompson static inline int CeedOperatorLinearAssembleAddDiagonalCompositeCore_Ref(
9832bba3ffaSJeremy L Thompson   CeedOperator op, CeedVector assembled, CeedRequest *request,
984d1d35e2fSjeremylt   const bool is_pointblock) {
985c04a41a7SJeremy L Thompson   int ierr;
986d1d35e2fSjeremylt   CeedInt num_sub;
987d1d35e2fSjeremylt   CeedOperator *suboperators;
988d1d35e2fSjeremylt   ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChkBackend(ierr);
989d1d35e2fSjeremylt   ierr = CeedOperatorGetSubList(op, &suboperators); CeedChkBackend(ierr);
990d1d35e2fSjeremylt   for (CeedInt i = 0; i < num_sub; i++) {
991d1d35e2fSjeremylt     ierr = CeedOperatorAssembleAddDiagonalCore_Ref(suboperators[i], assembled,
992d1d35e2fSjeremylt            request, is_pointblock); CeedChkBackend(ierr);
993c04a41a7SJeremy L Thompson   }
994e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
995c04a41a7SJeremy L Thompson }
996c04a41a7SJeremy L Thompson 
997c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
998d965c7a7SJeremy L Thompson // Assemble Linear Diagonal
999d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
100069af5e5fSJeremy L Thompson static int CeedOperatorLinearAssembleAddDiagonal_Ref(CeedOperator op,
10012bba3ffaSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1002c04a41a7SJeremy L Thompson   int ierr;
1003d1d35e2fSjeremylt   bool is_composite;
1004d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChkBackend(ierr);
1005d1d35e2fSjeremylt   if (is_composite) {
100669af5e5fSJeremy L Thompson     return CeedOperatorLinearAssembleAddDiagonalCompositeCore_Ref(op, assembled,
1007c04a41a7SJeremy L Thompson            request, false);
1008c04a41a7SJeremy L Thompson   } else {
100969af5e5fSJeremy L Thompson     return CeedOperatorAssembleAddDiagonalCore_Ref(op, assembled, request, false);
1010c04a41a7SJeremy L Thompson   }
1011d965c7a7SJeremy L Thompson }
1012d965c7a7SJeremy L Thompson 
1013d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
1014d965c7a7SJeremy L Thompson // Assemble Linear Point Block Diagonal
1015d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
101669af5e5fSJeremy L Thompson static int CeedOperatorLinearAssembleAddPointBlockDiagonal_Ref(CeedOperator op,
10172bba3ffaSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1018c04a41a7SJeremy L Thompson   int ierr;
1019d1d35e2fSjeremylt   bool is_composite;
1020d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChkBackend(ierr);
1021d1d35e2fSjeremylt   if (is_composite) {
102269af5e5fSJeremy L Thompson     return CeedOperatorLinearAssembleAddDiagonalCompositeCore_Ref(op, assembled,
1023c04a41a7SJeremy L Thompson            request, true);
1024c04a41a7SJeremy L Thompson   } else {
102569af5e5fSJeremy L Thompson     return CeedOperatorAssembleAddDiagonalCore_Ref(op, assembled, request, true);
1026c04a41a7SJeremy L Thompson   }
1027d965c7a7SJeremy L Thompson }
1028d965c7a7SJeremy L Thompson 
1029d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
1030713f43c3Sjeremylt // Create FDM Element Inverse
1031f10650afSjeremylt //------------------------------------------------------------------------------
1032713f43c3Sjeremylt int CeedOperatorCreateFDMElementInverse_Ref(CeedOperator op,
1033d1d35e2fSjeremylt     CeedOperator *fdm_inv, CeedRequest *request) {
1034713f43c3Sjeremylt   int ierr;
1035d1d35e2fSjeremylt   Ceed ceed, ceed_parent;
1036e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
1037d1d35e2fSjeremylt   ierr = CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent);
1038e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1039d1d35e2fSjeremylt   ceed_parent = ceed_parent ? ceed_parent : ceed;
1040713f43c3Sjeremylt   CeedQFunction qf;
1041e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
1042713f43c3Sjeremylt 
1043713f43c3Sjeremylt   // Determine active input basis
1044713f43c3Sjeremylt   bool interp = false, grad = false;
1045713f43c3Sjeremylt   CeedBasis basis = NULL;
1046713f43c3Sjeremylt   CeedElemRestriction rstr = NULL;
1047d1d35e2fSjeremylt   CeedOperatorField *op_fields;
1048d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
1049d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_fields, NULL); CeedChkBackend(ierr);
1050d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChkBackend(ierr);
1051d1d35e2fSjeremylt   CeedInt num_input_fields;
1052d1d35e2fSjeremylt   ierr = CeedQFunctionGetNumArgs(qf, &num_input_fields, NULL);
1053d1d35e2fSjeremylt   CeedChkBackend(ierr);
1054d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
1055713f43c3Sjeremylt     CeedVector vec;
1056d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChkBackend(ierr);
1057713f43c3Sjeremylt     if (vec == CEED_VECTOR_ACTIVE) {
1058d1d35e2fSjeremylt       CeedEvalMode eval_mode;
1059d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1060d1d35e2fSjeremylt       CeedChkBackend(ierr);
1061d1d35e2fSjeremylt       interp = interp || eval_mode == CEED_EVAL_INTERP;
1062d1d35e2fSjeremylt       grad = grad || eval_mode == CEED_EVAL_GRAD;
1063d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChkBackend(ierr);
1064d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr);
1065e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
1066713f43c3Sjeremylt     }
1067713f43c3Sjeremylt   }
1068713f43c3Sjeremylt   if (!basis)
1069d9995aecSjeremylt     // LCOV_EXCL_START
1070e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "No active field set");
1071d9995aecSjeremylt   // LCOV_EXCL_STOP
1072d1d35e2fSjeremylt   CeedInt P_1d, Q_1d, elem_size, num_qpts, dim, num_comp = 1, num_elem = 1,
1073d1d35e2fSjeremylt                                                 l_size = 1;
1074d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes1D(basis, &P_1d); CeedChkBackend(ierr);
1075d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes(basis, &elem_size); CeedChkBackend(ierr);
1076d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d); CeedChkBackend(ierr);
1077d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis, &num_qpts); CeedChkBackend(ierr);
1078e15f9bd0SJeremy L Thompson   ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
1079d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis, &num_comp); CeedChkBackend(ierr);
1080d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChkBackend(ierr);
1081d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetLVectorSize(rstr, &l_size); CeedChkBackend(ierr);
1082713f43c3Sjeremylt 
1083713f43c3Sjeremylt   // Build and diagonalize 1D Mass and Laplacian
1084d1d35e2fSjeremylt   bool tensor_basis;
1085d1d35e2fSjeremylt   ierr = CeedBasisIsTensor(basis, &tensor_basis); CeedChkBackend(ierr);
1086d1d35e2fSjeremylt   if (!tensor_basis)
1087d9995aecSjeremylt     // LCOV_EXCL_START
1088e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
1089e15f9bd0SJeremy L Thompson                      "FDMElementInverse only supported for tensor "
1090713f43c3Sjeremylt                      "bases");
1091d9995aecSjeremylt   // LCOV_EXCL_STOP
1092713f43c3Sjeremylt   CeedScalar *work, *mass, *laplace, *x, *x2, *lambda;
1093d1d35e2fSjeremylt   ierr = CeedMalloc(Q_1d*P_1d, &work); CeedChkBackend(ierr);
1094d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &mass); CeedChkBackend(ierr);
1095d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &laplace); CeedChkBackend(ierr);
1096d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &x); CeedChkBackend(ierr);
1097d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &x2); CeedChkBackend(ierr);
1098d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d, &lambda); CeedChkBackend(ierr);
1099713f43c3Sjeremylt   // -- Mass
1100d1d35e2fSjeremylt   const CeedScalar *interp_1d, *grad_1d, *q_weight_1d;
1101d1d35e2fSjeremylt   ierr = CeedBasisGetInterp1D(basis, &interp_1d); CeedChkBackend(ierr);
1102d1d35e2fSjeremylt   ierr = CeedBasisGetGrad1D(basis, &grad_1d); CeedChkBackend(ierr);
1103d1d35e2fSjeremylt   ierr = CeedBasisGetQWeights(basis, &q_weight_1d); CeedChkBackend(ierr);
1104d1d35e2fSjeremylt   for (CeedInt i=0; i<Q_1d; i++)
1105d1d35e2fSjeremylt     for (CeedInt j=0; j<P_1d; j++)
1106d1d35e2fSjeremylt       work[i+j*Q_1d] = interp_1d[i*P_1d+j]*q_weight_1d[i];
11079289e5bfSjeremylt   ierr = CeedMatrixMultiply(ceed, (const CeedScalar *)work,
1108d1d35e2fSjeremylt                             (const CeedScalar *)interp_1d, mass, P_1d, P_1d, Q_1d);
1109e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1110713f43c3Sjeremylt   // -- Laplacian
1111d1d35e2fSjeremylt   for (CeedInt i=0; i<Q_1d; i++)
1112d1d35e2fSjeremylt     for (CeedInt j=0; j<P_1d; j++)
1113d1d35e2fSjeremylt       work[i+j*Q_1d] = grad_1d[i*P_1d+j]*q_weight_1d[i];
11149289e5bfSjeremylt   ierr = CeedMatrixMultiply(ceed, (const CeedScalar *)work,
1115d1d35e2fSjeremylt                             (const CeedScalar *)grad_1d, laplace, P_1d, P_1d, Q_1d);
1116e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1117713f43c3Sjeremylt   // -- Diagonalize
1118d1d35e2fSjeremylt   ierr = CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d);
1119e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1120e15f9bd0SJeremy L Thompson   ierr = CeedFree(&work); CeedChkBackend(ierr);
1121e15f9bd0SJeremy L Thompson   ierr = CeedFree(&mass); CeedChkBackend(ierr);
1122e15f9bd0SJeremy L Thompson   ierr = CeedFree(&laplace); CeedChkBackend(ierr);
1123d1d35e2fSjeremylt   for (CeedInt i=0; i<P_1d; i++)
1124d1d35e2fSjeremylt     for (CeedInt j=0; j<P_1d; j++)
1125d1d35e2fSjeremylt       x2[i+j*P_1d] = x[j+i*P_1d];
1126e15f9bd0SJeremy L Thompson   ierr = CeedFree(&x); CeedChkBackend(ierr);
1127713f43c3Sjeremylt 
1128713f43c3Sjeremylt   // Assemble QFunction
1129713f43c3Sjeremylt   CeedVector assembled;
1130713f43c3Sjeremylt   CeedElemRestriction rstr_qf;
113180ac2e43SJeremy L Thompson   ierr =  CeedOperatorLinearAssembleQFunction(op, &assembled, &rstr_qf,
1132e15f9bd0SJeremy L Thompson           request); CeedChkBackend(ierr);
1133e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_qf); CeedChkBackend(ierr);
1134d1d35e2fSjeremylt   CeedScalar max_norm = 0;
1135d1d35e2fSjeremylt   ierr = CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm);
1136d1d35e2fSjeremylt   CeedChkBackend(ierr);
1137713f43c3Sjeremylt 
1138713f43c3Sjeremylt   // Calculate element averages
1139d1d35e2fSjeremylt   CeedInt num_fields = ((interp?1:0) + (grad?dim:0))*((interp?1:0) +
1140d1d35e2fSjeremylt                        (grad?dim:0));
1141d1d35e2fSjeremylt   CeedScalar *elem_avg;
1142d1d35e2fSjeremylt   const CeedScalar *assembledarray, *q_weight_array;
1143d1d35e2fSjeremylt   CeedVector q_weight;
1144d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed_parent, num_qpts, &q_weight); CeedChkBackend(ierr);
1145713f43c3Sjeremylt   ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
1146d1d35e2fSjeremylt                         CEED_VECTOR_NONE, q_weight); CeedChkBackend(ierr);
1147713f43c3Sjeremylt   ierr = CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembledarray);
1148e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1149d1d35e2fSjeremylt   ierr = CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array);
1150e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1151d1d35e2fSjeremylt   ierr = CeedCalloc(num_elem, &elem_avg); CeedChkBackend(ierr);
1152d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
1153713f43c3Sjeremylt     CeedInt count = 0;
1154d1d35e2fSjeremylt     for (CeedInt q=0; q<num_qpts; q++)
1155d1d35e2fSjeremylt       for (CeedInt i=0; i<num_comp*num_comp*num_fields; i++)
1156d1d35e2fSjeremylt         if (fabs(assembledarray[e*num_elem*num_qpts*num_comp*num_comp*num_fields +
1157d1d35e2fSjeremylt                                                                                  i*num_qpts + q]) > max_norm*1e-12) {
1158d1d35e2fSjeremylt           elem_avg[e] += assembledarray[e*num_elem*num_qpts*num_comp*num_comp*num_fields +
1159d1d35e2fSjeremylt                                         i*num_qpts + q] / q_weight_array[q];
1160713f43c3Sjeremylt           count++;
1161713f43c3Sjeremylt         }
1162713f43c3Sjeremylt     if (count)
1163d1d35e2fSjeremylt       elem_avg[e] /= count;
1164713f43c3Sjeremylt   }
1165e15f9bd0SJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(assembled, &assembledarray);
1166e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1167e15f9bd0SJeremy L Thompson   ierr = CeedVectorDestroy(&assembled); CeedChkBackend(ierr);
1168d1d35e2fSjeremylt   ierr = CeedVectorRestoreArrayRead(q_weight, &q_weight_array);
1169e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1170d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&q_weight); CeedChkBackend(ierr);
1171713f43c3Sjeremylt 
1172713f43c3Sjeremylt   // Build FDM diagonal
1173d1d35e2fSjeremylt   CeedVector q_data;
1174d1d35e2fSjeremylt   CeedScalar *q_data_array;
1175d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed_parent, num_elem*num_comp*l_size, &q_data);
1176e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1177d1d35e2fSjeremylt   ierr = CeedVectorSetArray(q_data, CEED_MEM_HOST, CEED_COPY_VALUES, NULL);
1178e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1179d1d35e2fSjeremylt   ierr = CeedVectorGetArray(q_data, CEED_MEM_HOST, &q_data_array);
1180e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1181d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++)
1182d1d35e2fSjeremylt     for (CeedInt c=0; c<num_comp; c++)
1183d1d35e2fSjeremylt       for (CeedInt n=0; n<l_size; n++) {
1184713f43c3Sjeremylt         if (interp)
1185d1d35e2fSjeremylt           q_data_array[(e*num_comp+c)*l_size+n] = 1;
1186713f43c3Sjeremylt         if (grad)
1187713f43c3Sjeremylt           for (CeedInt d=0; d<dim; d++) {
1188d1d35e2fSjeremylt             CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d;
1189d1d35e2fSjeremylt             q_data_array[(e*num_comp+c)*l_size+n] += lambda[i];
1190713f43c3Sjeremylt           }
1191d1d35e2fSjeremylt         q_data_array[(e*num_comp+c)*l_size+n] = 1 / (elem_avg[e] *
1192d1d35e2fSjeremylt                                                 q_data_array[(e*num_comp+c)*l_size+n]);
1193713f43c3Sjeremylt       }
1194d1d35e2fSjeremylt   ierr = CeedFree(&elem_avg); CeedChkBackend(ierr);
1195d1d35e2fSjeremylt   ierr = CeedVectorRestoreArray(q_data, &q_data_array); CeedChkBackend(ierr);
1196713f43c3Sjeremylt 
1197713f43c3Sjeremylt   // Setup FDM operator
1198713f43c3Sjeremylt   // -- Basis
1199713f43c3Sjeremylt   CeedBasis fdm_basis;
1200d1d35e2fSjeremylt   CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy;
1201d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d*P_1d, &grad_dummy); CeedChkBackend(ierr);
1202d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d, &q_ref_dummy); CeedChkBackend(ierr);
1203d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d, &q_weight_dummy); CeedChkBackend(ierr);
1204d1d35e2fSjeremylt   ierr = CeedBasisCreateTensorH1(ceed_parent, dim, num_comp, P_1d, P_1d, x2,
1205d1d35e2fSjeremylt                                  grad_dummy, q_ref_dummy, q_weight_dummy,
1206e15f9bd0SJeremy L Thompson                                  &fdm_basis); CeedChkBackend(ierr);
1207d1d35e2fSjeremylt   ierr = CeedFree(&grad_dummy); CeedChkBackend(ierr);
1208d1d35e2fSjeremylt   ierr = CeedFree(&q_ref_dummy); CeedChkBackend(ierr);
1209d1d35e2fSjeremylt   ierr = CeedFree(&q_weight_dummy); CeedChkBackend(ierr);
1210e15f9bd0SJeremy L Thompson   ierr = CeedFree(&x2); CeedChkBackend(ierr);
1211e15f9bd0SJeremy L Thompson   ierr = CeedFree(&lambda); CeedChkBackend(ierr);
1212713f43c3Sjeremylt 
1213713f43c3Sjeremylt   // -- Restriction
1214713f43c3Sjeremylt   CeedElemRestriction rstr_i;
1215d1d35e2fSjeremylt   CeedInt strides[3] = {1, l_size, l_size*num_comp};
1216d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateStrided(ceed_parent, num_elem, l_size, num_comp,
1217d1d35e2fSjeremylt                                           l_size*num_elem*num_comp, strides, &rstr_i);
1218e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1219713f43c3Sjeremylt   // -- QFunction
1220713f43c3Sjeremylt   CeedQFunction mass_qf;
1221d1d35e2fSjeremylt   ierr = CeedQFunctionCreateInteriorByName(ceed_parent, "MassApply", &mass_qf);
1222e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1223713f43c3Sjeremylt   // -- Operator
1224d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed_parent, mass_qf, NULL, NULL, fdm_inv);
1225e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1226d1d35e2fSjeremylt   CeedOperatorSetField(*fdm_inv, "u", rstr_i, fdm_basis, CEED_VECTOR_ACTIVE);
1227e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1228d1d35e2fSjeremylt   CeedOperatorSetField(*fdm_inv, "qdata", rstr_i, CEED_BASIS_COLLOCATED, q_data);
1229e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1230d1d35e2fSjeremylt   CeedOperatorSetField(*fdm_inv, "v", rstr_i, fdm_basis, CEED_VECTOR_ACTIVE);
1231e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1232713f43c3Sjeremylt 
1233713f43c3Sjeremylt   // Cleanup
1234d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&q_data); CeedChkBackend(ierr);
1235e15f9bd0SJeremy L Thompson   ierr = CeedBasisDestroy(&fdm_basis); CeedChkBackend(ierr);
1236e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_i); CeedChkBackend(ierr);
1237e15f9bd0SJeremy L Thompson   ierr = CeedQFunctionDestroy(&mass_qf); CeedChkBackend(ierr);
1238713f43c3Sjeremylt 
1239e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1240713f43c3Sjeremylt }
1241713f43c3Sjeremylt 
1242f10650afSjeremylt //------------------------------------------------------------------------------
1243f10650afSjeremylt // Operator Destroy
1244f10650afSjeremylt //------------------------------------------------------------------------------
1245f10650afSjeremylt static int CeedOperatorDestroy_Ref(CeedOperator op) {
1246f10650afSjeremylt   int ierr;
1247f10650afSjeremylt   CeedOperator_Ref *impl;
1248e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
1249f10650afSjeremylt 
1250d1d35e2fSjeremylt   for (CeedInt i=0; i<impl->num_e_vecs_in+impl->num_e_vecs_out; i++) {
1251d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->e_vecs[i]); CeedChkBackend(ierr);
1252f10650afSjeremylt   }
1253d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_vecs); CeedChkBackend(ierr);
1254d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_data); CeedChkBackend(ierr);
1255d1d35e2fSjeremylt   ierr = CeedFree(&impl->input_state); CeedChkBackend(ierr);
1256f10650afSjeremylt 
1257d1d35e2fSjeremylt   for (CeedInt i=0; i<impl->num_e_vecs_in; i++) {
1258d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->e_vecs_in[i]); CeedChkBackend(ierr);
1259d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->q_vecs_in[i]); CeedChkBackend(ierr);
1260f10650afSjeremylt   }
1261d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_vecs_in); CeedChkBackend(ierr);
1262d1d35e2fSjeremylt   ierr = CeedFree(&impl->q_vecs_in); CeedChkBackend(ierr);
1263f10650afSjeremylt 
1264d1d35e2fSjeremylt   for (CeedInt i=0; i<impl->num_e_vecs_out; i++) {
1265d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->e_vecs_out[i]); CeedChkBackend(ierr);
1266d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->q_vecs_out[i]); CeedChkBackend(ierr);
1267f10650afSjeremylt   }
1268d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_vecs_out); CeedChkBackend(ierr);
1269d1d35e2fSjeremylt   ierr = CeedFree(&impl->q_vecs_out); CeedChkBackend(ierr);
1270f10650afSjeremylt 
1271e15f9bd0SJeremy L Thompson   ierr = CeedFree(&impl); CeedChkBackend(ierr);
1272e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1273f10650afSjeremylt }
1274f10650afSjeremylt 
1275f10650afSjeremylt //------------------------------------------------------------------------------
1276713f43c3Sjeremylt // Operator Create
1277f10650afSjeremylt //------------------------------------------------------------------------------
127821617c04Sjeremylt int CeedOperatorCreate_Ref(CeedOperator op) {
127921617c04Sjeremylt   int ierr;
1280fe2413ffSjeremylt   Ceed ceed;
1281e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
12824ce2993fSjeremylt   CeedOperator_Ref *impl;
128321617c04Sjeremylt 
1284e15f9bd0SJeremy L Thompson   ierr = CeedCalloc(1, &impl); CeedChkBackend(ierr);
1285e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetData(op, impl); CeedChkBackend(ierr);
1286fe2413ffSjeremylt 
128780ac2e43SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op, "LinearAssembleQFunction",
128880ac2e43SJeremy L Thompson                                 CeedOperatorLinearAssembleQFunction_Ref);
1289e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
12909e9210b8SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op, "LinearAssembleAddDiagonal",
129169af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddDiagonal_Ref);
1292e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1293d965c7a7SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op,
12949e9210b8SJeremy L Thompson                                 "LinearAssembleAddPointBlockDiagonal",
129569af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddPointBlockDiagonal_Ref);
1296e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1297713f43c3Sjeremylt   ierr = CeedSetBackendFunction(ceed, "Operator", op, "CreateFDMElementInverse",
1298713f43c3Sjeremylt                                 CeedOperatorCreateFDMElementInverse_Ref);
1299e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1300cae8b89aSjeremylt   ierr = CeedSetBackendFunction(ceed, "Operator", op, "ApplyAdd",
1301e15f9bd0SJeremy L Thompson                                 CeedOperatorApplyAdd_Ref); CeedChkBackend(ierr);
1302fe2413ffSjeremylt   ierr = CeedSetBackendFunction(ceed, "Operator", op, "Destroy",
1303e15f9bd0SJeremy L Thompson                                 CeedOperatorDestroy_Ref); CeedChkBackend(ierr);
1304e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
130521617c04Sjeremylt }
1306c04a41a7SJeremy L Thompson 
1307c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
1308c04a41a7SJeremy L Thompson // Composite Operator Create
1309c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
1310c04a41a7SJeremy L Thompson int CeedCompositeOperatorCreate_Ref(CeedOperator op) {
1311c04a41a7SJeremy L Thompson   int ierr;
1312c04a41a7SJeremy L Thompson   Ceed ceed;
1313e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
13149e9210b8SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op, "LinearAssembleAddDiagonal",
131569af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddDiagonal_Ref);
1316e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1317c04a41a7SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op,
13189e9210b8SJeremy L Thompson                                 "LinearAssembleAddPointBlockDiagonal",
131969af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddPointBlockDiagonal_Ref);
1320e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1321e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1322c04a41a7SJeremy L Thompson }
1323f10650afSjeremylt //------------------------------------------------------------------------------
1324