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/backend.h> 10 #include <ceed/ceed.h> 11 #include <ceed/jit-tools.h> 12 #include <limits.h> 13 #include <stdbool.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 /// @file 19 /// Implementation of public CeedQFunction interfaces 20 21 /// @cond DOXYGEN_SKIP 22 static struct CeedQFunction_private ceed_qfunction_none; 23 /// @endcond 24 25 /// @addtogroup CeedQFunctionUser 26 /// @{ 27 28 // Indicate that no QFunction is provided by the user 29 const CeedQFunction CEED_QFUNCTION_NONE = &ceed_qfunction_none; 30 31 /// @} 32 33 /// @cond DOXYGEN_SKIP 34 static struct { 35 char name[CEED_MAX_RESOURCE_LEN]; 36 char source[CEED_MAX_RESOURCE_LEN]; 37 CeedInt vec_length; 38 CeedQFunctionUser f; 39 int (*init)(Ceed ceed, const char *name, CeedQFunction qf); 40 } gallery_qfunctions[1024]; 41 static size_t num_qfunctions; 42 /// @endcond 43 44 /// ---------------------------------------------------------------------------- 45 /// CeedQFunction Library Internal Functions 46 /// ---------------------------------------------------------------------------- 47 /// @addtogroup CeedQFunctionDeveloper 48 /// @{ 49 50 /** 51 @brief Register a gallery QFunction 52 53 @param[in] name Name for this backend to respond to 54 @param[in] source Absolute path to source of QFunction, "\path\CEED_DIR\gallery\folder\file.h:function_name" 55 @param[in] vec_length Vector length. Caller must ensure that number of quadrature points is a multiple of vec_length. 56 @param[in] f Function pointer to evaluate action at quadrature points. 57 See \ref CeedQFunctionUser. 58 @param[in] init Initialization function called by CeedQFunctionInit() when the QFunction is selected. 59 60 @return An error code: 0 - success, otherwise - failure 61 62 @ref Developer 63 **/ 64 int CeedQFunctionRegister(const char *name, const char *source, CeedInt vec_length, CeedQFunctionUser f, 65 int (*init)(Ceed, const char *, CeedQFunction)) { 66 if (num_qfunctions >= sizeof(gallery_qfunctions) / sizeof(gallery_qfunctions[0])) { 67 // LCOV_EXCL_START 68 return CeedError(NULL, CEED_ERROR_MAJOR, "Too many gallery QFunctions"); 69 // LCOV_EXCL_STOP 70 } 71 72 CeedDebugEnv("Gallery Register: %s", name); 73 74 const char *relative_file_path; 75 CeedCall(CeedGetJitRelativePath(source, &relative_file_path)); 76 77 strncpy(gallery_qfunctions[num_qfunctions].name, name, CEED_MAX_RESOURCE_LEN); 78 gallery_qfunctions[num_qfunctions].name[CEED_MAX_RESOURCE_LEN - 1] = 0; 79 strncpy(gallery_qfunctions[num_qfunctions].source, relative_file_path, CEED_MAX_RESOURCE_LEN); 80 gallery_qfunctions[num_qfunctions].source[CEED_MAX_RESOURCE_LEN - 1] = 0; 81 gallery_qfunctions[num_qfunctions].vec_length = vec_length; 82 gallery_qfunctions[num_qfunctions].f = f; 83 gallery_qfunctions[num_qfunctions].init = init; 84 num_qfunctions++; 85 return CEED_ERROR_SUCCESS; 86 } 87 88 /** 89 @brief Set a CeedQFunction field, used by CeedQFunctionAddInput/Output 90 91 @param[out] f CeedQFunctionField 92 @param[in] field_name Name of QFunction field 93 @param[in] size Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or (num_comp * 1) for @ref CEED_EVAL_NONE, @ref 94 CEED_EVAL_INTERP, and @ref CEED_EVAL_WEIGHT 95 @param[in] eval_mode \ref CEED_EVAL_NONE to use values directly, 96 \ref CEED_EVAL_INTERP to use interpolated values, 97 \ref CEED_EVAL_GRAD to use gradients, 98 \ref CEED_EVAL_WEIGHT to use quadrature weights. 99 100 @return An error code: 0 - success, otherwise - failure 101 102 @ref Developer 103 **/ 104 static int CeedQFunctionFieldSet(CeedQFunctionField *f, const char *field_name, CeedInt size, CeedEvalMode eval_mode) { 105 CeedCall(CeedCalloc(1, f)); 106 CeedCall(CeedStringAllocCopy(field_name, (char **)&(*f)->field_name)); 107 (*f)->size = size; 108 (*f)->eval_mode = eval_mode; 109 return CEED_ERROR_SUCCESS; 110 } 111 112 /** 113 @brief View a field of a CeedQFunction 114 115 @param[in] field QFunction field to view 116 @param[in] field_number Number of field being viewed 117 @param[in] in true for input field, false for output 118 @param[in] stream Stream to view to, e.g., stdout 119 120 @return An error code: 0 - success, otherwise - failure 121 122 @ref Utility 123 **/ 124 static int CeedQFunctionFieldView(CeedQFunctionField field, CeedInt field_number, bool in, FILE *stream) { 125 const char *inout = in ? "Input" : "Output"; 126 char *field_name; 127 CeedCall(CeedQFunctionFieldGetName(field, &field_name)); 128 CeedInt size; 129 CeedCall(CeedQFunctionFieldGetSize(field, &size)); 130 CeedEvalMode eval_mode; 131 CeedCall(CeedQFunctionFieldGetEvalMode(field, &eval_mode)); 132 fprintf(stream, 133 " %s field %" CeedInt_FMT 134 ":\n" 135 " Name: \"%s\"\n" 136 " Size: %" CeedInt_FMT 137 "\n" 138 " EvalMode: \"%s\"\n", 139 inout, field_number, field_name, size, CeedEvalModes[eval_mode]); 140 return CEED_ERROR_SUCCESS; 141 } 142 143 /** 144 @brief Set flag to determine if Fortran interface is used 145 146 @param[in,out] qf CeedQFunction 147 @param[in] status Boolean value to set as Fortran status 148 149 @return An error code: 0 - success, otherwise - failure 150 151 @ref Backend 152 **/ 153 int CeedQFunctionSetFortranStatus(CeedQFunction qf, bool status) { 154 qf->is_fortran = status; 155 return CEED_ERROR_SUCCESS; 156 } 157 158 /// @} 159 160 /// ---------------------------------------------------------------------------- 161 /// CeedQFunction Backend API 162 /// ---------------------------------------------------------------------------- 163 /// @addtogroup CeedQFunctionBackend 164 /// @{ 165 166 /** 167 @brief Get the vector length of a CeedQFunction 168 169 @param[in] qf CeedQFunction 170 @param[out] vec_length Variable to store vector length 171 172 @return An error code: 0 - success, otherwise - failure 173 174 @ref Backend 175 **/ 176 int CeedQFunctionGetVectorLength(CeedQFunction qf, CeedInt *vec_length) { 177 *vec_length = qf->vec_length; 178 return CEED_ERROR_SUCCESS; 179 } 180 181 /** 182 @brief Get the number of inputs and outputs to a CeedQFunction 183 184 @param[in] qf CeedQFunction 185 @param[out] num_input Variable to store number of input fields 186 @param[out] num_output Variable to store number of output fields 187 188 @return An error code: 0 - success, otherwise - failure 189 190 @ref Backend 191 **/ 192 int CeedQFunctionGetNumArgs(CeedQFunction qf, CeedInt *num_input, CeedInt *num_output) { 193 if (num_input) *num_input = qf->num_input_fields; 194 if (num_output) *num_output = qf->num_output_fields; 195 return CEED_ERROR_SUCCESS; 196 } 197 198 /** 199 @brief Get the name of the user function for a CeedQFunction 200 201 @param[in] qf CeedQFunction 202 @param[out] kernel_name Variable to store source path string 203 204 @return An error code: 0 - success, otherwise - failure 205 206 @ref Backend 207 **/ 208 int CeedQFunctionGetKernelName(CeedQFunction qf, char **kernel_name) { 209 if (!qf->kernel_name) { 210 Ceed ceed; 211 char *kernel_name_copy; 212 CeedCall(CeedQFunctionGetCeed(qf, &ceed)); 213 214 if (qf->user_source) { 215 const char *kernel_name = strrchr(qf->user_source, ':') + 1; 216 size_t kernel_name_len = strlen(kernel_name); 217 218 CeedCall(CeedCalloc(kernel_name_len + 1, &kernel_name_copy)); 219 memcpy(kernel_name_copy, kernel_name, kernel_name_len); 220 } else { 221 CeedCall(CeedCalloc(1, &kernel_name_copy)); 222 } 223 qf->kernel_name = kernel_name_copy; 224 } 225 226 *kernel_name = (char *)qf->kernel_name; 227 return CEED_ERROR_SUCCESS; 228 } 229 230 /** 231 @brief Get the source path string for a CeedQFunction 232 233 @param[in] qf CeedQFunction 234 @param[out] source_path Variable to store source path string 235 236 @return An error code: 0 - success, otherwise - failure 237 238 @ref Backend 239 **/ 240 int CeedQFunctionGetSourcePath(CeedQFunction qf, char **source_path) { 241 if (!qf->source_path && qf->user_source) { 242 Ceed ceed; 243 bool is_absolute_path; 244 char *absolute_path, *source_path_copy; 245 const char *kernel_name = strrchr(qf->user_source, ':') + 1; 246 size_t kernel_name_len = strlen(kernel_name); 247 248 CeedCall(CeedQFunctionGetCeed(qf, &ceed)); 249 250 CeedCall(CeedCheckFilePath(ceed, qf->user_source, &is_absolute_path)); 251 if (is_absolute_path) { 252 absolute_path = (char *)qf->user_source; 253 } else { 254 CeedCall(CeedGetJitAbsolutePath(ceed, qf->user_source, &absolute_path)); 255 } 256 257 size_t source_len = strlen(absolute_path) - kernel_name_len - 1; 258 CeedCall(CeedCalloc(source_len + 1, &source_path_copy)); 259 memcpy(source_path_copy, absolute_path, source_len); 260 qf->source_path = source_path_copy; 261 262 if (!is_absolute_path) CeedCall(CeedFree(&absolute_path)); 263 } 264 265 *source_path = (char *)qf->source_path; 266 return CEED_ERROR_SUCCESS; 267 } 268 269 /** 270 @brief Initialize and load QFunction source file into string buffer, including full text of local files in place of `#include "local.h"`. 271 The `buffer` is set to `NULL` if there is no QFunction source file. 272 Note: Caller is responsible for freeing the string buffer with `CeedFree()`. 273 274 @param[in] qf CeedQFunction 275 @param[out] source_buffer String buffer for source file contents 276 277 @return An error code: 0 - success, otherwise - failure 278 279 @ref Backend 280 **/ 281 int CeedQFunctionLoadSourceToBuffer(CeedQFunction qf, char **source_buffer) { 282 char *source_path; 283 284 CeedCall(CeedQFunctionGetSourcePath(qf, &source_path)); 285 *source_buffer = NULL; 286 if (source_path) { 287 CeedCall(CeedLoadSourceToBuffer(qf->ceed, source_path, source_buffer)); 288 } 289 290 return CEED_ERROR_SUCCESS; 291 } 292 293 /** 294 @brief Get the User Function for a CeedQFunction 295 296 @param[in] qf CeedQFunction 297 @param[out] f Variable to store user function 298 299 @return An error code: 0 - success, otherwise - failure 300 301 @ref Backend 302 **/ 303 int CeedQFunctionGetUserFunction(CeedQFunction qf, CeedQFunctionUser *f) { 304 *f = qf->function; 305 return CEED_ERROR_SUCCESS; 306 } 307 308 /** 309 @brief Get global context for a CeedQFunction. 310 Note: For QFunctions from the Fortran interface, this function will return the Fortran context CeedQFunctionContext. 311 312 @param[in] qf CeedQFunction 313 @param[out] ctx Variable to store CeedQFunctionContext 314 315 @return An error code: 0 - success, otherwise - failure 316 317 @ref Backend 318 **/ 319 int CeedQFunctionGetContext(CeedQFunction qf, CeedQFunctionContext *ctx) { 320 *ctx = qf->ctx; 321 return CEED_ERROR_SUCCESS; 322 } 323 324 /** 325 @brief Get context data of a CeedQFunction 326 327 @param[in] qf CeedQFunction 328 @param[in] mem_type Memory type on which to access the data. 329 If the backend uses a different memory type, this will perform a copy. 330 @param[out] data Data on memory type mem_type 331 332 @return An error code: 0 - success, otherwise - failure 333 334 @ref Backend 335 **/ 336 int CeedQFunctionGetContextData(CeedQFunction qf, CeedMemType mem_type, void *data) { 337 bool is_writable; 338 CeedQFunctionContext ctx; 339 340 CeedCall(CeedQFunctionGetContext(qf, &ctx)); 341 if (ctx) { 342 CeedCall(CeedQFunctionIsContextWritable(qf, &is_writable)); 343 if (is_writable) { 344 CeedCall(CeedQFunctionContextGetData(ctx, mem_type, data)); 345 } else { 346 CeedCall(CeedQFunctionContextGetDataRead(ctx, mem_type, data)); 347 } 348 } else { 349 *(void **)data = NULL; 350 } 351 return CEED_ERROR_SUCCESS; 352 } 353 354 /** 355 @brief Restore context data of a CeedQFunction 356 357 @param[in] qf CeedQFunction 358 @param[in,out] data Data to restore 359 360 @return An error code: 0 - success, otherwise - failure 361 362 @ref Backend 363 **/ 364 int CeedQFunctionRestoreContextData(CeedQFunction qf, void *data) { 365 bool is_writable; 366 CeedQFunctionContext ctx; 367 368 CeedCall(CeedQFunctionGetContext(qf, &ctx)); 369 if (ctx) { 370 CeedCall(CeedQFunctionIsContextWritable(qf, &is_writable)); 371 if (is_writable) { 372 CeedCall(CeedQFunctionContextRestoreData(ctx, data)); 373 } else { 374 CeedCall(CeedQFunctionContextRestoreDataRead(ctx, data)); 375 } 376 } 377 return CEED_ERROR_SUCCESS; 378 } 379 380 /** 381 @brief Get true user context for a CeedQFunction 382 Note: For all QFunctions this function will return the user CeedQFunctionContext and not interface context CeedQFunctionContext, if any 383 such object exists. 384 385 @param[in] qf CeedQFunction 386 @param[out] ctx Variable to store CeedQFunctionContext 387 388 @return An error code: 0 - success, otherwise - failure 389 @ref Backend 390 **/ 391 int CeedQFunctionGetInnerContext(CeedQFunction qf, CeedQFunctionContext *ctx) { 392 if (qf->is_fortran) { 393 CeedFortranContext fortran_ctx = NULL; 394 CeedCall(CeedQFunctionContextGetData(qf->ctx, CEED_MEM_HOST, &fortran_ctx)); 395 *ctx = fortran_ctx->inner_ctx; 396 CeedCall(CeedQFunctionContextRestoreData(qf->ctx, (void *)&fortran_ctx)); 397 } else { 398 *ctx = qf->ctx; 399 } 400 return CEED_ERROR_SUCCESS; 401 } 402 403 /** 404 @brief Get inner context data of a CeedQFunction 405 406 @param[in] qf CeedQFunction 407 @param[in] mem_type Memory type on which to access the data. 408 If the backend uses a different memory type, this will perform a copy. 409 @param[out] data Data on memory type mem_type 410 411 @return An error code: 0 - success, otherwise - failure 412 413 @ref Backend 414 **/ 415 int CeedQFunctionGetInnerContextData(CeedQFunction qf, CeedMemType mem_type, void *data) { 416 bool is_writable; 417 CeedQFunctionContext ctx; 418 419 CeedCall(CeedQFunctionGetInnerContext(qf, &ctx)); 420 if (ctx) { 421 CeedCall(CeedQFunctionIsContextWritable(qf, &is_writable)); 422 if (is_writable) { 423 CeedCall(CeedQFunctionContextGetData(ctx, mem_type, data)); 424 } else { 425 CeedCall(CeedQFunctionContextGetDataRead(ctx, mem_type, data)); 426 } 427 } else { 428 *(void **)data = NULL; 429 } 430 return CEED_ERROR_SUCCESS; 431 } 432 433 /** 434 @brief Restore inner context data of a CeedQFunction 435 436 @param[in] qf CeedQFunction 437 @param[in,out] data Data to restore 438 439 @return An error code: 0 - success, otherwise - failure 440 441 @ref Backend 442 **/ 443 int CeedQFunctionRestoreInnerContextData(CeedQFunction qf, void *data) { 444 bool is_writable; 445 CeedQFunctionContext ctx; 446 447 CeedCall(CeedQFunctionGetInnerContext(qf, &ctx)); 448 if (ctx) { 449 CeedCall(CeedQFunctionIsContextWritable(qf, &is_writable)); 450 if (is_writable) { 451 CeedCall(CeedQFunctionContextRestoreData(ctx, data)); 452 } else { 453 CeedCall(CeedQFunctionContextRestoreDataRead(ctx, data)); 454 } 455 } 456 return CEED_ERROR_SUCCESS; 457 } 458 459 /** 460 @brief Determine if QFunction is identity 461 462 @param[in] qf CeedQFunction 463 @param[out] is_identity Variable to store identity status 464 465 @return An error code: 0 - success, otherwise - failure 466 467 @ref Backend 468 **/ 469 int CeedQFunctionIsIdentity(CeedQFunction qf, bool *is_identity) { 470 *is_identity = qf->is_identity; 471 return CEED_ERROR_SUCCESS; 472 } 473 474 /** 475 @brief Determine if QFunctionContext is writable 476 477 @param[in] qf CeedQFunction 478 @param[out] is_writable Variable to store context writeable status 479 480 @return An error code: 0 - success, otherwise - failure 481 482 @ref Backend 483 **/ 484 int CeedQFunctionIsContextWritable(CeedQFunction qf, bool *is_writable) { 485 *is_writable = qf->is_context_writable; 486 return CEED_ERROR_SUCCESS; 487 } 488 489 /** 490 @brief Get backend data of a CeedQFunction 491 492 @param[in] qf CeedQFunction 493 @param[out] data Variable to store data 494 495 @return An error code: 0 - success, otherwise - failure 496 497 @ref Backend 498 **/ 499 int CeedQFunctionGetData(CeedQFunction qf, void *data) { 500 *(void **)data = qf->data; 501 return CEED_ERROR_SUCCESS; 502 } 503 504 /** 505 @brief Set backend data of a CeedQFunction 506 507 @param[in,out] qf CeedQFunction 508 @param[in] data Data to set 509 510 @return An error code: 0 - success, otherwise - failure 511 512 @ref Backend 513 **/ 514 int CeedQFunctionSetData(CeedQFunction qf, void *data) { 515 qf->data = data; 516 return CEED_ERROR_SUCCESS; 517 } 518 519 /** 520 @brief Increment the reference counter for a CeedQFunction 521 522 @param[in,out] qf CeedQFunction to increment the reference counter 523 524 @return An error code: 0 - success, otherwise - failure 525 526 @ref Backend 527 **/ 528 int CeedQFunctionReference(CeedQFunction qf) { 529 qf->ref_count++; 530 return CEED_ERROR_SUCCESS; 531 } 532 533 /** 534 @brief Estimate number of FLOPs per quadrature required to apply QFunction 535 536 @param[in] qf QFunction to estimate FLOPs for 537 @param[out] flops Address of variable to hold FLOPs estimate 538 539 @ref Backend 540 **/ 541 int CeedQFunctionGetFlopsEstimate(CeedQFunction qf, CeedSize *flops) { 542 if (qf->user_flop_estimate == -1) { 543 // LCOV_EXCL_START 544 return CeedError(qf->ceed, CEED_ERROR_INCOMPLETE, "Must set FLOPs estimate with CeedQFunctionSetUserFlopsEstimate"); 545 // LCOV_EXCL_STOP 546 } 547 *flops = qf->user_flop_estimate; 548 return CEED_ERROR_SUCCESS; 549 } 550 551 /// @} 552 553 /// ---------------------------------------------------------------------------- 554 /// CeedQFunction Public API 555 /// ---------------------------------------------------------------------------- 556 /// @addtogroup CeedQFunctionUser 557 /// @{ 558 559 /** 560 @brief Create a CeedQFunction for evaluating interior (volumetric) terms. 561 562 @param[in] ceed Ceed object where the CeedQFunction will be created 563 @param[in] vec_length Vector length. Caller must ensure that number of quadrature points is a multiple of vec_length. 564 @param[in] f Function pointer to evaluate action at quadrature points. 565 See \ref CeedQFunctionUser. 566 @param[in] source Absolute path to source of QFunction, "\abs_path\file.h:function_name". 567 For support across all backends, this source must only contain constructs supported by C99, C++11, and CUDA. 568 @param[out] qf Address of the variable where the newly created CeedQFunction will be stored 569 570 @return An error code: 0 - success, otherwise - failure 571 572 See \ref CeedQFunctionUser for details on the call-back function @a f's arguments. 573 574 @ref User 575 **/ 576 int CeedQFunctionCreateInterior(Ceed ceed, CeedInt vec_length, CeedQFunctionUser f, const char *source, CeedQFunction *qf) { 577 char *user_source_copy; 578 579 if (!ceed->QFunctionCreate) { 580 Ceed delegate; 581 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "QFunction")); 582 583 if (!delegate) { 584 // LCOV_EXCL_START 585 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support QFunctionCreate"); 586 // LCOV_EXCL_STOP 587 } 588 589 CeedCall(CeedQFunctionCreateInterior(delegate, vec_length, f, source, qf)); 590 return CEED_ERROR_SUCCESS; 591 } 592 593 if (strlen(source) && !strrchr(source, ':')) { 594 // LCOV_EXCL_START 595 return CeedError(ceed, CEED_ERROR_INCOMPLETE, 596 "Provided path to source does not include function name. Provided: \"%s\"\nRequired: \"\\abs_path\\file.h:function_name\"", 597 source); 598 // LCOV_EXCL_STOP 599 } 600 601 CeedCall(CeedCalloc(1, qf)); 602 (*qf)->ceed = ceed; 603 CeedCall(CeedReference(ceed)); 604 (*qf)->ref_count = 1; 605 (*qf)->vec_length = vec_length; 606 (*qf)->is_identity = false; 607 (*qf)->is_context_writable = true; 608 (*qf)->function = f; 609 (*qf)->user_flop_estimate = -1; 610 if (strlen(source)) { 611 size_t user_source_len = strlen(source); 612 613 CeedCall(CeedCalloc(user_source_len + 1, &user_source_copy)); 614 memcpy(user_source_copy, source, user_source_len); 615 (*qf)->user_source = user_source_copy; 616 } 617 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*qf)->input_fields)); 618 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*qf)->output_fields)); 619 CeedCall(ceed->QFunctionCreate(*qf)); 620 return CEED_ERROR_SUCCESS; 621 } 622 623 /** 624 @brief Create a CeedQFunction for evaluating interior (volumetric) terms by name. 625 626 @param[in] ceed Ceed object where the CeedQFunction will be created 627 @param[in] name Name of QFunction to use from gallery 628 @param[out] qf Address of the variable where the newly created CeedQFunction will be stored 629 630 @return An error code: 0 - success, otherwise - failure 631 632 @ref User 633 **/ 634 int CeedQFunctionCreateInteriorByName(Ceed ceed, const char *name, CeedQFunction *qf) { 635 size_t match_len = 0, match_index = UINT_MAX; 636 637 CeedCall(CeedQFunctionRegisterAll()); 638 // Find matching backend 639 if (!name) return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No QFunction name provided"); 640 for (size_t i = 0; i < num_qfunctions; i++) { 641 size_t n; 642 const char *curr_name = gallery_qfunctions[i].name; 643 for (n = 0; curr_name[n] && curr_name[n] == name[n]; n++) { 644 } 645 if (n > match_len) { 646 match_len = n; 647 match_index = i; 648 } 649 } 650 if (!match_len) { 651 // LCOV_EXCL_START 652 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "No suitable gallery QFunction"); 653 // LCOV_EXCL_STOP 654 } 655 656 // Create QFunction 657 CeedCall(CeedQFunctionCreateInterior(ceed, gallery_qfunctions[match_index].vec_length, gallery_qfunctions[match_index].f, 658 gallery_qfunctions[match_index].source, qf)); 659 660 // QFunction specific setup 661 CeedCall(gallery_qfunctions[match_index].init(ceed, name, *qf)); 662 663 // Copy name 664 CeedCall(CeedStringAllocCopy(name, (char **)&(*qf)->gallery_name)); 665 (*qf)->is_gallery = true; 666 return CEED_ERROR_SUCCESS; 667 } 668 669 /** 670 @brief Create an identity CeedQFunction. 671 Inputs are written into outputs in the order given. 672 This is useful for CeedOperators that can be represented with only the action of a CeedRestriction and CeedBasis, such as restriction and 673 prolongation operators for p-multigrid. Backends may optimize CeedOperators with this CeedQFunction to avoid the copy of input data to output fields 674 by using the same memory location for both. 675 676 @param[in] ceed Ceed object where the CeedQFunction will be created 677 @param[in] size Size of the QFunction fields 678 @param[in] in_mode CeedEvalMode for input to CeedQFunction 679 @param[in] out_mode CeedEvalMode for output to CeedQFunction 680 @param[out] qf Address of the variable where the newly created CeedQFunction will be stored 681 682 @return An error code: 0 - success, otherwise - failure 683 684 @ref User 685 **/ 686 int CeedQFunctionCreateIdentity(Ceed ceed, CeedInt size, CeedEvalMode in_mode, CeedEvalMode out_mode, CeedQFunction *qf) { 687 CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Identity", qf)); 688 CeedCall(CeedQFunctionAddInput(*qf, "input", size, in_mode)); 689 CeedCall(CeedQFunctionAddOutput(*qf, "output", size, out_mode)); 690 691 (*qf)->is_identity = true; 692 693 CeedQFunctionContext ctx; 694 CeedContextFieldLabel size_label; 695 CeedCall(CeedQFunctionGetContext(*qf, &ctx)); 696 CeedCall(CeedQFunctionContextGetFieldLabel(ctx, "size", &size_label)); 697 CeedCall(CeedQFunctionContextSetInt32(ctx, size_label, &size)); 698 699 return CEED_ERROR_SUCCESS; 700 } 701 702 /** 703 @brief Copy the pointer to a CeedQFunction. 704 Both pointers should be destroyed with `CeedQFunctionDestroy()`. 705 706 Note: If the value of `qf_copy` passed to this function is non-NULL, then it is assumed that `*qf_copy` is a pointer to a CeedQFunction. 707 This CeedQFunction will be destroyed if `*qf_copy` is the only reference to this CeedQFunction. 708 709 @param[in] qf CeedQFunction to copy reference to 710 @param[out] qf_copy Variable to store copied reference 711 712 @return An error code: 0 - success, otherwise - failure 713 714 @ref User 715 **/ 716 int CeedQFunctionReferenceCopy(CeedQFunction qf, CeedQFunction *qf_copy) { 717 CeedCall(CeedQFunctionReference(qf)); 718 CeedCall(CeedQFunctionDestroy(qf_copy)); 719 *qf_copy = qf; 720 return CEED_ERROR_SUCCESS; 721 } 722 723 /** 724 @brief Add a CeedQFunction input 725 726 @param[in,out] qf CeedQFunction 727 @param[in] field_name Name of QFunction field 728 @param[in] size Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or (num_comp * 1) for @ref CEED_EVAL_NONE and @ref 729 CEED_EVAL_INTERP 730 @param[in] eval_mode \ref CEED_EVAL_NONE to use values directly, 731 \ref CEED_EVAL_INTERP to use interpolated values, 732 \ref CEED_EVAL_GRAD to use gradients. 733 734 @return An error code: 0 - success, otherwise - failure 735 736 @ref User 737 **/ 738 int CeedQFunctionAddInput(CeedQFunction qf, const char *field_name, CeedInt size, CeedEvalMode eval_mode) { 739 if (qf->is_immutable) { 740 // LCOV_EXCL_START 741 return CeedError(qf->ceed, CEED_ERROR_MAJOR, "QFunction cannot be changed after set as immutable"); 742 // LCOV_EXCL_STOP 743 } 744 if ((eval_mode == CEED_EVAL_WEIGHT) && (size != 1)) { 745 // LCOV_EXCL_START 746 return CeedError(qf->ceed, CEED_ERROR_DIMENSION, "CEED_EVAL_WEIGHT should have size 1"); 747 // LCOV_EXCL_STOP 748 } 749 for (CeedInt i = 0; i < qf->num_input_fields; i++) { 750 if (!strcmp(field_name, qf->input_fields[i]->field_name)) { 751 // LCOV_EXCL_START 752 return CeedError(qf->ceed, CEED_ERROR_MINOR, "QFunction field names must be unique"); 753 // LCOV_EXCL_STOP 754 } 755 } 756 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 757 if (!strcmp(field_name, qf->output_fields[i]->field_name)) { 758 // LCOV_EXCL_START 759 return CeedError(qf->ceed, CEED_ERROR_MINOR, "QFunction field names must be unique"); 760 // LCOV_EXCL_STOP 761 } 762 } 763 CeedCall(CeedQFunctionFieldSet(&qf->input_fields[qf->num_input_fields], field_name, size, eval_mode)); 764 qf->num_input_fields++; 765 return CEED_ERROR_SUCCESS; 766 } 767 768 /** 769 @brief Add a CeedQFunction output 770 771 @param[in,out] qf CeedQFunction 772 @param[in] field_name Name of QFunction field 773 @param[in] size Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or (num_comp * 1) for @ref CEED_EVAL_NONE and @ref 774 CEED_EVAL_INTERP 775 @param[in] eval_mode \ref CEED_EVAL_NONE to use values directly, 776 \ref CEED_EVAL_INTERP to use interpolated values, 777 \ref CEED_EVAL_GRAD to use gradients. 778 779 @return An error code: 0 - success, otherwise - failure 780 781 @ref User 782 **/ 783 int CeedQFunctionAddOutput(CeedQFunction qf, const char *field_name, CeedInt size, CeedEvalMode eval_mode) { 784 if (qf->is_immutable) { 785 // LCOV_EXCL_START 786 return CeedError(qf->ceed, CEED_ERROR_MAJOR, "QFunction cannot be changed after set as immutable"); 787 // LCOV_EXCL_STOP 788 } 789 if (eval_mode == CEED_EVAL_WEIGHT) { 790 // LCOV_EXCL_START 791 return CeedError(qf->ceed, CEED_ERROR_DIMENSION, "Cannot create QFunction output with CEED_EVAL_WEIGHT"); 792 // LCOV_EXCL_STOP 793 } 794 for (CeedInt i = 0; i < qf->num_input_fields; i++) { 795 if (!strcmp(field_name, qf->input_fields[i]->field_name)) { 796 // LCOV_EXCL_START 797 return CeedError(qf->ceed, CEED_ERROR_MINOR, "QFunction field names must be unique"); 798 // LCOV_EXCL_STOP 799 } 800 } 801 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 802 if (!strcmp(field_name, qf->output_fields[i]->field_name)) { 803 // LCOV_EXCL_START 804 return CeedError(qf->ceed, CEED_ERROR_MINOR, "QFunction field names must be unique"); 805 // LCOV_EXCL_STOP 806 } 807 } 808 CeedCall(CeedQFunctionFieldSet(&qf->output_fields[qf->num_output_fields], field_name, size, eval_mode)); 809 qf->num_output_fields++; 810 return CEED_ERROR_SUCCESS; 811 } 812 813 /** 814 @brief Get the CeedQFunctionFields of a CeedQFunction 815 816 Note: Calling this function asserts that setup is complete and sets the CeedQFunction as immutable. 817 818 @param[in] qf CeedQFunction 819 @param[out] num_input_fields Variable to store number of input fields 820 @param[out] input_fields Variable to store input fields 821 @param[out] num_output_fields Variable to store number of output fields 822 @param[out] output_fields Variable to store output fields 823 824 @return An error code: 0 - success, otherwise - failure 825 826 @ref Advanced 827 **/ 828 int CeedQFunctionGetFields(CeedQFunction qf, CeedInt *num_input_fields, CeedQFunctionField **input_fields, CeedInt *num_output_fields, 829 CeedQFunctionField **output_fields) { 830 qf->is_immutable = true; 831 if (num_input_fields) *num_input_fields = qf->num_input_fields; 832 if (input_fields) *input_fields = qf->input_fields; 833 if (num_output_fields) *num_output_fields = qf->num_output_fields; 834 if (output_fields) *output_fields = qf->output_fields; 835 return CEED_ERROR_SUCCESS; 836 } 837 838 /** 839 @brief Get the name of a CeedQFunctionField 840 841 @param[in] qf_field CeedQFunctionField 842 @param[out] field_name Variable to store the field name 843 844 @return An error code: 0 - success, otherwise - failure 845 846 @ref Advanced 847 **/ 848 int CeedQFunctionFieldGetName(CeedQFunctionField qf_field, char **field_name) { 849 *field_name = (char *)qf_field->field_name; 850 return CEED_ERROR_SUCCESS; 851 } 852 853 /** 854 @brief Get the number of components of a CeedQFunctionField 855 856 @param[in] qf_field CeedQFunctionField 857 @param[out] size Variable to store the size of the field 858 859 @return An error code: 0 - success, otherwise - failure 860 861 @ref Advanced 862 **/ 863 int CeedQFunctionFieldGetSize(CeedQFunctionField qf_field, CeedInt *size) { 864 *size = qf_field->size; 865 return CEED_ERROR_SUCCESS; 866 } 867 868 /** 869 @brief Get the CeedEvalMode of a CeedQFunctionField 870 871 @param[in] qf_field CeedQFunctionField 872 @param[out] eval_mode Variable to store the field evaluation mode 873 874 @return An error code: 0 - success, otherwise - failure 875 876 @ref Advanced 877 **/ 878 int CeedQFunctionFieldGetEvalMode(CeedQFunctionField qf_field, CeedEvalMode *eval_mode) { 879 *eval_mode = qf_field->eval_mode; 880 return CEED_ERROR_SUCCESS; 881 } 882 883 /** 884 @brief Set global context for a CeedQFunction 885 886 @param[in,out] qf CeedQFunction 887 @param[in] ctx Context data to set 888 889 @return An error code: 0 - success, otherwise - failure 890 891 @ref User 892 **/ 893 int CeedQFunctionSetContext(CeedQFunction qf, CeedQFunctionContext ctx) { 894 CeedCall(CeedQFunctionContextDestroy(&qf->ctx)); 895 qf->ctx = ctx; 896 if (ctx) { 897 CeedCall(CeedQFunctionContextReference(ctx)); 898 } 899 return CEED_ERROR_SUCCESS; 900 } 901 902 /** 903 @brief Set writability of CeedQFunctionContext when calling the `CeedQFunctionUser`. 904 The default value is 'is_writable == true'. 905 906 Setting `is_writable == true` indicates the `CeedQFunctionUser` writes into the CeedQFunctionContextData and requires memory syncronization 907 after calling `CeedQFunctionApply()`. 908 909 Setting 'is_writable == false' asserts that `CeedQFunctionUser` does not modify the CeedQFunctionContextData. 910 Violating this assertion may lead to inconsistent data. 911 912 Setting `is_writable == false` may offer a performance improvement on GPU backends. 913 914 @param[in,out] qf CeedQFunction 915 @param[in] is_writable Writability status 916 917 @return An error code: 0 - success, otherwise - failure 918 919 @ref User 920 **/ 921 int CeedQFunctionSetContextWritable(CeedQFunction qf, bool is_writable) { 922 qf->is_context_writable = is_writable; 923 return CEED_ERROR_SUCCESS; 924 } 925 926 /** 927 @brief Set estimated number of FLOPs per quadrature required to apply QFunction 928 929 @param[in] qf QFunction to estimate FLOPs for 930 @param[out] flops FLOPs per quadrature point estimate 931 932 @ref Backend 933 **/ 934 int CeedQFunctionSetUserFlopsEstimate(CeedQFunction qf, CeedSize flops) { 935 if (flops < 0) { 936 // LCOV_EXCL_START 937 return CeedError(qf->ceed, CEED_ERROR_INCOMPATIBLE, "Must set non-negative FLOPs estimate"); 938 // LCOV_EXCL_STOP 939 } 940 qf->user_flop_estimate = flops; 941 return CEED_ERROR_SUCCESS; 942 } 943 944 /** 945 @brief View a CeedQFunction 946 947 @param[in] qf CeedQFunction to view 948 @param[in] stream Stream to write; typically stdout/stderr or a file 949 950 @return Error code: 0 - success, otherwise - failure 951 952 @ref User 953 **/ 954 int CeedQFunctionView(CeedQFunction qf, FILE *stream) { 955 char *kernel_name; 956 957 CeedCall(CeedQFunctionGetKernelName(qf, &kernel_name)); 958 fprintf(stream, "%sCeedQFunction - %s\n", qf->is_gallery ? "Gallery " : "User ", qf->is_gallery ? qf->gallery_name : kernel_name); 959 960 fprintf(stream, " %" CeedInt_FMT " input field%s:\n", qf->num_input_fields, qf->num_input_fields > 1 ? "s" : ""); 961 for (CeedInt i = 0; i < qf->num_input_fields; i++) { 962 CeedCall(CeedQFunctionFieldView(qf->input_fields[i], i, 1, stream)); 963 } 964 965 fprintf(stream, " %" CeedInt_FMT " output field%s:\n", qf->num_output_fields, qf->num_output_fields > 1 ? "s" : ""); 966 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 967 CeedCall(CeedQFunctionFieldView(qf->output_fields[i], i, 0, stream)); 968 } 969 return CEED_ERROR_SUCCESS; 970 } 971 972 /** 973 @brief Get the Ceed associated with a CeedQFunction 974 975 @param[in] qf CeedQFunction 976 @param[out] ceed Variable to store Ceed 977 978 @return An error code: 0 - success, otherwise - failure 979 980 @ref Advanced 981 **/ 982 int CeedQFunctionGetCeed(CeedQFunction qf, Ceed *ceed) { 983 *ceed = qf->ceed; 984 return CEED_ERROR_SUCCESS; 985 } 986 987 /** 988 @brief Apply the action of a CeedQFunction 989 990 Note: Calling this function asserts that setup is complete and sets the CeedQFunction as immutable. 991 992 @param[in] qf CeedQFunction 993 @param[in] Q Number of quadrature points 994 @param[in] u Array of input CeedVectors 995 @param[out] v Array of output CeedVectors 996 997 @return An error code: 0 - success, otherwise - failure 998 999 @ref User 1000 **/ 1001 int CeedQFunctionApply(CeedQFunction qf, CeedInt Q, CeedVector *u, CeedVector *v) { 1002 if (!qf->Apply) { 1003 // LCOV_EXCL_START 1004 return CeedError(qf->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support QFunctionApply"); 1005 // LCOV_EXCL_STOP 1006 } 1007 if (Q % qf->vec_length) { 1008 // LCOV_EXCL_START 1009 return CeedError(qf->ceed, CEED_ERROR_DIMENSION, "Number of quadrature points %" CeedInt_FMT " must be a multiple of %" CeedInt_FMT, Q, 1010 qf->vec_length); 1011 // LCOV_EXCL_STOP 1012 } 1013 qf->is_immutable = true; 1014 CeedCall(qf->Apply(qf, Q, u, v)); 1015 return CEED_ERROR_SUCCESS; 1016 } 1017 1018 /** 1019 @brief Destroy a CeedQFunction 1020 1021 @param[in,out] qf CeedQFunction to destroy 1022 1023 @return An error code: 0 - success, otherwise - failure 1024 1025 @ref User 1026 **/ 1027 int CeedQFunctionDestroy(CeedQFunction *qf) { 1028 if (!*qf || --(*qf)->ref_count > 0) { 1029 *qf = NULL; 1030 return CEED_ERROR_SUCCESS; 1031 } 1032 // Backend destroy 1033 if ((*qf)->Destroy) { 1034 CeedCall((*qf)->Destroy(*qf)); 1035 } 1036 // Free fields 1037 for (CeedInt i = 0; i < (*qf)->num_input_fields; i++) { 1038 CeedCall(CeedFree(&(*(*qf)->input_fields[i]).field_name)); 1039 CeedCall(CeedFree(&(*qf)->input_fields[i])); 1040 } 1041 for (CeedInt i = 0; i < (*qf)->num_output_fields; i++) { 1042 CeedCall(CeedFree(&(*(*qf)->output_fields[i]).field_name)); 1043 CeedCall(CeedFree(&(*qf)->output_fields[i])); 1044 } 1045 CeedCall(CeedFree(&(*qf)->input_fields)); 1046 CeedCall(CeedFree(&(*qf)->output_fields)); 1047 1048 // User context data object 1049 CeedCall(CeedQFunctionContextDestroy(&(*qf)->ctx)); 1050 1051 CeedCall(CeedFree(&(*qf)->user_source)); 1052 CeedCall(CeedFree(&(*qf)->source_path)); 1053 CeedCall(CeedFree(&(*qf)->gallery_name)); 1054 CeedCall(CeedFree(&(*qf)->kernel_name)); 1055 CeedCall(CeedDestroy(&(*qf)->ceed)); 1056 CeedCall(CeedFree(qf)); 1057 return CEED_ERROR_SUCCESS; 1058 } 1059 1060 /// @} 1061