| 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 |
| 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 |
| 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> |