11import pytest
22import numpy as np
33
4+ from base import DTYPES
45
5- @pytest .mark .parametrize ("dtypeIdx" , range (6 ))
6+
7+ @pytest .mark .parametrize ("dtypeIdx" , DTYPES .keys ())
68@pytest .mark .parametrize ("nDim" , range (3 ))
79def testHeader (nDim , dtypeIdx ):
8- from base import FieldsIO , Scal0D , Cart1D , Cart2D , DTYPES
10+ from base import FieldsIO , Scal0D , Cart1D , Cart2D
911
1012 fileName = "testHeader.pysdc"
1113 dtype = DTYPES [dtypeIdx ]
@@ -24,6 +26,7 @@ def testHeader(nDim, dtypeIdx):
2426 args = {"nVar" : 10 , "gridX" : gridX , "gridY" : gridY }
2527
2628 f1 = Class (dtype , fileName )
29+ assert f1 .__str__ () == f1 .__repr__ (), "__repr__ and __str__ do not return the same result"
2730 try :
2831 f1 .initialize ()
2932 except AssertionError :
@@ -56,11 +59,11 @@ def testHeader(nDim, dtypeIdx):
5659 assert np .allclose (val , f2 .header [key ]), f"header's discrepancy for { key } in written { f2 } "
5760
5861
59- @pytest .mark .parametrize ("dtypeIdx" , range ( 6 ))
62+ @pytest .mark .parametrize ("dtypeIdx" , DTYPES . keys ( ))
6063@pytest .mark .parametrize ("nSteps" , [1 , 2 , 10 , 100 ])
6164@pytest .mark .parametrize ("nVar" , [1 , 2 , 5 ])
6265def testScal0D (nVar , nSteps , dtypeIdx ):
63- from base import FieldsIO , Scal0D , DTYPES
66+ from base import FieldsIO , Scal0D
6467
6568 fileName = "testScal0D.pysdc"
6669 dtype = DTYPES [dtypeIdx ]
@@ -75,7 +78,8 @@ def testScal0D(nVar, nSteps, dtypeIdx):
7578 times = np .arange (nSteps )/ nSteps
7679
7780 for t in times :
78- f1 .addField (t , u0 * t )
81+ ut = (u0 * t ).astype (f1 .dtype )
82+ f1 .addField (t , ut )
7983
8084 assert f1 .nFields == nSteps , f"{ f1 } do not have nFields == nSteps"
8185 assert np .allclose (f1 .times , times ), f"{ f1 } has wrong times stored in file"
@@ -95,7 +99,7 @@ def testScal0D(nVar, nSteps, dtypeIdx):
9599 assert np .allclose (u2 , u1 ), f"{ idx } 's fields in { f1 } has incorrect values"
96100
97101
98- @pytest .mark .parametrize ("dtypeIdx" , range ( 6 ))
102+ @pytest .mark .parametrize ("dtypeIdx" , DTYPES . keys ( ))
99103@pytest .mark .parametrize ("nSteps" , [1 , 2 , 5 , 10 ])
100104@pytest .mark .parametrize ("nX" , [5 , 10 , 16 , 32 , 64 ])
101105@pytest .mark .parametrize ("nVar" , [1 , 2 , 5 ])
@@ -119,7 +123,8 @@ def testCart1D(nVar, nX, nSteps, dtypeIdx):
119123 times = np .arange (nSteps )/ nSteps
120124
121125 for t in times :
122- f1 .addField (t , u0 * t )
126+ ut = (u0 * t ).astype (f1 .dtype )
127+ f1 .addField (t , ut )
123128
124129 assert f1 .nFields == nSteps , f"{ f1 } do not have nFields == nSteps"
125130 assert np .allclose (f1 .times , times ), f"{ f1 } has wrong times stored in file"
@@ -140,15 +145,15 @@ def testCart1D(nVar, nX, nSteps, dtypeIdx):
140145
141146
142147
143- @pytest .mark .parametrize ("dtypeIdx" , range ( 6 ))
148+ @pytest .mark .parametrize ("dtypeIdx" , DTYPES . keys ( ))
144149@pytest .mark .parametrize ("nSteps" , [1 , 2 , 5 , 10 ])
145150@pytest .mark .parametrize ("nY" , [5 , 10 , 16 ])
146151@pytest .mark .parametrize ("nX" , [5 , 10 , 16 ])
147152@pytest .mark .parametrize ("nVar" , [1 , 2 , 5 ])
148153def testCart2D (nVar , nX , nY , nSteps , dtypeIdx ):
149154 from base import FieldsIO , Cart2D , DTYPES
150155
151- fileName = "testCart1D .pysdc"
156+ fileName = "testCart2D .pysdc"
152157 dtype = DTYPES [dtypeIdx ]
153158
154159 gridX = np .linspace (0 , 1 , num = nX , endpoint = False )
@@ -166,7 +171,8 @@ def testCart2D(nVar, nX, nY, nSteps, dtypeIdx):
166171 times = np .arange (nSteps )/ nSteps
167172
168173 for t in times :
169- f1 .addField (t , u0 * t )
174+ ut = (u0 * t ).astype (f1 .dtype )
175+ f1 .addField (t , ut )
170176
171177 assert f1 .nFields == nSteps , f"{ f1 } do not have nFields == nSteps"
172178 assert np .allclose (f1 .times , times ), f"{ f1 } has wrong times stored in file"
@@ -183,4 +189,181 @@ def testCart2D(nVar, nX, nY, nSteps, dtypeIdx):
183189 t2 , u2 = f2 .readField (idx )
184190 assert t2 == t , f"{ idx } 's fields in { f1 } has incorrect time"
185191 assert u2 .shape == u1 .shape , f"{ idx } 's fields in { f1 } has incorrect shape"
186- assert np .allclose (u2 , u1 ), f"{ idx } 's fields in { f1 } has incorrect values"
192+ assert np .allclose (u2 , u1 ), f"{ idx } 's fields in { f1 } has incorrect values"
193+
194+
195+ def initGrid (nVar , nX , nY = None ):
196+ nDim = 1
197+ if nY is not None :
198+ nDim += 1
199+ x = np .linspace (0 , 1 , num = nX , endpoint = False )
200+ grids = (x , )
201+ gridSizes = (nX , )
202+ u0 = np .array (np .arange (nVar )+ 1 )[:, None ]* x [None , :]
203+
204+ if nDim > 1 :
205+ y = np .linspace (0 , 1 , num = nY , endpoint = False )
206+ grids += (y , )
207+ gridSizes += (nY , )
208+ u0 = u0 [:, :, None ]* y [None , None , :]
209+
210+ return grids , gridSizes , u0
211+
212+
213+ def writeFields_MPI (fileName , nDim , dtypeIdx , algo , nSteps , nVar , nX , nY = None ):
214+ grids , gridSizes , u0 = initGrid (nVar , nX , nY )
215+
216+ from mpi4py import MPI
217+ comm = MPI .COMM_WORLD
218+ MPI_SIZE = comm .Get_size ()
219+ MPI_RANK = comm .Get_rank ()
220+
221+ from blocks import BlockDecomposition
222+ blocks = BlockDecomposition (MPI_SIZE , gridSizes , algo , MPI_RANK )
223+
224+ from time import sleep
225+
226+ from base import Cart1D , Cart2D
227+ if nDim == 1 :
228+ (iLocX , ), (nLocX , ) = blocks .localBounds
229+ pRankX , = blocks .ranks
230+ Cart1D .setupMPI (comm , iLocX , nLocX )
231+ u0 = u0 [:, iLocX :iLocX + nLocX ]
232+
233+ MPI .COMM_WORLD .Barrier ()
234+ sleep (0.01 * MPI_RANK )
235+ print (f"[Rank { MPI_RANK } ] pRankX={ pRankX } ({ iLocX } , { nLocX } )" )
236+ MPI .COMM_WORLD .Barrier ()
237+
238+ f1 = Cart1D (DTYPES [dtypeIdx ], fileName )
239+ f1 .setHeader (nVar = nVar , gridX = grids [0 ])
240+
241+ if nDim == 2 :
242+ (iLocX , iLocY ), (nLocX , nLocY ) = blocks .localBounds
243+ Cart2D .setupMPI (comm , iLocX , nLocX , iLocY , nLocY )
244+ u0 = u0 [:, iLocX :iLocX + nLocX , iLocY :iLocY + nLocY ]
245+
246+ f1 = Cart2D (DTYPES [dtypeIdx ], fileName )
247+ f1 .setHeader (nVar = nVar , gridX = grids [0 ], gridY = grids [1 ])
248+
249+ u0 = np .asarray (u0 , dtype = f1 .dtype )
250+ f1 .initialize ()
251+
252+ times = np .arange (nSteps )/ nSteps
253+ for t in times :
254+ ut = (u0 * t ).astype (f1 .dtype )
255+ f1 .addField (t , ut )
256+
257+ return u0
258+
259+
260+ def compareFields_MPI (fileName , u0 , nSteps ):
261+ from base import FieldsIO
262+ f2 = FieldsIO .fromFile (fileName )
263+
264+ times = np .arange (nSteps )/ nSteps
265+ for idx , t in enumerate (times ):
266+ u1 = u0 * t
267+ t2 , u2 = f2 .readField (idx )
268+ assert t2 == t , f"{ idx } 's fields in { f2 } has incorrect time"
269+ assert u2 .shape == u1 .shape , f"{ idx } 's fields in { f2 } has incorrect shape"
270+ assert np .allclose (u2 , u1 ), f"{ idx } 's fields in { f2 } has incorrect values"
271+
272+
273+ @pytest .mark .parametrize ("nX" , [61 , 16 , 32 ])
274+ @pytest .mark .parametrize ("nVar" , [1 , 4 ])
275+ @pytest .mark .parametrize ("nSteps" , [1 , 10 ])
276+ @pytest .mark .parametrize ("algo" , ["ChatGPT" , "Hybrid" ])
277+ @pytest .mark .parametrize ("dtypeIdx" , [0 , 1 ])
278+ @pytest .mark .parametrize ("nProcs" , [1 , 2 , 4 ])
279+ def testCart1D_MPI (nProcs , dtypeIdx , algo , nSteps , nVar , nX ):
280+
281+ import subprocess
282+ fileName = "testCart1D_MPI.pysdc"
283+
284+ cmd = f"mpirun -np { nProcs } python { __file__ } --fileName { fileName } --nDim 1 "
285+ cmd += f"--dtypeIdx { dtypeIdx } --algo { algo } --nSteps { nSteps } --nVar { nVar } --nX { nX } "
286+
287+ p = subprocess .Popen (cmd .split (), cwd = "." )
288+ p .wait ()
289+ assert p .returncode == 0 , f"MPI write with { nProcs } did not return code 0, but { p .returncode } "
290+
291+ from base import FieldsIO , Cart1D
292+
293+ f2 :Cart1D = FieldsIO .fromFile (fileName )
294+
295+ assert type (f2 ) == Cart1D , f"incorrect type in MPI written fields { f2 } "
296+ assert f2 .nFields == nSteps , f"incorrect nFields in MPI written fields { f2 } "
297+ assert f2 .nVar == nVar , f"incorrect nVar in MPI written fields { f2 } "
298+ assert f2 .nX == nX , f"incorrect nX in MPI written fields { f2 } "
299+
300+ grids , _ , u0 = initGrid (nVar , nX )
301+ assert np .allclose (f2 .header ['gridX' ], grids [0 ]), f"incorrect gridX in MPI written fields { f2 } "
302+
303+ times = np .arange (nSteps )/ nSteps
304+ for idx , t in enumerate (times ):
305+ u1 = u0 * t
306+ t2 , u2 = f2 .readField (idx )
307+ assert t2 == t , f"{ idx } 's fields in { f2 } has incorrect time"
308+ assert u2 .shape == u1 .shape , f"{ idx } 's fields in { f2 } has incorrect shape"
309+ assert np .allclose (u2 , u1 ), f"{ idx } 's fields in { f2 } has incorrect values"
310+
311+
312+ @pytest .mark .parametrize ("nY" , [61 , 16 , 32 ])
313+ @pytest .mark .parametrize ("nX" , [61 , 16 , 32 ])
314+ @pytest .mark .parametrize ("nVar" , [1 , 4 ])
315+ @pytest .mark .parametrize ("nSteps" , [1 , 10 ])
316+ @pytest .mark .parametrize ("algo" , ["ChatGPT" , "Hybrid" ])
317+ @pytest .mark .parametrize ("dtypeIdx" , [0 , 1 ])
318+ @pytest .mark .parametrize ("nProcs" , [1 , 2 , 4 ])
319+ def testCart2D_MPI (nProcs , dtypeIdx , algo , nSteps , nVar , nX , nY ):
320+
321+ import subprocess
322+ fileName = "testCart2D_MPI.pysdc"
323+
324+ cmd = f"mpirun -np { nProcs } python { __file__ } --fileName { fileName } --nDim 2 "
325+ cmd += f"--dtypeIdx { dtypeIdx } --algo { algo } --nSteps { nSteps } --nVar { nVar } --nX { nX } --nY { nY } "
326+
327+ p = subprocess .Popen (cmd .split (), cwd = "." )
328+ p .wait ()
329+ assert p .returncode == 0 , f"MPI write with { nProcs } did not return code 0, but { p .returncode } "
330+
331+ from base import FieldsIO , Cart2D
332+
333+ f2 :Cart2D = FieldsIO .fromFile (fileName )
334+
335+ assert type (f2 ) == Cart2D , f"incorrect type in MPI written fields { f2 } "
336+ assert f2 .nFields == nSteps , f"incorrect nFields in MPI written fields { f2 } "
337+ assert f2 .nVar == nVar , f"incorrect nVar in MPI written fields { f2 } "
338+ assert f2 .nX == nX , f"incorrect nX in MPI written fields { f2 } "
339+ assert f2 .nY == nY , f"incorrect nY in MPI written fields { f2 } "
340+
341+ grids , _ , u0 = initGrid (nVar , nX , nY )
342+ assert np .allclose (f2 .header ['gridX' ], grids [0 ]), f"incorrect gridX in MPI written fields { f2 } "
343+ assert np .allclose (f2 .header ['gridY' ], grids [1 ]), f"incorrect gridY in MPI written fields { f2 } "
344+
345+ times = np .arange (nSteps )/ nSteps
346+ for idx , t in enumerate (times ):
347+ u1 = u0 * t
348+ t2 , u2 = f2 .readField (idx )
349+ assert t2 == t , f"{ idx } 's fields in { f2 } has incorrect time"
350+ assert u2 .shape == u1 .shape , f"{ idx } 's fields in { f2 } has incorrect shape"
351+ assert np .allclose (u2 , u1 ), f"{ idx } 's fields in { f2 } has incorrect values"
352+
353+
354+ if __name__ == "__main__" :
355+ import argparse
356+
357+ parser = argparse .ArgumentParser ()
358+ parser .add_argument ('--fileName' , type = str , help = 'fileName of the file' )
359+ parser .add_argument ('--nDim' , type = int , help = 'space dimension' , choices = [1 , 2 ])
360+ parser .add_argument ('--dtypeIdx' , type = int , help = "dtype index" , choices = DTYPES .keys ())
361+ parser .add_argument ('--algo' , type = str , help = "algorithm used for block decomposition" , choices = ["ChatGPT" , "Hybrid" ])
362+ parser .add_argument ('--nSteps' , type = int , help = "number of field variables" )
363+ parser .add_argument ('--nVar' , type = int , help = "number of field variables" )
364+ parser .add_argument ('--nX' , type = int , help = "number of grid points in x dimension" )
365+ parser .add_argument ('--nY' , type = int , help = "number of grid points in y dimension" )
366+ args = parser .parse_args ()
367+
368+ u0 = writeFields_MPI (** args .__dict__ )
369+ compareFields_MPI (args .fileName , u0 , args .nSteps )
0 commit comments