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