1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors. 2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3 // 4 // SPDX-License-Identifier: BSD-2-Clause 5 // 6 // This file is part of CEED: http://github.com/ceed 7 8 #include <ceed-impl.h> 9 #include <ceed.h> 10 #include <ceed/backend.h> 11 #include <stdbool.h> 12 #include <stdio.h> 13 #include <string.h> 14 15 /// @file 16 /// Implementation of CeedOperator interfaces 17 18 /// ---------------------------------------------------------------------------- 19 /// CeedOperator Library Internal Functions 20 /// ---------------------------------------------------------------------------- 21 /// @addtogroup CeedOperatorDeveloper 22 /// @{ 23 24 /** 25 @brief Check if a CeedOperator Field matches the QFunction Field 26 27 @param[in] ceed Ceed object for error handling 28 @param[in] qf_field QFunction Field matching Operator Field 29 @param[in] r Operator Field ElemRestriction 30 @param[in] b Operator Field Basis 31 32 @return An error code: 0 - success, otherwise - failure 33 34 @ref Developer 35 **/ 36 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field, CeedElemRestriction r, CeedBasis b) { 37 CeedEvalMode eval_mode = qf_field->eval_mode; 38 CeedInt dim = 1, num_comp = 1, q_comp = 1, restr_num_comp = 1, size = qf_field->size; 39 40 // Restriction 41 CeedCheck((r == CEED_ELEMRESTRICTION_NONE) == (eval_mode == CEED_EVAL_WEIGHT), ceed, CEED_ERROR_INCOMPATIBLE, 42 "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT must be used together."); 43 if (r != CEED_ELEMRESTRICTION_NONE) { 44 CeedCall(CeedElemRestrictionGetNumComponents(r, &restr_num_comp)); 45 } 46 // Basis 47 CeedCheck((b == CEED_BASIS_COLLOCATED) == (eval_mode == CEED_EVAL_NONE), ceed, CEED_ERROR_INCOMPATIBLE, 48 "CEED_BASIS_COLLOCATED and CEED_EVAL_NONE must be used together."); 49 if (b != CEED_BASIS_COLLOCATED) { 50 CeedCall(CeedBasisGetDimension(b, &dim)); 51 CeedCall(CeedBasisGetNumComponents(b, &num_comp)); 52 CeedCall(CeedBasisGetNumQuadratureComponents(b, eval_mode, &q_comp)); 53 CeedCheck(r == CEED_ELEMRESTRICTION_NONE || restr_num_comp == num_comp, ceed, CEED_ERROR_DIMENSION, 54 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT " components, but Basis has %" CeedInt_FMT 55 " components", 56 qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], restr_num_comp, num_comp); 57 } 58 // Field size 59 switch (eval_mode) { 60 case CEED_EVAL_NONE: 61 CeedCheck(size == restr_num_comp, ceed, CEED_ERROR_DIMENSION, 62 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT " components", qf_field->field_name, 63 qf_field->size, CeedEvalModes[qf_field->eval_mode], restr_num_comp); 64 break; 65 case CEED_EVAL_INTERP: 66 case CEED_EVAL_GRAD: 67 case CEED_EVAL_DIV: 68 case CEED_EVAL_CURL: 69 CeedCheck(size == num_comp * q_comp, ceed, CEED_ERROR_DIMENSION, 70 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction/Basis has %" CeedInt_FMT " components", qf_field->field_name, 71 qf_field->size, CeedEvalModes[qf_field->eval_mode], num_comp * q_comp); 72 break; 73 case CEED_EVAL_WEIGHT: 74 // No additional checks required 75 break; 76 } 77 return CEED_ERROR_SUCCESS; 78 } 79 80 /** 81 @brief View a field of a CeedOperator 82 83 @param[in] field Operator field to view 84 @param[in] qf_field QFunction field (carries field name) 85 @param[in] field_number Number of field being viewed 86 @param[in] sub true indicates sub-operator, which increases indentation; false for top-level operator 87 @param[in] input true for an input field; false for output field 88 @param[in] stream Stream to view to, e.g., stdout 89 90 @return An error code: 0 - success, otherwise - failure 91 92 @ref Utility 93 **/ 94 static int CeedOperatorFieldView(CeedOperatorField field, CeedQFunctionField qf_field, CeedInt field_number, bool sub, bool input, FILE *stream) { 95 const char *pre = sub ? " " : ""; 96 const char *in_out = input ? "Input" : "Output"; 97 98 fprintf(stream, 99 "%s %s field %" CeedInt_FMT 100 ":\n" 101 "%s Name: \"%s\"\n", 102 pre, in_out, field_number, pre, qf_field->field_name); 103 104 fprintf(stream, "%s Size: %" CeedInt_FMT "\n", pre, qf_field->size); 105 106 fprintf(stream, "%s EvalMode: %s\n", pre, CeedEvalModes[qf_field->eval_mode]); 107 108 if (field->basis == CEED_BASIS_COLLOCATED) fprintf(stream, "%s Collocated basis\n", pre); 109 110 if (field->vec == CEED_VECTOR_ACTIVE) fprintf(stream, "%s Active vector\n", pre); 111 else if (field->vec == CEED_VECTOR_NONE) fprintf(stream, "%s No vector\n", pre); 112 113 return CEED_ERROR_SUCCESS; 114 } 115 116 /** 117 @brief View a single CeedOperator 118 119 @param[in] op CeedOperator to view 120 @param[in] sub Boolean flag for sub-operator 121 @param[in] stream Stream to write; typically stdout/stderr or a file 122 123 @return Error code: 0 - success, otherwise - failure 124 125 @ref Utility 126 **/ 127 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) { 128 const char *pre = sub ? " " : ""; 129 130 CeedInt num_elem, num_qpts; 131 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 132 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 133 134 CeedInt total_fields = 0; 135 CeedCall(CeedOperatorGetNumArgs(op, &total_fields)); 136 fprintf(stream, "%s %" CeedInt_FMT " elements with %" CeedInt_FMT " quadrature points each\n", pre, num_elem, num_qpts); 137 138 fprintf(stream, "%s %" CeedInt_FMT " field%s\n", pre, total_fields, total_fields > 1 ? "s" : ""); 139 140 fprintf(stream, "%s %" CeedInt_FMT " input field%s:\n", pre, op->qf->num_input_fields, op->qf->num_input_fields > 1 ? "s" : ""); 141 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 142 CeedCall(CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i], i, sub, 1, stream)); 143 } 144 145 fprintf(stream, "%s %" CeedInt_FMT " output field%s:\n", pre, op->qf->num_output_fields, op->qf->num_output_fields > 1 ? "s" : ""); 146 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 147 CeedCall(CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i], i, sub, 0, stream)); 148 } 149 return CEED_ERROR_SUCCESS; 150 } 151 152 /** 153 @brief Find the active vector basis for a non-composite CeedOperator 154 155 @param[in] op CeedOperator to find active basis for 156 @param[out] active_basis Basis for active input vector or NULL for composite operator 157 158 @return An error code: 0 - success, otherwise - failure 159 160 @ref Developer 161 **/ 162 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) { 163 Ceed ceed; 164 CeedCall(CeedOperatorGetCeed(op, &ceed)); 165 166 *active_basis = NULL; 167 if (op->is_composite) return CEED_ERROR_SUCCESS; 168 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 169 if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 170 CeedCheck(!*active_basis || *active_basis == op->input_fields[i]->basis, ceed, CEED_ERROR_MINOR, "Multiple active CeedBases found"); 171 *active_basis = op->input_fields[i]->basis; 172 } 173 } 174 175 CeedCheck(*active_basis, ceed, CEED_ERROR_INCOMPLETE, "No active CeedBasis found"); 176 return CEED_ERROR_SUCCESS; 177 } 178 179 /** 180 @brief Find the active vector ElemRestriction for a non-composite CeedOperator 181 182 @param[in] op CeedOperator to find active basis for 183 @param[out] active_rstr ElemRestriction for active input vector or NULL for composite operator 184 185 @return An error code: 0 - success, otherwise - failure 186 187 @ref Utility 188 **/ 189 int CeedOperatorGetActiveElemRestriction(CeedOperator op, CeedElemRestriction *active_rstr) { 190 Ceed ceed; 191 CeedCall(CeedOperatorGetCeed(op, &ceed)); 192 193 *active_rstr = NULL; 194 if (op->is_composite) return CEED_ERROR_SUCCESS; 195 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 196 if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 197 CeedCheck(!*active_rstr || *active_rstr == op->input_fields[i]->elem_rstr, ceed, CEED_ERROR_MINOR, 198 "Multiple active CeedElemRestrictions found"); 199 *active_rstr = op->input_fields[i]->elem_rstr; 200 } 201 } 202 203 CeedCheck(*active_rstr, ceed, CEED_ERROR_INCOMPLETE, "No active CeedElemRestriction found"); 204 return CEED_ERROR_SUCCESS; 205 } 206 207 /** 208 @brief Set QFunctionContext field values of the specified type. 209 210 For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`. 211 A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have 212 any field of a matching type. 213 214 @param[in,out] op CeedOperator 215 @param[in] field_label Label of field to set 216 @param[in] field_type Type of field to set 217 @param[in] values Values to set 218 219 @return An error code: 0 - success, otherwise - failure 220 221 @ref User 222 **/ 223 static int CeedOperatorContextSetGeneric(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) { 224 CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label"); 225 226 // Check if field_label and op correspond 227 if (field_label->from_op) { 228 CeedInt index = -1; 229 230 for (CeedInt i = 0; i < op->num_context_labels; i++) { 231 if (op->context_labels[i] == field_label) index = i; 232 } 233 CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator"); 234 } 235 236 bool is_composite = false; 237 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 238 if (is_composite) { 239 CeedInt num_sub; 240 CeedOperator *sub_operators; 241 242 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 243 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 244 CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED, 245 "Composite operator modified after ContextFieldLabel created"); 246 247 for (CeedInt i = 0; i < num_sub; i++) { 248 // Try every sub-operator, ok if some sub-operators do not have field 249 if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) { 250 CeedCall(CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values)); 251 } 252 } 253 } else { 254 CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data"); 255 CeedCall(CeedQFunctionContextSetGeneric(op->qf->ctx, field_label, field_type, values)); 256 } 257 258 return CEED_ERROR_SUCCESS; 259 } 260 261 /** 262 @brief Get QFunctionContext field values of the specified type, read-only. 263 264 For composite operators, the values retrieved are for the first sub-operator QFunctionContext that have a matching `field_name`. 265 A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have 266 any field of a matching type. 267 268 @param[in,out] op CeedOperator 269 @param[in] field_label Label of field to set 270 @param[in] field_type Type of field to set 271 @param[out] num_values Number of values of type `field_type` in array `values` 272 @param[out] values Values in the label 273 274 @return An error code: 0 - success, otherwise - failure 275 276 @ref User 277 **/ 278 static int CeedOperatorContextGetGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, size_t *num_values, 279 void *values) { 280 CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label"); 281 282 *(void **)values = NULL; 283 *num_values = 0; 284 285 // Check if field_label and op correspond 286 if (field_label->from_op) { 287 CeedInt index = -1; 288 289 for (CeedInt i = 0; i < op->num_context_labels; i++) { 290 if (op->context_labels[i] == field_label) index = i; 291 } 292 CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator"); 293 } 294 295 bool is_composite = false; 296 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 297 if (is_composite) { 298 CeedInt num_sub; 299 CeedOperator *sub_operators; 300 301 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 302 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 303 CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED, 304 "Composite operator modified after ContextFieldLabel created"); 305 306 for (CeedInt i = 0; i < num_sub; i++) { 307 // Try every sub-operator, ok if some sub-operators do not have field 308 if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) { 309 CeedCall(CeedQFunctionContextGetGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, num_values, values)); 310 return CEED_ERROR_SUCCESS; 311 } 312 } 313 } else { 314 CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data"); 315 CeedCall(CeedQFunctionContextGetGenericRead(op->qf->ctx, field_label, field_type, num_values, values)); 316 } 317 318 return CEED_ERROR_SUCCESS; 319 } 320 321 /** 322 @brief Restore QFunctionContext field values of the specified type, read-only. 323 324 For composite operators, the values restored are for the first sub-operator QFunctionContext that have a matching `field_name`. 325 A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have 326 any field of a matching type. 327 328 @param[in,out] op CeedOperator 329 @param[in] field_label Label of field to set 330 @param[in] field_type Type of field to set 331 @param[in] values Values array to restore 332 333 @return An error code: 0 - success, otherwise - failure 334 335 @ref User 336 **/ 337 static int CeedOperatorContextRestoreGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) { 338 CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label"); 339 340 // Check if field_label and op correspond 341 if (field_label->from_op) { 342 CeedInt index = -1; 343 344 for (CeedInt i = 0; i < op->num_context_labels; i++) { 345 if (op->context_labels[i] == field_label) index = i; 346 } 347 CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator"); 348 } 349 350 bool is_composite = false; 351 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 352 if (is_composite) { 353 CeedInt num_sub; 354 CeedOperator *sub_operators; 355 356 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 357 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 358 CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED, 359 "Composite operator modified after ContextFieldLabel created"); 360 361 for (CeedInt i = 0; i < num_sub; i++) { 362 // Try every sub-operator, ok if some sub-operators do not have field 363 if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) { 364 CeedCall(CeedQFunctionContextRestoreGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values)); 365 return CEED_ERROR_SUCCESS; 366 } 367 } 368 } else { 369 CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data"); 370 CeedCall(CeedQFunctionContextRestoreGenericRead(op->qf->ctx, field_label, field_type, values)); 371 } 372 373 return CEED_ERROR_SUCCESS; 374 } 375 376 /// @} 377 378 /// ---------------------------------------------------------------------------- 379 /// CeedOperator Backend API 380 /// ---------------------------------------------------------------------------- 381 /// @addtogroup CeedOperatorBackend 382 /// @{ 383 384 /** 385 @brief Get the number of arguments associated with a CeedOperator 386 387 @param[in] op CeedOperator 388 @param[out] num_args Variable to store vector number of arguments 389 390 @return An error code: 0 - success, otherwise - failure 391 392 @ref Backend 393 **/ 394 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) { 395 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operators"); 396 *num_args = op->num_fields; 397 return CEED_ERROR_SUCCESS; 398 } 399 400 /** 401 @brief Get the setup status of a CeedOperator 402 403 @param[in] op CeedOperator 404 @param[out] is_setup_done Variable to store setup status 405 406 @return An error code: 0 - success, otherwise - failure 407 408 @ref Backend 409 **/ 410 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) { 411 *is_setup_done = op->is_backend_setup; 412 return CEED_ERROR_SUCCESS; 413 } 414 415 /** 416 @brief Get the QFunction associated with a CeedOperator 417 418 @param[in] op CeedOperator 419 @param[out] qf Variable to store QFunction 420 421 @return An error code: 0 - success, otherwise - failure 422 423 @ref Backend 424 **/ 425 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) { 426 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 427 *qf = op->qf; 428 return CEED_ERROR_SUCCESS; 429 } 430 431 /** 432 @brief Get a boolean value indicating if the CeedOperator is composite 433 434 @param[in] op CeedOperator 435 @param[out] is_composite Variable to store composite status 436 437 @return An error code: 0 - success, otherwise - failure 438 439 @ref Backend 440 **/ 441 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) { 442 *is_composite = op->is_composite; 443 return CEED_ERROR_SUCCESS; 444 } 445 446 /** 447 @brief Get the backend data of a CeedOperator 448 449 @param[in] op CeedOperator 450 @param[out] data Variable to store data 451 452 @return An error code: 0 - success, otherwise - failure 453 454 @ref Backend 455 **/ 456 int CeedOperatorGetData(CeedOperator op, void *data) { 457 *(void **)data = op->data; 458 return CEED_ERROR_SUCCESS; 459 } 460 461 /** 462 @brief Set the backend data of a CeedOperator 463 464 @param[in,out] op CeedOperator 465 @param[in] data Data to set 466 467 @return An error code: 0 - success, otherwise - failure 468 469 @ref Backend 470 **/ 471 int CeedOperatorSetData(CeedOperator op, void *data) { 472 op->data = data; 473 return CEED_ERROR_SUCCESS; 474 } 475 476 /** 477 @brief Increment the reference counter for a CeedOperator 478 479 @param[in,out] op CeedOperator to increment the reference counter 480 481 @return An error code: 0 - success, otherwise - failure 482 483 @ref Backend 484 **/ 485 int CeedOperatorReference(CeedOperator op) { 486 op->ref_count++; 487 return CEED_ERROR_SUCCESS; 488 } 489 490 /** 491 @brief Set the setup flag of a CeedOperator to True 492 493 @param[in,out] op CeedOperator 494 495 @return An error code: 0 - success, otherwise - failure 496 497 @ref Backend 498 **/ 499 int CeedOperatorSetSetupDone(CeedOperator op) { 500 op->is_backend_setup = true; 501 return CEED_ERROR_SUCCESS; 502 } 503 504 /// @} 505 506 /// ---------------------------------------------------------------------------- 507 /// CeedOperator Public API 508 /// ---------------------------------------------------------------------------- 509 /// @addtogroup CeedOperatorUser 510 /// @{ 511 512 /** 513 @brief Create a CeedOperator and associate a CeedQFunction. 514 515 A CeedBasis and CeedElemRestriction can be associated with CeedQFunction fields with \ref CeedOperatorSetField. 516 517 @param[in] ceed Ceed object where the CeedOperator will be created 518 @param[in] qf QFunction defining the action of the operator at quadrature points 519 @param[in] dqf QFunction defining the action of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE) 520 @param[in] dqfT QFunction defining the action of the transpose of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE) 521 @param[out] op Address of the variable where the newly created CeedOperator will be stored 522 523 @return An error code: 0 - success, otherwise - failure 524 525 @ref User 526 */ 527 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf, CeedQFunction dqfT, CeedOperator *op) { 528 if (!ceed->OperatorCreate) { 529 Ceed delegate; 530 531 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator")); 532 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support OperatorCreate"); 533 CeedCall(CeedOperatorCreate(delegate, qf, dqf, dqfT, op)); 534 return CEED_ERROR_SUCCESS; 535 } 536 537 CeedCheck(qf && qf != CEED_QFUNCTION_NONE, ceed, CEED_ERROR_MINOR, "Operator must have a valid QFunction."); 538 539 CeedCall(CeedCalloc(1, op)); 540 CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed)); 541 (*op)->ref_count = 1; 542 (*op)->input_size = -1; 543 (*op)->output_size = -1; 544 CeedCall(CeedQFunctionReferenceCopy(qf, &(*op)->qf)); 545 if (dqf && dqf != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqf, &(*op)->dqf)); 546 if (dqfT && dqfT != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqfT, &(*op)->dqfT)); 547 CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled)); 548 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields)); 549 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields)); 550 CeedCall(ceed->OperatorCreate(*op)); 551 return CEED_ERROR_SUCCESS; 552 } 553 554 /** 555 @brief Create an operator that composes the action of several operators 556 557 @param[in] ceed Ceed object where the CeedOperator will be created 558 @param[out] op Address of the variable where the newly created Composite CeedOperator will be stored 559 560 @return An error code: 0 - success, otherwise - failure 561 562 @ref User 563 */ 564 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) { 565 if (!ceed->CompositeOperatorCreate) { 566 Ceed delegate; 567 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator")); 568 569 if (delegate) { 570 CeedCall(CeedCompositeOperatorCreate(delegate, op)); 571 return CEED_ERROR_SUCCESS; 572 } 573 } 574 575 CeedCall(CeedCalloc(1, op)); 576 CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed)); 577 (*op)->ref_count = 1; 578 (*op)->is_composite = true; 579 CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators)); 580 (*op)->input_size = -1; 581 (*op)->output_size = -1; 582 583 if (ceed->CompositeOperatorCreate) CeedCall(ceed->CompositeOperatorCreate(*op)); 584 return CEED_ERROR_SUCCESS; 585 } 586 587 /** 588 @brief Copy the pointer to a CeedOperator. 589 590 Both pointers should be destroyed with `CeedOperatorDestroy()`. 591 592 Note: If the value of `op_copy` passed to this function is non-NULL, then it is assumed that `op_copy` is a pointer to a CeedOperator. 593 This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator. 594 595 @param[in] op CeedOperator to copy reference to 596 @param[in,out] op_copy Variable to store copied reference 597 598 @return An error code: 0 - success, otherwise - failure 599 600 @ref User 601 **/ 602 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) { 603 CeedCall(CeedOperatorReference(op)); 604 CeedCall(CeedOperatorDestroy(op_copy)); 605 *op_copy = op; 606 return CEED_ERROR_SUCCESS; 607 } 608 609 /** 610 @brief Provide a field to a CeedOperator for use by its CeedQFunction. 611 612 This function is used to specify both active and passive fields to a CeedOperator. 613 For passive fields, a vector @arg v must be provided. 614 Passive fields can inputs or outputs (updated in-place when operator is applied). 615 616 Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply(). 617 There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply(). 618 619 @param[in,out] op CeedOperator on which to provide the field 620 @param[in] field_name Name of the field (to be matched with the name used by CeedQFunction) 621 @param[in] r CeedElemRestriction 622 @param[in] b CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points 623 @param[in] v CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE 624 if using @ref CEED_EVAL_WEIGHT in the QFunction 625 626 @return An error code: 0 - success, otherwise - failure 627 628 @ref User 629 **/ 630 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) { 631 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator."); 632 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 633 CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name); 634 CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name); 635 CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name); 636 637 CeedInt num_elem = 0; 638 CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem)); 639 CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION, 640 "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem); 641 642 CeedInt num_qpts = 0; 643 if (b == CEED_BASIS_COLLOCATED) CeedCall(CeedElemRestrictionGetElementSize(r, &num_qpts)); 644 else CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts)); 645 CeedCheck(!op->num_qpts || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION, 646 "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts); 647 CeedQFunctionField qf_field; 648 CeedOperatorField *op_field; 649 bool is_input = true; 650 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 651 if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) { 652 qf_field = op->qf->input_fields[i]; 653 op_field = &op->input_fields[i]; 654 goto found; 655 } 656 } 657 is_input = false; 658 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 659 if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) { 660 qf_field = op->qf->output_fields[i]; 661 op_field = &op->output_fields[i]; 662 goto found; 663 } 664 } 665 // LCOV_EXCL_START 666 return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name); 667 // LCOV_EXCL_STOP 668 found: 669 CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b)); 670 CeedCall(CeedCalloc(1, op_field)); 671 672 if (v == CEED_VECTOR_ACTIVE) { 673 CeedSize l_size; 674 CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size)); 675 if (is_input) { 676 if (op->input_size == -1) op->input_size = l_size; 677 CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 678 op->input_size); 679 } else { 680 if (op->output_size == -1) op->output_size = l_size; 681 CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 682 op->output_size); 683 } 684 } 685 686 CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec)); 687 CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr)); 688 if (r != CEED_ELEMRESTRICTION_NONE) { 689 op->num_elem = num_elem; 690 op->has_restriction = true; // Restriction set, but num_elem may be 0 691 } 692 CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis)); 693 if (op->num_qpts == 0) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts)); 694 695 op->num_fields += 1; 696 CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name)); 697 return CEED_ERROR_SUCCESS; 698 } 699 700 /** 701 @brief Get the CeedOperatorFields of a CeedOperator 702 703 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 704 705 @param[in] op CeedOperator 706 @param[out] num_input_fields Variable to store number of input fields 707 @param[out] input_fields Variable to store input_fields 708 @param[out] num_output_fields Variable to store number of output fields 709 @param[out] output_fields Variable to store output_fields 710 711 @return An error code: 0 - success, otherwise - failure 712 713 @ref Advanced 714 **/ 715 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields, 716 CeedOperatorField **output_fields) { 717 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 718 CeedCall(CeedOperatorCheckReady(op)); 719 720 if (num_input_fields) *num_input_fields = op->qf->num_input_fields; 721 if (input_fields) *input_fields = op->input_fields; 722 if (num_output_fields) *num_output_fields = op->qf->num_output_fields; 723 if (output_fields) *output_fields = op->output_fields; 724 return CEED_ERROR_SUCCESS; 725 } 726 727 /** 728 @brief Get a CeedOperatorField of an CeedOperator from its name 729 730 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 731 732 @param[in] op CeedOperator 733 @param[in] field_name Name of desired CeedOperatorField 734 @param[out] op_field CeedOperatorField corresponding to the name 735 736 @return An error code: 0 - success, otherwise - failure 737 738 @ref Advanced 739 **/ 740 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) { 741 CeedInt num_input_fields, num_output_fields; 742 CeedOperatorField *input_fields, *output_fields; 743 char *name; 744 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 745 746 for (CeedInt i = 0; i < num_input_fields; i++) { 747 CeedCall(CeedOperatorFieldGetName(input_fields[i], &name)); 748 if (!strcmp(name, field_name)) { 749 *op_field = input_fields[i]; 750 return CEED_ERROR_SUCCESS; 751 } 752 } 753 754 for (CeedInt i = 0; i < num_output_fields; i++) { 755 CeedCall(CeedOperatorFieldGetName(output_fields[i], &name)); 756 if (!strcmp(name, field_name)) { 757 *op_field = output_fields[i]; 758 return CEED_ERROR_SUCCESS; 759 } 760 } 761 762 bool has_name = op->name; 763 // LCOV_EXCL_START 764 return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "", 765 has_name ? op->name : "", has_name ? "\"" : ""); 766 // LCOV_EXCL_STOP 767 } 768 769 /** 770 @brief Get the name of a CeedOperatorField 771 772 @param[in] op_field CeedOperatorField 773 @param[out] field_name Variable to store the field name 774 775 @return An error code: 0 - success, otherwise - failure 776 777 @ref Advanced 778 **/ 779 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) { 780 *field_name = (char *)op_field->field_name; 781 return CEED_ERROR_SUCCESS; 782 } 783 784 /** 785 @brief Get the CeedElemRestriction of a CeedOperatorField 786 787 @param[in] op_field CeedOperatorField 788 @param[out] rstr Variable to store CeedElemRestriction 789 790 @return An error code: 0 - success, otherwise - failure 791 792 @ref Advanced 793 **/ 794 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) { 795 *rstr = op_field->elem_rstr; 796 return CEED_ERROR_SUCCESS; 797 } 798 799 /** 800 @brief Get the CeedBasis of a CeedOperatorField 801 802 @param[in] op_field CeedOperatorField 803 @param[out] basis Variable to store CeedBasis 804 805 @return An error code: 0 - success, otherwise - failure 806 807 @ref Advanced 808 **/ 809 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) { 810 *basis = op_field->basis; 811 return CEED_ERROR_SUCCESS; 812 } 813 814 /** 815 @brief Get the CeedVector of a CeedOperatorField 816 817 @param[in] op_field CeedOperatorField 818 @param[out] vec Variable to store CeedVector 819 820 @return An error code: 0 - success, otherwise - failure 821 822 @ref Advanced 823 **/ 824 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) { 825 *vec = op_field->vec; 826 return CEED_ERROR_SUCCESS; 827 } 828 829 /** 830 @brief Add a sub-operator to a composite CeedOperator 831 832 @param[in,out] composite_op Composite CeedOperator 833 @param[in] sub_op Sub-operator CeedOperator 834 835 @return An error code: 0 - success, otherwise - failure 836 837 @ref User 838 */ 839 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) { 840 CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator"); 841 CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators"); 842 CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 843 844 { 845 CeedSize input_size, output_size; 846 CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size)); 847 if (composite_op->input_size == -1) composite_op->input_size = input_size; 848 if (composite_op->output_size == -1) composite_op->output_size = output_size; 849 // Note, a size of -1 means no active vector restriction set, so no incompatibility 850 CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size), 851 composite_op->ceed, CEED_ERROR_MAJOR, 852 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 853 "shape (%td, %td)", 854 composite_op->input_size, composite_op->output_size, input_size, output_size); 855 } 856 857 composite_op->sub_operators[composite_op->num_suboperators] = sub_op; 858 CeedCall(CeedOperatorReference(sub_op)); 859 composite_op->num_suboperators++; 860 return CEED_ERROR_SUCCESS; 861 } 862 863 /** 864 @brief Get the number of sub_operators associated with a CeedOperator 865 866 @param[in] op CeedOperator 867 @param[out] num_suboperators Variable to store number of sub_operators 868 869 @return An error code: 0 - success, otherwise - failure 870 871 @ref Backend 872 **/ 873 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) { 874 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 875 *num_suboperators = op->num_suboperators; 876 return CEED_ERROR_SUCCESS; 877 } 878 879 /** 880 @brief Get the list of sub_operators associated with a CeedOperator 881 882 @param op CeedOperator 883 @param[out] sub_operators Variable to store list of sub_operators 884 885 @return An error code: 0 - success, otherwise - failure 886 887 @ref Backend 888 **/ 889 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) { 890 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 891 *sub_operators = op->sub_operators; 892 return CEED_ERROR_SUCCESS; 893 } 894 895 /** 896 @brief Check if a CeedOperator is ready to be used. 897 898 @param[in] op CeedOperator to check 899 900 @return An error code: 0 - success, otherwise - failure 901 902 @ref User 903 **/ 904 int CeedOperatorCheckReady(CeedOperator op) { 905 Ceed ceed; 906 CeedCall(CeedOperatorGetCeed(op, &ceed)); 907 908 if (op->is_interface_setup) return CEED_ERROR_SUCCESS; 909 910 CeedQFunction qf = op->qf; 911 if (op->is_composite) { 912 if (!op->num_suboperators) { 913 // Empty operator setup 914 op->input_size = 0; 915 op->output_size = 0; 916 } else { 917 for (CeedInt i = 0; i < op->num_suboperators; i++) { 918 CeedCall(CeedOperatorCheckReady(op->sub_operators[i])); 919 } 920 // Sub-operators could be modified after adding to composite operator 921 // Need to verify no lvec incompatibility from any changes 922 CeedSize input_size, output_size; 923 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 924 } 925 } else { 926 CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set"); 927 CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set"); 928 CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required"); 929 CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE, 930 "At least one non-collocated basis is required or the number of quadrature points must be set"); 931 } 932 933 // Flag as immutable and ready 934 op->is_interface_setup = true; 935 if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true; 936 if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true; 937 if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true; 938 return CEED_ERROR_SUCCESS; 939 } 940 941 /** 942 @brief Get vector lengths for the active input and/or output vectors of a CeedOperator. 943 944 Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output. 945 946 @param[in] op CeedOperator 947 @param[out] input_size Variable to store active input vector length, or NULL 948 @param[out] output_size Variable to store active output vector length, or NULL 949 950 @return An error code: 0 - success, otherwise - failure 951 952 @ref User 953 **/ 954 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) { 955 bool is_composite; 956 957 if (input_size) *input_size = op->input_size; 958 if (output_size) *output_size = op->output_size; 959 960 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 961 if (is_composite && (op->input_size == -1 || op->output_size == -1)) { 962 for (CeedInt i = 0; i < op->num_suboperators; i++) { 963 CeedSize sub_input_size, sub_output_size; 964 CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size)); 965 if (op->input_size == -1) op->input_size = sub_input_size; 966 if (op->output_size == -1) op->output_size = sub_output_size; 967 // Note, a size of -1 means no active vector restriction set, so no incompatibility 968 CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed, 969 CEED_ERROR_MAJOR, 970 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 971 "shape (%td, %td)", 972 op->input_size, op->output_size, input_size, output_size); 973 } 974 } 975 976 return CEED_ERROR_SUCCESS; 977 } 978 979 /** 980 @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions. 981 982 When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a 983 `CeedOperatorLinearAssemble*` function is called. 984 When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to 985 `CeedOperatorSetQFunctionAssemblyDataUpdated`. 986 987 @param[in] op CeedOperator 988 @param[in] reuse_assembly_data Boolean flag setting assembly data reuse 989 990 @return An error code: 0 - success, otherwise - failure 991 992 @ref Advanced 993 **/ 994 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) { 995 bool is_composite; 996 997 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 998 if (is_composite) { 999 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1000 CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data)); 1001 } 1002 } else { 1003 CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data)); 1004 } 1005 1006 return CEED_ERROR_SUCCESS; 1007 } 1008 1009 /** 1010 @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly. 1011 1012 @param[in] op CeedOperator 1013 @param[in] needs_data_update Boolean flag setting assembly data reuse 1014 1015 @return An error code: 0 - success, otherwise - failure 1016 1017 @ref Advanced 1018 **/ 1019 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) { 1020 bool is_composite; 1021 1022 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1023 if (is_composite) { 1024 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1025 CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update)); 1026 } 1027 } else { 1028 CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update)); 1029 } 1030 1031 return CEED_ERROR_SUCCESS; 1032 } 1033 1034 /** 1035 @brief Set the number of quadrature points associated with a CeedOperator. 1036 1037 This should be used when creating a CeedOperator where every field has a collocated basis. 1038 This function cannot be used for composite CeedOperators. 1039 1040 @param[in,out] op CeedOperator 1041 @param[in] num_qpts Number of quadrature points to set 1042 1043 @return An error code: 0 - success, otherwise - failure 1044 1045 @ref Advanced 1046 **/ 1047 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) { 1048 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1049 CeedCheck(op->num_qpts == 0, op->ceed, CEED_ERROR_MINOR, "Number of quadrature points already defined"); 1050 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 1051 op->num_qpts = num_qpts; 1052 return CEED_ERROR_SUCCESS; 1053 } 1054 1055 /** 1056 @brief Set name of CeedOperator for CeedOperatorView output 1057 1058 @param[in,out] op CeedOperator 1059 @param[in] name Name to set, or NULL to remove previously set name 1060 1061 @return An error code: 0 - success, otherwise - failure 1062 1063 @ref User 1064 **/ 1065 int CeedOperatorSetName(CeedOperator op, const char *name) { 1066 char *name_copy; 1067 size_t name_len = name ? strlen(name) : 0; 1068 1069 CeedCall(CeedFree(&op->name)); 1070 if (name_len > 0) { 1071 CeedCall(CeedCalloc(name_len + 1, &name_copy)); 1072 memcpy(name_copy, name, name_len); 1073 op->name = name_copy; 1074 } 1075 1076 return CEED_ERROR_SUCCESS; 1077 } 1078 1079 /** 1080 @brief View a CeedOperator 1081 1082 @param[in] op CeedOperator to view 1083 @param[in] stream Stream to write; typically stdout/stderr or a file 1084 1085 @return Error code: 0 - success, otherwise - failure 1086 1087 @ref User 1088 **/ 1089 int CeedOperatorView(CeedOperator op, FILE *stream) { 1090 bool has_name = op->name; 1091 1092 if (op->is_composite) { 1093 fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1094 1095 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1096 has_name = op->sub_operators[i]->name; 1097 fprintf(stream, " SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : ""); 1098 CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream)); 1099 } 1100 } else { 1101 fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1102 CeedCall(CeedOperatorSingleView(op, 0, stream)); 1103 } 1104 return CEED_ERROR_SUCCESS; 1105 } 1106 1107 /** 1108 @brief Get the Ceed associated with a CeedOperator 1109 1110 @param[in] op CeedOperator 1111 @param[out] ceed Variable to store Ceed 1112 1113 @return An error code: 0 - success, otherwise - failure 1114 1115 @ref Advanced 1116 **/ 1117 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) { 1118 *ceed = op->ceed; 1119 return CEED_ERROR_SUCCESS; 1120 } 1121 1122 /** 1123 @brief Get the number of elements associated with a CeedOperator 1124 1125 @param[in] op CeedOperator 1126 @param[out] num_elem Variable to store number of elements 1127 1128 @return An error code: 0 - success, otherwise - failure 1129 1130 @ref Advanced 1131 **/ 1132 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) { 1133 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1134 *num_elem = op->num_elem; 1135 return CEED_ERROR_SUCCESS; 1136 } 1137 1138 /** 1139 @brief Get the number of quadrature points associated with a CeedOperator 1140 1141 @param[in] op CeedOperator 1142 @param[out] num_qpts Variable to store vector number of quadrature points 1143 1144 @return An error code: 0 - success, otherwise - failure 1145 1146 @ref Advanced 1147 **/ 1148 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) { 1149 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1150 *num_qpts = op->num_qpts; 1151 return CEED_ERROR_SUCCESS; 1152 } 1153 1154 /** 1155 @brief Estimate number of FLOPs required to apply CeedOperator on the active vector 1156 1157 @param[in] op CeedOperator to estimate FLOPs for 1158 @param[out] flops Address of variable to hold FLOPs estimate 1159 1160 @ref Backend 1161 **/ 1162 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) { 1163 bool is_composite; 1164 CeedCall(CeedOperatorCheckReady(op)); 1165 1166 *flops = 0; 1167 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1168 if (is_composite) { 1169 CeedInt num_suboperators; 1170 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1171 CeedOperator *sub_operators; 1172 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1173 1174 // FLOPs for each suboperator 1175 for (CeedInt i = 0; i < num_suboperators; i++) { 1176 CeedSize suboperator_flops; 1177 CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops)); 1178 *flops += suboperator_flops; 1179 } 1180 } else { 1181 CeedInt num_input_fields, num_output_fields; 1182 CeedOperatorField *input_fields, *output_fields; 1183 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 1184 CeedInt num_elem = 0; 1185 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1186 1187 // Input FLOPs 1188 for (CeedInt i = 0; i < num_input_fields; i++) { 1189 if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1190 CeedSize restr_flops, basis_flops; 1191 1192 CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops)); 1193 *flops += restr_flops; 1194 CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops)); 1195 *flops += basis_flops * num_elem; 1196 } 1197 } 1198 // QF FLOPs 1199 CeedInt num_qpts; 1200 CeedSize qf_flops; 1201 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 1202 CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops)); 1203 *flops += num_elem * num_qpts * qf_flops; 1204 // Output FLOPs 1205 for (CeedInt i = 0; i < num_output_fields; i++) { 1206 if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1207 CeedSize restr_flops, basis_flops; 1208 1209 CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops)); 1210 *flops += restr_flops; 1211 CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops)); 1212 *flops += basis_flops * num_elem; 1213 } 1214 } 1215 } 1216 1217 return CEED_ERROR_SUCCESS; 1218 } 1219 1220 /** 1221 @brief Get CeedQFunction global context for a CeedOperator. 1222 1223 The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`. 1224 1225 Note: If the value of `ctx` passed into this function is non-NULL, then it is assumed that `ctx` is a pointer to a CeedQFunctionContext. 1226 This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext. 1227 1228 @param[in] op CeedOperator 1229 @param[out] ctx Variable to store CeedQFunctionContext 1230 1231 @return An error code: 0 - success, otherwise - failure 1232 1233 @ref Advanced 1234 **/ 1235 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) { 1236 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator"); 1237 if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx)); 1238 else *ctx = NULL; 1239 return CEED_ERROR_SUCCESS; 1240 } 1241 1242 /** 1243 @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`. 1244 1245 Fields are registered via `CeedQFunctionContextRegister*()` functions (eg. `CeedQFunctionContextRegisterDouble()`). 1246 1247 @param[in] op CeedOperator 1248 @param[in] field_name Name of field to retrieve label 1249 @param[out] field_label Variable to field label 1250 1251 @return An error code: 0 - success, otherwise - failure 1252 1253 @ref User 1254 **/ 1255 int CeedOperatorGetContextFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) { 1256 bool is_composite; 1257 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1258 1259 if (is_composite) { 1260 // Check if composite label already created 1261 for (CeedInt i = 0; i < op->num_context_labels; i++) { 1262 if (!strcmp(op->context_labels[i]->name, field_name)) { 1263 *field_label = op->context_labels[i]; 1264 return CEED_ERROR_SUCCESS; 1265 } 1266 } 1267 1268 // Create composite label if needed 1269 CeedInt num_sub; 1270 CeedOperator *sub_operators; 1271 CeedContextFieldLabel new_field_label; 1272 1273 CeedCall(CeedCalloc(1, &new_field_label)); 1274 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 1275 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1276 CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels)); 1277 new_field_label->num_sub_labels = num_sub; 1278 1279 bool label_found = false; 1280 for (CeedInt i = 0; i < num_sub; i++) { 1281 if (sub_operators[i]->qf->ctx) { 1282 CeedContextFieldLabel new_field_label_i; 1283 CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i)); 1284 if (new_field_label_i) { 1285 label_found = true; 1286 new_field_label->sub_labels[i] = new_field_label_i; 1287 new_field_label->name = new_field_label_i->name; 1288 new_field_label->description = new_field_label_i->description; 1289 if (new_field_label->type && new_field_label->type != new_field_label_i->type) { 1290 // LCOV_EXCL_START 1291 CeedCall(CeedFree(&new_field_label)); 1292 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s", 1293 CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]); 1294 // LCOV_EXCL_STOP 1295 } else { 1296 new_field_label->type = new_field_label_i->type; 1297 } 1298 if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) { 1299 // LCOV_EXCL_START 1300 CeedCall(CeedFree(&new_field_label)); 1301 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld", 1302 new_field_label->num_values, new_field_label_i->num_values); 1303 // LCOV_EXCL_STOP 1304 } else { 1305 new_field_label->num_values = new_field_label_i->num_values; 1306 } 1307 } 1308 } 1309 } 1310 if (!label_found) { 1311 // LCOV_EXCL_START 1312 CeedCall(CeedFree(&new_field_label->sub_labels)); 1313 CeedCall(CeedFree(&new_field_label)); 1314 *field_label = NULL; 1315 // LCOV_EXCL_STOP 1316 } else { 1317 *field_label = new_field_label; 1318 } 1319 } else { 1320 CeedCall(CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label)); 1321 } 1322 1323 // Set label in operator 1324 if (*field_label) { 1325 (*field_label)->from_op = true; 1326 1327 // Move new composite label to operator 1328 if (op->num_context_labels == 0) { 1329 CeedCall(CeedCalloc(1, &op->context_labels)); 1330 op->max_context_labels = 1; 1331 } else if (op->num_context_labels == op->max_context_labels) { 1332 CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels)); 1333 op->max_context_labels *= 2; 1334 } 1335 op->context_labels[op->num_context_labels] = *field_label; 1336 op->num_context_labels++; 1337 } 1338 1339 return CEED_ERROR_SUCCESS; 1340 } 1341 1342 /** 1343 @brief Set QFunctionContext field holding double precision values. 1344 1345 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1346 1347 @param[in,out] op CeedOperator 1348 @param[in] field_label Label of field to set 1349 @param[in] values Values to set 1350 1351 @return An error code: 0 - success, otherwise - failure 1352 1353 @ref User 1354 **/ 1355 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) { 1356 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1357 } 1358 1359 /** 1360 @brief Get QFunctionContext field holding double precision values, read-only. 1361 1362 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1363 1364 @param[in] op CeedOperator 1365 @param[in] field_label Label of field to get 1366 @param[out] num_values Number of values in the field label 1367 @param[out] values Pointer to context values 1368 1369 @return An error code: 0 - success, otherwise - failure 1370 1371 @ref User 1372 **/ 1373 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) { 1374 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values); 1375 } 1376 1377 /** 1378 @brief Restore QFunctionContext field holding double precision values, read-only. 1379 1380 @param[in] op CeedOperator 1381 @param[in] field_label Label of field to restore 1382 @param[out] values Pointer to context values 1383 1384 @return An error code: 0 - success, otherwise - failure 1385 1386 @ref User 1387 **/ 1388 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) { 1389 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1390 } 1391 1392 /** 1393 @brief Set QFunctionContext field holding int32 values. 1394 1395 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1396 1397 @param[in,out] op CeedOperator 1398 @param[in] field_label Label of field to set 1399 @param[in] values Values to set 1400 1401 @return An error code: 0 - success, otherwise - failure 1402 1403 @ref User 1404 **/ 1405 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) { 1406 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1407 } 1408 1409 /** 1410 @brief Get QFunctionContext field holding int32 values, read-only. 1411 1412 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1413 1414 @param[in] op CeedOperator 1415 @param[in] field_label Label of field to get 1416 @param[out] num_values Number of int32 values in `values` 1417 @param[out] values Pointer to context values 1418 1419 @return An error code: 0 - success, otherwise - failure 1420 1421 @ref User 1422 **/ 1423 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) { 1424 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values); 1425 } 1426 1427 /** 1428 @brief Restore QFunctionContext field holding int32 values, read-only. 1429 1430 @param[in] op CeedOperator 1431 @param[in] field_label Label of field to get 1432 @param[out] values Pointer to context values 1433 1434 @return An error code: 0 - success, otherwise - failure 1435 1436 @ref User 1437 **/ 1438 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) { 1439 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1440 } 1441 1442 /** 1443 @brief Apply CeedOperator to a vector 1444 1445 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1446 All inputs and outputs must be specified using CeedOperatorSetField(). 1447 1448 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1449 1450 @param[in] op CeedOperator to apply 1451 @param[in] in CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs 1452 @param[out] out CeedVector to store result of applying operator (must be distinct from @a in) or @ref CEED_VECTOR_NONE if there are no 1453 active outputs 1454 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1455 1456 @return An error code: 0 - success, otherwise - failure 1457 1458 @ref User 1459 **/ 1460 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1461 CeedCall(CeedOperatorCheckReady(op)); 1462 1463 if (op->is_composite) { 1464 // Composite Operator 1465 if (op->ApplyComposite) { 1466 CeedCall(op->ApplyComposite(op, in, out, request)); 1467 } else { 1468 CeedInt num_suboperators; 1469 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1470 CeedOperator *sub_operators; 1471 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1472 1473 // Zero all output vectors 1474 if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0)); 1475 for (CeedInt i = 0; i < num_suboperators; i++) { 1476 for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) { 1477 CeedVector vec = sub_operators[i]->output_fields[j]->vec; 1478 if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) { 1479 CeedCall(CeedVectorSetValue(vec, 0.0)); 1480 } 1481 } 1482 } 1483 // Apply 1484 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1485 CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request)); 1486 } 1487 } 1488 } else { 1489 // Standard Operator 1490 if (op->Apply) { 1491 CeedCall(op->Apply(op, in, out, request)); 1492 } else { 1493 // Zero all output vectors 1494 CeedQFunction qf = op->qf; 1495 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 1496 CeedVector vec = op->output_fields[i]->vec; 1497 if (vec == CEED_VECTOR_ACTIVE) vec = out; 1498 if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0)); 1499 } 1500 // Apply 1501 if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request)); 1502 } 1503 } 1504 return CEED_ERROR_SUCCESS; 1505 } 1506 1507 /** 1508 @brief Apply CeedOperator to a vector and add result to output vector 1509 1510 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1511 All inputs and outputs must be specified using CeedOperatorSetField(). 1512 1513 @param[in] op CeedOperator to apply 1514 @param[in] in CeedVector containing input state or NULL if there are no active inputs 1515 @param[out] out CeedVector to sum in result of applying operator (must be distinct from @a in) or NULL if there are no active outputs 1516 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1517 1518 @return An error code: 0 - success, otherwise - failure 1519 1520 @ref User 1521 **/ 1522 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1523 CeedCall(CeedOperatorCheckReady(op)); 1524 1525 if (op->is_composite) { 1526 // Composite Operator 1527 if (op->ApplyAddComposite) { 1528 CeedCall(op->ApplyAddComposite(op, in, out, request)); 1529 } else { 1530 CeedInt num_suboperators; 1531 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1532 CeedOperator *sub_operators; 1533 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1534 1535 for (CeedInt i = 0; i < num_suboperators; i++) { 1536 CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request)); 1537 } 1538 } 1539 } else if (op->num_elem) { 1540 // Standard Operator 1541 CeedCall(op->ApplyAdd(op, in, out, request)); 1542 } 1543 return CEED_ERROR_SUCCESS; 1544 } 1545 1546 /** 1547 @brief Destroy a CeedOperator 1548 1549 @param[in,out] op CeedOperator to destroy 1550 1551 @return An error code: 0 - success, otherwise - failure 1552 1553 @ref User 1554 **/ 1555 int CeedOperatorDestroy(CeedOperator *op) { 1556 if (!*op || --(*op)->ref_count > 0) { 1557 *op = NULL; 1558 return CEED_ERROR_SUCCESS; 1559 } 1560 if ((*op)->Destroy) CeedCall((*op)->Destroy(*op)); 1561 CeedCall(CeedDestroy(&(*op)->ceed)); 1562 // Free fields 1563 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1564 if ((*op)->input_fields[i]) { 1565 if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) { 1566 CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr)); 1567 } 1568 if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1569 CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis)); 1570 } 1571 if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) { 1572 CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec)); 1573 } 1574 CeedCall(CeedFree(&(*op)->input_fields[i]->field_name)); 1575 CeedCall(CeedFree(&(*op)->input_fields[i])); 1576 } 1577 } 1578 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1579 if ((*op)->output_fields[i]) { 1580 CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr)); 1581 if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1582 CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis)); 1583 } 1584 if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) { 1585 CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec)); 1586 } 1587 CeedCall(CeedFree(&(*op)->output_fields[i]->field_name)); 1588 CeedCall(CeedFree(&(*op)->output_fields[i])); 1589 } 1590 } 1591 // Destroy sub_operators 1592 for (CeedInt i = 0; i < (*op)->num_suboperators; i++) { 1593 if ((*op)->sub_operators[i]) { 1594 CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i])); 1595 } 1596 } 1597 CeedCall(CeedQFunctionDestroy(&(*op)->qf)); 1598 CeedCall(CeedQFunctionDestroy(&(*op)->dqf)); 1599 CeedCall(CeedQFunctionDestroy(&(*op)->dqfT)); 1600 // Destroy any composite labels 1601 if ((*op)->is_composite) { 1602 for (CeedInt i = 0; i < (*op)->num_context_labels; i++) { 1603 CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels)); 1604 CeedCall(CeedFree(&(*op)->context_labels[i])); 1605 } 1606 } 1607 CeedCall(CeedFree(&(*op)->context_labels)); 1608 1609 // Destroy fallback 1610 CeedCall(CeedOperatorDestroy(&(*op)->op_fallback)); 1611 1612 // Destroy assembly data 1613 CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled)); 1614 CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled)); 1615 1616 CeedCall(CeedFree(&(*op)->input_fields)); 1617 CeedCall(CeedFree(&(*op)->output_fields)); 1618 CeedCall(CeedFree(&(*op)->sub_operators)); 1619 CeedCall(CeedFree(&(*op)->name)); 1620 CeedCall(CeedFree(op)); 1621 return CEED_ERROR_SUCCESS; 1622 } 1623 1624 /// @} 1625