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