src/dolfinx_access/function.rs

changeset 3
c3a4f4bb87f7
parent 1
a4137aedcb3a
equal deleted inserted replaced
1:a4137aedcb3a 3:c3a4f4bb87f7
1 /*! 1 /*!
2 Python and C++ wrapper for Dolfinx Function<f64> 2 Python and C++ wrapper for Dolfinx `Function<f64>`.
3 */ 3 */
4 4
5 use super::ffi; 5 use super::ffi;
6 use super::ffi::Function_f64; 6 use super::ffi::Function_f64;
7 use alg_tools::bounds::{Bounds, GlobalAnalysis, MinMaxMapping}; 7 use alg_tools::bounds::{Bounds, GlobalAnalysis, MinMaxMapping};
41 owned: bool, 41 owned: bool,
42 /// C++ object pointer 42 /// C++ object pointer
43 cxx: *mut Function_f64, 43 cxx: *mut Function_f64,
44 } 44 }
45 45
46 /// References to Python side helper routines
46 struct Helpers { 47 struct Helpers {
48 /// Dot product
47 dot: Py<PyFunction>, 49 dot: Py<PyFunction>,
50 /// Squared 2-norm
48 norm2_squared: Py<PyFunction>, 51 norm2_squared: Py<PyFunction>,
52 /// Squared distance
49 dist2_squared: Py<PyFunction>, 53 dist2_squared: Py<PyFunction>,
50 } 54 }
51 55
52 use std::sync::OnceLock; 56 use std::sync::OnceLock;
57 /// A populated-once structure for referencing Python-side helper routines.
53 static HELPERS: OnceLock<Helpers> = OnceLock::new(); 58 static HELPERS: OnceLock<Helpers> = OnceLock::new();
54 59
60 /// Gets [`Helpers`], populates [`HELPERS`] if needed.
55 fn get_helpers(py: Python<'_>) -> PyResult<&Helpers> { 61 fn get_helpers(py: Python<'_>) -> PyResult<&Helpers> {
56 HELPERS.get_or_try_init(|| { 62 HELPERS.get_or_try_init(|| {
57 let extras = PyModule::import(py, "pointsource_pde.dolfinx_extras")?; 63 let extras = PyModule::import(py, "pointsource_pde.dolfinx_extras")?;
58 let g = |s| -> PyResult<Py<PyFunction>> { Ok(extras.getattr(s)?.cast_into()?.unbind()) }; 64 let g = |s| -> PyResult<Py<PyFunction>> { Ok(extras.getattr(s)?.cast_into()?.unbind()) };
59 Ok(Helpers { 65 Ok(Helpers {
70 if let None = self.u { 76 if let None = self.u {
71 if self.cxx != std::ptr::null_mut() { 77 if self.cxx != std::ptr::null_mut() {
72 unsafe { ffi::drop_Function_f64(self.cxx) }; 78 unsafe { ffi::drop_Function_f64(self.cxx) };
73 } 79 }
74 } 80 }
81 self.cxx = std::ptr::null_mut();
75 } 82 }
76 } 83 }
77 84
78 impl<'py, const N: u32, const O: u32, const D: u32> Clone for DolfinxPyFunction_f64<'py, N, O, D> { 85 impl<'py, const N: u32, const O: u32, const D: u32> Clone for DolfinxPyFunction_f64<'py, N, O, D> {
79 fn clone(&self) -> Self { 86 fn clone(&self) -> Self {
80 DolfinxPyFunction_f64 { 87 DolfinxPyFunction_f64 {
81 u: None, // It's a different new function, so no Python instance 88 u: None, // It's a different new function, so no Python instance
82 function_space: self.function_space.clone(), 89 function_space: self.function_space.clone(),
83 owned: self.owned, 90 owned: true,
84 cxx: unsafe { ffi::clone_Function_f64(self.cxx) }, 91 cxx: unsafe { ffi::clone_Function_f64(self.cxx) },
85 } 92 }
86 } 93 }
87 } 94 }
88 95
133 } 140 }
134 } 141 }
135 142
136 /// Returns a (possibly new) Python presentation, without consuming current object. 143 /// Returns a (possibly new) Python presentation, without consuming current object.
137 /// 144 ///
138 /// Please use this functionality through [`pyo3::types::IntoPyObject`]. 145 /// Please use this functionality through [`pyo3::IntoPyObject`].
139 pub(crate) fn _into_py_ref<'a>( 146 pub(crate) fn _into_py_ref<'a>(
140 &'a mut self, 147 &'a mut self,
141 py: Python<'py>, 148 py: Python<'py>,
142 ) -> PyResult<pyo3::Borrowed<'a, 'py, PyAny>> { 149 ) -> PyResult<pyo3::Borrowed<'a, 'py, PyAny>> {
143 self._make_py(py); 150 self._make_py(py);
148 } 155 }
149 } 156 }
150 157
151 /// Returns a (possibly new) Python presentation, consuming current object. 158 /// Returns a (possibly new) Python presentation, consuming current object.
152 /// 159 ///
153 /// Please use this functionality through [`pyo3::types::IntoPyObject`]. 160 /// Please use this functionality through [`pyo3::IntoPyObject`].
154 pub(crate) fn _into_py(mut self, py: Python<'py>) -> PyResult<pyo3::Bound<'py, PyAny>> { 161 pub(crate) fn _into_py(mut self, py: Python<'py>) -> PyResult<pyo3::Bound<'py, PyAny>> {
155 self._make_py(py); 162 self._make_py(py);
156 self.cxx = std::ptr::null_mut(); 163 self.cxx = std::ptr::null_mut();
157 self.owned = false; 164 self.owned = false;
158 match std::mem::replace(&mut self.u, None) { 165 match std::mem::replace(&mut self.u, None) {
179 } 186 }
180 new 187 new
181 } 188 }
182 } 189 }
183 190
191 /// An extremely durty hack to create a Python-side Dolfinx `Function` referencing
192 /// a C++-side Dolfinx `Function` that we already have. For some unfathomable reason,
193 /// Dolfinx provies no way to do this cleanly.
184 pub(crate) static CREATE_FUNCTION_HACK: &CStr = c_str!( 194 pub(crate) static CREATE_FUNCTION_HACK: &CStr = c_str!(
185 "\ 195 "\
186 from dolfinx.fem import Function 196 from dolfinx.fem import Function
187 from dolfinx.la import Vector 197 from dolfinx.la import Vector
188 x = Vector(cpp_object.x) 198 x = Vector(cpp_object.x)
196 wrapper._x = x 206 wrapper._x = x
197 wrapper.name = \"?\" 207 wrapper.name = \"?\"
198 " 208 "
199 ); 209 );
200 210
211 /// Trait for points that can be expanded into Dolfinx' always-3D presentation
201 trait ExpandCoordDolphinx { 212 trait ExpandCoordDolphinx {
213 /// Convert to always-3D presentation
202 fn to_dolphinx(self) -> [f64; 3]; 214 fn to_dolphinx(self) -> [f64; 3];
203 #[allow(dead_code)] 215 #[allow(dead_code)]
216 /// Convert from always-3D presentation
204 fn from_dolphinx(val: [f64; 3]) -> Self; 217 fn from_dolphinx(val: [f64; 3]) -> Self;
205 } 218 }
206 219
207 impl ExpandCoordDolphinx for Loc<1, f64> { 220 impl ExpandCoordDolphinx for Loc<1, f64> {
208 #[inline] 221 #[inline]
241 fn from_dolphinx([x1, x2, x3]: [f64; 3]) -> Self { 254 fn from_dolphinx([x1, x2, x3]: [f64; 3]) -> Self {
242 [x1, x2, x3].into() 255 [x1, x2, x3].into()
243 } 256 }
244 } 257 }
245 258
259 /// Implements [`DifferentiableImpl`]
246 macro_rules! impl_diff { 260 macro_rules! impl_diff {
247 ($n:literal ; $($o:literal)+) => { $( 261 ($n:literal ; $($o:literal)+) => { $(
248 impl<'py> DifferentiableImpl<Loc<$n, f64>> for DolfinxPyFunction_f64<'py, $n, $o, 1> { 262 impl<'py> DifferentiableImpl<Loc<$n, f64>> for DolfinxPyFunction_f64<'py, $n, $o, 1> {
249 type Derivative = Loc<$n, f64>; 263 type Derivative = Loc<$n, f64>;
250 264
272 } 286 }
273 } 287 }
274 )+ }; 288 )+ };
275 } 289 }
276 290
291 /// Implements [`Mapping`]
277 macro_rules! impl_mapping { 292 macro_rules! impl_mapping {
278 ($($n:literal)+) => { $( 293 ($($n:literal)+) => { $(
279 impl<'py, const O: u32> Mapping<Loc<$n, f64>> for DolfinxPyFunction_f64<'py, $n, O, 1> { 294 impl<'py, const O: u32> Mapping<Loc<$n, f64>> for DolfinxPyFunction_f64<'py, $n, O, 1> {
280 type Codomain = f64; 295 type Codomain = f64;
281 296
340 } 355 }
341 } 356 }
342 */ 357 */
343 358
344 impl<'py, const N: u32, const O: u32, const D: u32> DolfinxPyFunction_f64<'py, N, O, D> { 359 impl<'py, const N: u32, const O: u32, const D: u32> DolfinxPyFunction_f64<'py, N, O, D> {
360 fn new_cxx(&mut self, new: *mut Function_f64) {
361 if let None = self.u {
362 if self.cxx != std::ptr::null_mut() {
363 unsafe { ffi::drop_Function_f64(self.cxx) };
364 }
365 }
366 self.cxx = new;
367 self.u = None;
368 self.owned = true;
369 }
370
345 /// If owned, call `f_valid` with mutable data. 371 /// If owned, call `f_valid` with mutable data.
346 /// If not owned, create a new C++ fem.Function, uninitialised, and call `f_invalid` with its 372 /// If not owned, create a new C++ fem.Function, uninitialised, and call `f_invalid` with its
347 /// mutable data, as well as the original immutable data. 373 /// mutable data, as well as the original immutable data.
348 #[inline] 374 #[inline]
349 fn with_mut_data_and_orig<F, G, Z>(&mut self, f_valid: F, f_invalid: G) -> Z 375 fn with_mut_data_and_orig<F, G, Z>(&mut self, f_valid: F, f_invalid: G) -> Z
350 where 376 where
351 F: Fn(&mut [f64]) -> Z, 377 F: Fn(&mut [f64]) -> Z,
352 G: Fn(&mut [f64], &[f64]) -> Z, 378 G: Fn(&mut [f64], &[f64]) -> Z,
353 { 379 {
354 if self.owned { 380 if self.owned {
355 return f_valid(unsafe { ffi::data_mut_Function_f64(self.cxx) }); 381 let res = f_valid(unsafe { ffi::data_mut_Function_f64(self.cxx) });
382 unsafe { ffi::scatter_fwd_Function_f64(self.cxx) };
383 return res;
356 } else { 384 } else {
357 // Create a completely new Function 385 // Create a completely new Function
358 let old_data = unsafe { ffi::data_mut_Function_f64(self.cxx) }; 386 let old_data = unsafe { ffi::data_mut_Function_f64(self.cxx) };
359 let new = unsafe { ffi::similar_Function_f64(self.cxx) }; 387 let new = unsafe { ffi::similar_Function_f64(self.cxx) };
360 let new_data = unsafe { ffi::data_mut_Function_f64(new) }; 388 let new_data = unsafe { ffi::data_mut_Function_f64(new) };
361 let res = f_invalid(new_data, old_data); 389 let res = f_invalid(new_data, old_data);
390 unsafe { ffi::scatter_fwd_Function_f64(new) };
362 // Invalidate Python reference; we're managing the object now 391 // Invalidate Python reference; we're managing the object now
363 self.cxx = new; 392 self.new_cxx(new);
364 self.u = None;
365 self.owned = true;
366 return res; 393 return res;
367 } 394 }
368 } 395 }
369 396
370 /// If owned, call `f` with mutable data. 397 /// If owned, call `f` with mutable data.
374 fn with_mut_data<F, Z>(&mut self, f: F) -> Z 401 fn with_mut_data<F, Z>(&mut self, f: F) -> Z
375 where 402 where
376 F: Fn(&mut [f64]) -> Z, 403 F: Fn(&mut [f64]) -> Z,
377 { 404 {
378 if self.owned { 405 if self.owned {
379 return f(unsafe { ffi::data_mut_Function_f64(self.cxx) }); 406 let res = f(unsafe { ffi::data_mut_Function_f64(self.cxx) });
407 unsafe { ffi::scatter_fwd_Function_f64(self.cxx) };
408 return res;
380 } else { 409 } else {
381 // Create a completely new Function 410 // Create a completely new Function
382 let new = unsafe { ffi::similar_Function_f64(self.cxx) }; 411 let new = unsafe { ffi::similar_Function_f64(self.cxx) };
383 let new_data = unsafe { ffi::data_mut_Function_f64(new) }; 412 let new_data = unsafe { ffi::data_mut_Function_f64(new) };
384 let res = f(new_data); 413 let res = f(new_data);
414 unsafe { ffi::scatter_fwd_Function_f64(new) };
385 // Invalidate Python reference; we're managing the object now 415 // Invalidate Python reference; we're managing the object now
386 self.cxx = new; 416 self.new_cxx(new);
387 self.u = None;
388 self.owned = true;
389 return res; 417 return res;
390 } 418 }
391 } 419 }
392 420
393 /// Create a similar new function, and call `f` with both the mutable (unininitialised) 421 /// Create a similar new function, and call `f` with both the mutable (unininitialised)
402 // Create a completely new Function 430 // Create a completely new Function
403 let new = self.similar(); 431 let new = self.similar();
404 let old_data = unsafe { ffi::data_Function_f64(self.cxx) }; 432 let old_data = unsafe { ffi::data_Function_f64(self.cxx) };
405 let new_data = unsafe { ffi::data_mut_Function_f64(new.cxx) }; 433 let new_data = unsafe { ffi::data_mut_Function_f64(new.cxx) };
406 f(new_data, old_data); 434 f(new_data, old_data);
435 unsafe { ffi::scatter_fwd_Function_f64(new.cxx) };
407 new 436 new
408 } 437 }
409 438
410 /// Similar to [`with_mut_data`], but check compatibility with `other` and also pass its 439 /// Similar to [`Self::with_mut_data`], but check compatibility with `other` and also pass its
411 /// immutable data slice as the final argument. 440 /// immutable data slice as the final argument.
412 #[inline] 441 #[inline]
413 fn with_mut_data_and_orig_binop<F, G, Z>( 442 fn with_mut_data_and_orig_binop<F, G, Z>(
414 &mut self, 443 &mut self,
415 other: &DolfinxPyFunction_f64<'py, N, O, D>, 444 other: &DolfinxPyFunction_f64<'py, N, O, D>,
422 { 451 {
423 let data_other = unsafe { 452 let data_other = unsafe {
424 ffi::check_compat_Function_f64(self.cxx, other.cxx).unwrap(); 453 ffi::check_compat_Function_f64(self.cxx, other.cxx).unwrap();
425 ffi::data_Function_f64(other.cxx) 454 ffi::data_Function_f64(other.cxx)
426 }; 455 };
427 self.with_mut_data_and_orig( 456 let res = self.with_mut_data_and_orig(
428 |data_self_valid| { 457 |data_self_valid| {
429 assert_eq!(data_self_valid.len(), data_other.len()); 458 assert_eq!(data_self_valid.len(), data_other.len());
430 f_valid(data_self_valid, data_other) 459 f_valid(data_self_valid, data_other)
431 }, 460 },
432 |data_self_invalid, data_orig| { 461 |data_self_invalid, data_orig| {
433 assert_eq!(data_self_invalid.len(), data_other.len()); 462 assert_eq!(data_self_invalid.len(), data_other.len());
434 assert_eq!(data_orig.len(), data_other.len()); 463 assert_eq!(data_orig.len(), data_other.len());
435 f_invalid(data_self_invalid, data_orig, data_other) 464 f_invalid(data_self_invalid, data_orig, data_other)
436 }, 465 },
437 ) 466 );
438 } 467 unsafe { ffi::scatter_fwd_Function_f64(self.cxx) };
439 468 return res;
440 /// Similar to [`with_new_data_and_orig`], but check compatibility with `other` and also 469 }
470
471 /// Similar to [`Self::with_new_data_and_orig`], but check compatibility with `other` and also
441 /// pass its immutable data slice as the final argument. 472 /// pass its immutable data slice as the final argument.
442 #[inline] 473 #[inline]
443 fn with_new_data_and_orig_binop<F>( 474 fn with_new_data_and_orig_binop<F>(
444 &self, 475 &self,
445 other: &DolfinxPyFunction_f64<'py, N, O, D>, 476 other: &DolfinxPyFunction_f64<'py, N, O, D>,
458 f(data, data_orig, data_other) 489 f(data, data_orig, data_other)
459 }) 490 })
460 } 491 }
461 } 492 }
462 493
494 /// Implements unary operations
463 macro_rules! impl_unop { 495 macro_rules! impl_unop {
464 ($trait:ident, $fn:ident) => { 496 ($trait:ident, $fn:ident) => {
465 impl<'py, const N: u32, const O: u32, const D: u32> $trait 497 impl<'py, const N: u32, const O: u32, const D: u32> $trait
466 for DolfinxPyFunction_f64<'py, N, O, D> 498 for DolfinxPyFunction_f64<'py, N, O, D>
467 { 499 {
498 530
499 use std::ops::Neg; 531 use std::ops::Neg;
500 532
501 impl_unop!(Neg, neg); 533 impl_unop!(Neg, neg);
502 534
535 /// Implements scalar operations
503 macro_rules! impl_scalarop { 536 macro_rules! impl_scalarop {
504 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { 537 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
505 impl<'py, const N: u32, const O: u32, const D: u32> $trait<f64> 538 impl<'py, const N: u32, const O: u32, const D: u32> $trait<f64>
506 for DolfinxPyFunction_f64<'py, N, O, D> 539 for DolfinxPyFunction_f64<'py, N, O, D>
507 { 540 {
562 use std::ops::{Div, DivAssign, Mul, MulAssign}; 595 use std::ops::{Div, DivAssign, Mul, MulAssign};
563 596
564 impl_scalarop!(Mul, mul, MulAssign, mul_assign); 597 impl_scalarop!(Mul, mul, MulAssign, mul_assign);
565 impl_scalarop!(Div, div, DivAssign, div_assign); 598 impl_scalarop!(Div, div, DivAssign, div_assign);
566 599
600 /// Implements consuming binary operations
567 macro_rules! impl_binop_consume { 601 macro_rules! impl_binop_consume {
568 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { 602 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
569 impl<'py, const N: u32, const O: u32, const D: u32> $trait<Self> 603 impl<'py, const N: u32, const O: u32, const D: u32> $trait<Self>
570 for DolfinxPyFunction_f64<'py, N, O, D> 604 for DolfinxPyFunction_f64<'py, N, O, D>
571 { 605 {
633 } 667 }
634 } 668 }
635 }; 669 };
636 } 670 }
637 671
672 /// Implements binary operations on references
638 macro_rules! impl_binop_ref { 673 macro_rules! impl_binop_ref {
639 ($trait:ident, $fn:ident) => { 674 ($trait:ident, $fn:ident) => {
640 impl<'py, 'b, const N: u32, const O: u32, const D: u32> $trait<Self> 675 impl<'py, 'b, const N: u32, const O: u32, const D: u32> $trait<Self>
641 for &'b DolfinxPyFunction_f64<'py, N, O, D> 676 for &'b DolfinxPyFunction_f64<'py, N, O, D>
642 { 677 {
663 } 698 }
664 } 699 }
665 }; 700 };
666 } 701 }
667 702
703 /// Implements binary operations, both consuming and on references
668 macro_rules! impl_binop { 704 macro_rules! impl_binop {
669 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => { 705 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
670 impl_binop_consume!($trait, $fn, $trait_assign, $fn_assign); 706 impl_binop_consume!($trait, $fn, $trait_assign, $fn_assign);
671 impl_binop_ref!($trait, $fn); 707 impl_binop_ref!($trait, $fn);
672 }; 708 };
675 use std::ops::{Add, AddAssign, Sub, SubAssign}; 711 use std::ops::{Add, AddAssign, Sub, SubAssign};
676 712
677 impl_binop!(Add, add, AddAssign, add_assign); 713 impl_binop!(Add, add, AddAssign, add_assign);
678 impl_binop!(Sub, sub, SubAssign, sub_assign); 714 impl_binop!(Sub, sub, SubAssign, sub_assign);
679 715
680 //use alg_tools::euclidean::Euclidean;
681 use alg_tools::linops::{VectorSpace, AXPY}; 716 use alg_tools::linops::{VectorSpace, AXPY};
682 //use alg_tools::norms::{Dist, HasDual, Norm, Normed, L2};
683 use alg_tools::self_ownable; 717 use alg_tools::self_ownable;
684 718
685 self_ownable!(DolfinxPyFunction_f64<'py, N, O, D> where 'py, const N: u32, const O: u32, const D: u32); 719 self_ownable!(DolfinxPyFunction_f64<'py, N, O, D> where 'py, const N: u32, const O: u32, const D: u32);
686 720
687 impl<'py, const N: u32, const O: u32, const D: u32> Dist<L2, f64> 721 impl<'py, const N: u32, const O: u32, const D: u32> Dist<L2, f64>

mercurial