feectools 0.1.7__tar.gz → 0.1.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. {feectools-0.1.7/feectools.egg-info → feectools-0.1.10}/PKG-INFO +1 -1
  2. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/bsplines.py +47 -24
  3. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/bsplines_kernels.py +22 -4
  4. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/blocking_data_exchanger.py +2 -5
  5. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/cart.py +36 -7
  6. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/mpi.py +5 -0
  7. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/partition.py +1 -5
  8. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/petsc.py +3 -6
  9. {feectools-0.1.7 → feectools-0.1.10}/feectools/feec/global_geometric_projectors.py +81 -13
  10. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/grid.py +4 -5
  11. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/partitioning.py +11 -2
  12. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/splines.py +3 -3
  13. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tensor.py +1 -0
  14. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/utilities.py +3 -5
  15. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/basic.py +1 -1
  16. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/solvers.py +224 -4
  17. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/stencil.py +127 -17
  18. {feectools-0.1.7 → feectools-0.1.10}/feectools/utilities/quadratures.py +1 -2
  19. {feectools-0.1.7 → feectools-0.1.10}/feectools/utilities/utils.py +40 -11
  20. {feectools-0.1.7 → feectools-0.1.10/feectools.egg-info}/PKG-INFO +1 -1
  21. {feectools-0.1.7 → feectools-0.1.10}/pyproject.toml +1 -1
  22. {feectools-0.1.7 → feectools-0.1.10}/AUTHORS +0 -0
  23. {feectools-0.1.7 → feectools-0.1.10}/LICENSE +0 -0
  24. {feectools-0.1.7 → feectools-0.1.10}/README.md +0 -0
  25. {feectools-0.1.7 → feectools-0.1.10}/feectools/__init__.py +0 -0
  26. {feectools-0.1.7 → feectools-0.1.10}/feectools/accelerate/__init__.py +0 -0
  27. {feectools-0.1.7 → feectools-0.1.10}/feectools/accelerate/accelerate.py +0 -0
  28. {feectools-0.1.7 → feectools-0.1.10}/feectools/accelerate/compile_psydac.mk +0 -0
  29. {feectools-0.1.7 → feectools-0.1.10}/feectools/api/__init__.py +0 -0
  30. {feectools-0.1.7 → feectools-0.1.10}/feectools/api/essential_bc.py +0 -0
  31. {feectools-0.1.7 → feectools-0.1.10}/feectools/api/fem_bilinear_form.py +0 -0
  32. {feectools-0.1.7 → feectools-0.1.10}/feectools/api/fem_common.py +0 -0
  33. {feectools-0.1.7 → feectools-0.1.10}/feectools/api/fem_sum_form.py +0 -0
  34. {feectools-0.1.7 → feectools-0.1.10}/feectools/api/settings.py +0 -0
  35. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/__init__.py +0 -0
  36. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/field_evaluation_kernels.py +0 -0
  37. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/tests/__init__.py +0 -0
  38. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/tests/test_bsplines.py +0 -0
  39. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/tests/test_bsplines_kernel.py +0 -0
  40. {feectools-0.1.7 → feectools-0.1.10}/feectools/core/tests/test_bsplines_pyccel.py +0 -0
  41. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/__init__.py +0 -0
  42. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/basic.py +0 -0
  43. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/interface_data_exchanger.py +0 -0
  44. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/nonblocking_data_exchanger.py +0 -0
  45. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/tests/__init__.py +0 -0
  46. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/tests/test_cart_1d.py +0 -0
  47. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/tests/test_cart_2d.py +0 -0
  48. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/tests/test_cart_3d.py +0 -0
  49. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/tests/test_multicart_2d.py +0 -0
  50. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/tests/test_partition.py +0 -0
  51. {feectools-0.1.7 → feectools-0.1.10}/feectools/ddm/utilities.py +0 -0
  52. {feectools-0.1.7 → feectools-0.1.10}/feectools/feec/__init__.py +0 -0
  53. {feectools-0.1.7 → feectools-0.1.10}/feectools/feec/derivatives.py +0 -0
  54. {feectools-0.1.7 → feectools-0.1.10}/feectools/feec/dof_kernels.py +0 -0
  55. {feectools-0.1.7 → feectools-0.1.10}/feectools/feec/hodge.py +0 -0
  56. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/__init__.py +0 -0
  57. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/basic.py +0 -0
  58. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/lst_preconditioner.py +0 -0
  59. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/projectors.py +0 -0
  60. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/__init__.py +0 -0
  61. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/analytical_profiles_1d.py +0 -0
  62. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/analytical_profiles_base.py +0 -0
  63. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/splines_error_bounds.py +0 -0
  64. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_dirichlet_projectors.py +0 -0
  65. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_spline_histopolation.py +0 -0
  66. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_spline_interpolation.py +0 -0
  67. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_splines.py +0 -0
  68. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_splines_par.py +0 -0
  69. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_tensor.py +0 -0
  70. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/tests/test_vector_spaces.py +0 -0
  71. {feectools-0.1.7 → feectools-0.1.10}/feectools/fem/vector.py +0 -0
  72. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/__init__.py +0 -0
  73. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/block.py +0 -0
  74. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/direct_solvers.py +0 -0
  75. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/fft.py +0 -0
  76. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/__init__.py +0 -0
  77. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/axpy_kernels.py +0 -0
  78. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/inner_kernels.py +0 -0
  79. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/matvec_kernels.py +0 -0
  80. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/stencil2IJV_kernels.py +0 -0
  81. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/stencil2coo_kernels.py +0 -0
  82. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kernels/transpose_kernels.py +0 -0
  83. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/kron.py +0 -0
  84. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/memory.py +0 -0
  85. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/sparse.py +0 -0
  86. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/stencil_dot_kernels.py +0 -0
  87. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/stencil_transpose_kernels.py +0 -0
  88. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/__init__.py +0 -0
  89. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_block.py +0 -0
  90. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_fft.py +0 -0
  91. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_kron_stencil_matrix.py +0 -0
  92. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_linalg.py +0 -0
  93. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_matrix_free.py +0 -0
  94. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_solvers.py +0 -0
  95. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_stencil_interface_matrix.py +0 -0
  96. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_stencil_vector.py +0 -0
  97. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/test_stencil_vector_space.py +0 -0
  98. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/tests/utilities.py +0 -0
  99. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/topetsc.py +0 -0
  100. {feectools-0.1.7 → feectools-0.1.10}/feectools/linalg/utilities.py +0 -0
  101. {feectools-0.1.7 → feectools-0.1.10}/feectools/utilities/__init__.py +0 -0
  102. {feectools-0.1.7 → feectools-0.1.10}/feectools/version.py +0 -0
  103. {feectools-0.1.7 → feectools-0.1.10}/feectools.egg-info/SOURCES.txt +0 -0
  104. {feectools-0.1.7 → feectools-0.1.10}/feectools.egg-info/dependency_links.txt +0 -0
  105. {feectools-0.1.7 → feectools-0.1.10}/feectools.egg-info/entry_points.txt +0 -0
  106. {feectools-0.1.7 → feectools-0.1.10}/feectools.egg-info/requires.txt +0 -0
  107. {feectools-0.1.7 → feectools-0.1.10}/feectools.egg-info/top_level.txt +0 -0
  108. {feectools-0.1.7 → feectools-0.1.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: feectools
3
- Version: 0.1.7
3
+ Version: 0.1.10
4
4
  Summary: Slimmed-down fork of Psydac (https://github.com/pyccel/psydac) with less functionality and fewer dependencies.
5
5
  Author-email: Psydac development team <psydac@googlegroups.com>
6
6
  Maintainer-email: Stefan Possanner <stefan.possanner@ipp.mpg.de>, Max Lindqvist <max.lindqvist@ipp.mpg.de>, Yaman Güçlü <yaman.guclu@gmail.com>, Martin Campos Pinto <martin.campos-pinto@ipp.mpg.de>, Ahmed Ratnani <ratnaniahmed@gmail.com>
@@ -17,6 +17,7 @@ References:
17
17
  """
18
18
  import cunumpy as xp
19
19
  from cunumpy.xp import array_backend
20
+ import numpy as np
20
21
 
21
22
  from feectools.core.bsplines_kernels import (find_span_p,
22
23
  find_spans_p,
@@ -352,9 +353,9 @@ def collocation_matrix(knots, degree, periodic, normalization, xgrid, out=None,
352
353
  if periodic:
353
354
  nb -= degree + 1 - multiplicity
354
355
 
355
- out = xp.zeros((int(xgrid.shape[0]), int(nb)), dtype=float)
356
+ out = np.zeros((int(xgrid.shape[0]), int(nb)), dtype=float)
356
357
  else:
357
- assert out.shape == ((int(xgrid.shape[0]), int(nb))) and out.dtype == xp.dtype('float')
358
+ assert out.shape == ((int(xgrid.shape[0]), int(nb))) and out.dtype == np.dtype('float')
358
359
 
359
360
  bool_normalization = normalization == "M"
360
361
  multiplicity = int(multiplicity)
@@ -437,9 +438,9 @@ def histopolation_matrix(knots, degree, periodic, normalization, xgrid, multipli
437
438
 
438
439
  if out is None:
439
440
  if periodic:
440
- out = xp.zeros((len(xgrid), len(knots) - 2 * int(degree) - 2 + int(multiplicity)), dtype=float)
441
+ out = np.zeros((len(xgrid), len(knots) - 2 * int(degree) - 2 + int(multiplicity)), dtype=float)
441
442
  else:
442
- out = xp.zeros((len(xgrid) - 1, len(elevated_knots) - (int(degree) + 1) - 1 - 1), dtype=float)
443
+ out = np.zeros((len(xgrid) - 1, len(elevated_knots) - (int(degree) + 1) - 1 - 1), dtype=float)
443
444
  else:
444
445
  if periodic:
445
446
  assert out.shape == (len(xgrid), len(knots) - 2 * degree - 2 + multiplicity)
@@ -514,10 +515,15 @@ def greville(knots, degree, periodic, out=None, multiplicity=1):
514
515
  Abscissas of all Greville points.
515
516
 
516
517
  """
517
- knots = xp.ascontiguousarray(knots, dtype=float)
518
+ # Greville points are index arrays, keep on NumPy
519
+ if isinstance(knots, (list, tuple)):
520
+ knots = np.asarray(knots, dtype=float)
521
+ if hasattr(knots, 'get'):
522
+ knots = knots.get() # Convert CuPy to NumPy
523
+ knots = np.ascontiguousarray(knots, dtype=float)
518
524
  if out is None:
519
525
  n = len(knots) - 2 * degree - 2 + multiplicity if periodic else len(knots) - degree - 1
520
- out = xp.zeros(int(n))
526
+ out = np.zeros(int(n))
521
527
  multiplicity = int(multiplicity)
522
528
  greville_p(knots, degree, periodic, out, multiplicity)
523
529
  return out
@@ -568,7 +574,7 @@ def elements_spans(knots, degree, out=None):
568
574
  """
569
575
  knots = xp.ascontiguousarray(knots, dtype=float)
570
576
  if out is None:
571
- out = xp.zeros(len(knots), dtype=xp.int64)
577
+ out = np.zeros(len(knots), dtype=xp.int64)
572
578
  else:
573
579
  assert out.shape == knots.shape and out.dtype == xp.dtype('int64')
574
580
  i_final = elements_spans_p(knots, degree, out)
@@ -617,7 +623,11 @@ def make_knots(breaks, degree, periodic, multiplicity=1, out=None):
617
623
 
618
624
  # Consistency checks
619
625
  assert len(breaks) > 1
620
- assert all( xp.diff(breaks) > 0 )
626
+ # Convert to numpy for comparison since assertion needs Python bool
627
+ breaks_np = breaks.get() if hasattr(breaks, 'get') else breaks
628
+ if isinstance(breaks_np, (list, tuple)):
629
+ breaks_np = np.asarray(breaks_np)
630
+ assert all( np.diff(breaks_np) > 0 )
621
631
  assert degree >= 0
622
632
  assert 1 <= multiplicity and multiplicity <= degree + 1
623
633
  # Cast potential numpy.int64 into python native int
@@ -626,9 +636,14 @@ def make_knots(breaks, degree, periodic, multiplicity=1, out=None):
626
636
  if periodic:
627
637
  assert len(breaks) > degree
628
638
 
629
- breaks = xp.ascontiguousarray(breaks, dtype=float)
639
+ # Keep breaks on NumPy for initialization - knots are index arrays needed for CPU operations
640
+ breaks = np.asarray(breaks, dtype=float) if isinstance(breaks, (list, tuple)) else breaks
641
+ if hasattr(breaks, 'get'):
642
+ breaks = breaks.get() # Convert CuPy to NumPy
643
+ breaks = np.ascontiguousarray(breaks, dtype=float)
630
644
  if out is None:
631
- out = xp.zeros(multiplicity * len(breaks[1:-1]) + 2 + 2 * degree)
645
+ # Knots are index arrays, keep them on NumPy
646
+ out = np.zeros(multiplicity * len(breaks[1:-1]) + 2 + 2 * degree)
632
647
  else:
633
648
  assert out.shape == (multiplicity * len(breaks[1:-1]) + 2 + 2 * degree,) \
634
649
  and out.dtype == xp.dtype('float')
@@ -676,25 +691,29 @@ def elevate_knots(knots, degree, periodic, multiplicity=1, tol=1e-15, out=None):
676
691
  Knots sequence of spline space of degree p+1.
677
692
  """
678
693
  multiplicity = int(multiplicity)
679
- knots = xp.ascontiguousarray(knots, dtype=float)
694
+ if isinstance(knots, (list, tuple)):
695
+ knots = np.asarray(knots, dtype=float)
696
+ if hasattr(knots, 'get'):
697
+ knots = knots.get() # Convert CuPy to NumPy
698
+ knots = np.ascontiguousarray(knots, dtype=float)
680
699
  if out is None:
681
700
  if periodic:
682
- out = xp.zeros(knots.shape[0] + 2, dtype=float)
701
+ out = np.zeros(knots.shape[0] + 2, dtype=float)
683
702
  else:
684
703
  shape = 2*(degree + 2)
685
704
  if len(knots) - 2 * (degree + 1) > 0:
686
- uniques = (xp.diff(knots[degree + 1:-degree - 1]) > tol).nonzero()
705
+ uniques = (np.diff(knots[degree + 1:-degree - 1]) > tol).nonzero()
687
706
  shape += multiplicity * (1 + uniques[0].shape[0])
688
- out = xp.zeros(shape, dtype=float)
707
+ out = np.zeros(shape, dtype=float)
689
708
  else:
690
709
  if periodic:
691
- assert out.shape == (knots.shape[0] + 2,) and out.dtype == xp.dtype('float')
710
+ assert out.shape == (knots.shape[0] + 2,) and out.dtype == np.dtype('float')
692
711
  else:
693
712
  shape = 2*(degree + 2)
694
713
  if len(knots) - 2 * (degree + 1) > 0:
695
- uniques = (xp.diff(knots[degree + 1:-degree - 1]) > tol).nonzero()
714
+ uniques = (np.diff(knots[degree + 1:-degree - 1]) > tol).nonzero()
696
715
  shape += multiplicity * (1 + uniques[0].shape[0])
697
- assert out.shape == shape and out.dtype == xp.dtype('float')
716
+ assert out.shape == shape and out.dtype == np.dtype('float')
698
717
 
699
718
  elevate_knots_p(knots, degree, periodic, out, multiplicity, tol)
700
719
  return out
@@ -751,14 +770,18 @@ def quadrature_grid(breaks, quad_rule_x, quad_rule_w):
751
770
  assert min(quad_rule_x) >= -1
752
771
  assert max(quad_rule_x) <= +1
753
772
 
754
- breaks = xp.ascontiguousarray(breaks, dtype=float)
773
+ # Convert breaks to numpy if CuPy (breaks/grids should stay on CPU)
774
+ if hasattr(breaks, 'get'):
775
+ breaks = breaks.get()
776
+ breaks = np.ascontiguousarray(breaks, dtype=float)
755
777
 
756
778
  if array_backend.backend == "cupy":
757
- quad_rule_x = xp.ascontiguousarray(xp.array(quad_rule_x), dtype=float)
758
- quad_rule_w = xp.ascontiguousarray( xp.array(quad_rule_w), dtype=float )
759
- else:
760
- quad_rule_x = xp.ascontiguousarray(quad_rule_x, dtype=float)
761
- quad_rule_w = xp.ascontiguousarray( quad_rule_w, dtype=float )
779
+ # Convert CuPy arrays to NumPy
780
+ quad_rule_x = quad_rule_x.get() if hasattr(quad_rule_x, 'get') else quad_rule_x
781
+ quad_rule_w = quad_rule_w.get() if hasattr(quad_rule_w, 'get') else quad_rule_w
782
+
783
+ quad_rule_x = np.ascontiguousarray(quad_rule_x, dtype=float)
784
+ quad_rule_w = np.ascontiguousarray(quad_rule_w, dtype=float)
762
785
 
763
786
 
764
787
  out1 = xp.zeros((len(breaks) - 1, len(quad_rule_x)))
@@ -914,7 +937,7 @@ def cell_index(breaks, i_grid, tol=1e-15, out=None):
914
937
  breaks = xp.ascontiguousarray(breaks, dtype=float)
915
938
  i_grid = xp.ascontiguousarray(i_grid, dtype=float)
916
939
  if out is None:
917
- out = xp.zeros_like(i_grid, dtype=xp.int64)
940
+ out = np.zeros_like(i_grid, dtype=xp.int64)
918
941
  else:
919
942
  assert out.shape == i_grid.shape and out.dtype == xp.dtype('int64')
920
943
  status = cell_index_p(breaks, i_grid, tol, out)
@@ -7,6 +7,8 @@
7
7
  # This file holds the pyccelisable versions of the functions in bsplines.py
8
8
  # This will be changed once pyccel can return arrays and can get out=None arguments
9
9
  # like Numpy functions.
10
+ # NOTE: This file must use ONLY numpy for pyccel compilation compatibility.
11
+ # Backend conversion (NumPy/CuPy) happens at the Python wrapper level.
10
12
 
11
13
  from pyccel.decorators import pure
12
14
  from numpy import shape, abs
@@ -401,6 +403,9 @@ def basis_funs_all_ders_p(knots: 'float[:]', degree: int, x: float, span: int, n
401
403
  .. [1] L. Piegl and W. Tiller. The NURBS Book, 2nd ed.,
402
404
  Springer-Verlag Berlin Heidelberg GmbH, 1997.
403
405
  """
406
+ # Detect backend from output array
407
+ # Backend array operations removed - always use numpy
408
+
404
409
  sh_a = np.empty(2)
405
410
  sh_b = np.empty(2)
406
411
  left = np.empty(degree)
@@ -556,6 +561,9 @@ def collocation_matrix_p(knots: 'float[:]', degree: int, periodic: bool, normali
556
561
  multiplicity applies to each interior knot.
557
562
 
558
563
  """
564
+ # Detect backend from output array
565
+ # Backend array operations removed - always use numpy
566
+
559
567
  # Number of basis functions (in periodic case remove degree repeated elements)
560
568
  nb = len(knots)-degree-1
561
569
  if periodic:
@@ -565,7 +573,7 @@ def collocation_matrix_p(knots: 'float[:]', degree: int, periodic: bool, normali
565
573
  nx = len(xgrid)
566
574
 
567
575
  basis = np.zeros((nx, degree + 1))
568
- spans = np.zeros(nx, dtype=int)
576
+ spans = np.zeros(nx, dtype=int) # Keep indices on CPU
569
577
  find_spans_p(knots, degree, xgrid, spans)
570
578
  basis_funs_array_p(knots, degree, xgrid, spans, basis)
571
579
 
@@ -644,6 +652,9 @@ def histopolation_matrix_p(knots: 'float[:]', degree: int, periodic: bool, norma
644
652
  contains the integrals of each B-spline basis function :math:`B_j` between
645
653
  two successive grid points.
646
654
  """
655
+ # Detect backend from output array
656
+ # Backend array operations removed - always use numpy
657
+
647
658
  nb = len(knots) - degree - 1
648
659
  if periodic:
649
660
  nb -= degree + 1 - multiplicity
@@ -700,7 +711,7 @@ def histopolation_matrix_p(knots: 'float[:]', degree: int, periodic: bool, norma
700
711
  m = colloc.shape[0] - 1
701
712
  n = colloc.shape[1] - 1
702
713
 
703
- spans = np.zeros(colloc.shape[0], dtype=int)
714
+ spans = np.zeros(colloc.shape[0], dtype=int) # Keep indices on CPU
704
715
  for i in range(colloc.shape[0]):
705
716
  local_span = 0
706
717
  for j in range(colloc.shape[1]):
@@ -767,7 +778,10 @@ def merge_sort(a: 'float[:]'):
767
778
  """
768
779
  if len(a) != 1 and len(a) != 0:
769
780
  n = len(a)
770
-
781
+
782
+ # Detect backend and use the appropriate array module
783
+ # Backend array operations removed - always use numpy
784
+
771
785
  a1 = np.zeros(n // 2)
772
786
  a1[:] = a[:n // 2]
773
787
  a2 = np.zeros(n - n // 2)
@@ -1167,12 +1181,16 @@ def basis_ders_on_quad_grid_p(knots: 'float[:]', degree: int, quad_grid: 'float[
1167
1181
  """
1168
1182
  ne = quad_grid.shape[0]
1169
1183
  nq = quad_grid.shape[1]
1184
+
1185
+ # Detect backend from output array
1186
+ # Backend array operations removed - always use numpy
1187
+
1170
1188
  if normalization:
1171
1189
  integrals = np.zeros(knots.shape[0] - degree - 1)
1172
1190
  basis_integrals_p(knots, degree, integrals)
1173
1191
  scaling = 1.0 /integrals
1174
1192
 
1175
- temp_spans = np.zeros(len(knots), dtype=int)
1193
+ temp_spans = np.zeros(len(knots), dtype=int) # Keep indices on CPU
1176
1194
  actual_index = elements_spans_p(knots, degree, temp_spans)
1177
1195
  spans = temp_spans[:actual_index]
1178
1196
 
@@ -1,8 +1,5 @@
1
- #---------------------------------------------------------------------------#
2
- # This file is part of PSYDAC which is released under MIT License. See the #
3
- # LICENSE file or go to https://github.com/pyccel/psydac/blob/devel/LICENSE #
4
- # for full license details. #
5
- #---------------------------------------------------------------------------#
1
+ # coding: utf-8
2
+
6
3
  import cunumpy as xp
7
4
  import numpy as np
8
5
  from feectools.ddm.mpi import mpi as MPI
@@ -1,9 +1,20 @@
1
1
  # coding: utf-8
2
2
 
3
3
  import os
4
+ import numpy as np
4
5
  import cunumpy as xp
6
+ from cunumpy.xp import array_backend
5
7
  from itertools import product
6
8
 
9
+ # Initialize CUDA context before MPI if using CuPy backend
10
+ if array_backend.backend == "cupy":
11
+ try:
12
+ import cupy as cp
13
+ cp.cuda.Device(0).use()
14
+ cp.cuda.Stream.null.synchronize()
15
+ except Exception:
16
+ pass
17
+
7
18
  from feectools.ddm.mpi import mpi as MPI
8
19
  from feectools.ddm.mpi import MockMPI
9
20
  from feectools.ddm.partition import compute_dims, partition_procs_per_patch
@@ -43,6 +54,17 @@ def find_mpi_type( dtype ):
43
54
 
44
55
  return mpi_type
45
56
 
57
+ #===============================================================================
58
+ def _cuda_sync_before_mpi():
59
+ """Synchronize CUDA before MPI operations to avoid conflicts."""
60
+ if array_backend.backend == "cupy":
61
+ try:
62
+ import cupy as cp
63
+ cp.cuda.Stream.null.synchronize()
64
+ except Exception:
65
+ pass
66
+
67
+ #===============================================================================
46
68
  class MultiPatchDomainDecomposition:
47
69
  """
48
70
  Cartesian decomposition of multiple N-Cube grids.
@@ -260,11 +282,14 @@ class DomainDecomposition:
260
282
 
261
283
  if comm is None:
262
284
  # compute the coords for all processes
263
- self._global_coords = xp.array([xp.unravel_index(xp.int64(rank), nprocs) for rank in range(self._size)])
285
+ self._global_coords = np.array([np.unravel_index(int(rank), nprocs) for rank in range(self._size)])
264
286
  self._coords = self._global_coords[self._rank]
265
287
  self._rank_in_topo = 0
266
288
  self._ranks_in_topo = xp.array([0])
267
289
  else:
290
+ # Synchronize CUDA before MPI operations
291
+ _cuda_sync_before_mpi()
292
+
268
293
  # Create a MPI cart
269
294
  self._comm_cart = comm.Create_cart(
270
295
  dims = self._nprocs,
@@ -288,6 +313,7 @@ class DomainDecomposition:
288
313
  # Create (N-1)-dimensional communicators within the Cartesian topology
289
314
  self._subcomm = [None]*self._ndims
290
315
  for i in range(self._ndims):
316
+ _cuda_sync_before_mpi() # Synchronize before each Sub() call
291
317
  remain_dims = [i==j for j in range( self._ndims )]
292
318
  self._subcomm[i] = self._comm_cart.Sub( remain_dims )
293
319
 
@@ -467,8 +493,9 @@ class CartDecomposition():
467
493
  # Store input arguments
468
494
  self._domain_decomposition = domain_decomposition
469
495
  self._npts = tuple( npts )
470
- self._global_starts = tuple( [ xp.asarray(gs) for gs in global_starts] )
471
- self._global_ends = tuple( [ xp.asarray(ge) for ge in global_ends] )
496
+ # Convert to NumPy arrays for MPI compatibility (MPI can't handle CuPy arrays)
497
+ self._global_starts = tuple( [ np.asarray(gs.get() if hasattr(gs, 'get') else gs) for gs in global_starts] )
498
+ self._global_ends = tuple( [ np.asarray(ge.get() if hasattr(ge, 'get') else ge) for ge in global_ends] )
472
499
  self._pads = tuple( pads )
473
500
  self._shifts = tuple( shifts )
474
501
  self._periods = domain_decomposition.periods
@@ -494,10 +521,12 @@ class CartDecomposition():
494
521
 
495
522
  # Know my coordinates in the topology
496
523
  self._coords = domain_decomposition.coords
524
+ # Convert coords to NumPy for indexing (MPI coords should be on CPU)
525
+ coords_np = [c.get() if hasattr(c, 'get') else c for c in self._coords]
497
526
 
498
527
  # Start/end values of global indices (without ghost regions)
499
- self._starts = tuple( self._global_starts[axis][c] for axis,c in zip(range(self._ndims), self._coords) )
500
- self._ends = tuple( self._global_ends [axis][c] for axis,c in zip(range(self._ndims), self._coords) )
528
+ self._starts = tuple( self._global_starts[axis][c] for axis,c in zip(range(self._ndims), coords_np) )
529
+ self._ends = tuple( self._global_ends [axis][c] for axis,c in zip(range(self._ndims), coords_np) )
501
530
 
502
531
  # List of 1D global indices (without ghost regions)
503
532
  # self._grids = tuple( range(s,e+1) for s,e in zip( self._starts, self._ends ) )
@@ -906,7 +935,7 @@ class CartDecomposition():
906
935
  if len([i for i in shift if i==0]) == 2 and rank_dest != MPI.PROC_NULL:
907
936
  direction = [i for i,s in enumerate(shift) if s != 0][0]
908
937
  comm = self._subcomm[direction]
909
- # local_dest_rank = self._comm_cart.group.Translate_ranks(xp.array([rank_dest]), comm.group)[0]
938
+ # local_dest_rank = self._comm_cart.group.Translate_ranks(np.array([rank_dest]), comm.group)[0]
910
939
  local_dest_rank = self._comm_cart.group.Translate_ranks([int(rank_dest)], comm.group)[0]
911
940
 
912
941
  else:
@@ -921,7 +950,7 @@ class CartDecomposition():
921
950
  if len([i for i in shift if i==0]) == 2 and rank_source != MPI.PROC_NULL:
922
951
  direction = [i for i,s in enumerate(shift) if s != 0][0]
923
952
  comm = self._subcomm[direction]
924
- # local_source_rank = self._comm_cart.group.Translate_ranks(xp.array([rank_source]), comm.group)[0]
953
+ # local_source_rank = self._comm_cart.group.Translate_ranks(np.array([rank_source]), comm.group)[0]
925
954
  local_source_rank = self._comm_cart.group.Translate_ranks([int(rank_source)], comm.group)[0]
926
955
  else:
927
956
  local_source_rank = rank_source
@@ -81,6 +81,11 @@ class MockMPI:
81
81
 
82
82
 
83
83
  try:
84
+ # Disable MPI when using CuPy due to known segfault issues with OpenMPI + CUDA
85
+ import os
86
+ if os.environ.get('ARRAY_BACKEND') == 'cupy':
87
+ raise ImportError("MPI disabled when using CuPy backend")
88
+
84
89
  from mpi4py import MPI
85
90
 
86
91
  _comm = MPI.COMM_WORLD
@@ -1,9 +1,5 @@
1
- #---------------------------------------------------------------------------#
2
- # This file is part of PSYDAC which is released under MIT License. See the #
3
- # LICENSE file or go to https://github.com/pyccel/psydac/blob/devel/LICENSE #
4
- # for full license details. #
5
- #---------------------------------------------------------------------------#
6
1
  import cunumpy as xp
2
+ import numpy as np
7
3
  import numpy.ma as ma
8
4
 
9
5
  from sympy.ntheory import factorint
@@ -1,8 +1,6 @@
1
- #---------------------------------------------------------------------------#
2
- # This file is part of PSYDAC which is released under MIT License. See the #
3
- # LICENSE file or go to https://github.com/pyccel/psydac/blob/devel/LICENSE #
4
- # for full license details. #
5
- #---------------------------------------------------------------------------#
1
+ # coding: utf-8
2
+
3
+ import cunumpy as xp
6
4
  from itertools import product
7
5
 
8
6
  import cunumpy as xp
@@ -113,4 +111,3 @@ class PetscCart:
113
111
  to_is = self.petsc.IS().createGeneral(indices, comm=cart.comm)
114
112
 
115
113
  return self.petsc.Scatter().create(gvec, from_is, natural, to_is)
116
-
@@ -2,6 +2,7 @@
2
2
 
3
3
  import cunumpy as xp
4
4
  from cunumpy.xp import array_backend
5
+ import numpy as np
5
6
 
6
7
  from feectools.linalg.kron import KroneckerLinearSolver, KroneckerStencilMatrix
7
8
  from feectools.linalg.stencil import StencilMatrix, StencilVectorSpace
@@ -25,6 +26,18 @@ __all__ = ('GlobalGeometricProjector', 'GlobalGeometricProjectorH1', 'GlobalGeom
25
26
  'evaluate_dofs_2d_0form', 'evaluate_dofs_2d_1form_hcurl', 'evaluate_dofs_2d_1form_hdiv', 'evaluate_dofs_2d_2form',
26
27
  'evaluate_dofs_3d_0form', 'evaluate_dofs_3d_1form', 'evaluate_dofs_3d_2form', 'evaluate_dofs_3d_3form')
27
28
 
29
+
30
+ def _to_numpy_for_kernel(*args):
31
+ """Convert CuPy arrays to NumPy for compiled kernel calls."""
32
+ result = []
33
+ for arg in args:
34
+ if hasattr(arg, 'get'): # CuPy array
35
+ result.append(arg.get())
36
+ else:
37
+ result.append(arg)
38
+ return result if len(result) > 1 else result[0]
39
+
40
+
28
41
  #==============================================================================
29
42
  class GlobalGeometricProjector(metaclass=ABCMeta):
30
43
  """
@@ -207,8 +220,9 @@ class GlobalGeometricProjector(metaclass=ABCMeta):
207
220
  solvercells += [V._histopolator]
208
221
 
209
222
  # make 1D collocation matrix in stencil format
223
+ # Always use NumPy for indices since they're used for indexing/comparison
210
224
  if array_backend.backend == "cupy":
211
- row_indices, col_indices = xp.nonzero(xp.array(V.hmat))
225
+ row_indices, col_indices = np.nonzero(np.asarray(V.hmat))
212
226
  else:
213
227
  row_indices, col_indices = xp.nonzero(V.hmat)
214
228
 
@@ -800,7 +814,10 @@ def evaluate_dofs_1d_0form(
800
814
 
801
815
  F_temp = xp.zeros_like(F, order='C')
802
816
 
803
- dof_kernels.evaluate_dofs_1d_0form(F_temp, f_pts)
817
+ F_temp_np, f_pts_np = _to_numpy_for_kernel(F_temp, f_pts)
818
+ dof_kernels.evaluate_dofs_1d_0form(F_temp_np, f_pts_np)
819
+ if hasattr(F_temp, 'get'):
820
+ F_temp[:] = xp.asarray(F_temp_np)
804
821
 
805
822
  F[:] = F_temp
806
823
 
@@ -819,7 +836,10 @@ def evaluate_dofs_1d_1form(
819
836
  # call kernel
820
837
  F_temp = xp.zeros_like(F, order='C')
821
838
 
822
- dof_kernels.evaluate_dofs_1d_1form(quad_w1, F_temp, f_pts)
839
+ quad_w1_np, F_temp_np, f_pts_np = _to_numpy_for_kernel(quad_w1, F_temp, f_pts)
840
+ dof_kernels.evaluate_dofs_1d_1form(quad_w1_np, F_temp_np, f_pts_np)
841
+ if hasattr(F_temp, 'get'):
842
+ F_temp[:] = xp.asarray(F_temp_np)
823
843
 
824
844
  F[:] = F_temp
825
845
 
@@ -842,7 +862,10 @@ def evaluate_dofs_2d_0form(
842
862
 
843
863
  F_temp = xp.zeros_like(F, order='C')
844
864
 
845
- dof_kernels.evaluate_dofs_2d_0form(F_temp, f_pts)
865
+ F_temp_np, f_pts_np = _to_numpy_for_kernel(F_temp, f_pts)
866
+ dof_kernels.evaluate_dofs_2d_0form(F_temp_np, f_pts_np)
867
+ if hasattr(F_temp, 'get'):
868
+ F_temp[:] = xp.asarray(F_temp_np)
846
869
 
847
870
  F[:, :] = F_temp
848
871
 
@@ -869,7 +892,12 @@ def evaluate_dofs_2d_1form_hcurl(
869
892
  F1_temp = xp.zeros_like(F1, order='C')
870
893
  F2_temp = xp.zeros_like(F2, order='C')
871
894
 
872
- dof_kernels.evaluate_dofs_2d_1form_hcurl(quad_w1, quad_w2, F1_temp, F2_temp, f1_pts, f2_pts)
895
+ quad_w1_np, quad_w2_np, F1_temp_np, F2_temp_np, f1_pts_np, f2_pts_np = _to_numpy_for_kernel(quad_w1, quad_w2, F1_temp, F2_temp, f1_pts, f2_pts)
896
+ dof_kernels.evaluate_dofs_2d_1form_hcurl(quad_w1_np, quad_w2_np, F1_temp_np, F2_temp_np, f1_pts_np, f2_pts_np)
897
+ if hasattr(F1_temp, 'get'):
898
+ F1_temp[:] = xp.asarray(F1_temp_np)
899
+ if hasattr(F2_temp, 'get'):
900
+ F2_temp[:] = xp.asarray(F2_temp_np)
873
901
 
874
902
  F1[:, :] = F1_temp
875
903
  F2[:, :] = F2_temp
@@ -897,7 +925,12 @@ def evaluate_dofs_2d_1form_hdiv(
897
925
  F1_temp = xp.zeros_like(F1, order='C')
898
926
  F2_temp = xp.zeros_like(F2, order='C')
899
927
 
900
- dof_kernels.evaluate_dofs_2d_1form_hdiv(quad_w1, quad_w2, F1_temp, F2_temp, f1_pts, f2_pts)
928
+ quad_w1_np, quad_w2_np, F1_temp_np, F2_temp_np, f1_pts_np, f2_pts_np = _to_numpy_for_kernel(quad_w1, quad_w2, F1_temp, F2_temp, f1_pts, f2_pts)
929
+ dof_kernels.evaluate_dofs_2d_1form_hdiv(quad_w1_np, quad_w2_np, F1_temp_np, F2_temp_np, f1_pts_np, f2_pts_np)
930
+ if hasattr(F1_temp, 'get'):
931
+ F1_temp[:] = xp.asarray(F1_temp_np)
932
+ if hasattr(F2_temp, 'get'):
933
+ F2_temp[:] = xp.asarray(F2_temp_np)
901
934
 
902
935
  F1[:, :] = F1_temp
903
936
  F2[:, :] = F2_temp
@@ -917,7 +950,10 @@ def evaluate_dofs_2d_2form(
917
950
  # call kernel
918
951
  F_temp = xp.zeros_like(F, order='C')
919
952
 
920
- dof_kernels.evaluate_dofs_2d_2form(quad_w1, quad_w2, F_temp, f_pts)
953
+ quad_w1_np, quad_w2_np, F_temp_np, f_pts_np = _to_numpy_for_kernel(quad_w1, quad_w2, F_temp, f_pts)
954
+ dof_kernels.evaluate_dofs_2d_2form(quad_w1_np, quad_w2_np, F_temp_np, f_pts_np)
955
+ if hasattr(F_temp, 'get'):
956
+ F_temp[:] = xp.asarray(F_temp_np)
921
957
 
922
958
  F[:, :] = F_temp
923
959
 
@@ -940,7 +976,12 @@ def evaluate_dofs_2d_vec(
940
976
  F1_temp = xp.zeros_like(F1, order='C')
941
977
  F2_temp = xp.zeros_like(F2, order='C')
942
978
 
943
- dof_kernels.evaluate_dofs_2d_vec(F1_temp, F2_temp, f1_pts, f2_pts)
979
+ F1_temp_np, F2_temp_np, f1_pts_np, f2_pts_np = _to_numpy_for_kernel(F1_temp, F2_temp, f1_pts, f2_pts)
980
+ dof_kernels.evaluate_dofs_2d_vec(F1_temp_np, F2_temp_np, f1_pts_np, f2_pts_np)
981
+ if hasattr(F1_temp, 'get'):
982
+ F1_temp[:] = xp.asarray(F1_temp_np)
983
+ if hasattr(F2_temp, 'get'):
984
+ F2_temp[:] = xp.asarray(F2_temp_np)
944
985
 
945
986
  F1[:, :] = F1_temp
946
987
  F2[:, :] = F2_temp
@@ -965,7 +1006,10 @@ def evaluate_dofs_3d_0form(
965
1006
 
966
1007
  F_temp = xp.zeros_like(F, order='C')
967
1008
 
968
- dof_kernels.evaluate_dofs_3d_0form(F_temp, f_pts)
1009
+ F_temp_np, f_pts_np = _to_numpy_for_kernel(F_temp, f_pts)
1010
+ dof_kernels.evaluate_dofs_3d_0form(F_temp_np, f_pts_np)
1011
+ if hasattr(F_temp, 'get'):
1012
+ F_temp[:] = xp.asarray(F_temp_np)
969
1013
 
970
1014
  F[:, :, :] = F_temp
971
1015
 
@@ -997,7 +1041,14 @@ def evaluate_dofs_3d_1form(
997
1041
  F2_temp = xp.zeros_like(F2, order='C')
998
1042
  F3_temp = xp.zeros_like(F3, order='C')
999
1043
 
1000
- dof_kernels.evaluate_dofs_3d_1form(quad_w1, quad_w2, quad_w3, F1_temp, F2_temp, F3_temp, f1_pts, f2_pts, f3_pts)
1044
+ quad_w1_np, quad_w2_np, quad_w3_np, F1_temp_np, F2_temp_np, F3_temp_np, f1_pts_np, f2_pts_np, f3_pts_np = _to_numpy_for_kernel(quad_w1, quad_w2, quad_w3, F1_temp, F2_temp, F3_temp, f1_pts, f2_pts, f3_pts)
1045
+ dof_kernels.evaluate_dofs_3d_1form(quad_w1_np, quad_w2_np, quad_w3_np, F1_temp_np, F2_temp_np, F3_temp_np, f1_pts_np, f2_pts_np, f3_pts_np)
1046
+ if hasattr(F1_temp, 'get'):
1047
+ F1_temp[:] = xp.asarray(F1_temp_np)
1048
+ if hasattr(F2_temp, 'get'):
1049
+ F2_temp[:] = xp.asarray(F2_temp_np)
1050
+ if hasattr(F3_temp, 'get'):
1051
+ F3_temp[:] = xp.asarray(F3_temp_np)
1001
1052
 
1002
1053
  F1[:, :, :] = F1_temp
1003
1054
  F2[:, :, :] = F2_temp
@@ -1031,7 +1082,14 @@ def evaluate_dofs_3d_2form(
1031
1082
  F2_temp = xp.zeros_like(F2, order='C')
1032
1083
  F3_temp = xp.zeros_like(F3, order='C')
1033
1084
 
1034
- dof_kernels.evaluate_dofs_3d_2form(quad_w1, quad_w2, quad_w3, F1_temp, F2_temp, F3_temp, f1_pts, f2_pts, f3_pts)
1085
+ quad_w1_np, quad_w2_np, quad_w3_np, F1_temp_np, F2_temp_np, F3_temp_np, f1_pts_np, f2_pts_np, f3_pts_np = _to_numpy_for_kernel(quad_w1, quad_w2, quad_w3, F1_temp, F2_temp, F3_temp, f1_pts, f2_pts, f3_pts)
1086
+ dof_kernels.evaluate_dofs_3d_2form(quad_w1_np, quad_w2_np, quad_w3_np, F1_temp_np, F2_temp_np, F3_temp_np, f1_pts_np, f2_pts_np, f3_pts_np)
1087
+ if hasattr(F1_temp, 'get'):
1088
+ F1_temp[:] = xp.asarray(F1_temp_np)
1089
+ if hasattr(F2_temp, 'get'):
1090
+ F2_temp[:] = xp.asarray(F2_temp_np)
1091
+ if hasattr(F3_temp, 'get'):
1092
+ F3_temp[:] = xp.asarray(F3_temp_np)
1035
1093
 
1036
1094
  F1[:, :, :] = F1_temp
1037
1095
  F2[:, :, :] = F2_temp
@@ -1052,7 +1110,10 @@ def evaluate_dofs_3d_3form(
1052
1110
  # call kernel
1053
1111
  F_temp = xp.zeros_like(F, order='C')
1054
1112
 
1055
- dof_kernels.evaluate_dofs_3d_3form(quad_w1, quad_w2, quad_w3, F_temp, f_pts)
1113
+ quad_w1_np, quad_w2_np, quad_w3_np, F_temp_np, f_pts_np = _to_numpy_for_kernel(quad_w1, quad_w2, quad_w3, F_temp, f_pts)
1114
+ dof_kernels.evaluate_dofs_3d_3form(quad_w1_np, quad_w2_np, quad_w3_np, F_temp_np, f_pts_np)
1115
+ if hasattr(F_temp, 'get'):
1116
+ F_temp[:] = xp.asarray(F_temp_np)
1056
1117
 
1057
1118
  F[:, :, :] = F_temp
1058
1119
 
@@ -1078,7 +1139,14 @@ def evaluate_dofs_3d_vec(
1078
1139
  F2_temp = xp.zeros_like(F2, order='C')
1079
1140
  F3_temp = xp.zeros_like(F3, order='C')
1080
1141
 
1081
- dof_kernels.evaluate_dofs_3d_vec(F1_temp, F2_temp, F3_temp, f1_pts, f2_pts, f3_pts)
1142
+ F1_temp_np, F2_temp_np, F3_temp_np, f1_pts_np, f2_pts_np, f3_pts_np = _to_numpy_for_kernel(F1_temp, F2_temp, F3_temp, f1_pts, f2_pts, f3_pts)
1143
+ dof_kernels.evaluate_dofs_3d_vec(F1_temp_np, F2_temp_np, F3_temp_np, f1_pts_np, f2_pts_np, f3_pts_np)
1144
+ if hasattr(F1_temp, 'get'):
1145
+ F1_temp[:] = xp.asarray(F1_temp_np)
1146
+ if hasattr(F2_temp, 'get'):
1147
+ F2_temp[:] = xp.asarray(F2_temp_np)
1148
+ if hasattr(F3_temp, 'get'):
1149
+ F3_temp[:] = xp.asarray(F3_temp_np)
1082
1150
 
1083
1151
  F1[:, :, :] = F1_temp
1084
1152
  F2[:, :, :] = F2_temp
@@ -1,8 +1,7 @@
1
- #---------------------------------------------------------------------------#
2
- # This file is part of PSYDAC which is released under MIT License. See the #
3
- # LICENSE file or go to https://github.com/pyccel/psydac/blob/devel/LICENSE #
4
- # for full license details. #
5
- #---------------------------------------------------------------------------#
1
+ # coding: utf-8
2
+ #
3
+ # Copyright 2018 Yaman Güçlü
4
+
6
5
  import cunumpy as xp
7
6
 
8
7
  from feectools.core.bsplines import elements_spans
@@ -1,6 +1,7 @@
1
1
  # -*- coding: UTF-8 -*-
2
2
  import os
3
3
 
4
+ import numpy as np
4
5
  import cunumpy as xp
5
6
 
6
7
  from feectools.ddm.cart import CartDecomposition, InterfaceCartDecomposition, create_interfaces_cart
@@ -56,6 +57,10 @@ def partition_coefficients(domain_decomposition, spaces, min_blocks=None):
56
57
  ee = domain_decomposition.global_element_ends [axis]
57
58
  m = multiplicity[axis]
58
59
 
60
+ # Convert to numpy if CuPy (needed for MPI operations later)
61
+ if hasattr(ee, 'get'):
62
+ ee = ee.get()
63
+
59
64
  global_ends [axis] = m*(ee+1)-1
60
65
  global_ends [axis][-1] = npts[axis]-1
61
66
  global_starts[axis] = xp.array([0] + (global_ends[axis][:-1]+1).tolist())
@@ -64,14 +69,18 @@ def partition_coefficients(domain_decomposition, spaces, min_blocks=None):
64
69
  min_blocks = [None] * ndims
65
70
 
66
71
  for s, e, V, mb in zip(global_starts, global_ends, spaces, min_blocks):
72
+ s_host = s.get() if hasattr(s, 'get') else np.asarray(s)
73
+ e_host = e.get() if hasattr(e, 'get') else np.asarray(e)
74
+ local_sizes = e_host - s_host + 1
75
+
67
76
  if V.periodic or mb is None:
68
- assert all(e-s+1 >= V.degree), f"Local number of elements (after domain decomposition) is to small for spline degree p={V.degree}: {e-s+1} is not >= {V.degree} everywhere.\n \
77
+ assert all(local_sizes >= V.degree), f"Local number of elements (after domain decomposition) is to small for spline degree p={V.degree}: {local_sizes} is not >= {V.degree} everywhere.\n \
69
78
  You can:\n \
70
79
  1. increase Nel\n \
71
80
  2. lower p\n \
72
81
  3. decrease the MPI size."
73
82
  else:
74
- assert all(e-s+1 >= mb)
83
+ assert all(local_sizes >= mb)
75
84
 
76
85
  return global_starts, global_ends
77
86