xref: /libCEED/rust/libceed-sys/c-src/backends/ref/ceed-ref-operator.c (revision d1d35e2f02dc969aee8debf3fd943dd784aa847a)
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,
30*d1d35e2fSjeremylt                                        CeedVector *full_evecs, CeedVector *e_vecs,
31*d1d35e2fSjeremylt                                        CeedVector *q_vecs, CeedInt starte,
32*d1d35e2fSjeremylt                                        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;
37*d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
38*d1d35e2fSjeremylt   CeedOperatorField *op_fields;
39*d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
40fe2413ffSjeremylt   if (inOrOut) {
41*d1d35e2fSjeremylt     ierr = CeedOperatorGetFields(op, NULL, &op_fields); CeedChkBackend(ierr);
42*d1d35e2fSjeremylt     ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields); CeedChkBackend(ierr);
43fe2413ffSjeremylt   } else {
44*d1d35e2fSjeremylt     ierr = CeedOperatorGetFields(op, &op_fields, NULL); CeedChkBackend(ierr);
45*d1d35e2fSjeremylt     ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChkBackend(ierr);
46fe2413ffSjeremylt   }
4721617c04Sjeremylt 
48885ac19cSjeremylt   // Loop over fields
49*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_fields; i++) {
50*d1d35e2fSjeremylt     CeedEvalMode eval_mode;
51*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
52e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
53*d1d35e2fSjeremylt 
54*d1d35e2fSjeremylt     if (eval_mode != CEED_EVAL_WEIGHT) {
55*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_restr);
56*d1d35e2fSjeremylt       CeedChkBackend(ierr);
57*d1d35e2fSjeremylt       ierr = CeedElemRestrictionCreateVector(elem_restr, NULL,
58*d1d35e2fSjeremylt                                              &full_evecs[i+starte]);
59e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
60135a076eSjeremylt     }
61135a076eSjeremylt 
62*d1d35e2fSjeremylt     switch(eval_mode) {
63885ac19cSjeremylt     case CEED_EVAL_NONE:
64*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_fields[i], &size); CeedChkBackend(ierr);
65*d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q*size, &q_vecs[i]); CeedChkBackend(ierr);
66aedaa0e5Sjeremylt       break;
67aedaa0e5Sjeremylt     case CEED_EVAL_INTERP:
68*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_fields[i], &size); CeedChkBackend(ierr);
69*d1d35e2fSjeremylt       ierr = CeedElemRestrictionGetElementSize(elem_restr, &P);
70e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
71*d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, P*size, &e_vecs[i]); CeedChkBackend(ierr);
72*d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q*size, &q_vecs[i]); CeedChkBackend(ierr);
73885ac19cSjeremylt       break;
74885ac19cSjeremylt     case CEED_EVAL_GRAD:
75*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChkBackend(ierr);
76*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_fields[i], &size); CeedChkBackend(ierr);
77e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
78*d1d35e2fSjeremylt       ierr = CeedElemRestrictionGetElementSize(elem_restr, &P);
79e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
80*d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, P*size/dim, &e_vecs[i]); CeedChkBackend(ierr);
81*d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q*size, &q_vecs[i]); CeedChkBackend(ierr);
82885ac19cSjeremylt       break;
83885ac19cSjeremylt     case CEED_EVAL_WEIGHT: // Only on input fields
84*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChkBackend(ierr);
85*d1d35e2fSjeremylt       ierr = CeedVectorCreate(ceed, Q, &q_vecs[i]); CeedChkBackend(ierr);
86d1bcdac9Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
87*d1d35e2fSjeremylt                             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;
103*d1d35e2fSjeremylt   bool setup_done;
104*d1d35e2fSjeremylt   ierr = CeedOperatorIsSetupDone(op, &setup_done); CeedChkBackend(ierr);
105*d1d35e2fSjeremylt   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);
112*d1d35e2fSjeremylt   CeedInt Q, num_input_fields, num_output_fields;
113e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetNumQuadraturePoints(op, &Q); CeedChkBackend(ierr);
114*d1d35e2fSjeremylt   ierr = CeedQFunctionIsIdentity(qf, &impl->identity_qf); CeedChkBackend(ierr);
115*d1d35e2fSjeremylt   ierr = CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
116e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
117*d1d35e2fSjeremylt   CeedOperatorField *op_input_fields, *op_output_fields;
118*d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_input_fields, &op_output_fields);
119e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
120*d1d35e2fSjeremylt   CeedQFunctionField *qf_input_fields, *qf_output_fields;
121*d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_input_fields, &qf_output_fields);
122e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
123885ac19cSjeremylt 
124885ac19cSjeremylt   // Allocate
125*d1d35e2fSjeremylt   ierr = CeedCalloc(num_input_fields + num_output_fields, &impl->e_vecs);
126e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
127*d1d35e2fSjeremylt   ierr = CeedCalloc(num_input_fields + num_output_fields, &impl->e_data);
128e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
129885ac19cSjeremylt 
130*d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->input_state); CeedChkBackend(ierr);
131*d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->e_vecs_in); CeedChkBackend(ierr);
132*d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->e_vecs_out); CeedChkBackend(ierr);
133*d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->q_vecs_in); CeedChkBackend(ierr);
134*d1d35e2fSjeremylt   ierr = CeedCalloc(16, &impl->q_vecs_out); CeedChkBackend(ierr);
135885ac19cSjeremylt 
136*d1d35e2fSjeremylt   impl->num_e_vecs_in = num_input_fields;
137*d1d35e2fSjeremylt   impl->num_e_vecs_out = num_output_fields;
138885ac19cSjeremylt 
139*d1d35e2fSjeremylt   // Set up infield and outfield e_vecs and q_vecs
140885ac19cSjeremylt   // Infields
141*d1d35e2fSjeremylt   ierr = CeedOperatorSetupFields_Ref(qf, op, 0, impl->e_vecs,
142*d1d35e2fSjeremylt                                      impl->e_vecs_in, impl->q_vecs_in, 0,
143*d1d35e2fSjeremylt                                      num_input_fields, Q);
144e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
145885ac19cSjeremylt   // Outfields
146*d1d35e2fSjeremylt   ierr = CeedOperatorSetupFields_Ref(qf, op, 1, impl->e_vecs,
147*d1d35e2fSjeremylt                                      impl->e_vecs_out, impl->q_vecs_out,
148*d1d35e2fSjeremylt                                      num_input_fields, num_output_fields, Q);
149e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
150885ac19cSjeremylt 
15116911fdaSjeremylt   // Identity QFunctions
152*d1d35e2fSjeremylt   if (impl->identity_qf) {
153*d1d35e2fSjeremylt     CeedEvalMode in_mode, out_mode;
154*d1d35e2fSjeremylt     CeedQFunctionField *in_fields, *out_fields;
155*d1d35e2fSjeremylt     ierr = CeedQFunctionGetFields(qf, &in_fields, &out_fields);
156e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
15716911fdaSjeremylt 
158*d1d35e2fSjeremylt     for (CeedInt i=0; i<num_input_fields; i++) {
159*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(in_fields[i], &in_mode);
160*d1d35e2fSjeremylt       CeedChkBackend(ierr);
161*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(out_fields[i], &out_mode);
162*d1d35e2fSjeremylt       CeedChkBackend(ierr);
163*d1d35e2fSjeremylt 
164*d1d35e2fSjeremylt       ierr = CeedVectorDestroy(&impl->q_vecs_out[i]); CeedChkBackend(ierr);
165*d1d35e2fSjeremylt       impl->q_vecs_out[i] = impl->q_vecs_in[i];
166*d1d35e2fSjeremylt       ierr = CeedVectorAddReference(impl->q_vecs_in[i]); CeedChkBackend(ierr);
16716911fdaSjeremylt     }
16816911fdaSjeremylt   }
16916911fdaSjeremylt 
170e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetSetupDone(op); CeedChkBackend(ierr);
171885ac19cSjeremylt 
172e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
173885ac19cSjeremylt }
174885ac19cSjeremylt 
175f10650afSjeremylt //------------------------------------------------------------------------------
176f10650afSjeremylt // Setup Operator Inputs
177f10650afSjeremylt //------------------------------------------------------------------------------
178*d1d35e2fSjeremylt static inline int CeedOperatorSetupInputs_Ref(CeedInt num_input_fields,
179*d1d35e2fSjeremylt     CeedQFunctionField *qf_input_fields, CeedOperatorField *op_input_fields,
180*d1d35e2fSjeremylt     CeedVector in_vec, const bool skip_active, CeedOperator_Ref *impl,
1811d102b48SJeremy L Thompson     CeedRequest *request) {
1821d102b48SJeremy L Thompson   CeedInt ierr;
183*d1d35e2fSjeremylt   CeedEvalMode eval_mode;
184d1bcdac9Sjeremylt   CeedVector vec;
185*d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
1868d713cf6Sjeremylt   uint64_t state;
187885ac19cSjeremylt 
188*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
189d1bcdac9Sjeremylt     // Get input vector
190*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
191*d1d35e2fSjeremylt     CeedChkBackend(ierr);
1921d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
193*d1d35e2fSjeremylt       if (skip_active)
1941d102b48SJeremy L Thompson         continue;
1951d102b48SJeremy L Thompson       else
196*d1d35e2fSjeremylt         vec = in_vec;
1971d102b48SJeremy L Thompson     }
1981d102b48SJeremy L Thompson 
199*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_input_fields[i], &eval_mode);
200e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
2011d102b48SJeremy L Thompson     // Restrict and Evec
202*d1d35e2fSjeremylt     if (eval_mode == CEED_EVAL_WEIGHT) { // Skip
2031d102b48SJeremy L Thompson     } else {
204668048e2SJed Brown       // Restrict
205e15f9bd0SJeremy L Thompson       ierr = CeedVectorGetState(vec, &state); CeedChkBackend(ierr);
2068d713cf6Sjeremylt       // Skip restriction if input is unchanged
207*d1d35e2fSjeremylt       if (state != impl->input_state[i] || vec == in_vec) {
208*d1d35e2fSjeremylt         ierr = CeedOperatorFieldGetElemRestriction(op_input_fields[i], &elem_restr);
209e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
210*d1d35e2fSjeremylt         ierr = CeedElemRestrictionApply(elem_restr, CEED_NOTRANSPOSE, vec,
211*d1d35e2fSjeremylt                                         impl->e_vecs[i], request); CeedChkBackend(ierr);
212*d1d35e2fSjeremylt         impl->input_state[i] = state;
2138d713cf6Sjeremylt       }
214668048e2SJed Brown       // Get evec
215*d1d35e2fSjeremylt       ierr = CeedVectorGetArrayRead(impl->e_vecs[i], CEED_MEM_HOST,
216*d1d35e2fSjeremylt                                     (const CeedScalar **) &impl->e_data[i]);
217e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
218885ac19cSjeremylt     }
219885ac19cSjeremylt   }
220e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
221885ac19cSjeremylt }
222885ac19cSjeremylt 
223f10650afSjeremylt //------------------------------------------------------------------------------
224f10650afSjeremylt // Input Basis Action
225f10650afSjeremylt //------------------------------------------------------------------------------
2261d102b48SJeremy L Thompson static inline int CeedOperatorInputBasis_Ref(CeedInt e, CeedInt Q,
227*d1d35e2fSjeremylt     CeedQFunctionField *qf_input_fields, CeedOperatorField *op_input_fields,
228*d1d35e2fSjeremylt     CeedInt num_input_fields, const bool skip_active, CeedOperator_Ref *impl) {
2291d102b48SJeremy L Thompson   CeedInt ierr;
230*d1d35e2fSjeremylt   CeedInt dim, elem_size, size;
231*d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
232*d1d35e2fSjeremylt   CeedEvalMode eval_mode;
2331d102b48SJeremy L Thompson   CeedBasis basis;
2341d102b48SJeremy L Thompson 
235*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
2361d102b48SJeremy L Thompson     // Skip active input
237*d1d35e2fSjeremylt     if (skip_active) {
2381d102b48SJeremy L Thompson       CeedVector vec;
239*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
240*d1d35e2fSjeremylt       CeedChkBackend(ierr);
2411d102b48SJeremy L Thompson       if (vec == CEED_VECTOR_ACTIVE)
2421d102b48SJeremy L Thompson         continue;
2431d102b48SJeremy L Thompson     }
244*d1d35e2fSjeremylt     // Get elem_size, eval_mode, size
245*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_input_fields[i], &elem_restr);
246e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
247*d1d35e2fSjeremylt     ierr = CeedElemRestrictionGetElementSize(elem_restr, &elem_size);
248e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
249*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_input_fields[i], &eval_mode);
250e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
251*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetSize(qf_input_fields[i], &size);
252*d1d35e2fSjeremylt     CeedChkBackend(ierr);
253885ac19cSjeremylt     // Basis action
254*d1d35e2fSjeremylt     switch(eval_mode) {
255885ac19cSjeremylt     case CEED_EVAL_NONE:
256*d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->q_vecs_in[i], CEED_MEM_HOST,
257*d1d35e2fSjeremylt                                 CEED_USE_POINTER, &impl->e_data[i][e*Q*size]);
258*d1d35e2fSjeremylt       CeedChkBackend(ierr);
259885ac19cSjeremylt       break;
260885ac19cSjeremylt     case CEED_EVAL_INTERP:
261*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_input_fields[i], &basis);
262e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
263*d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_in[i], CEED_MEM_HOST,
264*d1d35e2fSjeremylt                                 CEED_USE_POINTER, &impl->e_data[i][e*elem_size*size]);
265e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
266*d1d35e2fSjeremylt       ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_INTERP,
267*d1d35e2fSjeremylt                             impl->e_vecs_in[i], impl->q_vecs_in[i]); CeedChkBackend(ierr);
268885ac19cSjeremylt       break;
269885ac19cSjeremylt     case CEED_EVAL_GRAD:
270*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_input_fields[i], &basis);
271e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
272e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
273*d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_in[i], CEED_MEM_HOST,
274*d1d35e2fSjeremylt                                 CEED_USE_POINTER, &impl->e_data[i][e*elem_size*size/dim]);
275e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
276d1bcdac9Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE,
277*d1d35e2fSjeremylt                             CEED_EVAL_GRAD, impl->e_vecs_in[i],
278*d1d35e2fSjeremylt                             impl->q_vecs_in[i]); CeedChkBackend(ierr);
279885ac19cSjeremylt       break;
280885ac19cSjeremylt     case CEED_EVAL_WEIGHT:
281885ac19cSjeremylt       break;  // No action
282bbfacfcdSjeremylt     // LCOV_EXCL_START
283885ac19cSjeremylt     case CEED_EVAL_DIV:
2841d102b48SJeremy L Thompson     case CEED_EVAL_CURL: {
285*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_input_fields[i], &basis);
286e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
2871d102b48SJeremy L Thompson       Ceed ceed;
288e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetCeed(basis, &ceed); CeedChkBackend(ierr);
289e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_BACKEND,
290e15f9bd0SJeremy L Thompson                        "Ceed evaluation mode not implemented");
291bbfacfcdSjeremylt       // LCOV_EXCL_STOP
292885ac19cSjeremylt     }
293885ac19cSjeremylt     }
294885ac19cSjeremylt   }
295e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
296885ac19cSjeremylt }
297885ac19cSjeremylt 
298f10650afSjeremylt //------------------------------------------------------------------------------
299f10650afSjeremylt // Output Basis Action
300f10650afSjeremylt //------------------------------------------------------------------------------
3011d102b48SJeremy L Thompson static inline int CeedOperatorOutputBasis_Ref(CeedInt e, CeedInt Q,
302*d1d35e2fSjeremylt     CeedQFunctionField *qf_output_fields, CeedOperatorField *op_output_fields,
303*d1d35e2fSjeremylt     CeedInt num_input_fields, CeedInt num_output_fields, CeedOperator op,
3041d102b48SJeremy L Thompson     CeedOperator_Ref *impl) {
3051d102b48SJeremy L Thompson   CeedInt ierr;
306*d1d35e2fSjeremylt   CeedInt dim, elem_size, size;
307*d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
308*d1d35e2fSjeremylt   CeedEvalMode eval_mode;
3091d102b48SJeremy L Thompson   CeedBasis basis;
3101d102b48SJeremy L Thompson 
311*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
312*d1d35e2fSjeremylt     // Get elem_size, eval_mode, size
313*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_output_fields[i], &elem_restr);
314e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
315*d1d35e2fSjeremylt     ierr = CeedElemRestrictionGetElementSize(elem_restr, &elem_size);
316e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
317*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_output_fields[i], &eval_mode);
318e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
319*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetSize(qf_output_fields[i], &size);
320e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
321885ac19cSjeremylt     // Basis action
322*d1d35e2fSjeremylt     switch(eval_mode) {
323885ac19cSjeremylt     case CEED_EVAL_NONE:
324885ac19cSjeremylt       break; // No action
325885ac19cSjeremylt     case CEED_EVAL_INTERP:
326*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_output_fields[i], &basis);
327e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
328*d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_out[i], CEED_MEM_HOST,
329aedaa0e5Sjeremylt                                 CEED_USE_POINTER,
330*d1d35e2fSjeremylt                                 &impl->e_data[i + num_input_fields][e*elem_size*size]);
331e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
332aedaa0e5Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_TRANSPOSE,
333*d1d35e2fSjeremylt                             CEED_EVAL_INTERP, impl->q_vecs_out[i],
334*d1d35e2fSjeremylt                             impl->e_vecs_out[i]); CeedChkBackend(ierr);
335885ac19cSjeremylt       break;
336885ac19cSjeremylt     case CEED_EVAL_GRAD:
337*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_output_fields[i], &basis);
338e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
339e15f9bd0SJeremy L Thompson       ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
340*d1d35e2fSjeremylt       ierr = CeedVectorSetArray(impl->e_vecs_out[i], CEED_MEM_HOST,
341aedaa0e5Sjeremylt                                 CEED_USE_POINTER,
342*d1d35e2fSjeremylt                                 &impl->e_data[i + num_input_fields][e*elem_size*size/dim]);
343e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
344aedaa0e5Sjeremylt       ierr = CeedBasisApply(basis, 1, CEED_TRANSPOSE,
345*d1d35e2fSjeremylt                             CEED_EVAL_GRAD, impl->q_vecs_out[i],
346*d1d35e2fSjeremylt                             impl->e_vecs_out[i]); CeedChkBackend(ierr);
347885ac19cSjeremylt       break;
348c042f62fSJeremy L Thompson     // LCOV_EXCL_START
349bbfacfcdSjeremylt     case CEED_EVAL_WEIGHT: {
3504ce2993fSjeremylt       Ceed ceed;
351e15f9bd0SJeremy L Thompson       ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
352e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_BACKEND,
353e15f9bd0SJeremy L Thompson                        "CEED_EVAL_WEIGHT cannot be an output "
3541d102b48SJeremy L Thompson                        "evaluation mode");
3554ce2993fSjeremylt     }
356885ac19cSjeremylt     case CEED_EVAL_DIV:
3571d102b48SJeremy L Thompson     case CEED_EVAL_CURL: {
3581d102b48SJeremy L Thompson       Ceed ceed;
359e15f9bd0SJeremy L Thompson       ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
360e15f9bd0SJeremy L Thompson       return CeedError(ceed, CEED_ERROR_BACKEND,
361e15f9bd0SJeremy L Thompson                        "Ceed evaluation mode not implemented");
3621d102b48SJeremy L Thompson       // LCOV_EXCL_STOP
363885ac19cSjeremylt     }
364885ac19cSjeremylt     }
365885ac19cSjeremylt   }
366e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
3671d102b48SJeremy L Thompson }
3681d102b48SJeremy L Thompson 
369f10650afSjeremylt //------------------------------------------------------------------------------
370f10650afSjeremylt // Restore Input Vectors
371f10650afSjeremylt //------------------------------------------------------------------------------
372*d1d35e2fSjeremylt static inline int CeedOperatorRestoreInputs_Ref(CeedInt num_input_fields,
373*d1d35e2fSjeremylt     CeedQFunctionField *qf_input_fields, CeedOperatorField *op_input_fields,
374*d1d35e2fSjeremylt     const bool skip_active, CeedOperator_Ref *impl) {
3751d102b48SJeremy L Thompson   CeedInt ierr;
376*d1d35e2fSjeremylt   CeedEvalMode eval_mode;
3771d102b48SJeremy L Thompson 
378*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
3791d102b48SJeremy L Thompson     // Skip active inputs
380*d1d35e2fSjeremylt     if (skip_active) {
3811d102b48SJeremy L Thompson       CeedVector vec;
382*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
383*d1d35e2fSjeremylt       CeedChkBackend(ierr);
3841d102b48SJeremy L Thompson       if (vec == CEED_VECTOR_ACTIVE)
3851d102b48SJeremy L Thompson         continue;
3861d102b48SJeremy L Thompson     }
3871d102b48SJeremy L Thompson     // Restore input
388*d1d35e2fSjeremylt     ierr = CeedQFunctionFieldGetEvalMode(qf_input_fields[i], &eval_mode);
389e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
390*d1d35e2fSjeremylt     if (eval_mode == CEED_EVAL_WEIGHT) { // Skip
3911d102b48SJeremy L Thompson     } else {
392*d1d35e2fSjeremylt       ierr = CeedVectorRestoreArrayRead(impl->e_vecs[i],
393*d1d35e2fSjeremylt                                         (const CeedScalar **) &impl->e_data[i]);
394e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
3951d102b48SJeremy L Thompson     }
3961d102b48SJeremy L Thompson   }
397e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
3981d102b48SJeremy L Thompson }
3991d102b48SJeremy L Thompson 
400f10650afSjeremylt //------------------------------------------------------------------------------
401f10650afSjeremylt // Operator Apply
402f10650afSjeremylt //------------------------------------------------------------------------------
403*d1d35e2fSjeremylt static int CeedOperatorApplyAdd_Ref(CeedOperator op, CeedVector in_vec,
404*d1d35e2fSjeremylt                                     CeedVector out_vec, CeedRequest *request) {
4051d102b48SJeremy L Thompson   int ierr;
4061d102b48SJeremy L Thompson   CeedOperator_Ref *impl;
407e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
4081d102b48SJeremy L Thompson   CeedQFunction qf;
409e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
410*d1d35e2fSjeremylt   CeedInt Q, num_elem, num_input_fields, num_output_fields, size;
411e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetNumQuadraturePoints(op, &Q); CeedChkBackend(ierr);
412*d1d35e2fSjeremylt   ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChkBackend(ierr);
413*d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
414e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
415*d1d35e2fSjeremylt   CeedOperatorField *op_input_fields, *op_output_fields;
416*d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_input_fields, &op_output_fields);
417e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
418*d1d35e2fSjeremylt   CeedQFunctionField *qf_input_fields, *qf_output_fields;
419*d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_input_fields, &qf_output_fields);
420e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
421*d1d35e2fSjeremylt   CeedEvalMode eval_mode;
4221d102b48SJeremy L Thompson   CeedVector vec;
423*d1d35e2fSjeremylt   CeedElemRestriction elem_restr;
4241d102b48SJeremy L Thompson 
4251d102b48SJeremy L Thompson   // Setup
426e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetup_Ref(op); CeedChkBackend(ierr);
4271d102b48SJeremy L Thompson 
4281d102b48SJeremy L Thompson   // Input Evecs and Restriction
429*d1d35e2fSjeremylt   ierr = CeedOperatorSetupInputs_Ref(num_input_fields, qf_input_fields,
430*d1d35e2fSjeremylt                                      op_input_fields, in_vec, false, impl,
431e15f9bd0SJeremy L Thompson                                      request); CeedChkBackend(ierr);
4321d102b48SJeremy L Thompson 
4331d102b48SJeremy L Thompson   // Output Evecs
434*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
435*d1d35e2fSjeremylt     ierr = CeedVectorGetArray(impl->e_vecs[i+impl->num_e_vecs_in], CEED_MEM_HOST,
436*d1d35e2fSjeremylt                               &impl->e_data[i + num_input_fields]); CeedChkBackend(ierr);
4371d102b48SJeremy L Thompson   }
4381d102b48SJeremy L Thompson 
4391d102b48SJeremy L Thompson   // Loop through elements
440*d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
4411d102b48SJeremy L Thompson     // Output pointers
442*d1d35e2fSjeremylt     for (CeedInt i=0; i<num_output_fields; i++) {
443*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_output_fields[i], &eval_mode);
444e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
445*d1d35e2fSjeremylt       if (eval_mode == CEED_EVAL_NONE) {
446*d1d35e2fSjeremylt         ierr = CeedQFunctionFieldGetSize(qf_output_fields[i], &size);
447e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
448*d1d35e2fSjeremylt         ierr = CeedVectorSetArray(impl->q_vecs_out[i], CEED_MEM_HOST,
4491d102b48SJeremy L Thompson                                   CEED_USE_POINTER,
450*d1d35e2fSjeremylt                                   &impl->e_data[i + num_input_fields][e*Q*size]);
451e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
4521d102b48SJeremy L Thompson       }
4531d102b48SJeremy L Thompson     }
4541d102b48SJeremy L Thompson 
45516911fdaSjeremylt     // Input basis apply
456*d1d35e2fSjeremylt     ierr = CeedOperatorInputBasis_Ref(e, Q, qf_input_fields, op_input_fields,
457*d1d35e2fSjeremylt                                       num_input_fields, false, impl);
458e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
45916911fdaSjeremylt 
4601d102b48SJeremy L Thompson     // Q function
461*d1d35e2fSjeremylt     if (!impl->identity_qf) {
462*d1d35e2fSjeremylt       ierr = CeedQFunctionApply(qf, Q, impl->q_vecs_in, impl->q_vecs_out);
463e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
46416911fdaSjeremylt     }
4651d102b48SJeremy L Thompson 
4661d102b48SJeremy L Thompson     // Output basis apply
467*d1d35e2fSjeremylt     ierr = CeedOperatorOutputBasis_Ref(e, Q, qf_output_fields, op_output_fields,
468*d1d35e2fSjeremylt                                        num_input_fields, num_output_fields, op, impl);
469e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
4701d102b48SJeremy L Thompson   }
471885ac19cSjeremylt 
472885ac19cSjeremylt   // Output restriction
473*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
474*d1d35e2fSjeremylt     // Restore Evec
475*d1d35e2fSjeremylt     ierr = CeedVectorRestoreArray(impl->e_vecs[i+impl->num_e_vecs_in],
476*d1d35e2fSjeremylt                                   &impl->e_data[i + num_input_fields]);
477e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
478d1bcdac9Sjeremylt     // Get output vector
479*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_output_fields[i], &vec);
480e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
481668048e2SJed Brown     // Active
482d1bcdac9Sjeremylt     if (vec == CEED_VECTOR_ACTIVE)
483*d1d35e2fSjeremylt       vec = out_vec;
4847ca8db16Sjeremylt     // Restrict
485*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetElemRestriction(op_output_fields[i], &elem_restr);
486e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
487*d1d35e2fSjeremylt     ierr = CeedElemRestrictionApply(elem_restr, CEED_TRANSPOSE,
488*d1d35e2fSjeremylt                                     impl->e_vecs[i+impl->num_e_vecs_in], vec, request);
489e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
490885ac19cSjeremylt   }
491885ac19cSjeremylt 
4927ca8db16Sjeremylt   // Restore input arrays
493*d1d35e2fSjeremylt   ierr = CeedOperatorRestoreInputs_Ref(num_input_fields, qf_input_fields,
494*d1d35e2fSjeremylt                                        op_input_fields, false, impl);
495e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
4967ca8db16Sjeremylt 
497e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
49821617c04Sjeremylt }
49921617c04Sjeremylt 
500f10650afSjeremylt //------------------------------------------------------------------------------
5011d102b48SJeremy L Thompson // Assemble Linear QFunction
502f10650afSjeremylt //------------------------------------------------------------------------------
50380ac2e43SJeremy L Thompson static int CeedOperatorLinearAssembleQFunction_Ref(CeedOperator op,
5041d102b48SJeremy L Thompson     CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) {
5051d102b48SJeremy L Thompson   int ierr;
5061d102b48SJeremy L Thompson   CeedOperator_Ref *impl;
507e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
5081d102b48SJeremy L Thompson   CeedQFunction qf;
509e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
510*d1d35e2fSjeremylt   CeedInt Q, num_elem, num_input_fields, num_output_fields, size;
511e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetNumQuadraturePoints(op, &Q); CeedChkBackend(ierr);
512*d1d35e2fSjeremylt   ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChkBackend(ierr);
513*d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
514e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
515*d1d35e2fSjeremylt   CeedOperatorField *op_input_fields, *op_output_fields;
516*d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_input_fields, &op_output_fields);
517e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
518*d1d35e2fSjeremylt   CeedQFunctionField *qf_input_fields, *qf_output_fields;
519*d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_input_fields, &qf_output_fields);
520e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
5211d102b48SJeremy L Thompson   CeedVector vec;
522*d1d35e2fSjeremylt   CeedInt num_active_in = 0, num_active_out = 0;
523*d1d35e2fSjeremylt   CeedVector *active_in = NULL;
5241d102b48SJeremy L Thompson   CeedScalar *a, *tmp;
525*d1d35e2fSjeremylt   Ceed ceed, ceed_parent;
526e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
527*d1d35e2fSjeremylt   ierr = CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent);
528e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
529*d1d35e2fSjeremylt   ceed_parent = ceed_parent ? ceed_parent : ceed;
5301d102b48SJeremy L Thompson 
5311d102b48SJeremy L Thompson   // Setup
532e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetup_Ref(op); CeedChkBackend(ierr);
5331d102b48SJeremy L Thompson 
53416911fdaSjeremylt   // Check for identity
535*d1d35e2fSjeremylt   if (impl->identity_qf)
53616911fdaSjeremylt     // LCOV_EXCL_START
537e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
538e15f9bd0SJeremy L Thompson                      "Assembling identity QFunctions not supported");
53916911fdaSjeremylt   // LCOV_EXCL_STOP
54016911fdaSjeremylt 
5411d102b48SJeremy L Thompson   // Input Evecs and Restriction
542*d1d35e2fSjeremylt   ierr = CeedOperatorSetupInputs_Ref(num_input_fields, qf_input_fields,
543*d1d35e2fSjeremylt                                      op_input_fields, NULL, true, impl, request);
544e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
5451d102b48SJeremy L Thompson 
5461d102b48SJeremy L Thompson   // Count number of active input fields
547*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
5481d102b48SJeremy L Thompson     // Get input vector
549*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_input_fields[i], &vec);
550*d1d35e2fSjeremylt     CeedChkBackend(ierr);
5511d102b48SJeremy L Thompson     // Check if active input
5521d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
553*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_input_fields[i], &size);
554e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
555*d1d35e2fSjeremylt       ierr = CeedVectorSetValue(impl->q_vecs_in[i], 0.0); CeedChkBackend(ierr);
556*d1d35e2fSjeremylt       ierr = CeedVectorGetArray(impl->q_vecs_in[i], CEED_MEM_HOST, &tmp);
557*d1d35e2fSjeremylt       CeedChkBackend(ierr);
558*d1d35e2fSjeremylt       ierr = CeedRealloc(num_active_in + size, &active_in); CeedChkBackend(ierr);
5591d102b48SJeremy L Thompson       for (CeedInt field=0; field<size; field++) {
560*d1d35e2fSjeremylt         ierr = CeedVectorCreate(ceed, Q, &active_in[num_active_in+field]);
561e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
562*d1d35e2fSjeremylt         ierr = CeedVectorSetArray(active_in[num_active_in+field], CEED_MEM_HOST,
56342ea3801Sjeremylt                                   CEED_USE_POINTER, &tmp[field*Q]);
564e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
5651d102b48SJeremy L Thompson       }
566*d1d35e2fSjeremylt       num_active_in += size;
567*d1d35e2fSjeremylt       ierr = CeedVectorRestoreArray(impl->q_vecs_in[i], &tmp); CeedChkBackend(ierr);
5681d102b48SJeremy L Thompson     }
5691d102b48SJeremy L Thompson   }
5701d102b48SJeremy L Thompson 
5711d102b48SJeremy L Thompson   // Count number of active output fields
572*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
5731d102b48SJeremy L Thompson     // Get output vector
574*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_output_fields[i], &vec);
575e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
5761d102b48SJeremy L Thompson     // Check if active output
5771d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
578*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetSize(qf_output_fields[i], &size);
579e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
580*d1d35e2fSjeremylt       num_active_out += size;
5811d102b48SJeremy L Thompson     }
5821d102b48SJeremy L Thompson   }
5831d102b48SJeremy L Thompson 
5841d102b48SJeremy L Thompson   // Check sizes
585*d1d35e2fSjeremylt   if (!num_active_in || !num_active_out)
586138d4072Sjeremylt     // LCOV_EXCL_START
587e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
588e15f9bd0SJeremy L Thompson                      "Cannot assemble QFunction without active inputs "
589138d4072Sjeremylt                      "and outputs");
590138d4072Sjeremylt   // LCOV_EXCL_STOP
5911d102b48SJeremy L Thompson 
5921d102b48SJeremy L Thompson   // Create output restriction
593*d1d35e2fSjeremylt   CeedInt strides[3] = {1, Q, num_active_in*num_active_out*Q}; /* *NOPAD* */
594*d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateStrided(ceed_parent, num_elem, Q,
595*d1d35e2fSjeremylt                                           num_active_in*num_active_out,
596*d1d35e2fSjeremylt                                           num_active_in*num_active_out*num_elem*Q,
597e15f9bd0SJeremy L Thompson                                           strides, rstr); CeedChkBackend(ierr);
5981d102b48SJeremy L Thompson   // Create assembled vector
599*d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed_parent, num_elem*Q*num_active_in*num_active_out,
600e15f9bd0SJeremy L Thompson                           assembled); CeedChkBackend(ierr);
601e15f9bd0SJeremy L Thompson   ierr = CeedVectorSetValue(*assembled, 0.0); CeedChkBackend(ierr);
602e15f9bd0SJeremy L Thompson   ierr = CeedVectorGetArray(*assembled, CEED_MEM_HOST, &a); CeedChkBackend(ierr);
6031d102b48SJeremy L Thompson 
6041d102b48SJeremy L Thompson   // Loop through elements
605*d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
6061d102b48SJeremy L Thompson     // Input basis apply
607*d1d35e2fSjeremylt     ierr = CeedOperatorInputBasis_Ref(e, Q, qf_input_fields, op_input_fields,
608*d1d35e2fSjeremylt                                       num_input_fields, true, impl);
609e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
6101d102b48SJeremy L Thompson 
6111d102b48SJeremy L Thompson     // Assemble QFunction
612*d1d35e2fSjeremylt     for (CeedInt in=0; in<num_active_in; in++) {
6131d102b48SJeremy L Thompson       // Set Inputs
614*d1d35e2fSjeremylt       ierr = CeedVectorSetValue(active_in[in], 1.0); CeedChkBackend(ierr);
615*d1d35e2fSjeremylt       if (num_active_in > 1) {
616*d1d35e2fSjeremylt         ierr = CeedVectorSetValue(active_in[(in+num_active_in-1)%num_active_in],
617e15f9bd0SJeremy L Thompson                                   0.0); CeedChkBackend(ierr);
61842ea3801Sjeremylt       }
6191d102b48SJeremy L Thompson       // Set Outputs
620*d1d35e2fSjeremylt       for (CeedInt out=0; out<num_output_fields; out++) {
6211d102b48SJeremy L Thompson         // Get output vector
622*d1d35e2fSjeremylt         ierr = CeedOperatorFieldGetVector(op_output_fields[out], &vec);
623e15f9bd0SJeremy L Thompson         CeedChkBackend(ierr);
6241d102b48SJeremy L Thompson         // Check if active output
6251d102b48SJeremy L Thompson         if (vec == CEED_VECTOR_ACTIVE) {
626*d1d35e2fSjeremylt           CeedVectorSetArray(impl->q_vecs_out[out], CEED_MEM_HOST,
627e15f9bd0SJeremy L Thompson                              CEED_USE_POINTER, a); CeedChkBackend(ierr);
628*d1d35e2fSjeremylt           ierr = CeedQFunctionFieldGetSize(qf_output_fields[out], &size);
629e15f9bd0SJeremy L Thompson           CeedChkBackend(ierr);
6301d102b48SJeremy L Thompson           a += size*Q; // Advance the pointer by the size of the output
6311d102b48SJeremy L Thompson         }
6321d102b48SJeremy L Thompson       }
6331d102b48SJeremy L Thompson       // Apply QFunction
634*d1d35e2fSjeremylt       ierr = CeedQFunctionApply(qf, Q, impl->q_vecs_in, impl->q_vecs_out);
635e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
6361d102b48SJeremy L Thompson     }
6371d102b48SJeremy L Thompson   }
6381d102b48SJeremy L Thompson 
6391d102b48SJeremy L Thompson   // Un-set output Qvecs to prevent accidental overwrite of Assembled
640*d1d35e2fSjeremylt   for (CeedInt out=0; out<num_output_fields; out++) {
6411d102b48SJeremy L Thompson     // Get output vector
642*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_output_fields[out], &vec);
643e15f9bd0SJeremy L Thompson     CeedChkBackend(ierr);
6441d102b48SJeremy L Thompson     // Check if active output
6451d102b48SJeremy L Thompson     if (vec == CEED_VECTOR_ACTIVE) {
646*d1d35e2fSjeremylt       CeedVectorTakeArray(impl->q_vecs_out[out], CEED_MEM_HOST, NULL);
647e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
6481d102b48SJeremy L Thompson     }
6491d102b48SJeremy L Thompson   }
6501d102b48SJeremy L Thompson 
6511d102b48SJeremy L Thompson   // Restore input arrays
652*d1d35e2fSjeremylt   ierr = CeedOperatorRestoreInputs_Ref(num_input_fields, qf_input_fields,
653*d1d35e2fSjeremylt                                        op_input_fields, true, impl);
654e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
6551d102b48SJeremy L Thompson 
6561d102b48SJeremy L Thompson   // Restore output
657e15f9bd0SJeremy L Thompson   ierr = CeedVectorRestoreArray(*assembled, &a); CeedChkBackend(ierr);
6581d102b48SJeremy L Thompson 
6591d102b48SJeremy L Thompson   // Cleanup
660*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_active_in; i++) {
661*d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&active_in[i]); CeedChkBackend(ierr);
66242ea3801Sjeremylt   }
663*d1d35e2fSjeremylt   ierr = CeedFree(&active_in); CeedChkBackend(ierr);
6641d102b48SJeremy L Thompson 
665e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
6661d102b48SJeremy L Thompson }
6671d102b48SJeremy L Thompson 
668f10650afSjeremylt //------------------------------------------------------------------------------
669dfffd467Sjeremylt // Get Basis Emode Pointer
670dfffd467Sjeremylt //------------------------------------------------------------------------------
671*d1d35e2fSjeremylt static inline void CeedOperatorGetBasisPointer_Ref(const CeedScalar **basis_ptr,
672*d1d35e2fSjeremylt     CeedEvalMode eval_mode, const CeedScalar *identity, const CeedScalar *interp,
6736c58de82SJeremy L Thompson     const CeedScalar *grad) {
674*d1d35e2fSjeremylt   switch (eval_mode) {
675dfffd467Sjeremylt   case CEED_EVAL_NONE:
676*d1d35e2fSjeremylt     *basis_ptr = identity;
677dfffd467Sjeremylt     break;
678dfffd467Sjeremylt   case CEED_EVAL_INTERP:
679*d1d35e2fSjeremylt     *basis_ptr = interp;
680dfffd467Sjeremylt     break;
681dfffd467Sjeremylt   case CEED_EVAL_GRAD:
682*d1d35e2fSjeremylt     *basis_ptr = grad;
683dfffd467Sjeremylt     break;
684dfffd467Sjeremylt   case CEED_EVAL_WEIGHT:
685dfffd467Sjeremylt   case CEED_EVAL_DIV:
686dfffd467Sjeremylt   case CEED_EVAL_CURL:
687dfffd467Sjeremylt     break; // Caught by QF Assembly
688dfffd467Sjeremylt   }
689dfffd467Sjeremylt }
690dfffd467Sjeremylt 
691dfffd467Sjeremylt //------------------------------------------------------------------------------
692d965c7a7SJeremy L Thompson // Create point block restriction
693d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
694868092e3SJeremy L Thompson static int CreatePBRestriction_Ref(CeedElemRestriction rstr,
695*d1d35e2fSjeremylt                                    CeedElemRestriction *pb_rstr) {
696d965c7a7SJeremy L Thompson   int ierr;
697d965c7a7SJeremy L Thompson   Ceed ceed;
698e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionGetCeed(rstr, &ceed); CeedChkBackend(ierr);
699d965c7a7SJeremy L Thompson   const CeedInt *offsets;
700d965c7a7SJeremy L Thompson   ierr = CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets);
701e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
702d965c7a7SJeremy L Thompson 
703d965c7a7SJeremy L Thompson   // Expand offsets
704*d1d35e2fSjeremylt   CeedInt num_elem, num_comp, elem_size, comp_stride, max = 1, *pbOffsets;
705*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChkBackend(ierr);
706*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumComponents(rstr, &num_comp);
707e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
708*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetElementSize(rstr, &elem_size);
709*d1d35e2fSjeremylt   CeedChkBackend(ierr);
710*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetCompStride(rstr, &comp_stride);
711*d1d35e2fSjeremylt   CeedChkBackend(ierr);
712*d1d35e2fSjeremylt   CeedInt shift = num_comp;
713*d1d35e2fSjeremylt   if (comp_stride != 1)
714*d1d35e2fSjeremylt     shift *= num_comp;
715*d1d35e2fSjeremylt   ierr = CeedCalloc(num_elem*elem_size, &pbOffsets); CeedChkBackend(ierr);
716*d1d35e2fSjeremylt   for (CeedInt i = 0; i < num_elem*elem_size; i++) {
717d965c7a7SJeremy L Thompson     pbOffsets[i] = offsets[i]*shift;
718d965c7a7SJeremy L Thompson     if (pbOffsets[i] > max)
719d965c7a7SJeremy L Thompson       max = pbOffsets[i];
720d965c7a7SJeremy L Thompson   }
721d965c7a7SJeremy L Thompson 
722d965c7a7SJeremy L Thompson   // Create new restriction
723*d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp*num_comp,
724*d1d35e2fSjeremylt                                    1,
725*d1d35e2fSjeremylt                                    max + num_comp*num_comp, CEED_MEM_HOST,
726*d1d35e2fSjeremylt                                    CEED_OWN_POINTER, pbOffsets, pb_rstr);
727e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
728d965c7a7SJeremy L Thompson 
729d965c7a7SJeremy L Thompson   // Cleanup
730e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionRestoreOffsets(rstr, &offsets); CeedChkBackend(ierr);
731d965c7a7SJeremy L Thompson 
732e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
733d965c7a7SJeremy L Thompson }
734d965c7a7SJeremy L Thompson 
735d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
736c04a41a7SJeremy L Thompson // Assemble diagonal common code
737f10650afSjeremylt //------------------------------------------------------------------------------
73869af5e5fSJeremy L Thompson static inline int CeedOperatorAssembleAddDiagonalCore_Ref(CeedOperator op,
739*d1d35e2fSjeremylt     CeedVector assembled, CeedRequest *request, const bool is_pointblock) {
7407172caadSjeremylt   int ierr;
741c04a41a7SJeremy L Thompson   Ceed ceed;
742e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
7437172caadSjeremylt 
7447172caadSjeremylt   // Assemble QFunction
7457172caadSjeremylt   CeedQFunction qf;
746e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
747*d1d35e2fSjeremylt   CeedInt num_input_fields, num_output_fields;
748*d1d35e2fSjeremylt   ierr= CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields);
749e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
750*d1d35e2fSjeremylt   CeedVector assembled_qf;
7517172caadSjeremylt   CeedElemRestriction rstr;
752*d1d35e2fSjeremylt   ierr = CeedOperatorLinearAssembleQFunction(op,  &assembled_qf, &rstr, request);
753e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
754e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr); CeedChkBackend(ierr);
755*d1d35e2fSjeremylt   CeedScalar max_norm = 0;
756*d1d35e2fSjeremylt   ierr = CeedVectorNorm(assembled_qf, CEED_NORM_MAX, &max_norm);
757e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
7587172caadSjeremylt 
7597172caadSjeremylt   // Determine active input basis
760*d1d35e2fSjeremylt   CeedOperatorField *op_fields;
761*d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
762*d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_fields, NULL); CeedChkBackend(ierr);
763*d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChkBackend(ierr);
764*d1d35e2fSjeremylt   CeedInt num_eval_mode_in = 0, num_comp, dim = 1;
765*d1d35e2fSjeremylt   CeedEvalMode *eval_mode_in = NULL;
766*d1d35e2fSjeremylt   CeedBasis basis_in = NULL;
767*d1d35e2fSjeremylt   CeedElemRestriction rstr_in = NULL;
768*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
7697172caadSjeremylt     CeedVector vec;
770*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChkBackend(ierr);
7717172caadSjeremylt     if (vec == CEED_VECTOR_ACTIVE) {
772c04a41a7SJeremy L Thompson       CeedElemRestriction rstr;
773*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis_in); CeedChkBackend(ierr);
774*d1d35e2fSjeremylt       ierr = CeedBasisGetNumComponents(basis_in, &num_comp); CeedChkBackend(ierr);
775*d1d35e2fSjeremylt       ierr = CeedBasisGetDimension(basis_in, &dim); CeedChkBackend(ierr);
776*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr);
777e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
778*d1d35e2fSjeremylt       if (rstr_in && rstr_in != rstr)
779c04a41a7SJeremy L Thompson         // LCOV_EXCL_START
780e15f9bd0SJeremy L Thompson         return CeedError(ceed, CEED_ERROR_BACKEND,
781c04a41a7SJeremy L Thompson                          "Multi-field non-composite operator diagonal assembly not supported");
782c04a41a7SJeremy L Thompson       // LCOV_EXCL_STOP
783*d1d35e2fSjeremylt       rstr_in = rstr;
784*d1d35e2fSjeremylt       CeedEvalMode eval_mode;
785*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
786e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
787*d1d35e2fSjeremylt       switch (eval_mode) {
7887172caadSjeremylt       case CEED_EVAL_NONE:
7897172caadSjeremylt       case CEED_EVAL_INTERP:
790*d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + 1, &eval_mode_in); CeedChkBackend(ierr);
791*d1d35e2fSjeremylt         eval_mode_in[num_eval_mode_in] = eval_mode;
792*d1d35e2fSjeremylt         num_eval_mode_in += 1;
7937172caadSjeremylt         break;
7947172caadSjeremylt       case CEED_EVAL_GRAD:
795*d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_in + dim, &eval_mode_in); CeedChkBackend(ierr);
7967172caadSjeremylt         for (CeedInt d=0; d<dim; d++)
797*d1d35e2fSjeremylt           eval_mode_in[num_eval_mode_in+d] = eval_mode;
798*d1d35e2fSjeremylt         num_eval_mode_in += dim;
7997172caadSjeremylt         break;
8007172caadSjeremylt       case CEED_EVAL_WEIGHT:
8017172caadSjeremylt       case CEED_EVAL_DIV:
8027172caadSjeremylt       case CEED_EVAL_CURL:
8037172caadSjeremylt         break; // Caught by QF Assembly
8047172caadSjeremylt       }
8057172caadSjeremylt     }
8067172caadSjeremylt   }
8077172caadSjeremylt 
8087172caadSjeremylt   // Determine active output basis
809*d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, NULL, &op_fields); CeedChkBackend(ierr);
810*d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, NULL, &qf_fields); CeedChkBackend(ierr);
811*d1d35e2fSjeremylt   CeedInt num_eval_mode_out = 0;
812*d1d35e2fSjeremylt   CeedEvalMode *eval_mode_out = NULL;
813*d1d35e2fSjeremylt   CeedBasis basis_out = NULL;
814*d1d35e2fSjeremylt   CeedElemRestriction rstr_out = NULL;
815*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_output_fields; i++) {
8167172caadSjeremylt     CeedVector vec;
817*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChkBackend(ierr);
8187172caadSjeremylt     if (vec == CEED_VECTOR_ACTIVE) {
819c04a41a7SJeremy L Thompson       CeedElemRestriction rstr;
820*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis_out);
821e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
822*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr);
823*d1d35e2fSjeremylt       CeedChkBackend(ierr);
824*d1d35e2fSjeremylt       if (rstr_out && rstr_out != rstr)
825c04a41a7SJeremy L Thompson         // LCOV_EXCL_START
826e15f9bd0SJeremy L Thompson         return CeedError(ceed, CEED_ERROR_BACKEND,
827c04a41a7SJeremy L Thompson                          "Multi-field non-composite operator diagonal assembly not supported");
828c04a41a7SJeremy L Thompson       // LCOV_EXCL_STOP
829*d1d35e2fSjeremylt       rstr_out = rstr;
830*d1d35e2fSjeremylt       CeedEvalMode eval_mode;
831*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
832*d1d35e2fSjeremylt       CeedChkBackend(ierr);
833*d1d35e2fSjeremylt       switch (eval_mode) {
8347172caadSjeremylt       case CEED_EVAL_NONE:
8357172caadSjeremylt       case CEED_EVAL_INTERP:
836*d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + 1, &eval_mode_out); CeedChkBackend(ierr);
837*d1d35e2fSjeremylt         eval_mode_out[num_eval_mode_out] = eval_mode;
838*d1d35e2fSjeremylt         num_eval_mode_out += 1;
8397172caadSjeremylt         break;
8407172caadSjeremylt       case CEED_EVAL_GRAD:
841*d1d35e2fSjeremylt         ierr = CeedRealloc(num_eval_mode_out + dim, &eval_mode_out);
842*d1d35e2fSjeremylt         CeedChkBackend(ierr);
8437172caadSjeremylt         for (CeedInt d=0; d<dim; d++)
844*d1d35e2fSjeremylt           eval_mode_out[num_eval_mode_out+d] = eval_mode;
845*d1d35e2fSjeremylt         num_eval_mode_out += dim;
8467172caadSjeremylt         break;
8477172caadSjeremylt       case CEED_EVAL_WEIGHT:
8487172caadSjeremylt       case CEED_EVAL_DIV:
8497172caadSjeremylt       case CEED_EVAL_CURL:
8507172caadSjeremylt         break; // Caught by QF Assembly
8517172caadSjeremylt       }
8527172caadSjeremylt     }
8537172caadSjeremylt   }
8547172caadSjeremylt 
855d965c7a7SJeremy L Thompson   // Assemble point-block diagonal restriction, if needed
856*d1d35e2fSjeremylt   CeedElemRestriction diag_rstr = rstr_out;
857*d1d35e2fSjeremylt   if (is_pointblock) {
858*d1d35e2fSjeremylt     ierr = CreatePBRestriction_Ref(rstr_out, &diag_rstr); CeedChkBackend(ierr);
859d965c7a7SJeremy L Thompson   }
860d965c7a7SJeremy L Thompson 
8617172caadSjeremylt   // Create diagonal vector
862*d1d35e2fSjeremylt   CeedVector elem_diag;
863*d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateVector(diag_rstr, NULL, &elem_diag);
864e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
8657172caadSjeremylt 
8667172caadSjeremylt   // Assemble element operator diagonals
867*d1d35e2fSjeremylt   CeedScalar *elem_diag_array, *assembled_qf_array;
868*d1d35e2fSjeremylt   ierr = CeedVectorSetValue(elem_diag, 0.0); CeedChkBackend(ierr);
869*d1d35e2fSjeremylt   ierr = CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array);
870e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
871*d1d35e2fSjeremylt   ierr = CeedVectorGetArray(assembled_qf, CEED_MEM_HOST, &assembled_qf_array);
872e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
873*d1d35e2fSjeremylt   CeedInt num_elem, num_nodes, num_qpts;
874*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(diag_rstr, &num_elem);
875e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
876*d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes(basis_in, &num_nodes); CeedChkBackend(ierr);
877*d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts);
878*d1d35e2fSjeremylt   CeedChkBackend(ierr);
879dfffd467Sjeremylt   // Basis matrices
880*d1d35e2fSjeremylt   const CeedScalar *interp_in, *interp_out, *grad_in, *grad_out;
8816c58de82SJeremy L Thompson   CeedScalar *identity = NULL;
882dfffd467Sjeremylt   bool evalNone = false;
883*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_eval_mode_in; i++)
884*d1d35e2fSjeremylt     evalNone = evalNone || (eval_mode_in[i] == CEED_EVAL_NONE);
885*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_eval_mode_out; i++)
886*d1d35e2fSjeremylt     evalNone = evalNone || (eval_mode_out[i] == CEED_EVAL_NONE);
887dfffd467Sjeremylt   if (evalNone) {
888*d1d35e2fSjeremylt     ierr = CeedCalloc(num_qpts*num_nodes, &identity); CeedChkBackend(ierr);
889*d1d35e2fSjeremylt     for (CeedInt i=0; i<(num_nodes<num_qpts?num_nodes:num_qpts); i++)
890*d1d35e2fSjeremylt       identity[i*num_nodes+i] = 1.0;
891dfffd467Sjeremylt   }
892*d1d35e2fSjeremylt   ierr = CeedBasisGetInterp(basis_in, &interp_in); CeedChkBackend(ierr);
893*d1d35e2fSjeremylt   ierr = CeedBasisGetInterp(basis_out, &interp_out); CeedChkBackend(ierr);
894*d1d35e2fSjeremylt   ierr = CeedBasisGetGrad(basis_in, &grad_in); CeedChkBackend(ierr);
895*d1d35e2fSjeremylt   ierr = CeedBasisGetGrad(basis_out, &grad_out); CeedChkBackend(ierr);
896dfffd467Sjeremylt   // Compute the diagonal of B^T D B
897dfffd467Sjeremylt   // Each element
898*d1d35e2fSjeremylt   const CeedScalar qf_value_bound = max_norm*1e-12;
899*d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
900*d1d35e2fSjeremylt     CeedInt d_out = -1;
9017172caadSjeremylt     // Each basis eval mode pair
902*d1d35e2fSjeremylt     for (CeedInt e_out=0; e_out<num_eval_mode_out; e_out++) {
9036c58de82SJeremy L Thompson       const CeedScalar *bt = NULL;
904*d1d35e2fSjeremylt       if (eval_mode_out[e_out] == CEED_EVAL_GRAD)
905*d1d35e2fSjeremylt         d_out += 1;
906*d1d35e2fSjeremylt       CeedOperatorGetBasisPointer_Ref(&bt, eval_mode_out[e_out], identity, interp_out,
907*d1d35e2fSjeremylt                                       &grad_out[d_out*num_qpts*num_nodes]);
908*d1d35e2fSjeremylt       CeedInt d_in = -1;
909*d1d35e2fSjeremylt       for (CeedInt e_in=0; e_in<num_eval_mode_in; e_in++) {
9106c58de82SJeremy L Thompson         const CeedScalar *b = NULL;
911*d1d35e2fSjeremylt         if (eval_mode_in[e_in] == CEED_EVAL_GRAD)
912*d1d35e2fSjeremylt           d_in += 1;
913*d1d35e2fSjeremylt         CeedOperatorGetBasisPointer_Ref(&b, eval_mode_in[e_in], identity, interp_in,
914*d1d35e2fSjeremylt                                         &grad_in[d_in*num_qpts*num_nodes]);
915efcf4563Sjeremylt         // Each component
916*d1d35e2fSjeremylt         for (CeedInt c_out=0; c_out<num_comp; c_out++)
917dfffd467Sjeremylt           // Each qpoint/node pair
918*d1d35e2fSjeremylt           for (CeedInt q=0; q<num_qpts; q++)
919*d1d35e2fSjeremylt             if (is_pointblock) {
920d965c7a7SJeremy L Thompson               // Point Block Diagonal
921*d1d35e2fSjeremylt               for (CeedInt c_in=0; c_in<num_comp; c_in++) {
922*d1d35e2fSjeremylt                 const CeedScalar qf_value =
923*d1d35e2fSjeremylt                   assembled_qf_array[((((e*num_eval_mode_in+e_in)*num_comp+c_in)*
924*d1d35e2fSjeremylt                                        num_eval_mode_out+e_out)*num_comp+c_out)*num_qpts+q];
925*d1d35e2fSjeremylt                 if (fabs(qf_value) > qf_value_bound)
926*d1d35e2fSjeremylt                   for (CeedInt n=0; n<num_nodes; n++)
927*d1d35e2fSjeremylt                     elem_diag_array[((e*num_comp+c_out)*num_comp+c_in)*num_nodes+n] +=
928*d1d35e2fSjeremylt                       bt[q*num_nodes+n] * qf_value * b[q*num_nodes+n];
929d965c7a7SJeremy L Thompson               }
930d965c7a7SJeremy L Thompson             } else {
931d965c7a7SJeremy L Thompson               // Diagonal Only
932*d1d35e2fSjeremylt               const CeedScalar qf_value =
933*d1d35e2fSjeremylt                 assembled_qf_array[((((e*num_eval_mode_in+e_in)*num_comp+c_out)*
934*d1d35e2fSjeremylt                                      num_eval_mode_out+e_out)*num_comp+c_out)*num_qpts+q];
935*d1d35e2fSjeremylt               if (fabs(qf_value) > qf_value_bound)
936*d1d35e2fSjeremylt                 for (CeedInt n=0; n<num_nodes; n++)
937*d1d35e2fSjeremylt                   elem_diag_array[(e*num_comp+c_out)*num_nodes+n] +=
938*d1d35e2fSjeremylt                     bt[q*num_nodes+n] * qf_value * b[q*num_nodes+n];
9394ff7492eSjeremylt             }
9407172caadSjeremylt       }
9417172caadSjeremylt     }
9427172caadSjeremylt   }
943*d1d35e2fSjeremylt   ierr = CeedVectorRestoreArray(elem_diag, &elem_diag_array);
944*d1d35e2fSjeremylt   CeedChkBackend(ierr);
945*d1d35e2fSjeremylt   ierr = CeedVectorRestoreArray(assembled_qf, &assembled_qf_array);
946e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
9477172caadSjeremylt 
9487172caadSjeremylt   // Assemble local operator diagonal
949*d1d35e2fSjeremylt   ierr = CeedElemRestrictionApply(diag_rstr, CEED_TRANSPOSE, elem_diag,
950e15f9bd0SJeremy L Thompson                                   assembled, request); CeedChkBackend(ierr);
9517172caadSjeremylt 
9527172caadSjeremylt   // Cleanup
953*d1d35e2fSjeremylt   if (is_pointblock) {
954*d1d35e2fSjeremylt     ierr = CeedElemRestrictionDestroy(&diag_rstr); CeedChkBackend(ierr);
955d965c7a7SJeremy L Thompson   }
956*d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&assembled_qf); CeedChkBackend(ierr);
957*d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&elem_diag); CeedChkBackend(ierr);
958*d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_in); CeedChkBackend(ierr);
959*d1d35e2fSjeremylt   ierr = CeedFree(&eval_mode_out); CeedChkBackend(ierr);
960e15f9bd0SJeremy L Thompson   ierr = CeedFree(&identity); CeedChkBackend(ierr);
9617172caadSjeremylt 
962e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
9637172caadSjeremylt }
9647172caadSjeremylt 
965f10650afSjeremylt //------------------------------------------------------------------------------
966c04a41a7SJeremy L Thompson // Assemble composite diagonal common code
967c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
96869af5e5fSJeremy L Thompson static inline int CeedOperatorLinearAssembleAddDiagonalCompositeCore_Ref(
9692bba3ffaSJeremy L Thompson   CeedOperator op, CeedVector assembled, CeedRequest *request,
970*d1d35e2fSjeremylt   const bool is_pointblock) {
971c04a41a7SJeremy L Thompson   int ierr;
972*d1d35e2fSjeremylt   CeedInt num_sub;
973*d1d35e2fSjeremylt   CeedOperator *suboperators;
974*d1d35e2fSjeremylt   ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChkBackend(ierr);
975*d1d35e2fSjeremylt   ierr = CeedOperatorGetSubList(op, &suboperators); CeedChkBackend(ierr);
976*d1d35e2fSjeremylt   for (CeedInt i = 0; i < num_sub; i++) {
977*d1d35e2fSjeremylt     ierr = CeedOperatorAssembleAddDiagonalCore_Ref(suboperators[i], assembled,
978*d1d35e2fSjeremylt            request, is_pointblock); CeedChkBackend(ierr);
979c04a41a7SJeremy L Thompson   }
980e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
981c04a41a7SJeremy L Thompson }
982c04a41a7SJeremy L Thompson 
983c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
984d965c7a7SJeremy L Thompson // Assemble Linear Diagonal
985d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
98669af5e5fSJeremy L Thompson static int CeedOperatorLinearAssembleAddDiagonal_Ref(CeedOperator op,
9872bba3ffaSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
988c04a41a7SJeremy L Thompson   int ierr;
989*d1d35e2fSjeremylt   bool is_composite;
990*d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChkBackend(ierr);
991*d1d35e2fSjeremylt   if (is_composite) {
99269af5e5fSJeremy L Thompson     return CeedOperatorLinearAssembleAddDiagonalCompositeCore_Ref(op, assembled,
993c04a41a7SJeremy L Thompson            request, false);
994c04a41a7SJeremy L Thompson   } else {
99569af5e5fSJeremy L Thompson     return CeedOperatorAssembleAddDiagonalCore_Ref(op, assembled, request, false);
996c04a41a7SJeremy L Thompson   }
997d965c7a7SJeremy L Thompson }
998d965c7a7SJeremy L Thompson 
999d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
1000d965c7a7SJeremy L Thompson // Assemble Linear Point Block Diagonal
1001d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
100269af5e5fSJeremy L Thompson static int CeedOperatorLinearAssembleAddPointBlockDiagonal_Ref(CeedOperator op,
10032bba3ffaSJeremy L Thompson     CeedVector assembled, CeedRequest *request) {
1004c04a41a7SJeremy L Thompson   int ierr;
1005*d1d35e2fSjeremylt   bool is_composite;
1006*d1d35e2fSjeremylt   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChkBackend(ierr);
1007*d1d35e2fSjeremylt   if (is_composite) {
100869af5e5fSJeremy L Thompson     return CeedOperatorLinearAssembleAddDiagonalCompositeCore_Ref(op, assembled,
1009c04a41a7SJeremy L Thompson            request, true);
1010c04a41a7SJeremy L Thompson   } else {
101169af5e5fSJeremy L Thompson     return CeedOperatorAssembleAddDiagonalCore_Ref(op, assembled, request, true);
1012c04a41a7SJeremy L Thompson   }
1013d965c7a7SJeremy L Thompson }
1014d965c7a7SJeremy L Thompson 
1015d965c7a7SJeremy L Thompson //------------------------------------------------------------------------------
1016713f43c3Sjeremylt // Create FDM Element Inverse
1017f10650afSjeremylt //------------------------------------------------------------------------------
1018713f43c3Sjeremylt int CeedOperatorCreateFDMElementInverse_Ref(CeedOperator op,
1019*d1d35e2fSjeremylt     CeedOperator *fdm_inv, CeedRequest *request) {
1020713f43c3Sjeremylt   int ierr;
1021*d1d35e2fSjeremylt   Ceed ceed, ceed_parent;
1022e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
1023*d1d35e2fSjeremylt   ierr = CeedGetOperatorFallbackParentCeed(ceed, &ceed_parent);
1024e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1025*d1d35e2fSjeremylt   ceed_parent = ceed_parent ? ceed_parent : ceed;
1026713f43c3Sjeremylt   CeedQFunction qf;
1027e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetQFunction(op, &qf); CeedChkBackend(ierr);
1028713f43c3Sjeremylt 
1029713f43c3Sjeremylt   // Determine active input basis
1030713f43c3Sjeremylt   bool interp = false, grad = false;
1031713f43c3Sjeremylt   CeedBasis basis = NULL;
1032713f43c3Sjeremylt   CeedElemRestriction rstr = NULL;
1033*d1d35e2fSjeremylt   CeedOperatorField *op_fields;
1034*d1d35e2fSjeremylt   CeedQFunctionField *qf_fields;
1035*d1d35e2fSjeremylt   ierr = CeedOperatorGetFields(op, &op_fields, NULL); CeedChkBackend(ierr);
1036*d1d35e2fSjeremylt   ierr = CeedQFunctionGetFields(qf, &qf_fields, NULL); CeedChkBackend(ierr);
1037*d1d35e2fSjeremylt   CeedInt num_input_fields;
1038*d1d35e2fSjeremylt   ierr = CeedQFunctionGetNumArgs(qf, &num_input_fields, NULL);
1039*d1d35e2fSjeremylt   CeedChkBackend(ierr);
1040*d1d35e2fSjeremylt   for (CeedInt i=0; i<num_input_fields; i++) {
1041713f43c3Sjeremylt     CeedVector vec;
1042*d1d35e2fSjeremylt     ierr = CeedOperatorFieldGetVector(op_fields[i], &vec); CeedChkBackend(ierr);
1043713f43c3Sjeremylt     if (vec == CEED_VECTOR_ACTIVE) {
1044*d1d35e2fSjeremylt       CeedEvalMode eval_mode;
1045*d1d35e2fSjeremylt       ierr = CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode);
1046*d1d35e2fSjeremylt       CeedChkBackend(ierr);
1047*d1d35e2fSjeremylt       interp = interp || eval_mode == CEED_EVAL_INTERP;
1048*d1d35e2fSjeremylt       grad = grad || eval_mode == CEED_EVAL_GRAD;
1049*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetBasis(op_fields[i], &basis); CeedChkBackend(ierr);
1050*d1d35e2fSjeremylt       ierr = CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr);
1051e15f9bd0SJeremy L Thompson       CeedChkBackend(ierr);
1052713f43c3Sjeremylt     }
1053713f43c3Sjeremylt   }
1054713f43c3Sjeremylt   if (!basis)
1055d9995aecSjeremylt     // LCOV_EXCL_START
1056e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND, "No active field set");
1057d9995aecSjeremylt   // LCOV_EXCL_STOP
1058*d1d35e2fSjeremylt   CeedInt P_1d, Q_1d, elem_size, num_qpts, dim, num_comp = 1, num_elem = 1,
1059*d1d35e2fSjeremylt                                                 l_size = 1;
1060*d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes1D(basis, &P_1d); CeedChkBackend(ierr);
1061*d1d35e2fSjeremylt   ierr = CeedBasisGetNumNodes(basis, &elem_size); CeedChkBackend(ierr);
1062*d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d); CeedChkBackend(ierr);
1063*d1d35e2fSjeremylt   ierr = CeedBasisGetNumQuadraturePoints(basis, &num_qpts); CeedChkBackend(ierr);
1064e15f9bd0SJeremy L Thompson   ierr = CeedBasisGetDimension(basis, &dim); CeedChkBackend(ierr);
1065*d1d35e2fSjeremylt   ierr = CeedBasisGetNumComponents(basis, &num_comp); CeedChkBackend(ierr);
1066*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetNumElements(rstr, &num_elem); CeedChkBackend(ierr);
1067*d1d35e2fSjeremylt   ierr = CeedElemRestrictionGetLVectorSize(rstr, &l_size); CeedChkBackend(ierr);
1068713f43c3Sjeremylt 
1069713f43c3Sjeremylt   // Build and diagonalize 1D Mass and Laplacian
1070*d1d35e2fSjeremylt   bool tensor_basis;
1071*d1d35e2fSjeremylt   ierr = CeedBasisIsTensor(basis, &tensor_basis); CeedChkBackend(ierr);
1072*d1d35e2fSjeremylt   if (!tensor_basis)
1073d9995aecSjeremylt     // LCOV_EXCL_START
1074e15f9bd0SJeremy L Thompson     return CeedError(ceed, CEED_ERROR_BACKEND,
1075e15f9bd0SJeremy L Thompson                      "FDMElementInverse only supported for tensor "
1076713f43c3Sjeremylt                      "bases");
1077d9995aecSjeremylt   // LCOV_EXCL_STOP
1078713f43c3Sjeremylt   CeedScalar *work, *mass, *laplace, *x, *x2, *lambda;
1079*d1d35e2fSjeremylt   ierr = CeedMalloc(Q_1d*P_1d, &work); CeedChkBackend(ierr);
1080*d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &mass); CeedChkBackend(ierr);
1081*d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &laplace); CeedChkBackend(ierr);
1082*d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &x); CeedChkBackend(ierr);
1083*d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d*P_1d, &x2); CeedChkBackend(ierr);
1084*d1d35e2fSjeremylt   ierr = CeedMalloc(P_1d, &lambda); CeedChkBackend(ierr);
1085713f43c3Sjeremylt   // -- Mass
1086*d1d35e2fSjeremylt   const CeedScalar *interp_1d, *grad_1d, *q_weight_1d;
1087*d1d35e2fSjeremylt   ierr = CeedBasisGetInterp1D(basis, &interp_1d); CeedChkBackend(ierr);
1088*d1d35e2fSjeremylt   ierr = CeedBasisGetGrad1D(basis, &grad_1d); CeedChkBackend(ierr);
1089*d1d35e2fSjeremylt   ierr = CeedBasisGetQWeights(basis, &q_weight_1d); CeedChkBackend(ierr);
1090*d1d35e2fSjeremylt   for (CeedInt i=0; i<Q_1d; i++)
1091*d1d35e2fSjeremylt     for (CeedInt j=0; j<P_1d; j++)
1092*d1d35e2fSjeremylt       work[i+j*Q_1d] = interp_1d[i*P_1d+j]*q_weight_1d[i];
10939289e5bfSjeremylt   ierr = CeedMatrixMultiply(ceed, (const CeedScalar *)work,
1094*d1d35e2fSjeremylt                             (const CeedScalar *)interp_1d, mass, P_1d, P_1d, Q_1d);
1095e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1096713f43c3Sjeremylt   // -- Laplacian
1097*d1d35e2fSjeremylt   for (CeedInt i=0; i<Q_1d; i++)
1098*d1d35e2fSjeremylt     for (CeedInt j=0; j<P_1d; j++)
1099*d1d35e2fSjeremylt       work[i+j*Q_1d] = grad_1d[i*P_1d+j]*q_weight_1d[i];
11009289e5bfSjeremylt   ierr = CeedMatrixMultiply(ceed, (const CeedScalar *)work,
1101*d1d35e2fSjeremylt                             (const CeedScalar *)grad_1d, laplace, P_1d, P_1d, Q_1d);
1102e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1103713f43c3Sjeremylt   // -- Diagonalize
1104*d1d35e2fSjeremylt   ierr = CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d);
1105e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1106e15f9bd0SJeremy L Thompson   ierr = CeedFree(&work); CeedChkBackend(ierr);
1107e15f9bd0SJeremy L Thompson   ierr = CeedFree(&mass); CeedChkBackend(ierr);
1108e15f9bd0SJeremy L Thompson   ierr = CeedFree(&laplace); CeedChkBackend(ierr);
1109*d1d35e2fSjeremylt   for (CeedInt i=0; i<P_1d; i++)
1110*d1d35e2fSjeremylt     for (CeedInt j=0; j<P_1d; j++)
1111*d1d35e2fSjeremylt       x2[i+j*P_1d] = x[j+i*P_1d];
1112e15f9bd0SJeremy L Thompson   ierr = CeedFree(&x); CeedChkBackend(ierr);
1113713f43c3Sjeremylt 
1114713f43c3Sjeremylt   // Assemble QFunction
1115713f43c3Sjeremylt   CeedVector assembled;
1116713f43c3Sjeremylt   CeedElemRestriction rstr_qf;
111780ac2e43SJeremy L Thompson   ierr =  CeedOperatorLinearAssembleQFunction(op, &assembled, &rstr_qf,
1118e15f9bd0SJeremy L Thompson           request); CeedChkBackend(ierr);
1119e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_qf); CeedChkBackend(ierr);
1120*d1d35e2fSjeremylt   CeedScalar max_norm = 0;
1121*d1d35e2fSjeremylt   ierr = CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm);
1122*d1d35e2fSjeremylt   CeedChkBackend(ierr);
1123713f43c3Sjeremylt 
1124713f43c3Sjeremylt   // Calculate element averages
1125*d1d35e2fSjeremylt   CeedInt num_fields = ((interp?1:0) + (grad?dim:0))*((interp?1:0) +
1126*d1d35e2fSjeremylt                        (grad?dim:0));
1127*d1d35e2fSjeremylt   CeedScalar *elem_avg;
1128*d1d35e2fSjeremylt   const CeedScalar *assembledarray, *q_weight_array;
1129*d1d35e2fSjeremylt   CeedVector q_weight;
1130*d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed_parent, num_qpts, &q_weight); CeedChkBackend(ierr);
1131713f43c3Sjeremylt   ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
1132*d1d35e2fSjeremylt                         CEED_VECTOR_NONE, q_weight); CeedChkBackend(ierr);
1133713f43c3Sjeremylt   ierr = CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembledarray);
1134e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1135*d1d35e2fSjeremylt   ierr = CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array);
1136e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1137*d1d35e2fSjeremylt   ierr = CeedCalloc(num_elem, &elem_avg); CeedChkBackend(ierr);
1138*d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++) {
1139713f43c3Sjeremylt     CeedInt count = 0;
1140*d1d35e2fSjeremylt     for (CeedInt q=0; q<num_qpts; q++)
1141*d1d35e2fSjeremylt       for (CeedInt i=0; i<num_comp*num_comp*num_fields; i++)
1142*d1d35e2fSjeremylt         if (fabs(assembledarray[e*num_elem*num_qpts*num_comp*num_comp*num_fields +
1143*d1d35e2fSjeremylt                                                                                  i*num_qpts + q]) > max_norm*1e-12) {
1144*d1d35e2fSjeremylt           elem_avg[e] += assembledarray[e*num_elem*num_qpts*num_comp*num_comp*num_fields +
1145*d1d35e2fSjeremylt                                         i*num_qpts + q] / q_weight_array[q];
1146713f43c3Sjeremylt           count++;
1147713f43c3Sjeremylt         }
1148713f43c3Sjeremylt     if (count)
1149*d1d35e2fSjeremylt       elem_avg[e] /= count;
1150713f43c3Sjeremylt   }
1151e15f9bd0SJeremy L Thompson   ierr = CeedVectorRestoreArrayRead(assembled, &assembledarray);
1152e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1153e15f9bd0SJeremy L Thompson   ierr = CeedVectorDestroy(&assembled); CeedChkBackend(ierr);
1154*d1d35e2fSjeremylt   ierr = CeedVectorRestoreArrayRead(q_weight, &q_weight_array);
1155e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1156*d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&q_weight); CeedChkBackend(ierr);
1157713f43c3Sjeremylt 
1158713f43c3Sjeremylt   // Build FDM diagonal
1159*d1d35e2fSjeremylt   CeedVector q_data;
1160*d1d35e2fSjeremylt   CeedScalar *q_data_array;
1161*d1d35e2fSjeremylt   ierr = CeedVectorCreate(ceed_parent, num_elem*num_comp*l_size, &q_data);
1162e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1163*d1d35e2fSjeremylt   ierr = CeedVectorSetArray(q_data, CEED_MEM_HOST, CEED_COPY_VALUES, NULL);
1164e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1165*d1d35e2fSjeremylt   ierr = CeedVectorGetArray(q_data, CEED_MEM_HOST, &q_data_array);
1166e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1167*d1d35e2fSjeremylt   for (CeedInt e=0; e<num_elem; e++)
1168*d1d35e2fSjeremylt     for (CeedInt c=0; c<num_comp; c++)
1169*d1d35e2fSjeremylt       for (CeedInt n=0; n<l_size; n++) {
1170713f43c3Sjeremylt         if (interp)
1171*d1d35e2fSjeremylt           q_data_array[(e*num_comp+c)*l_size+n] = 1;
1172713f43c3Sjeremylt         if (grad)
1173713f43c3Sjeremylt           for (CeedInt d=0; d<dim; d++) {
1174*d1d35e2fSjeremylt             CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d;
1175*d1d35e2fSjeremylt             q_data_array[(e*num_comp+c)*l_size+n] += lambda[i];
1176713f43c3Sjeremylt           }
1177*d1d35e2fSjeremylt         q_data_array[(e*num_comp+c)*l_size+n] = 1 / (elem_avg[e] *
1178*d1d35e2fSjeremylt                                                 q_data_array[(e*num_comp+c)*l_size+n]);
1179713f43c3Sjeremylt       }
1180*d1d35e2fSjeremylt   ierr = CeedFree(&elem_avg); CeedChkBackend(ierr);
1181*d1d35e2fSjeremylt   ierr = CeedVectorRestoreArray(q_data, &q_data_array); CeedChkBackend(ierr);
1182713f43c3Sjeremylt 
1183713f43c3Sjeremylt   // Setup FDM operator
1184713f43c3Sjeremylt   // -- Basis
1185713f43c3Sjeremylt   CeedBasis fdm_basis;
1186*d1d35e2fSjeremylt   CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy;
1187*d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d*P_1d, &grad_dummy); CeedChkBackend(ierr);
1188*d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d, &q_ref_dummy); CeedChkBackend(ierr);
1189*d1d35e2fSjeremylt   ierr = CeedCalloc(P_1d, &q_weight_dummy); CeedChkBackend(ierr);
1190*d1d35e2fSjeremylt   ierr = CeedBasisCreateTensorH1(ceed_parent, dim, num_comp, P_1d, P_1d, x2,
1191*d1d35e2fSjeremylt                                  grad_dummy, q_ref_dummy, q_weight_dummy,
1192e15f9bd0SJeremy L Thompson                                  &fdm_basis); CeedChkBackend(ierr);
1193*d1d35e2fSjeremylt   ierr = CeedFree(&grad_dummy); CeedChkBackend(ierr);
1194*d1d35e2fSjeremylt   ierr = CeedFree(&q_ref_dummy); CeedChkBackend(ierr);
1195*d1d35e2fSjeremylt   ierr = CeedFree(&q_weight_dummy); CeedChkBackend(ierr);
1196e15f9bd0SJeremy L Thompson   ierr = CeedFree(&x2); CeedChkBackend(ierr);
1197e15f9bd0SJeremy L Thompson   ierr = CeedFree(&lambda); CeedChkBackend(ierr);
1198713f43c3Sjeremylt 
1199713f43c3Sjeremylt   // -- Restriction
1200713f43c3Sjeremylt   CeedElemRestriction rstr_i;
1201*d1d35e2fSjeremylt   CeedInt strides[3] = {1, l_size, l_size*num_comp};
1202*d1d35e2fSjeremylt   ierr = CeedElemRestrictionCreateStrided(ceed_parent, num_elem, l_size, num_comp,
1203*d1d35e2fSjeremylt                                           l_size*num_elem*num_comp, strides, &rstr_i);
1204e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1205713f43c3Sjeremylt   // -- QFunction
1206713f43c3Sjeremylt   CeedQFunction mass_qf;
1207*d1d35e2fSjeremylt   ierr = CeedQFunctionCreateInteriorByName(ceed_parent, "MassApply", &mass_qf);
1208e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1209713f43c3Sjeremylt   // -- Operator
1210*d1d35e2fSjeremylt   ierr = CeedOperatorCreate(ceed_parent, mass_qf, NULL, NULL, fdm_inv);
1211e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1212*d1d35e2fSjeremylt   CeedOperatorSetField(*fdm_inv, "u", rstr_i, fdm_basis, CEED_VECTOR_ACTIVE);
1213e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1214*d1d35e2fSjeremylt   CeedOperatorSetField(*fdm_inv, "qdata", rstr_i, CEED_BASIS_COLLOCATED, q_data);
1215e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1216*d1d35e2fSjeremylt   CeedOperatorSetField(*fdm_inv, "v", rstr_i, fdm_basis, CEED_VECTOR_ACTIVE);
1217e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1218713f43c3Sjeremylt 
1219713f43c3Sjeremylt   // Cleanup
1220*d1d35e2fSjeremylt   ierr = CeedVectorDestroy(&q_data); CeedChkBackend(ierr);
1221e15f9bd0SJeremy L Thompson   ierr = CeedBasisDestroy(&fdm_basis); CeedChkBackend(ierr);
1222e15f9bd0SJeremy L Thompson   ierr = CeedElemRestrictionDestroy(&rstr_i); CeedChkBackend(ierr);
1223e15f9bd0SJeremy L Thompson   ierr = CeedQFunctionDestroy(&mass_qf); CeedChkBackend(ierr);
1224713f43c3Sjeremylt 
1225e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1226713f43c3Sjeremylt }
1227713f43c3Sjeremylt 
1228f10650afSjeremylt //------------------------------------------------------------------------------
1229f10650afSjeremylt // Operator Destroy
1230f10650afSjeremylt //------------------------------------------------------------------------------
1231f10650afSjeremylt static int CeedOperatorDestroy_Ref(CeedOperator op) {
1232f10650afSjeremylt   int ierr;
1233f10650afSjeremylt   CeedOperator_Ref *impl;
1234e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetData(op, &impl); CeedChkBackend(ierr);
1235f10650afSjeremylt 
1236*d1d35e2fSjeremylt   for (CeedInt i=0; i<impl->num_e_vecs_in+impl->num_e_vecs_out; i++) {
1237*d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->e_vecs[i]); CeedChkBackend(ierr);
1238f10650afSjeremylt   }
1239*d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_vecs); CeedChkBackend(ierr);
1240*d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_data); CeedChkBackend(ierr);
1241*d1d35e2fSjeremylt   ierr = CeedFree(&impl->input_state); CeedChkBackend(ierr);
1242f10650afSjeremylt 
1243*d1d35e2fSjeremylt   for (CeedInt i=0; i<impl->num_e_vecs_in; i++) {
1244*d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->e_vecs_in[i]); CeedChkBackend(ierr);
1245*d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->q_vecs_in[i]); CeedChkBackend(ierr);
1246f10650afSjeremylt   }
1247*d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_vecs_in); CeedChkBackend(ierr);
1248*d1d35e2fSjeremylt   ierr = CeedFree(&impl->q_vecs_in); CeedChkBackend(ierr);
1249f10650afSjeremylt 
1250*d1d35e2fSjeremylt   for (CeedInt i=0; i<impl->num_e_vecs_out; i++) {
1251*d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->e_vecs_out[i]); CeedChkBackend(ierr);
1252*d1d35e2fSjeremylt     ierr = CeedVectorDestroy(&impl->q_vecs_out[i]); CeedChkBackend(ierr);
1253f10650afSjeremylt   }
1254*d1d35e2fSjeremylt   ierr = CeedFree(&impl->e_vecs_out); CeedChkBackend(ierr);
1255*d1d35e2fSjeremylt   ierr = CeedFree(&impl->q_vecs_out); CeedChkBackend(ierr);
1256f10650afSjeremylt 
1257e15f9bd0SJeremy L Thompson   ierr = CeedFree(&impl); CeedChkBackend(ierr);
1258e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1259f10650afSjeremylt }
1260f10650afSjeremylt 
1261f10650afSjeremylt //------------------------------------------------------------------------------
1262713f43c3Sjeremylt // Operator Create
1263f10650afSjeremylt //------------------------------------------------------------------------------
126421617c04Sjeremylt int CeedOperatorCreate_Ref(CeedOperator op) {
126521617c04Sjeremylt   int ierr;
1266fe2413ffSjeremylt   Ceed ceed;
1267e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
12684ce2993fSjeremylt   CeedOperator_Ref *impl;
126921617c04Sjeremylt 
1270e15f9bd0SJeremy L Thompson   ierr = CeedCalloc(1, &impl); CeedChkBackend(ierr);
1271e15f9bd0SJeremy L Thompson   ierr = CeedOperatorSetData(op, impl); CeedChkBackend(ierr);
1272fe2413ffSjeremylt 
127380ac2e43SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op, "LinearAssembleQFunction",
127480ac2e43SJeremy L Thompson                                 CeedOperatorLinearAssembleQFunction_Ref);
1275e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
12769e9210b8SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op, "LinearAssembleAddDiagonal",
127769af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddDiagonal_Ref);
1278e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1279d965c7a7SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op,
12809e9210b8SJeremy L Thompson                                 "LinearAssembleAddPointBlockDiagonal",
128169af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddPointBlockDiagonal_Ref);
1282e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1283713f43c3Sjeremylt   ierr = CeedSetBackendFunction(ceed, "Operator", op, "CreateFDMElementInverse",
1284713f43c3Sjeremylt                                 CeedOperatorCreateFDMElementInverse_Ref);
1285e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1286cae8b89aSjeremylt   ierr = CeedSetBackendFunction(ceed, "Operator", op, "ApplyAdd",
1287e15f9bd0SJeremy L Thompson                                 CeedOperatorApplyAdd_Ref); CeedChkBackend(ierr);
1288fe2413ffSjeremylt   ierr = CeedSetBackendFunction(ceed, "Operator", op, "Destroy",
1289e15f9bd0SJeremy L Thompson                                 CeedOperatorDestroy_Ref); CeedChkBackend(ierr);
1290e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
129121617c04Sjeremylt }
1292c04a41a7SJeremy L Thompson 
1293c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
1294c04a41a7SJeremy L Thompson // Composite Operator Create
1295c04a41a7SJeremy L Thompson //------------------------------------------------------------------------------
1296c04a41a7SJeremy L Thompson int CeedCompositeOperatorCreate_Ref(CeedOperator op) {
1297c04a41a7SJeremy L Thompson   int ierr;
1298c04a41a7SJeremy L Thompson   Ceed ceed;
1299e15f9bd0SJeremy L Thompson   ierr = CeedOperatorGetCeed(op, &ceed); CeedChkBackend(ierr);
13009e9210b8SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op, "LinearAssembleAddDiagonal",
130169af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddDiagonal_Ref);
1302e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1303c04a41a7SJeremy L Thompson   ierr = CeedSetBackendFunction(ceed, "Operator", op,
13049e9210b8SJeremy L Thompson                                 "LinearAssembleAddPointBlockDiagonal",
130569af5e5fSJeremy L Thompson                                 CeedOperatorLinearAssembleAddPointBlockDiagonal_Ref);
1306e15f9bd0SJeremy L Thompson   CeedChkBackend(ierr);
1307e15f9bd0SJeremy L Thompson   return CEED_ERROR_SUCCESS;
1308c04a41a7SJeremy L Thompson }
1309f10650afSjeremylt //------------------------------------------------------------------------------
1310