Coverage for pySDC/implementations/transfer_classes/TransferMesh_MPIFFT.py: 100%
78 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
1from pySDC.core.errors import TransferError
2from pySDC.core.space_transfer import SpaceTransfer
3from mpi4py_fft import newDistArray
5from pySDC.helpers.fft_helper import PFFT
8class fft_to_fft(SpaceTransfer):
9 """
10 Space transfer between distributed periodic mpi4py-fft meshes: injection to restrict, spectral padding to prolong.
12 This implementation can restrict and prolong between ``mesh`` or ``cupy_mesh`` datatypes, or datatypes made of
13 several such components, with FFT for periodic boundaries
15 """
17 def __init__(self, fine_prob, coarse_prob, params):
18 """
19 Initialization routine
21 Args:
22 fine_prob: fine problem
23 coarse_prob: coarse problem
24 params: parameters for the transfer operators
25 """
26 # invoke super initialization
27 super().__init__(fine_prob, coarse_prob, params)
29 assert self.fine_prob.spectral == self.coarse_prob.spectral
31 self.spectral = self.fine_prob.spectral
33 Nf = list(self.fine_prob.fft.global_shape())
34 Nc = list(self.coarse_prob.fft.global_shape())
35 self.ratio = [int(nf / nc) for nf, nc in zip(Nf, Nc, strict=True)]
36 # slice for injection, one entry per dimension rather than hardcoded for 2d
37 self.injection = tuple(slice(None, None, r) for r in self.ratio)
38 axes = tuple(range(len(Nf)))
40 fft_args = {}
41 useGPU = 'cupy' in self.fine_prob.dtype_u.__name__.lower()
42 if useGPU:
43 fft_args['backend'] = 'cupy'
44 fft_args['comm_backend'] = 'NCCL'
46 self.fft_pad = PFFT(
47 self.coarse_prob.comm,
48 Nc,
49 padding=self.ratio,
50 axes=axes,
51 dtype=self.coarse_prob.fft.dtype(False),
52 slab=True,
53 **fft_args,
54 )
56 def restrict(self, F):
57 """
58 Restriction implementation
60 Args:
61 F: the fine level data (easier to access than via the fine attribute)
62 """
63 G = type(F)(self.coarse_prob.init)
65 def _restrict(fine, coarse):
66 if self.spectral:
67 if hasattr(self.fine_prob, 'ncomp'):
68 for i in range(self.fine_prob.ncomp):
69 if fine.shape[-1] == self.fine_prob.ncomp:
70 tmpF = newDistArray(self.fine_prob.fft, False)
71 tmpF = self.fine_prob.fft.backward(fine[..., i], tmpF)
72 tmpG = tmpF[self.injection]
73 coarse[..., i] = self.coarse_prob.fft.forward(tmpG, coarse[..., i])
74 elif fine.shape[0] == self.fine_prob.ncomp:
75 tmpF = newDistArray(self.fine_prob.fft, False)
76 tmpF = self.fine_prob.fft.backward(fine[i, ...], tmpF)
77 tmpG = tmpF[self.injection]
78 coarse[i, ...] = self.coarse_prob.fft.forward(tmpG, coarse[i, ...])
79 else:
80 raise TransferError('Don\'t know how to restrict for this problem with multiple components')
81 else:
82 tmpF = self.fine_prob.fft.backward(fine)
83 tmpG = tmpF[self.injection]
84 coarse[:] = self.coarse_prob.fft.forward(tmpG, coarse)
85 elif hasattr(self.fine_prob, 'ncomp') and fine.shape[0] == self.fine_prob.ncomp:
86 # the component axis comes first here, so inject on the spatial axes behind it. A
87 # trailing component axis needs no special case: the slice simply does not reach it.
88 coarse[:] = fine[(slice(None),) + self.injection]
89 else:
90 coarse[:] = fine[self.injection]
92 if hasattr(type(F), 'components'):
93 for comp in F.components:
94 _restrict(getattr(F, comp), getattr(G, comp))
95 elif type(F).__name__ in ['mesh', 'cupy_mesh']:
96 _restrict(F, G)
97 else:
98 raise TransferError('Wrong data type for restriction, got %s' % type(F))
100 return G
102 def prolong(self, G):
103 """
104 Prolongation implementation
106 Args:
107 G: the coarse level data (easier to access than via the coarse attribute)
108 """
109 F = type(G)(self.fine_prob.init)
111 def _prolong(coarse, fine):
112 if self.spectral:
113 if hasattr(self.fine_prob, 'ncomp'):
114 for i in range(self.fine_prob.ncomp):
115 if coarse.shape[-1] == self.fine_prob.ncomp:
116 tmpF = self.fft_pad.backward(coarse[..., i])
117 fine[..., i] = self.fine_prob.fft.forward(tmpF, fine[..., i])
118 elif coarse.shape[0] == self.fine_prob.ncomp:
119 tmpF = self.fft_pad.backward(coarse[i, ...])
120 fine[i, ...] = self.fine_prob.fft.forward(tmpF, fine[i, ...])
121 else:
122 raise TransferError('Don\'t know how to prolong for this problem with multiple components')
124 else:
125 tmpF = self.fft_pad.backward(coarse)
126 fine[:] = self.fine_prob.fft.forward(tmpF, fine)
127 else:
128 if hasattr(self.fine_prob, 'ncomp'):
129 for i in range(self.fine_prob.ncomp):
130 if coarse.shape[-1] == self.fine_prob.ncomp:
131 G_hat = self.coarse_prob.fft.forward(coarse[..., i])
132 fine[..., i] = self.fft_pad.backward(G_hat, fine[..., i])
133 elif coarse.shape[0] == self.fine_prob.ncomp:
134 G_hat = self.coarse_prob.fft.forward(coarse[i, ...])
135 fine[i, ...] = self.fft_pad.backward(G_hat, fine[i, ...])
136 else:
137 raise TransferError('Don\'t know how to prolong for this problem with multiple components')
138 else:
139 G_hat = self.coarse_prob.fft.forward(coarse)
140 fine[:] = self.fft_pad.backward(G_hat, fine)
142 if hasattr(type(F), 'components'):
143 for comp in F.components:
144 _prolong(getattr(G, comp), getattr(F, comp))
145 elif type(G).__name__ in ['mesh', 'cupy_mesh']:
146 _prolong(G, F)
148 else:
149 raise TransferError('Unknown data type, got %s' % type(G))
151 return F