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 CeedCall(CeedCalloc(1, op)); 539 (*op)->ceed = ceed; 540 CeedCall(CeedReference(ceed)); 541 (*op)->ref_count = 1; 542 (*op)->qf = qf; 543 (*op)->input_size = -1; 544 (*op)->output_size = -1; 545 CeedCall(CeedQFunctionReference(qf)); 546 if (dqf && dqf != CEED_QFUNCTION_NONE) { 547 (*op)->dqf = dqf; 548 CeedCall(CeedQFunctionReference(dqf)); 549 } 550 if (dqfT && dqfT != CEED_QFUNCTION_NONE) { 551 (*op)->dqfT = dqfT; 552 CeedCall(CeedQFunctionReference(dqfT)); 553 } 554 CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled)); 555 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields)); 556 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields)); 557 CeedCall(ceed->OperatorCreate(*op)); 558 return CEED_ERROR_SUCCESS; 559 } 560 561 /** 562 @brief Create an operator that composes the action of several operators 563 564 @param[in] ceed Ceed object where the CeedOperator will be created 565 @param[out] op Address of the variable where the newly created Composite CeedOperator will be stored 566 567 @return An error code: 0 - success, otherwise - failure 568 569 @ref User 570 */ 571 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) { 572 if (!ceed->CompositeOperatorCreate) { 573 Ceed delegate; 574 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator")); 575 576 if (delegate) { 577 CeedCall(CeedCompositeOperatorCreate(delegate, op)); 578 return CEED_ERROR_SUCCESS; 579 } 580 } 581 582 CeedCall(CeedCalloc(1, op)); 583 (*op)->ceed = ceed; 584 CeedCall(CeedReference(ceed)); 585 (*op)->ref_count = 1; 586 (*op)->is_composite = true; 587 CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators)); 588 (*op)->input_size = -1; 589 (*op)->output_size = -1; 590 591 if (ceed->CompositeOperatorCreate) { 592 CeedCall(ceed->CompositeOperatorCreate(*op)); 593 } 594 return CEED_ERROR_SUCCESS; 595 } 596 597 /** 598 @brief Copy the pointer to a CeedOperator. 599 600 Both pointers should be destroyed with `CeedOperatorDestroy()`. 601 602 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. 603 This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator. 604 605 @param[in] op CeedOperator to copy reference to 606 @param[in,out] op_copy Variable to store copied reference 607 608 @return An error code: 0 - success, otherwise - failure 609 610 @ref User 611 **/ 612 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) { 613 CeedCall(CeedOperatorReference(op)); 614 CeedCall(CeedOperatorDestroy(op_copy)); 615 *op_copy = op; 616 return CEED_ERROR_SUCCESS; 617 } 618 619 /** 620 @brief Provide a field to a CeedOperator for use by its CeedQFunction. 621 622 This function is used to specify both active and passive fields to a CeedOperator. 623 For passive fields, a vector @arg v must be provided. 624 Passive fields can inputs or outputs (updated in-place when operator is applied). 625 626 Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply(). 627 There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply(). 628 629 @param[in,out] op CeedOperator on which to provide the field 630 @param[in] field_name Name of the field (to be matched with the name used by CeedQFunction) 631 @param[in] r CeedElemRestriction 632 @param[in] b CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points 633 @param[in] v CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE 634 if using @ref CEED_EVAL_WEIGHT in the QFunction 635 636 @return An error code: 0 - success, otherwise - failure 637 638 @ref User 639 **/ 640 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) { 641 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator."); 642 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 643 CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name); 644 CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name); 645 CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name); 646 647 CeedInt num_elem = 0; 648 CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem)); 649 CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION, 650 "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem); 651 652 CeedInt num_qpts = 0; 653 CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts)); 654 CeedCheck(b == CEED_BASIS_COLLOCATED || !op->num_qpts || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION, 655 "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts); 656 CeedQFunctionField qf_field; 657 CeedOperatorField *op_field; 658 bool is_input = true; 659 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 660 if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) { 661 qf_field = op->qf->input_fields[i]; 662 op_field = &op->input_fields[i]; 663 goto found; 664 } 665 } 666 is_input = false; 667 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 668 if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) { 669 qf_field = op->qf->output_fields[i]; 670 op_field = &op->output_fields[i]; 671 goto found; 672 } 673 } 674 // LCOV_EXCL_START 675 return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name); 676 // LCOV_EXCL_STOP 677 found: 678 CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b)); 679 CeedCall(CeedCalloc(1, op_field)); 680 681 if (v == CEED_VECTOR_ACTIVE) { 682 CeedSize l_size; 683 CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size)); 684 if (is_input) { 685 if (op->input_size == -1) op->input_size = l_size; 686 CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 687 op->input_size); 688 } else { 689 if (op->output_size == -1) op->output_size = l_size; 690 CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 691 op->output_size); 692 } 693 } 694 695 CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec)); 696 CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr)); 697 if (r != CEED_ELEMRESTRICTION_NONE) { 698 op->num_elem = num_elem; 699 op->has_restriction = true; // Restriction set, but num_elem may be 0 700 } 701 CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis)); 702 if (!op->num_qpts && b != CEED_BASIS_COLLOCATED) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts)); 703 704 op->num_fields += 1; 705 CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name)); 706 return CEED_ERROR_SUCCESS; 707 } 708 709 /** 710 @brief Get the CeedOperatorFields of a CeedOperator 711 712 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 713 714 @param[in] op CeedOperator 715 @param[out] num_input_fields Variable to store number of input fields 716 @param[out] input_fields Variable to store input_fields 717 @param[out] num_output_fields Variable to store number of output fields 718 @param[out] output_fields Variable to store output_fields 719 720 @return An error code: 0 - success, otherwise - failure 721 722 @ref Advanced 723 **/ 724 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields, 725 CeedOperatorField **output_fields) { 726 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 727 CeedCall(CeedOperatorCheckReady(op)); 728 729 if (num_input_fields) *num_input_fields = op->qf->num_input_fields; 730 if (input_fields) *input_fields = op->input_fields; 731 if (num_output_fields) *num_output_fields = op->qf->num_output_fields; 732 if (output_fields) *output_fields = op->output_fields; 733 return CEED_ERROR_SUCCESS; 734 } 735 736 /** 737 @brief Get a CeedOperatorField of an CeedOperator from its name 738 739 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 740 741 @param[in] op CeedOperator 742 @param[in] field_name Name of desired CeedOperatorField 743 @param[out] op_field CeedOperatorField corresponding to the name 744 745 @return An error code: 0 - success, otherwise - failure 746 747 @ref Advanced 748 **/ 749 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) { 750 CeedInt num_input_fields, num_output_fields; 751 CeedOperatorField *input_fields, *output_fields; 752 char *name; 753 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 754 755 for (CeedInt i = 0; i < num_input_fields; i++) { 756 CeedCall(CeedOperatorFieldGetName(input_fields[i], &name)); 757 if (!strcmp(name, field_name)) { 758 *op_field = input_fields[i]; 759 return CEED_ERROR_SUCCESS; 760 } 761 } 762 763 for (CeedInt i = 0; i < num_output_fields; i++) { 764 CeedCall(CeedOperatorFieldGetName(output_fields[i], &name)); 765 if (!strcmp(name, field_name)) { 766 *op_field = output_fields[i]; 767 return CEED_ERROR_SUCCESS; 768 } 769 } 770 771 bool has_name = op->name; 772 // LCOV_EXCL_START 773 return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "", 774 has_name ? op->name : "", has_name ? "\"" : ""); 775 // LCOV_EXCL_STOP 776 } 777 778 /** 779 @brief Get the name of a CeedOperatorField 780 781 @param[in] op_field CeedOperatorField 782 @param[out] field_name Variable to store the field name 783 784 @return An error code: 0 - success, otherwise - failure 785 786 @ref Advanced 787 **/ 788 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) { 789 *field_name = (char *)op_field->field_name; 790 return CEED_ERROR_SUCCESS; 791 } 792 793 /** 794 @brief Get the CeedElemRestriction of a CeedOperatorField 795 796 @param[in] op_field CeedOperatorField 797 @param[out] rstr Variable to store CeedElemRestriction 798 799 @return An error code: 0 - success, otherwise - failure 800 801 @ref Advanced 802 **/ 803 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) { 804 *rstr = op_field->elem_rstr; 805 return CEED_ERROR_SUCCESS; 806 } 807 808 /** 809 @brief Get the CeedBasis of a CeedOperatorField 810 811 @param[in] op_field CeedOperatorField 812 @param[out] basis Variable to store CeedBasis 813 814 @return An error code: 0 - success, otherwise - failure 815 816 @ref Advanced 817 **/ 818 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) { 819 *basis = op_field->basis; 820 return CEED_ERROR_SUCCESS; 821 } 822 823 /** 824 @brief Get the CeedVector of a CeedOperatorField 825 826 @param[in] op_field CeedOperatorField 827 @param[out] vec Variable to store CeedVector 828 829 @return An error code: 0 - success, otherwise - failure 830 831 @ref Advanced 832 **/ 833 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) { 834 *vec = op_field->vec; 835 return CEED_ERROR_SUCCESS; 836 } 837 838 /** 839 @brief Add a sub-operator to a composite CeedOperator 840 841 @param[in,out] composite_op Composite CeedOperator 842 @param[in] sub_op Sub-operator CeedOperator 843 844 @return An error code: 0 - success, otherwise - failure 845 846 @ref User 847 */ 848 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) { 849 CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator"); 850 CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators"); 851 CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 852 853 { 854 CeedSize input_size, output_size; 855 CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size)); 856 if (composite_op->input_size == -1) composite_op->input_size = input_size; 857 if (composite_op->output_size == -1) composite_op->output_size = output_size; 858 // Note, a size of -1 means no active vector restriction set, so no incompatibility 859 CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size), 860 composite_op->ceed, CEED_ERROR_MAJOR, 861 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 862 "shape (%td, %td)", 863 composite_op->input_size, composite_op->output_size, input_size, output_size); 864 } 865 866 composite_op->sub_operators[composite_op->num_suboperators] = sub_op; 867 CeedCall(CeedOperatorReference(sub_op)); 868 composite_op->num_suboperators++; 869 return CEED_ERROR_SUCCESS; 870 } 871 872 /** 873 @brief Get the number of sub_operators associated with a CeedOperator 874 875 @param[in] op CeedOperator 876 @param[out] num_suboperators Variable to store number of sub_operators 877 878 @return An error code: 0 - success, otherwise - failure 879 880 @ref Backend 881 **/ 882 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) { 883 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 884 *num_suboperators = op->num_suboperators; 885 return CEED_ERROR_SUCCESS; 886 } 887 888 /** 889 @brief Get the list of sub_operators associated with a CeedOperator 890 891 @param op CeedOperator 892 @param[out] sub_operators Variable to store list of sub_operators 893 894 @return An error code: 0 - success, otherwise - failure 895 896 @ref Backend 897 **/ 898 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) { 899 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 900 *sub_operators = op->sub_operators; 901 return CEED_ERROR_SUCCESS; 902 } 903 904 /** 905 @brief Check if a CeedOperator is ready to be used. 906 907 @param[in] op CeedOperator to check 908 909 @return An error code: 0 - success, otherwise - failure 910 911 @ref User 912 **/ 913 int CeedOperatorCheckReady(CeedOperator op) { 914 Ceed ceed; 915 CeedCall(CeedOperatorGetCeed(op, &ceed)); 916 917 if (op->is_interface_setup) return CEED_ERROR_SUCCESS; 918 919 CeedQFunction qf = op->qf; 920 if (op->is_composite) { 921 if (!op->num_suboperators) { 922 // Empty operator setup 923 op->input_size = 0; 924 op->output_size = 0; 925 } else { 926 for (CeedInt i = 0; i < op->num_suboperators; i++) { 927 CeedCall(CeedOperatorCheckReady(op->sub_operators[i])); 928 } 929 // Sub-operators could be modified after adding to composite operator 930 // Need to verify no lvec incompatibility from any changes 931 CeedSize input_size, output_size; 932 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 933 } 934 } else { 935 CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set"); 936 CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set"); 937 CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required"); 938 CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE, 939 "At least one non-collocated basis is required or the number of quadrature points must be set"); 940 } 941 942 // Flag as immutable and ready 943 op->is_interface_setup = true; 944 if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true; 945 if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true; 946 if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true; 947 return CEED_ERROR_SUCCESS; 948 } 949 950 /** 951 @brief Get vector lengths for the active input and/or output vectors of a CeedOperator. 952 953 Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output. 954 955 @param[in] op CeedOperator 956 @param[out] input_size Variable to store active input vector length, or NULL 957 @param[out] output_size Variable to store active output vector length, or NULL 958 959 @return An error code: 0 - success, otherwise - failure 960 961 @ref User 962 **/ 963 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) { 964 bool is_composite; 965 966 if (input_size) *input_size = op->input_size; 967 if (output_size) *output_size = op->output_size; 968 969 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 970 if (is_composite && (op->input_size == -1 || op->output_size == -1)) { 971 for (CeedInt i = 0; i < op->num_suboperators; i++) { 972 CeedSize sub_input_size, sub_output_size; 973 CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size)); 974 if (op->input_size == -1) op->input_size = sub_input_size; 975 if (op->output_size == -1) op->output_size = sub_output_size; 976 // Note, a size of -1 means no active vector restriction set, so no incompatibility 977 CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed, 978 CEED_ERROR_MAJOR, 979 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 980 "shape (%td, %td)", 981 op->input_size, op->output_size, input_size, output_size); 982 } 983 } 984 985 return CEED_ERROR_SUCCESS; 986 } 987 988 /** 989 @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions. 990 991 When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a 992 `CeedOperatorLinearAssemble*` function is called. 993 When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to 994 `CeedOperatorSetQFunctionAssemblyDataUpdated`. 995 996 @param[in] op CeedOperator 997 @param[in] reuse_assembly_data Boolean flag setting assembly data reuse 998 999 @return An error code: 0 - success, otherwise - failure 1000 1001 @ref Advanced 1002 **/ 1003 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) { 1004 bool is_composite; 1005 1006 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1007 if (is_composite) { 1008 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1009 CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data)); 1010 } 1011 } else { 1012 CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data)); 1013 } 1014 1015 return CEED_ERROR_SUCCESS; 1016 } 1017 1018 /** 1019 @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly. 1020 1021 @param[in] op CeedOperator 1022 @param[in] needs_data_update Boolean flag setting assembly data reuse 1023 1024 @return An error code: 0 - success, otherwise - failure 1025 1026 @ref Advanced 1027 **/ 1028 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) { 1029 bool is_composite; 1030 1031 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1032 if (is_composite) { 1033 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1034 CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update)); 1035 } 1036 } else { 1037 CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update)); 1038 } 1039 1040 return CEED_ERROR_SUCCESS; 1041 } 1042 1043 /** 1044 @brief Set the number of quadrature points associated with a CeedOperator. 1045 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 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 1193 CeedInt num_elem = 0; 1194 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1195 1196 // Input FLOPs 1197 for (CeedInt i = 0; i < num_input_fields; i++) { 1198 if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1199 CeedSize restr_flops, basis_flops; 1200 1201 CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops)); 1202 *flops += restr_flops; 1203 CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops)); 1204 *flops += basis_flops * num_elem; 1205 } 1206 } 1207 // QF FLOPs 1208 CeedInt num_qpts; 1209 CeedSize qf_flops; 1210 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 1211 CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops)); 1212 *flops += num_elem * num_qpts * qf_flops; 1213 // Output FLOPs 1214 for (CeedInt i = 0; i < num_output_fields; i++) { 1215 if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1216 CeedSize restr_flops, basis_flops; 1217 1218 CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops)); 1219 *flops += restr_flops; 1220 CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops)); 1221 *flops += basis_flops * num_elem; 1222 } 1223 } 1224 } 1225 1226 return CEED_ERROR_SUCCESS; 1227 } 1228 1229 /** 1230 @brief Get CeedQFunction global context for a CeedOperator. 1231 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 CeedQFunctionContext. 1235 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 1354 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1355 1356 @param[in,out] op CeedOperator 1357 @param[in] field_label Label of field to set 1358 @param[in] values Values to set 1359 1360 @return An error code: 0 - success, otherwise - failure 1361 1362 @ref User 1363 **/ 1364 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) { 1365 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1366 } 1367 1368 /** 1369 @brief Get QFunctionContext field holding double precision values, read-only. 1370 1371 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1372 1373 @param[in] op CeedOperator 1374 @param[in] field_label Label of field to get 1375 @param[out] num_values Number of values in the field label 1376 @param[out] values Pointer to context values 1377 1378 @return An error code: 0 - success, otherwise - failure 1379 1380 @ref User 1381 **/ 1382 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) { 1383 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values); 1384 } 1385 1386 /** 1387 @brief Restore QFunctionContext field holding double precision values, read-only. 1388 1389 @param[in] op CeedOperator 1390 @param[in] field_label Label of field to restore 1391 @param[out] values Pointer to context values 1392 1393 @return An error code: 0 - success, otherwise - failure 1394 1395 @ref User 1396 **/ 1397 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) { 1398 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1399 } 1400 1401 /** 1402 @brief Set QFunctionContext field holding int32 values. 1403 1404 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1405 1406 @param[in,out] op CeedOperator 1407 @param[in] field_label Label of field to set 1408 @param[in] values Values to set 1409 1410 @return An error code: 0 - success, otherwise - failure 1411 1412 @ref User 1413 **/ 1414 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) { 1415 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1416 } 1417 1418 /** 1419 @brief Get QFunctionContext field holding int32 values, read-only. 1420 1421 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1422 1423 @param[in] op CeedOperator 1424 @param[in] field_label Label of field to get 1425 @param[out] num_values Number of int32 values in `values` 1426 @param[out] values Pointer to context values 1427 1428 @return An error code: 0 - success, otherwise - failure 1429 1430 @ref User 1431 **/ 1432 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) { 1433 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values); 1434 } 1435 1436 /** 1437 @brief Restore QFunctionContext field holding int32 values, read-only. 1438 1439 @param[in] op CeedOperator 1440 @param[in] field_label Label of field to get 1441 @param[out] values Pointer to context values 1442 1443 @return An error code: 0 - success, otherwise - failure 1444 1445 @ref User 1446 **/ 1447 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) { 1448 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1449 } 1450 1451 /** 1452 @brief Apply CeedOperator to a vector 1453 1454 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1455 All inputs and outputs must be specified using CeedOperatorSetField(). 1456 1457 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1458 1459 @param[in] op CeedOperator to apply 1460 @param[in] in CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs 1461 @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 1462 active outputs 1463 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1464 1465 @return An error code: 0 - success, otherwise - failure 1466 1467 @ref User 1468 **/ 1469 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1470 CeedCall(CeedOperatorCheckReady(op)); 1471 1472 if (op->is_composite) { 1473 // Composite Operator 1474 if (op->ApplyComposite) { 1475 CeedCall(op->ApplyComposite(op, in, out, request)); 1476 } else { 1477 CeedInt num_suboperators; 1478 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1479 CeedOperator *sub_operators; 1480 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1481 1482 // Zero all output vectors 1483 if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0)); 1484 for (CeedInt i = 0; i < num_suboperators; i++) { 1485 for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) { 1486 CeedVector vec = sub_operators[i]->output_fields[j]->vec; 1487 if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) { 1488 CeedCall(CeedVectorSetValue(vec, 0.0)); 1489 } 1490 } 1491 } 1492 // Apply 1493 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1494 CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request)); 1495 } 1496 } 1497 } else { 1498 // Standard Operator 1499 if (op->Apply) { 1500 CeedCall(op->Apply(op, in, out, request)); 1501 } else { 1502 // Zero all output vectors 1503 CeedQFunction qf = op->qf; 1504 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 1505 CeedVector vec = op->output_fields[i]->vec; 1506 if (vec == CEED_VECTOR_ACTIVE) vec = out; 1507 if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0)); 1508 } 1509 // Apply 1510 if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request)); 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->is_composite) { 1535 // Composite Operator 1536 if (op->ApplyAddComposite) { 1537 CeedCall(op->ApplyAddComposite(op, in, out, request)); 1538 } else { 1539 CeedInt num_suboperators; 1540 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1541 CeedOperator *sub_operators; 1542 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1543 1544 for (CeedInt i = 0; i < num_suboperators; i++) { 1545 CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request)); 1546 } 1547 } 1548 } else if (op->num_elem) { 1549 // Standard Operator 1550 CeedCall(op->ApplyAdd(op, in, out, request)); 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