feectools 0.1.9__tar.gz → 0.1.11__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.
- {feectools-0.1.9/feectools.egg-info → feectools-0.1.11}/PKG-INFO +1 -1
- feectools-0.1.11/feectools/__init__.py +4 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/api/settings.py +8 -5
- feectools-0.1.11/feectools/ddm/mpi.py +225 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/partition.py +41 -2
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/basic.py +1 -1
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/direct_solvers.py +5 -2
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/solvers.py +222 -2
- {feectools-0.1.9 → feectools-0.1.11/feectools.egg-info}/PKG-INFO +1 -1
- {feectools-0.1.9 → feectools-0.1.11}/pyproject.toml +1 -1
- feectools-0.1.9/feectools/ddm/mpi.py +0 -114
- feectools-0.1.9/feectools/utilities/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/AUTHORS +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/LICENSE +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/README.md +0 -0
- {feectools-0.1.9/feectools → feectools-0.1.11/feectools/accelerate}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/accelerate/accelerate.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/accelerate/compile_psydac.mk +0 -0
- {feectools-0.1.9/feectools/accelerate → feectools-0.1.11/feectools/api}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/api/essential_bc.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/api/fem_bilinear_form.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/api/fem_common.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/api/fem_sum_form.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/bsplines.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/bsplines_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/field_evaluation_kernels.py +0 -0
- {feectools-0.1.9/feectools/api → feectools-0.1.11/feectools/core/tests}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/tests/test_bsplines.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/tests/test_bsplines_kernel.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/core/tests/test_bsplines_pyccel.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/basic.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/blocking_data_exchanger.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/cart.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/interface_data_exchanger.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/nonblocking_data_exchanger.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/petsc.py +0 -0
- {feectools-0.1.9/feectools/core → feectools-0.1.11/feectools/ddm}/tests/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/tests/test_cart_1d.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/tests/test_cart_2d.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/tests/test_cart_3d.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/tests/test_multicart_2d.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/tests/test_partition.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/ddm/utilities.py +0 -0
- {feectools-0.1.9/feectools/ddm/tests → feectools-0.1.11/feectools/feec}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/feec/derivatives.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/feec/dof_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/feec/global_geometric_projectors.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/feec/hodge.py +0 -0
- {feectools-0.1.9/feectools/feec → feectools-0.1.11/feectools/fem}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/basic.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/grid.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/lst_preconditioner.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/partitioning.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/projectors.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/splines.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tensor.py +0 -0
- {feectools-0.1.9/feectools/fem → feectools-0.1.11/feectools/fem/tests}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/analytical_profiles_1d.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/analytical_profiles_base.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/splines_error_bounds.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_dirichlet_projectors.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_spline_histopolation.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_spline_interpolation.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_splines.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_splines_par.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_tensor.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/test_vector_spaces.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/tests/utilities.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/fem/vector.py +0 -0
- {feectools-0.1.9/feectools/fem/tests → feectools-0.1.11/feectools/linalg}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/block.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/fft.py +0 -0
- {feectools-0.1.9/feectools/linalg → feectools-0.1.11/feectools/linalg/kernels}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kernels/axpy_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kernels/inner_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kernels/matvec_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kernels/stencil2IJV_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kernels/stencil2coo_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kernels/transpose_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/kron.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/memory.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/sparse.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/stencil.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/stencil_dot_kernels.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/stencil_transpose_kernels.py +0 -0
- {feectools-0.1.9/feectools/linalg/kernels → feectools-0.1.11/feectools/linalg/tests}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_block.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_fft.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_kron_stencil_matrix.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_linalg.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_matrix_free.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_solvers.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_stencil_interface_matrix.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_stencil_vector.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_stencil_vector_space.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/utilities.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/topetsc.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/utilities.py +0 -0
- {feectools-0.1.9/feectools/linalg/tests → feectools-0.1.11/feectools/utilities}/__init__.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/utilities/quadratures.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/utilities/utils.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools/version.py +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools.egg-info/SOURCES.txt +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools.egg-info/dependency_links.txt +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools.egg-info/entry_points.txt +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools.egg-info/requires.txt +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/feectools.egg-info/top_level.txt +0 -0
- {feectools-0.1.9 → feectools-0.1.11}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feectools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
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>
|
|
@@ -43,13 +43,16 @@ PSYDAC_BACKEND_NVPYCCEL = {'name': 'pyccel',
|
|
|
43
43
|
'openmp' : False}
|
|
44
44
|
# ...
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
def _get_gfortran_version():
|
|
47
|
+
"""Version of the installed gfortran. Only queried where it is needed (see below), since it spawns a subprocess."""
|
|
48
|
+
gfortran_version_output = subprocess.check_output(['gfortran', '--version']).decode('utf-8') # nosec B603, B607
|
|
49
|
+
gfortran_version_string = re.search(r"(\d+\.\d+\.\d+)", gfortran_version_output).group()
|
|
50
|
+
return Version(gfortran_version_string)
|
|
50
51
|
|
|
51
52
|
# Platform-dependent flags
|
|
52
|
-
|
|
53
|
+
# (the gfortran version is only relevant on Apple silicon; evaluating it lazily avoids running
|
|
54
|
+
# a subprocess on every import and lets feectools be imported without gfortran on other platforms)
|
|
55
|
+
if platform.system() == "Darwin" and platform.machine() == 'arm64' and _get_gfortran_version() >= Version("14"):
|
|
53
56
|
|
|
54
57
|
# Apple silicon requires architecture-specific flags (see https://github.com/pyccel/psydac/pull/411)
|
|
55
58
|
# which are only available on GCC version >= 14
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Detection of whether the process was launched by an MPI launcher.
|
|
2
|
+
|
|
3
|
+
Importing ``mpi4py.MPI`` calls ``MPI_Init``, and any collective (``bcast``,
|
|
4
|
+
``Barrier``, ...) issued afterwards costs something even on a single process.
|
|
5
|
+
A plain ``python script.py`` run should therefore never touch MPI at all, even
|
|
6
|
+
when mpi4py happens to be installed. This module answers the only question
|
|
7
|
+
that decides it: was this process started by ``mpirun``/``mpiexec``/``srun``
|
|
8
|
+
(or an equivalent launcher)?
|
|
9
|
+
|
|
10
|
+
The answer is read from the environment the launcher sets up, so it is
|
|
11
|
+
available before mpi4py is imported.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from time import time
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Per-process variables exported by the process managers behind the common
|
|
23
|
+
# launchers. Each is set only for processes started *by* the launcher, so the
|
|
24
|
+
# presence of any one of them means "this rank belongs to an MPI job".
|
|
25
|
+
# SLURM_PROCID is deliberately absent: it is also set for the script of a
|
|
26
|
+
# plain `sbatch` job, which is not an MPI launch. `srun` is covered by the
|
|
27
|
+
# PMI/PMIX variables its MPI plugin exports.
|
|
28
|
+
_LAUNCHER_ENV_VARS = (
|
|
29
|
+
"OMPI_COMM_WORLD_RANK", # Open MPI (and derivatives: Spectrum, ...)
|
|
30
|
+
"PMI_RANK", # MPICH, Intel MPI, MS-MPI, Cray, srun (pmi2)
|
|
31
|
+
"PMIX_RANK", # PMIx, used by srun --mpi=pmix and Open MPI 5
|
|
32
|
+
"MV2_COMM_WORLD_RANK", # MVAPICH2
|
|
33
|
+
"MPI_LOCALRANKID", # Hydra (mpiexec.hydra)
|
|
34
|
+
"ALPS_APP_PE", # Cray ALPS aprun
|
|
35
|
+
"PALS_RANKID", # Cray PALS palsrun
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Escape hatch: force the decision either way without touching code, e.g. for
|
|
39
|
+
# a launcher whose variables are not listed above.
|
|
40
|
+
_OVERRIDE_ENV_VAR = "STRUPHY_MPI"
|
|
41
|
+
|
|
42
|
+
_TRUE_VALUES = ("1", "true", "yes", "on")
|
|
43
|
+
_FALSE_VALUES = ("0", "false", "no", "off")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _override() -> bool | None:
|
|
47
|
+
"""Value of ``STRUPHY_MPI``, or None if unset/unrecognized."""
|
|
48
|
+
value = os.environ.get(_OVERRIDE_ENV_VAR)
|
|
49
|
+
if value is None:
|
|
50
|
+
return None
|
|
51
|
+
value = value.strip().lower()
|
|
52
|
+
if value in _TRUE_VALUES:
|
|
53
|
+
return True
|
|
54
|
+
if value in _FALSE_VALUES:
|
|
55
|
+
return False
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def launched_under_mpi() -> bool:
|
|
60
|
+
"""Whether this process was started by an MPI launcher.
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
bool
|
|
65
|
+
True if a launcher's per-rank environment variable is present, or if
|
|
66
|
+
the application itself already initialized MPI (in which case using
|
|
67
|
+
the communicator is free). ``STRUPHY_MPI=0``/``1`` overrides
|
|
68
|
+
the detection.
|
|
69
|
+
"""
|
|
70
|
+
override = _override()
|
|
71
|
+
if override is not None:
|
|
72
|
+
return override
|
|
73
|
+
|
|
74
|
+
if any(var in os.environ for var in _LAUNCHER_ENV_VARS):
|
|
75
|
+
return True
|
|
76
|
+
|
|
77
|
+
# The application may have initialized MPI itself (embedded interpreter,
|
|
78
|
+
# or an explicit `from mpi4py import MPI`). Only inspect mpi4py if it is
|
|
79
|
+
# already imported: importing it here is exactly what must be avoided.
|
|
80
|
+
mpi_module = sys.modules.get("mpi4py.MPI")
|
|
81
|
+
if mpi_module is not None:
|
|
82
|
+
try:
|
|
83
|
+
return bool(mpi_module.Is_initialized())
|
|
84
|
+
except AttributeError:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Might not be needed
|
|
91
|
+
class MPICommWrapper:
|
|
92
|
+
def __init__(self, use_mpi=True):
|
|
93
|
+
self.use_mpi = use_mpi
|
|
94
|
+
if use_mpi:
|
|
95
|
+
from mpi4py import MPI
|
|
96
|
+
|
|
97
|
+
self.comm = MPI.COMM_WORLD
|
|
98
|
+
else:
|
|
99
|
+
self.comm = MockComm()
|
|
100
|
+
|
|
101
|
+
def __getattr__(self, name):
|
|
102
|
+
return getattr(self.comm, name)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class MockComm:
|
|
106
|
+
def __getattr__(self, name):
|
|
107
|
+
# Return a function that does nothing and returns None
|
|
108
|
+
def dummy(*args, **kwargs):
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
return dummy
|
|
112
|
+
|
|
113
|
+
# Override some functions
|
|
114
|
+
def Get_rank(self):
|
|
115
|
+
return 0
|
|
116
|
+
|
|
117
|
+
def Get_size(self):
|
|
118
|
+
return 1
|
|
119
|
+
|
|
120
|
+
def Barrier(self):
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class MPIwrapper:
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
use_mpi: bool = False,
|
|
128
|
+
verbose: bool = False,
|
|
129
|
+
):
|
|
130
|
+
self.use_mpi = use_mpi
|
|
131
|
+
if use_mpi:
|
|
132
|
+
from mpi4py import MPI
|
|
133
|
+
|
|
134
|
+
self._MPI = MPI
|
|
135
|
+
if verbose:
|
|
136
|
+
print("MPI is enabled")
|
|
137
|
+
else:
|
|
138
|
+
self._MPI = MockMPI()
|
|
139
|
+
if verbose:
|
|
140
|
+
print("MPI is NOT enabled")
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def MPI(self):
|
|
144
|
+
return self._MPI
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class MockMPI:
|
|
148
|
+
def __getattr__(self, name):
|
|
149
|
+
# Return a function that does nothing and returns None
|
|
150
|
+
def dummy(*args, **kwargs):
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
return dummy
|
|
154
|
+
|
|
155
|
+
# Override some functions
|
|
156
|
+
@property
|
|
157
|
+
def COMM_WORLD(self):
|
|
158
|
+
return MockComm()
|
|
159
|
+
|
|
160
|
+
# def comm_Get_rank(self):
|
|
161
|
+
# return 0
|
|
162
|
+
|
|
163
|
+
# def comm_Get_size(self):
|
|
164
|
+
# return 1
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _mpi_disabled():
|
|
168
|
+
"""True if the user or the host application opted out of MPI.
|
|
169
|
+
|
|
170
|
+
Importing mpi4py initializes MPI, which takes close to a second. Serial runs that
|
|
171
|
+
never use MPI can skip it entirely, in two ways:
|
|
172
|
+
|
|
173
|
+
* export ``FEECTOOLS_MPI=0`` before starting Python, or
|
|
174
|
+
* set ``feectools.use_mpi = False`` before this module is first imported
|
|
175
|
+
(in-process, so it is not inherited by subprocesses such as ``mpirun``).
|
|
176
|
+
|
|
177
|
+
The MockMPI wrapper below is then used, exactly as if mpi4py were not installed.
|
|
178
|
+
"""
|
|
179
|
+
import os
|
|
180
|
+
|
|
181
|
+
import feectools
|
|
182
|
+
|
|
183
|
+
if getattr(feectools, 'use_mpi', None) is False:
|
|
184
|
+
return True
|
|
185
|
+
return os.environ.get('FEECTOOLS_MPI', '').strip().lower() in ('0', 'false', 'no', 'off')
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if launched_under_mpi():
|
|
189
|
+
try:
|
|
190
|
+
# Disable MPI when using CuPy due to known segfault issues with OpenMPI + CUDA
|
|
191
|
+
import os
|
|
192
|
+
if os.environ.get('ARRAY_BACKEND') == 'cupy':
|
|
193
|
+
raise ImportError("MPI disabled when using CuPy backend")
|
|
194
|
+
|
|
195
|
+
if _mpi_disabled():
|
|
196
|
+
raise ImportError("MPI disabled (feectools.use_mpi = False or FEECTOOLS_MPI=0)")
|
|
197
|
+
|
|
198
|
+
from mpi4py import MPI
|
|
199
|
+
|
|
200
|
+
_comm = MPI.COMM_WORLD
|
|
201
|
+
# rank = _comm.Get_rank()
|
|
202
|
+
# size = _comm.Get_size()
|
|
203
|
+
mpi_enabled = True
|
|
204
|
+
except ImportError:
|
|
205
|
+
# mpi4py not installed
|
|
206
|
+
mpi_enabled = False
|
|
207
|
+
except Exception:
|
|
208
|
+
# mpi4py installed but not running under mpirun
|
|
209
|
+
mpi_enabled = False
|
|
210
|
+
else:
|
|
211
|
+
mpi_enabled = False
|
|
212
|
+
|
|
213
|
+
# TODO: add environment variable for mpi use
|
|
214
|
+
mpi_wrapper = MPIwrapper(
|
|
215
|
+
use_mpi=mpi_enabled,
|
|
216
|
+
verbose=False,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
|
|
220
|
+
if TYPE_CHECKING:
|
|
221
|
+
from mpi4py import MPI
|
|
222
|
+
|
|
223
|
+
mpi = MPI
|
|
224
|
+
else:
|
|
225
|
+
mpi = mpi_wrapper.MPI
|
|
@@ -2,10 +2,49 @@ import cunumpy as xp
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import numpy.ma as ma
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
__all__ = ('compute_dims', 'partition_procs_per_patch')
|
|
6
|
+
|
|
7
|
+
#==============================================================================
|
|
8
|
+
def factorint(n, multiple=False):
|
|
9
|
+
"""
|
|
10
|
+
Prime factorization of an integer by trial division.
|
|
6
11
|
|
|
12
|
+
Drop-in replacement for the subset of ``sympy.ntheory.factorint`` used here.
|
|
13
|
+
Importing sympy takes ~1 s and is not needed for the small integers
|
|
14
|
+
(process counts, number of grid points) that are factorized in this module.
|
|
7
15
|
|
|
8
|
-
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
n : int
|
|
19
|
+
Integer to factorize.
|
|
20
|
+
|
|
21
|
+
multiple : bool
|
|
22
|
+
If False (default), return a dict {prime: multiplicity}.
|
|
23
|
+
If True, return the list of primes in ascending order, repeated
|
|
24
|
+
according to their multiplicity.
|
|
25
|
+
"""
|
|
26
|
+
n = int(n)
|
|
27
|
+
factors = {}
|
|
28
|
+
|
|
29
|
+
# same conventions as sympy for non-positive input
|
|
30
|
+
if n == 0:
|
|
31
|
+
factors[0] = 1
|
|
32
|
+
else:
|
|
33
|
+
if n < 0:
|
|
34
|
+
factors[-1] = 1
|
|
35
|
+
n = -n
|
|
36
|
+
p = 2
|
|
37
|
+
while p * p <= n:
|
|
38
|
+
while n % p == 0:
|
|
39
|
+
factors[p] = factors.get(p, 0) + 1
|
|
40
|
+
n //= p
|
|
41
|
+
p += 1 if p == 2 else 2
|
|
42
|
+
if n > 1:
|
|
43
|
+
factors[n] = factors.get(n, 0) + 1
|
|
44
|
+
|
|
45
|
+
if multiple:
|
|
46
|
+
return [p for p in sorted(factors) for _ in range(factors[p])]
|
|
47
|
+
return factors
|
|
9
48
|
|
|
10
49
|
#==============================================================================
|
|
11
50
|
def partition_procs_per_patch(npts, size):
|
|
@@ -756,7 +756,7 @@ class ScaledLinearOperator(LinearOperator):
|
|
|
756
756
|
self._scalar = c
|
|
757
757
|
|
|
758
758
|
def toarray(self):
|
|
759
|
-
return self._scalar * self._operator.toarray
|
|
759
|
+
return self._scalar * self._operator.toarray
|
|
760
760
|
|
|
761
761
|
def tosparse(self):
|
|
762
762
|
return self._scalar * self._operator.tosparse().tocsr()
|
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
from abc import abstractmethod
|
|
7
7
|
import cunumpy as xp
|
|
8
8
|
from cunumpy.xp import array_backend
|
|
9
|
-
from scipy.linalg.lapack import dgbtrf, dgbtrs, sgbtrf, sgbtrs, cgbtrf, cgbtrs, zgbtrf, zgbtrs
|
|
10
9
|
from scipy.sparse import spmatrix, dia_matrix
|
|
11
|
-
from scipy.sparse.linalg import splu
|
|
12
10
|
|
|
13
11
|
from feectools.linalg.basic import LinearSolver
|
|
14
12
|
|
|
@@ -53,6 +51,9 @@ class BandedSolver(LinearSolver):
|
|
|
53
51
|
self._l = l
|
|
54
52
|
self._transposed = transposed
|
|
55
53
|
|
|
54
|
+
# imported here: scipy.linalg costs ~0.3 s to import and is only needed once a solver is built
|
|
55
|
+
from scipy.linalg.lapack import dgbtrf, dgbtrs, sgbtrf, sgbtrs, cgbtrf, cgbtrs, zgbtrf, zgbtrs
|
|
56
|
+
|
|
56
57
|
# ... LU factorization
|
|
57
58
|
if bmat.dtype == xp.float32:
|
|
58
59
|
self._factor_function = sgbtrf
|
|
@@ -186,6 +187,8 @@ class SparseSolver (LinearSolver):
|
|
|
186
187
|
|
|
187
188
|
assert isinstance(spmat, spmatrix)
|
|
188
189
|
|
|
190
|
+
from scipy.sparse.linalg import splu # deferred, see BandedSolver
|
|
191
|
+
|
|
189
192
|
self._space = xp.ndarray
|
|
190
193
|
self._splu = splu(spmat.tocsc())
|
|
191
194
|
self._transposed = transposed
|
|
@@ -4,12 +4,14 @@ This module provides iterative solvers and preconditioners.
|
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
6
|
import cunumpy as xp
|
|
7
|
-
from math import sqrt
|
|
7
|
+
from math import sqrt, inf
|
|
8
8
|
|
|
9
9
|
from feectools.utilities.utils import is_real
|
|
10
10
|
from feectools.linalg.utilities import _sym_ortho
|
|
11
11
|
from feectools.linalg.basic import (Vector, LinearOperator,
|
|
12
12
|
InverseLinearOperator, IdentityOperator, ScaledLinearOperator)
|
|
13
|
+
from feectools.linalg.block import BlockVector, BlockVectorSpace
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
__all__ = (
|
|
15
17
|
'inverse',
|
|
@@ -20,7 +22,7 @@ __all__ = (
|
|
|
20
22
|
'PBiConjugateGradientStabilized',
|
|
21
23
|
'MinimumResidual',
|
|
22
24
|
'LSMR',
|
|
23
|
-
'GMRES'
|
|
25
|
+
'GMRES',
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
#===============================================================================
|
|
@@ -64,6 +66,8 @@ def inverse(A, solver, **kwargs):
|
|
|
64
66
|
'minres' : MinimumResidual,
|
|
65
67
|
'lsmr' : LSMR,
|
|
66
68
|
'gmres' : GMRES,
|
|
69
|
+
'uzawa' : UzawaSolver,
|
|
70
|
+
'schur' : SchurComplementSolver
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
# Check solver input
|
|
@@ -1912,3 +1916,219 @@ class GMRES(InverseLinearOperator):
|
|
|
1912
1916
|
def dot(self, b, out=None):
|
|
1913
1917
|
return self.solve(b, out=out)
|
|
1914
1918
|
|
|
1919
|
+
|
|
1920
|
+
class UzawaSolver(InverseLinearOperator):
|
|
1921
|
+
def __init__(self, A, *, A11, A22, B1, B2,
|
|
1922
|
+
x0=None, tol=1e-6, maxiter=1000, verbose=False, recycle=False,
|
|
1923
|
+
inner_tol=1e-7, inner_maxiter=1000):
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
self._options = {
|
|
1927
|
+
"x0": x0, "tol": tol, "maxiter": maxiter,
|
|
1928
|
+
"verbose": verbose, "recycle": recycle,
|
|
1929
|
+
}
|
|
1930
|
+
super().__init__(A, **self._options)
|
|
1931
|
+
|
|
1932
|
+
self._inner_tol = inner_tol if inner_tol is not None else tol
|
|
1933
|
+
self._inner_maxiter = inner_maxiter
|
|
1934
|
+
|
|
1935
|
+
self._A11 = A11
|
|
1936
|
+
self._A22 = A22
|
|
1937
|
+
self._B1 = B1
|
|
1938
|
+
self._B2 = B2
|
|
1939
|
+
|
|
1940
|
+
self._A11inv = self._inner_solve(A11, tol=self._inner_tol, maxiter=self._inner_maxiter)
|
|
1941
|
+
self._A22inv = self._inner_solve(A22, tol=self._inner_tol, maxiter=self._inner_maxiter)
|
|
1942
|
+
|
|
1943
|
+
self._info = None
|
|
1944
|
+
|
|
1945
|
+
@staticmethod
|
|
1946
|
+
def _inner_solve(A, tol=1e-10, maxiter=200):
|
|
1947
|
+
"""Iterative GMRES inverse — works for non-symmetric positive definite operators."""
|
|
1948
|
+
from feectools.linalg.solvers import inverse
|
|
1949
|
+
return inverse(A, "gmres", tol=tol, maxiter=maxiter, recycle=True)
|
|
1950
|
+
|
|
1951
|
+
def update_A11(self, A11):
|
|
1952
|
+
"""Recompute A11 inverse when dt changes."""
|
|
1953
|
+
self._A11 = A11
|
|
1954
|
+
self._A11inv = self._inner_solve(A11, tol=self._inner_tol, maxiter=self._inner_maxiter)
|
|
1955
|
+
|
|
1956
|
+
def solve(self, b, out=None):
|
|
1957
|
+
|
|
1958
|
+
A11 = self._A11
|
|
1959
|
+
A22 = self._A22
|
|
1960
|
+
B1 = self._B1
|
|
1961
|
+
B2 = self._B2
|
|
1962
|
+
A11inv = self._A11inv
|
|
1963
|
+
A22inv = self._A22inv
|
|
1964
|
+
|
|
1965
|
+
tol = self._options["tol"]
|
|
1966
|
+
maxiter = self._options["maxiter"]
|
|
1967
|
+
verbose = self._options["verbose"]
|
|
1968
|
+
recycle = self._options["recycle"]
|
|
1969
|
+
|
|
1970
|
+
F = b[0]
|
|
1971
|
+
g = b[1]
|
|
1972
|
+
f_u = F[0]
|
|
1973
|
+
f_ue = F[1]
|
|
1974
|
+
|
|
1975
|
+
# initial guess
|
|
1976
|
+
x0 = self._options["x0"]
|
|
1977
|
+
if x0 is not None:
|
|
1978
|
+
u = x0[0][0].copy()
|
|
1979
|
+
ue = x0[0][1].copy()
|
|
1980
|
+
p = x0[1].copy()
|
|
1981
|
+
else:
|
|
1982
|
+
u = A11.domain.zeros()
|
|
1983
|
+
ue = A22.domain.zeros()
|
|
1984
|
+
p = B1.codomain.zeros()
|
|
1985
|
+
|
|
1986
|
+
|
|
1987
|
+
if verbose:
|
|
1988
|
+
print("Uzawa solver:")
|
|
1989
|
+
print("+---------+---------------------+")
|
|
1990
|
+
print("+ Iter. # | L2-norm of residual |")
|
|
1991
|
+
print("+---------+---------------------+")
|
|
1992
|
+
template = "| {:7d} | {:19.2e} |"
|
|
1993
|
+
|
|
1994
|
+
for iteration in range(1, maxiter + 1):
|
|
1995
|
+
|
|
1996
|
+
# solve A11 * u = f_u - B1^T * p
|
|
1997
|
+
rhs_u = f_u - B1.T.dot(p)
|
|
1998
|
+
u = A11inv.dot(rhs_u)
|
|
1999
|
+
|
|
2000
|
+
# solve A22 * ue = f_ue - B2^T * p
|
|
2001
|
+
rhs_ue = f_ue - B2.T.dot(p)
|
|
2002
|
+
ue = A22inv.dot(rhs_ue)
|
|
2003
|
+
|
|
2004
|
+
# constraint residual: R = B1*u + B2*ue - g
|
|
2005
|
+
R = B1.dot(u) + B2.dot(ue) - g
|
|
2006
|
+
residual_norm = sqrt(R.inner(R).real)
|
|
2007
|
+
|
|
2008
|
+
if verbose:
|
|
2009
|
+
print(template.format(iteration, residual_norm))
|
|
2010
|
+
|
|
2011
|
+
if residual_norm < tol:
|
|
2012
|
+
break
|
|
2013
|
+
|
|
2014
|
+
# pressure update: steepest descent step size
|
|
2015
|
+
S_R = B1.dot(A11inv.dot(B1.T.dot(R))) + B2.dot(A22inv.dot(B2.T.dot(R)))
|
|
2016
|
+
alpha = R.inner(R).real / R.inner(S_R).real
|
|
2017
|
+
p += alpha * R
|
|
2018
|
+
|
|
2019
|
+
if verbose:
|
|
2020
|
+
print("+---------+---------------------+")
|
|
2021
|
+
|
|
2022
|
+
self._info = {
|
|
2023
|
+
'niter' : iteration,
|
|
2024
|
+
'success' : residual_norm < tol,
|
|
2025
|
+
'res_norm': residual_norm,
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
if recycle:
|
|
2029
|
+
block_u = BlockVector(self.domain.spaces[0], blocks=[u, ue])
|
|
2030
|
+
self._options["x0"] = BlockVector(self.domain, blocks=[block_u, p])
|
|
2031
|
+
|
|
2032
|
+
if out is not None:
|
|
2033
|
+
out[0][0] = u
|
|
2034
|
+
out[0][1] = ue
|
|
2035
|
+
out[1] = p
|
|
2036
|
+
return out
|
|
2037
|
+
|
|
2038
|
+
block_u = BlockVector(BlockVectorSpace(A11.domain, A22.domain), blocks=[u, ue])
|
|
2039
|
+
return BlockVector(self.domain, blocks=[block_u, p])
|
|
2040
|
+
|
|
2041
|
+
def dot(self, b, out=None):
|
|
2042
|
+
return self.solve(b, out=out)
|
|
2043
|
+
|
|
2044
|
+
|
|
2045
|
+
class SchurComplementSolver(InverseLinearOperator):
|
|
2046
|
+
def __init__(self, A, *, A11, A22, B1, B2,
|
|
2047
|
+
x0=None, tol=1e-6, maxiter=1000, verbose=False, recycle=False,
|
|
2048
|
+
inner_tol=1e-7, inner_maxiter=1000):
|
|
2049
|
+
|
|
2050
|
+
self._options = {
|
|
2051
|
+
"x0": x0, "tol": tol, "maxiter": maxiter,
|
|
2052
|
+
"verbose": verbose, "recycle": recycle,
|
|
2053
|
+
}
|
|
2054
|
+
super().__init__(A, **self._options)
|
|
2055
|
+
|
|
2056
|
+
self._inner_tol = inner_tol if inner_tol is not None else tol
|
|
2057
|
+
self._inner_maxiter = inner_maxiter
|
|
2058
|
+
|
|
2059
|
+
self._A11 = A11
|
|
2060
|
+
self._A22 = A22
|
|
2061
|
+
self._B1 = B1
|
|
2062
|
+
self._B2 = B2
|
|
2063
|
+
|
|
2064
|
+
self._A11inv = self._make_inner_solver(A11, self._inner_tol, self._inner_maxiter)
|
|
2065
|
+
self._A22inv = self._make_inner_solver(A22, self._inner_tol, self._inner_maxiter)
|
|
2066
|
+
|
|
2067
|
+
self._Sinv = self._build_schur_solver()
|
|
2068
|
+
|
|
2069
|
+
self._info = None
|
|
2070
|
+
|
|
2071
|
+
def _make_inner_solver(self, A, tol, maxiter):
|
|
2072
|
+
from feectools.linalg.solvers import inverse
|
|
2073
|
+
return inverse(A, "gmres", tol=tol, maxiter=maxiter, recycle=True)
|
|
2074
|
+
|
|
2075
|
+
def _build_schur_operator(self):
|
|
2076
|
+
return (self._B1 @ self._A11inv @ self._B1.T
|
|
2077
|
+
+ self._B2 @ self._A22inv @ self._B2.T)
|
|
2078
|
+
|
|
2079
|
+
def _build_schur_solver(self):
|
|
2080
|
+
from feectools.linalg.solvers import inverse
|
|
2081
|
+
S = self._build_schur_operator()
|
|
2082
|
+
return inverse(S, "gmres",
|
|
2083
|
+
tol=self._options["tol"],
|
|
2084
|
+
maxiter=self._options["maxiter"],
|
|
2085
|
+
verbose=self._options["verbose"],
|
|
2086
|
+
recycle=self._options["recycle"])
|
|
2087
|
+
|
|
2088
|
+
def update_A11(self, A11):
|
|
2089
|
+
"""Recompute A11 inverse and update Schur operator when dt changes."""
|
|
2090
|
+
self._A11 = A11
|
|
2091
|
+
self._A11inv = self._make_inner_solver(A11, self._inner_tol, self._inner_maxiter)
|
|
2092
|
+
# update operator in-place so Sinv keeps its recycled state
|
|
2093
|
+
self._Sinv.linop = self._build_schur_operator()
|
|
2094
|
+
|
|
2095
|
+
def solve(self, b, out=None):
|
|
2096
|
+
|
|
2097
|
+
A11inv = self._A11inv
|
|
2098
|
+
A22inv = self._A22inv
|
|
2099
|
+
B1 = self._B1
|
|
2100
|
+
B2 = self._B2
|
|
2101
|
+
Sinv = self._Sinv
|
|
2102
|
+
|
|
2103
|
+
F = b[0]
|
|
2104
|
+
g = b[1]
|
|
2105
|
+
f_u = F[0]
|
|
2106
|
+
f_ue = F[1]
|
|
2107
|
+
|
|
2108
|
+
# Schur complement RHS: B1*u_f + B2*ue_f - g
|
|
2109
|
+
rhs_p = B1.dot(A11inv.dot(f_u)) + B2.dot(A22inv.dot(f_ue)) - g
|
|
2110
|
+
|
|
2111
|
+
# solve S * p = rhs_p
|
|
2112
|
+
p = Sinv.dot(rhs_p)
|
|
2113
|
+
|
|
2114
|
+
# recover velocities
|
|
2115
|
+
u = A11inv.dot(f_u - B1.T.dot(p))
|
|
2116
|
+
ue = A22inv.dot(f_ue - B2.T.dot(p))
|
|
2117
|
+
|
|
2118
|
+
self._info = Sinv.get_info()
|
|
2119
|
+
|
|
2120
|
+
if self._options["recycle"]:
|
|
2121
|
+
block_u = BlockVector(self.domain.spaces[0], blocks=[u, ue])
|
|
2122
|
+
self._options["x0"] = BlockVector(self.domain, blocks=[block_u, p])
|
|
2123
|
+
|
|
2124
|
+
if out is not None:
|
|
2125
|
+
out[0][0] = u
|
|
2126
|
+
out[0][1] = ue
|
|
2127
|
+
out[1] = p
|
|
2128
|
+
return out
|
|
2129
|
+
|
|
2130
|
+
block_u = BlockVector(self.domain.spaces[0], blocks=[u, ue])
|
|
2131
|
+
return BlockVector(self.domain, blocks=[block_u, p])
|
|
2132
|
+
|
|
2133
|
+
def dot(self, b, out=None):
|
|
2134
|
+
return self.solve(b, out=out)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feectools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
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>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "feectools"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.11"
|
|
8
8
|
description = "Slimmed-down fork of Psydac (https://github.com/pyccel/psydac) with less functionality and fewer dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">= 3.10"
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from time import time
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Might not be needed
|
|
7
|
-
class MPICommWrapper:
|
|
8
|
-
def __init__(self, use_mpi=True):
|
|
9
|
-
self.use_mpi = use_mpi
|
|
10
|
-
if use_mpi:
|
|
11
|
-
from mpi4py import MPI
|
|
12
|
-
|
|
13
|
-
self.comm = MPI.COMM_WORLD
|
|
14
|
-
else:
|
|
15
|
-
self.comm = MockComm()
|
|
16
|
-
|
|
17
|
-
def __getattr__(self, name):
|
|
18
|
-
return getattr(self.comm, name)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class MockComm:
|
|
22
|
-
def __getattr__(self, name):
|
|
23
|
-
# Return a function that does nothing and returns None
|
|
24
|
-
def dummy(*args, **kwargs):
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
return dummy
|
|
28
|
-
|
|
29
|
-
# Override some functions
|
|
30
|
-
def Get_rank(self):
|
|
31
|
-
return 0
|
|
32
|
-
|
|
33
|
-
def Get_size(self):
|
|
34
|
-
return 1
|
|
35
|
-
|
|
36
|
-
def Barrier(self):
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class MPIwrapper:
|
|
41
|
-
def __init__(
|
|
42
|
-
self,
|
|
43
|
-
use_mpi: bool = False,
|
|
44
|
-
verbose: bool = False,
|
|
45
|
-
):
|
|
46
|
-
self.use_mpi = use_mpi
|
|
47
|
-
if use_mpi:
|
|
48
|
-
from mpi4py import MPI
|
|
49
|
-
|
|
50
|
-
self._MPI = MPI
|
|
51
|
-
if verbose:
|
|
52
|
-
print("MPI is enabled")
|
|
53
|
-
else:
|
|
54
|
-
self._MPI = MockMPI()
|
|
55
|
-
if verbose:
|
|
56
|
-
print("MPI is NOT enabled")
|
|
57
|
-
|
|
58
|
-
@property
|
|
59
|
-
def MPI(self):
|
|
60
|
-
return self._MPI
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class MockMPI:
|
|
64
|
-
def __getattr__(self, name):
|
|
65
|
-
# Return a function that does nothing and returns None
|
|
66
|
-
def dummy(*args, **kwargs):
|
|
67
|
-
return None
|
|
68
|
-
|
|
69
|
-
return dummy
|
|
70
|
-
|
|
71
|
-
# Override some functions
|
|
72
|
-
@property
|
|
73
|
-
def COMM_WORLD(self):
|
|
74
|
-
return MockComm()
|
|
75
|
-
|
|
76
|
-
# def comm_Get_rank(self):
|
|
77
|
-
# return 0
|
|
78
|
-
|
|
79
|
-
# def comm_Get_size(self):
|
|
80
|
-
# return 1
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
89
|
-
from mpi4py import MPI
|
|
90
|
-
|
|
91
|
-
_comm = MPI.COMM_WORLD
|
|
92
|
-
# rank = _comm.Get_rank()
|
|
93
|
-
# size = _comm.Get_size()
|
|
94
|
-
mpi_enabled = True
|
|
95
|
-
except ImportError:
|
|
96
|
-
# mpi4py not installed
|
|
97
|
-
mpi_enabled = False
|
|
98
|
-
except Exception:
|
|
99
|
-
# mpi4py installed but not running under mpirun
|
|
100
|
-
mpi_enabled = False
|
|
101
|
-
|
|
102
|
-
# TODO: add environment variable for mpi use
|
|
103
|
-
mpi_wrapper = MPIwrapper(
|
|
104
|
-
use_mpi=mpi_enabled,
|
|
105
|
-
verbose=False,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
# TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
|
|
109
|
-
if TYPE_CHECKING:
|
|
110
|
-
from mpi4py import MPI
|
|
111
|
-
|
|
112
|
-
mpi = MPI
|
|
113
|
-
else:
|
|
114
|
-
mpi = mpi_wrapper.MPI
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{feectools-0.1.9/feectools/linalg/kernels → feectools-0.1.11/feectools/linalg/tests}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{feectools-0.1.9 → feectools-0.1.11}/feectools/linalg/tests/test_stencil_interface_matrix.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|