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