1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3 // reserved. See files LICENSE and NOTICE for details. 4 // 5 // This file is part of CEED, a collection of benchmarks, miniapps, software 6 // libraries and APIs for efficient high-order finite element and spectral 7 // element discretizations for exascale applications. For more information and 8 // source code availability see http://github.com/ceed. 9 // 10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11 // a collaborative effort of two U.S. Department of Energy organizations (Office 12 // of Science and the National Nuclear Security Administration) responsible for 13 // the planning and preparation of a capable exascale ecosystem, including 14 // software, applications, hardware, advanced system engineering and early 15 // testbed platforms, in support of the nation's exascale computing imperative 16 17 //! A Ceed QFunction represents the spatial terms of the point-wise functions 18 //! describing the physics at the quadrature points. 19 20 use std::pin::Pin; 21 22 use crate::prelude::*; 23 24 pub type QFunctionInputs<'a> = [&'a [crate::Scalar]; MAX_QFUNCTION_FIELDS]; 25 pub type QFunctionOutputs<'a> = [&'a mut [crate::Scalar]; MAX_QFUNCTION_FIELDS]; 26 27 // ----------------------------------------------------------------------------- 28 // CeedQFunction option 29 // ----------------------------------------------------------------------------- 30 pub enum QFunctionOpt<'a> { 31 SomeQFunction(&'a QFunction<'a>), 32 SomeQFunctionByName(&'a QFunctionByName<'a>), 33 None, 34 } 35 36 /// Construct a QFunctionOpt reference from a QFunction reference 37 impl<'a> From<&'a QFunction<'_>> for QFunctionOpt<'a> { 38 fn from(qfunc: &'a QFunction) -> Self { 39 debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE }); 40 Self::SomeQFunction(qfunc) 41 } 42 } 43 44 /// Construct a QFunctionOpt reference from a QFunction by Name reference 45 impl<'a> From<&'a QFunctionByName<'_>> for QFunctionOpt<'a> { 46 fn from(qfunc: &'a QFunctionByName) -> Self { 47 debug_assert!(qfunc.qf_core.ptr != unsafe { bind_ceed::CEED_QFUNCTION_NONE }); 48 Self::SomeQFunctionByName(qfunc) 49 } 50 } 51 52 impl<'a> QFunctionOpt<'a> { 53 /// Transform a Rust libCEED QFunctionOpt into C libCEED CeedQFunction 54 pub(crate) fn to_raw(self) -> bind_ceed::CeedQFunction { 55 match self { 56 Self::SomeQFunction(qfunc) => qfunc.qf_core.ptr, 57 Self::SomeQFunctionByName(qfunc) => qfunc.qf_core.ptr, 58 Self::None => unsafe { bind_ceed::CEED_QFUNCTION_NONE }, 59 } 60 } 61 62 /// Check if a QFunctionOpt is Some 63 /// 64 /// ``` 65 /// # use libceed::prelude::*; 66 /// # fn main() -> Result<(), libceed::CeedError> { 67 /// # let ceed = libceed::Ceed::default_init(); 68 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 69 /// // Iterate over quadrature points 70 /// v.iter_mut() 71 /// .zip(u.iter().zip(weights.iter())) 72 /// .for_each(|(v, (u, w))| *v = u * w); 73 /// 74 /// // Return clean error code 75 /// 0 76 /// }; 77 /// 78 /// let qf = ceed 79 /// .q_function_interior(1, Box::new(user_f))? 80 /// .input("u", 1, EvalMode::Interp)? 81 /// .input("weights", 1, EvalMode::Weight)? 82 /// .output("v", 1, EvalMode::Interp)?; 83 /// let qf_opt = QFunctionOpt::from(&qf); 84 /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt"); 85 /// 86 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?; 87 /// let qf_opt = QFunctionOpt::from(&qf); 88 /// assert!(qf_opt.is_some(), "Incorrect QFunctionOpt"); 89 /// 90 /// let qf_opt = QFunctionOpt::None; 91 /// assert!(!qf_opt.is_some(), "Incorrect QFunctionOpt"); 92 /// # Ok(()) 93 /// # } 94 /// ``` 95 pub fn is_some(&self) -> bool { 96 match self { 97 Self::SomeQFunction(_) => true, 98 Self::SomeQFunctionByName(_) => true, 99 Self::None => false, 100 } 101 } 102 103 /// Check if a QFunctionOpt is SomeQFunction 104 /// 105 /// ``` 106 /// # use libceed::prelude::*; 107 /// # fn main() -> Result<(), libceed::CeedError> { 108 /// # let ceed = libceed::Ceed::default_init(); 109 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 110 /// // Iterate over quadrature points 111 /// v.iter_mut() 112 /// .zip(u.iter().zip(weights.iter())) 113 /// .for_each(|(v, (u, w))| *v = u * w); 114 /// 115 /// // Return clean error code 116 /// 0 117 /// }; 118 /// 119 /// let qf = ceed 120 /// .q_function_interior(1, Box::new(user_f))? 121 /// .input("u", 1, EvalMode::Interp)? 122 /// .input("weights", 1, EvalMode::Weight)? 123 /// .output("v", 1, EvalMode::Interp)?; 124 /// let qf_opt = QFunctionOpt::from(&qf); 125 /// assert!(qf_opt.is_some_q_function(), "Incorrect QFunctionOpt"); 126 /// 127 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?; 128 /// let qf_opt = QFunctionOpt::from(&qf); 129 /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt"); 130 /// 131 /// let qf_opt = QFunctionOpt::None; 132 /// assert!(!qf_opt.is_some_q_function(), "Incorrect QFunctionOpt"); 133 /// # Ok(()) 134 /// # } 135 /// ``` 136 pub fn is_some_q_function(&self) -> bool { 137 match self { 138 Self::SomeQFunction(_) => true, 139 Self::SomeQFunctionByName(_) => false, 140 Self::None => false, 141 } 142 } 143 144 /// Check if a QFunctionOpt is SomeQFunctionByName 145 /// 146 /// ``` 147 /// # use libceed::prelude::*; 148 /// # fn main() -> Result<(), libceed::CeedError> { 149 /// # let ceed = libceed::Ceed::default_init(); 150 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 151 /// // Iterate over quadrature points 152 /// v.iter_mut() 153 /// .zip(u.iter().zip(weights.iter())) 154 /// .for_each(|(v, (u, w))| *v = u * w); 155 /// 156 /// // Return clean error code 157 /// 0 158 /// }; 159 /// 160 /// let qf = ceed 161 /// .q_function_interior(1, Box::new(user_f))? 162 /// .input("u", 1, EvalMode::Interp)? 163 /// .input("weights", 1, EvalMode::Weight)? 164 /// .output("v", 1, EvalMode::Interp)?; 165 /// let qf_opt = QFunctionOpt::from(&qf); 166 /// assert!( 167 /// !qf_opt.is_some_q_function_by_name(), 168 /// "Incorrect QFunctionOpt" 169 /// ); 170 /// 171 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?; 172 /// let qf_opt = QFunctionOpt::from(&qf); 173 /// assert!( 174 /// qf_opt.is_some_q_function_by_name(), 175 /// "Incorrect QFunctionOpt" 176 /// ); 177 /// 178 /// let qf_opt = QFunctionOpt::None; 179 /// assert!( 180 /// !qf_opt.is_some_q_function_by_name(), 181 /// "Incorrect QFunctionOpt" 182 /// ); 183 /// # Ok(()) 184 /// # } 185 /// ``` 186 pub fn is_some_q_function_by_name(&self) -> bool { 187 match self { 188 Self::SomeQFunction(_) => false, 189 Self::SomeQFunctionByName(_) => true, 190 Self::None => false, 191 } 192 } 193 194 /// Check if a QFunctionOpt is None 195 /// 196 /// ``` 197 /// # use libceed::prelude::*; 198 /// # fn main() -> Result<(), libceed::CeedError> { 199 /// # let ceed = libceed::Ceed::default_init(); 200 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 201 /// // Iterate over quadrature points 202 /// v.iter_mut() 203 /// .zip(u.iter().zip(weights.iter())) 204 /// .for_each(|(v, (u, w))| *v = u * w); 205 /// 206 /// // Return clean error code 207 /// 0 208 /// }; 209 /// 210 /// let qf = ceed 211 /// .q_function_interior(1, Box::new(user_f))? 212 /// .input("u", 1, EvalMode::Interp)? 213 /// .input("weights", 1, EvalMode::Weight)? 214 /// .output("v", 1, EvalMode::Interp)?; 215 /// let qf_opt = QFunctionOpt::from(&qf); 216 /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt"); 217 /// 218 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?; 219 /// let qf_opt = QFunctionOpt::from(&qf); 220 /// assert!(!qf_opt.is_none(), "Incorrect QFunctionOpt"); 221 /// 222 /// let qf_opt = QFunctionOpt::None; 223 /// assert!(qf_opt.is_none(), "Incorrect QFunctionOpt"); 224 /// # Ok(()) 225 /// # } 226 /// ``` 227 pub fn is_none(&self) -> bool { 228 match self { 229 Self::SomeQFunction(_) => false, 230 Self::SomeQFunctionByName(_) => false, 231 Self::None => true, 232 } 233 } 234 } 235 236 // ----------------------------------------------------------------------------- 237 // CeedQFunction context wrapper 238 // ----------------------------------------------------------------------------- 239 #[derive(Debug)] 240 pub(crate) struct QFunctionCore<'a> { 241 ptr: bind_ceed::CeedQFunction, 242 _lifeline: PhantomData<&'a ()>, 243 } 244 245 struct QFunctionTrampolineData { 246 number_inputs: usize, 247 number_outputs: usize, 248 input_sizes: [usize; MAX_QFUNCTION_FIELDS], 249 output_sizes: [usize; MAX_QFUNCTION_FIELDS], 250 user_f: Box<QFunctionUserClosure>, 251 } 252 253 pub struct QFunction<'a> { 254 qf_core: QFunctionCore<'a>, 255 qf_ctx_ptr: bind_ceed::CeedQFunctionContext, 256 trampoline_data: Pin<Box<QFunctionTrampolineData>>, 257 } 258 259 #[derive(Debug)] 260 pub struct QFunctionByName<'a> { 261 qf_core: QFunctionCore<'a>, 262 } 263 264 // ----------------------------------------------------------------------------- 265 // Destructor 266 // ----------------------------------------------------------------------------- 267 impl<'a> Drop for QFunctionCore<'a> { 268 fn drop(&mut self) { 269 unsafe { 270 if self.ptr != bind_ceed::CEED_QFUNCTION_NONE { 271 bind_ceed::CeedQFunctionDestroy(&mut self.ptr); 272 } 273 } 274 } 275 } 276 277 impl<'a> Drop for QFunction<'a> { 278 fn drop(&mut self) { 279 unsafe { 280 bind_ceed::CeedQFunctionContextDestroy(&mut self.qf_ctx_ptr); 281 } 282 } 283 } 284 285 // ----------------------------------------------------------------------------- 286 // Display 287 // ----------------------------------------------------------------------------- 288 impl<'a> fmt::Display for QFunctionCore<'a> { 289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 290 let mut ptr = std::ptr::null_mut(); 291 let mut sizeloc = crate::MAX_BUFFER_LENGTH; 292 let cstring = unsafe { 293 let file = bind_ceed::open_memstream(&mut ptr, &mut sizeloc); 294 bind_ceed::CeedQFunctionView(self.ptr, file); 295 bind_ceed::fclose(file); 296 CString::from_raw(ptr) 297 }; 298 cstring.to_string_lossy().fmt(f) 299 } 300 } 301 /// View a QFunction 302 /// 303 /// ``` 304 /// # use libceed::prelude::*; 305 /// # fn main() -> Result<(), libceed::CeedError> { 306 /// # let ceed = libceed::Ceed::default_init(); 307 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 308 /// // Iterate over quadrature points 309 /// v.iter_mut() 310 /// .zip(u.iter().zip(weights.iter())) 311 /// .for_each(|(v, (u, w))| *v = u * w); 312 /// 313 /// // Return clean error code 314 /// 0 315 /// }; 316 /// 317 /// let qf = ceed 318 /// .q_function_interior(1, Box::new(user_f))? 319 /// .input("u", 1, EvalMode::Interp)? 320 /// .input("weights", 1, EvalMode::Weight)? 321 /// .output("v", 1, EvalMode::Interp)?; 322 /// 323 /// println!("{}", qf); 324 /// # Ok(()) 325 /// # } 326 /// ``` 327 impl<'a> fmt::Display for QFunction<'a> { 328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 329 self.qf_core.fmt(f) 330 } 331 } 332 333 /// View a QFunction by Name 334 /// 335 /// ``` 336 /// # use libceed::prelude::*; 337 /// # fn main() -> Result<(), libceed::CeedError> { 338 /// # let ceed = libceed::Ceed::default_init(); 339 /// let qf = ceed.q_function_interior_by_name("Mass1DBuild")?; 340 /// println!("{}", qf); 341 /// # Ok(()) 342 /// # } 343 /// ``` 344 impl<'a> fmt::Display for QFunctionByName<'a> { 345 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 346 self.qf_core.fmt(f) 347 } 348 } 349 350 // ----------------------------------------------------------------------------- 351 // Core functionality 352 // ----------------------------------------------------------------------------- 353 impl<'a> QFunctionCore<'a> { 354 // Error handling 355 #[doc(hidden)] 356 fn check_error(&self, ierr: i32) -> crate::Result<i32> { 357 let mut ptr = std::ptr::null_mut(); 358 unsafe { 359 bind_ceed::CeedQFunctionGetCeed(self.ptr, &mut ptr); 360 } 361 crate::check_error(ptr, ierr) 362 } 363 364 // Common implementation 365 pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> { 366 let mut u_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS]; 367 for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, u.len()) { 368 u_c[i] = u[i].ptr; 369 } 370 let mut v_c = [std::ptr::null_mut(); MAX_QFUNCTION_FIELDS]; 371 for i in 0..std::cmp::min(MAX_QFUNCTION_FIELDS, v.len()) { 372 v_c[i] = v[i].ptr; 373 } 374 let Q = i32::try_from(Q).unwrap(); 375 let ierr = unsafe { 376 bind_ceed::CeedQFunctionApply(self.ptr, Q, u_c.as_mut_ptr(), v_c.as_mut_ptr()) 377 }; 378 self.check_error(ierr) 379 } 380 } 381 382 // ----------------------------------------------------------------------------- 383 // User QFunction Closure 384 // ----------------------------------------------------------------------------- 385 pub type QFunctionUserClosure = dyn FnMut( 386 [&[crate::Scalar]; MAX_QFUNCTION_FIELDS], 387 [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS], 388 ) -> i32; 389 390 macro_rules! mut_max_fields { 391 ($e:expr) => { 392 [ 393 $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, $e, 394 ] 395 }; 396 } 397 unsafe extern "C" fn trampoline( 398 ctx: *mut ::std::os::raw::c_void, 399 q: bind_ceed::CeedInt, 400 inputs: *const *const bind_ceed::CeedScalar, 401 outputs: *const *mut bind_ceed::CeedScalar, 402 ) -> ::std::os::raw::c_int { 403 let trampoline_data: Pin<&mut QFunctionTrampolineData> = std::mem::transmute(ctx); 404 405 // Inputs 406 let inputs_slice: &[*const bind_ceed::CeedScalar] = 407 std::slice::from_raw_parts(inputs, MAX_QFUNCTION_FIELDS); 408 let mut inputs_array: [&[crate::Scalar]; MAX_QFUNCTION_FIELDS] = [&[0.0]; MAX_QFUNCTION_FIELDS]; 409 inputs_slice 410 .iter() 411 .enumerate() 412 .map(|(i, &x)| { 413 std::slice::from_raw_parts(x, trampoline_data.input_sizes[i] * q as usize) 414 as &[crate::Scalar] 415 }) 416 .zip(inputs_array.iter_mut()) 417 .for_each(|(x, a)| *a = x); 418 419 // Outputs 420 let outputs_slice: &[*mut bind_ceed::CeedScalar] = 421 std::slice::from_raw_parts(outputs, MAX_QFUNCTION_FIELDS); 422 let mut outputs_array: [&mut [crate::Scalar]; MAX_QFUNCTION_FIELDS] = 423 mut_max_fields!(&mut [0.0]); 424 outputs_slice 425 .iter() 426 .enumerate() 427 .map(|(i, &x)| { 428 std::slice::from_raw_parts_mut(x, trampoline_data.output_sizes[i] * q as usize) 429 as &mut [crate::Scalar] 430 }) 431 .zip(outputs_array.iter_mut()) 432 .for_each(|(x, a)| *a = x); 433 434 // User closure 435 (trampoline_data.get_unchecked_mut().user_f)(inputs_array, outputs_array) 436 } 437 438 // ----------------------------------------------------------------------------- 439 // QFunction 440 // ----------------------------------------------------------------------------- 441 impl<'a> QFunction<'a> { 442 // Constructor 443 pub fn create( 444 ceed: &'a crate::Ceed, 445 vlength: usize, 446 user_f: Box<QFunctionUserClosure>, 447 ) -> crate::Result<Self> { 448 let source_c = CString::new("").expect("CString::new failed"); 449 let mut ptr = std::ptr::null_mut(); 450 451 // Context for closure 452 let number_inputs = 0; 453 let number_outputs = 0; 454 let input_sizes = [0; MAX_QFUNCTION_FIELDS]; 455 let output_sizes = [0; MAX_QFUNCTION_FIELDS]; 456 let trampoline_data = unsafe { 457 Pin::new_unchecked(Box::new(QFunctionTrampolineData { 458 number_inputs, 459 number_outputs, 460 input_sizes, 461 output_sizes, 462 user_f, 463 })) 464 }; 465 466 // Create QFunction 467 let vlength = i32::try_from(vlength).unwrap(); 468 let mut ierr = unsafe { 469 bind_ceed::CeedQFunctionCreateInterior( 470 ceed.ptr, 471 vlength, 472 Some(trampoline), 473 source_c.as_ptr(), 474 &mut ptr, 475 ) 476 }; 477 ceed.check_error(ierr)?; 478 479 // Set closure 480 let mut qf_ctx_ptr = std::ptr::null_mut(); 481 ierr = unsafe { bind_ceed::CeedQFunctionContextCreate(ceed.ptr, &mut qf_ctx_ptr) }; 482 ceed.check_error(ierr)?; 483 ierr = unsafe { 484 bind_ceed::CeedQFunctionContextSetData( 485 qf_ctx_ptr, 486 crate::MemType::Host as bind_ceed::CeedMemType, 487 crate::CopyMode::UsePointer as bind_ceed::CeedCopyMode, 488 std::mem::size_of::<QFunctionTrampolineData>() as u64, 489 std::mem::transmute(trampoline_data.as_ref()), 490 ) 491 }; 492 ceed.check_error(ierr)?; 493 ierr = unsafe { bind_ceed::CeedQFunctionSetContext(ptr, qf_ctx_ptr) }; 494 ceed.check_error(ierr)?; 495 Ok(Self { 496 qf_core: QFunctionCore { 497 ptr, 498 _lifeline: PhantomData, 499 }, 500 qf_ctx_ptr, 501 trampoline_data, 502 }) 503 } 504 505 /// Apply the action of a QFunction 506 /// 507 /// * `Q` - The number of quadrature points 508 /// * `input` - Array of input Vectors 509 /// * `output` - Array of output Vectors 510 /// 511 /// ``` 512 /// # use libceed::prelude::*; 513 /// # fn main() -> Result<(), libceed::CeedError> { 514 /// # let ceed = libceed::Ceed::default_init(); 515 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 516 /// // Iterate over quadrature points 517 /// v.iter_mut() 518 /// .zip(u.iter().zip(weights.iter())) 519 /// .for_each(|(v, (u, w))| *v = u * w); 520 /// 521 /// // Return clean error code 522 /// 0 523 /// }; 524 /// 525 /// let qf = ceed 526 /// .q_function_interior(1, Box::new(user_f))? 527 /// .input("u", 1, EvalMode::Interp)? 528 /// .input("weights", 1, EvalMode::Weight)? 529 /// .output("v", 1, EvalMode::Interp)?; 530 /// 531 /// const Q: usize = 8; 532 /// let mut w = [0.; Q]; 533 /// let mut u = [0.; Q]; 534 /// let mut v = [0.; Q]; 535 /// 536 /// for i in 0..Q { 537 /// let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.; 538 /// u[i] = 2. + 3. * x + 5. * x * x; 539 /// w[i] = 1. - x * x; 540 /// v[i] = u[i] * w[i]; 541 /// } 542 /// 543 /// let uu = ceed.vector_from_slice(&u)?; 544 /// let ww = ceed.vector_from_slice(&w)?; 545 /// let mut vv = ceed.vector(Q)?; 546 /// vv.set_value(0.0); 547 /// { 548 /// let input = vec![uu, ww]; 549 /// let mut output = vec![vv]; 550 /// qf.apply(Q, &input, &output)?; 551 /// vv = output.remove(0); 552 /// } 553 /// 554 /// vv.view() 555 /// .iter() 556 /// .zip(v.iter()) 557 /// .for_each(|(computed, actual)| { 558 /// assert_eq!( 559 /// *computed, *actual, 560 /// "Incorrect value in QFunction application" 561 /// ); 562 /// }); 563 /// # Ok(()) 564 /// # } 565 /// ``` 566 pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> { 567 self.qf_core.apply(Q, u, v) 568 } 569 570 /// Add a QFunction input 571 /// 572 /// * `fieldname` - Name of QFunction field 573 /// * `size` - Size of QFunction field, `(ncomp * dim)` for `Grad` or 574 /// `(ncomp * 1)` for `None`, `Interp`, and `Weight` 575 /// * `emode` - `EvalMode::None` to use values directly, `EvalMode::Interp` 576 /// to use interpolated values, `EvalMode::Grad` to use 577 /// gradients, `EvalMode::Weight` to use quadrature weights 578 /// 579 /// ``` 580 /// # use libceed::prelude::*; 581 /// # fn main() -> Result<(), libceed::CeedError> { 582 /// # let ceed = libceed::Ceed::default_init(); 583 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 584 /// // Iterate over quadrature points 585 /// v.iter_mut() 586 /// .zip(u.iter().zip(weights.iter())) 587 /// .for_each(|(v, (u, w))| *v = u * w); 588 /// 589 /// // Return clean error code 590 /// 0 591 /// }; 592 /// 593 /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?; 594 /// 595 /// qf = qf.input("u", 1, EvalMode::Interp)?; 596 /// qf = qf.input("weights", 1, EvalMode::Weight)?; 597 /// # Ok(()) 598 /// # } 599 /// ``` 600 pub fn input( 601 mut self, 602 fieldname: &str, 603 size: usize, 604 emode: crate::EvalMode, 605 ) -> crate::Result<Self> { 606 let name_c = CString::new(fieldname).expect("CString::new failed"); 607 let idx = self.trampoline_data.number_inputs; 608 self.trampoline_data.input_sizes[idx] = size; 609 self.trampoline_data.number_inputs += 1; 610 let (size, emode) = ( 611 i32::try_from(size).unwrap(), 612 emode as bind_ceed::CeedEvalMode, 613 ); 614 let ierr = unsafe { 615 bind_ceed::CeedQFunctionAddInput(self.qf_core.ptr, name_c.as_ptr(), size, emode) 616 }; 617 self.qf_core.check_error(ierr)?; 618 Ok(self) 619 } 620 621 /// Add a QFunction output 622 /// 623 /// * `fieldname` - Name of QFunction field 624 /// * `size` - Size of QFunction field, `(ncomp * dim)` for `Grad` or 625 /// `(ncomp * 1)` for `None` and `Interp` 626 /// * `emode` - `EvalMode::None` to use values directly, `EvalMode::Interp` 627 /// to use interpolated values, `EvalMode::Grad` to use 628 /// gradients 629 /// 630 /// ``` 631 /// # use libceed::prelude::*; 632 /// # fn main() -> Result<(), libceed::CeedError> { 633 /// # let ceed = libceed::Ceed::default_init(); 634 /// let mut user_f = |[u, weights, ..]: QFunctionInputs, [v, ..]: QFunctionOutputs| { 635 /// // Iterate over quadrature points 636 /// v.iter_mut() 637 /// .zip(u.iter().zip(weights.iter())) 638 /// .for_each(|(v, (u, w))| *v = u * w); 639 /// 640 /// // Return clean error code 641 /// 0 642 /// }; 643 /// 644 /// let mut qf = ceed.q_function_interior(1, Box::new(user_f))?; 645 /// 646 /// qf.output("v", 1, EvalMode::Interp)?; 647 /// # Ok(()) 648 /// # } 649 /// ``` 650 pub fn output( 651 mut self, 652 fieldname: &str, 653 size: usize, 654 emode: crate::EvalMode, 655 ) -> crate::Result<Self> { 656 let name_c = CString::new(fieldname).expect("CString::new failed"); 657 let idx = self.trampoline_data.number_outputs; 658 self.trampoline_data.output_sizes[idx] = size; 659 self.trampoline_data.number_outputs += 1; 660 let (size, emode) = ( 661 i32::try_from(size).unwrap(), 662 emode as bind_ceed::CeedEvalMode, 663 ); 664 let ierr = unsafe { 665 bind_ceed::CeedQFunctionAddOutput(self.qf_core.ptr, name_c.as_ptr(), size, emode) 666 }; 667 self.qf_core.check_error(ierr)?; 668 Ok(self) 669 } 670 } 671 672 // ----------------------------------------------------------------------------- 673 // QFunction 674 // ----------------------------------------------------------------------------- 675 impl<'a> QFunctionByName<'a> { 676 // Constructor 677 pub fn create(ceed: &'a crate::Ceed, name: &str) -> crate::Result<Self> { 678 let name_c = CString::new(name).expect("CString::new failed"); 679 let mut ptr = std::ptr::null_mut(); 680 let ierr = unsafe { 681 bind_ceed::CeedQFunctionCreateInteriorByName(ceed.ptr, name_c.as_ptr(), &mut ptr) 682 }; 683 ceed.check_error(ierr)?; 684 Ok(Self { 685 qf_core: QFunctionCore { 686 ptr, 687 _lifeline: PhantomData, 688 }, 689 }) 690 } 691 692 /// Apply the action of a QFunction 693 /// 694 /// * `Q` - The number of quadrature points 695 /// * `input` - Array of input Vectors 696 /// * `output` - Array of output Vectors 697 /// 698 /// ``` 699 /// # use libceed::prelude::*; 700 /// # fn main() -> Result<(), libceed::CeedError> { 701 /// # let ceed = libceed::Ceed::default_init(); 702 /// const Q: usize = 8; 703 /// let qf_build = ceed.q_function_interior_by_name("Mass1DBuild")?; 704 /// let qf_mass = ceed.q_function_interior_by_name("MassApply")?; 705 /// 706 /// let mut j = [0.; Q]; 707 /// let mut w = [0.; Q]; 708 /// let mut u = [0.; Q]; 709 /// let mut v = [0.; Q]; 710 /// 711 /// for i in 0..Q { 712 /// let x = 2. * (i as Scalar) / ((Q as Scalar) - 1.) - 1.; 713 /// j[i] = 1.; 714 /// w[i] = 1. - x * x; 715 /// u[i] = 2. + 3. * x + 5. * x * x; 716 /// v[i] = w[i] * u[i]; 717 /// } 718 /// 719 /// let jj = ceed.vector_from_slice(&j)?; 720 /// let ww = ceed.vector_from_slice(&w)?; 721 /// let uu = ceed.vector_from_slice(&u)?; 722 /// let mut vv = ceed.vector(Q)?; 723 /// vv.set_value(0.0); 724 /// let mut qdata = ceed.vector(Q)?; 725 /// qdata.set_value(0.0); 726 /// 727 /// { 728 /// let mut input = vec![jj, ww]; 729 /// let mut output = vec![qdata]; 730 /// qf_build.apply(Q, &input, &output)?; 731 /// qdata = output.remove(0); 732 /// } 733 /// 734 /// { 735 /// let mut input = vec![qdata, uu]; 736 /// let mut output = vec![vv]; 737 /// qf_mass.apply(Q, &input, &output)?; 738 /// vv = output.remove(0); 739 /// } 740 /// 741 /// vv.view() 742 /// .iter() 743 /// .zip(v.iter()) 744 /// .for_each(|(computed, actual)| { 745 /// assert_eq!( 746 /// *computed, *actual, 747 /// "Incorrect value in QFunction application" 748 /// ); 749 /// }); 750 /// # Ok(()) 751 /// # } 752 /// ``` 753 pub fn apply(&self, Q: usize, u: &[Vector], v: &[Vector]) -> crate::Result<i32> { 754 self.qf_core.apply(Q, u, v) 755 } 756 } 757 758 // ----------------------------------------------------------------------------- 759