Skip to content

Commit 20e8520

Browse files
Antoine Lavandierrenefritze
authored andcommitted
Adding VtkParallelFiles class, high level functions and examples with StructuredGrid and RectilinearGrid.
1 parent d46c0f0 commit 20e8520

4 files changed

Lines changed: 484 additions & 16 deletions

File tree

examples/rectilinear.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@
3030
# * This example shows how to export a rectilinear grid. *
3131
# **************************************************************
3232

33-
from pyevtk.hl import gridToVTK
33+
from pyevtk.hl import gridToVTK, writeParallelVTKGrid
3434
import numpy as np
3535

36+
# ==================
37+
# Serial example
38+
# ==================
39+
3640
# Dimensions
3741
nx, ny, nz = 6, 6, 2
3842
lx, ly, lz = 1.0, 1.0, 1.0
@@ -58,3 +62,26 @@
5862
cellData={"pressure": pressure},
5963
pointData={"temp": temp},
6064
)
65+
66+
67+
# ==================
68+
# Parallel example
69+
# ==================
70+
71+
# Dimensions
72+
x1 = np.linspace(0, 1, 10)
73+
x2 = np.linspace(1, 2, 20)
74+
75+
y = np.linspace(0, 1, 10)
76+
z = np.linspace(0, 1, 10)
77+
78+
gridToVTK("test.0", x1, y, z, start=(0, 0, 0))
79+
gridToVTK("test.1", x2, y, z, start=(9, 0, 0))
80+
81+
writeParallelVTKGrid(
82+
"test_full",
83+
coordsData=((29, 10, 10), x.dtype),
84+
starts=[(0, 0, 0), (9, 0, 0)],
85+
ends=[(9, 9, 9), (28, 9, 9)],
86+
sources=["test.0.vtr", "test.1.vtr"],
87+
)

examples/structured.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@
3030
# * This example shows how to export a structured grid. *
3131
# **************************************************************
3232

33-
from pyevtk.hl import gridToVTK
33+
from pyevtk.hl import gridToVTK, writeParallelVTKGrid
3434
import numpy as np
3535
import random as rnd
3636

37+
# ===================
38+
# Serial Example
39+
# ===================
40+
3741
# Dimensions
3842
nx, ny, nz = 6, 6, 2
3943
lx, ly, lz = 1.0, 1.0, 1.0
@@ -72,3 +76,55 @@
7276
cellData={"pressure": pressure},
7377
pointData={"temp": temp},
7478
)
79+
80+
81+
# ===================
82+
# Parallel example
83+
# ===================
84+
85+
# Dimensions
86+
x1 = np.linspace(0, 1, 20)
87+
88+
x2_1 = np.linspace(0, 0.5, 10)
89+
x2_2 = np.linspace(0.5, 1, 10)
90+
91+
x3 = np.linspace(0, 2, 30)
92+
93+
XX1_1, XX2_1, XX3_1 = np.meshgrid(x1, x2_1, x3, indexing="ij")
94+
XX1_2, XX2_2, XX3_2 = np.meshgrid(x1, x2_2, x3, indexing="ij")
95+
96+
pi = np.pi
97+
sphere = lambda R, Th, Ph: (
98+
R * np.cos(pi * Ph) * np.sin(pi * Th),
99+
R * np.sin(pi * Ph) * np.sin(pi * Th),
100+
R * np.cos(pi * Th),
101+
)
102+
103+
# First Half sphere
104+
gridToVTK(
105+
"sphere.0",
106+
*sphere(XX1_1, XX2_1, XX3_1),
107+
start=(0, 0, 0),
108+
pointData={"R": XX1_1, "Theta": XX2_1, "Phi": XX3_1}
109+
)
110+
# Second Half sphere
111+
gridToVTK(
112+
"sphere.1",
113+
*sphere(XX1_2, XX2_2, XX3_2),
114+
start=(0, 9, 0),
115+
pointData={"R": XX1_2, "Theta": XX2_2, "Phi": XX3_2}
116+
)
117+
118+
# Write parallel file
119+
writeParallelVTKGrid(
120+
"sphere_full",
121+
coordsData=((20, 19, 30), XX1_1.dtype),
122+
starts=[(0, 0, 0), (0, 9, 0)],
123+
ends=[(19, 9, 29), (19, 18, 29)],
124+
sources=["sphere.0.vts", "sphere.1.vts"],
125+
pointData={
126+
"R": (XX1_1.dtype, 1),
127+
"Theta": (XX2_1.dtype, 1),
128+
"Phi": (XX3_1.dtype, 1),
129+
},
130+
)

pyevtk/hl.py

Lines changed: 142 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@
2727
import numpy as np
2828
from .vtk import (
2929
VtkFile,
30+
VtkParallelFile,
3031
VtkUnstructuredGrid,
3132
VtkImageData,
3233
VtkRectilinearGrid,
3334
VtkStructuredGrid,
35+
VtkPImageData,
36+
VtkPRectilinearGrid,
37+
VtkPStructuredGrid,
38+
VtkUnstructuredGrid,
3439
VtkVertex,
3540
VtkLine,
3641
VtkPolyLine,
@@ -81,6 +86,33 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None):
8186
vtkFile.closeData("Field")
8287

8388

89+
def _addDataToParallelFile(vtkParallelFile, cellData, pointData):
90+
assert isinstance(vtkParallelFile, VtkParallelFile)
91+
# Point data
92+
if pointData:
93+
keys = list(pointData.keys())
94+
# find first scalar and vector data key to set it as attribute
95+
scalars = next((key for key in keys if pointData[key][1] == 1), None)
96+
vectors = next((key for key in keys if pointData[key][1] == 3), None)
97+
vtkParallelFile.openData("PPoint", scalars=scalars, vectors=vectors)
98+
for key in keys:
99+
dtype, ncomp = pointData[key]
100+
vtkParallelFile.addHeader(key, dtype=dtype, ncomp=ncomp)
101+
vtkParallelFile.closeData("PPoint")
102+
103+
# Cell data
104+
if cellData:
105+
keys = list(cellData.keys())
106+
# find first scalar and vector data key to set it as attribute
107+
scalars = next((key for key in keys if cellData[key][1] == 1), None)
108+
vectors = next((key for key in keys if cellData[key][1] == 3), None)
109+
vtkParallelFile.openData("PCell", scalars=scalars, vectors=vectors)
110+
for key in keys:
111+
dtype, ncomp = pointData[key]
112+
vtkParallelFile.addHeader(key, dtype=dtype, ncomp=ncomp)
113+
vtkParallelFile.closeData("PCell")
114+
115+
84116
def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None):
85117
# Append data to binary section
86118
if pointData is not None:
@@ -107,6 +139,7 @@ def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None):
107139
# =================================
108140
def imageToVTK(
109141
path,
142+
start=(0, 0, 0),
110143
origin=(0.0, 0.0, 0.0),
111144
spacing=(1.0, 1.0, 1.0),
112145
cellData=None,
@@ -115,11 +148,14 @@ def imageToVTK(
115148
):
116149
"""
117150
Export data values as a rectangular image.
118-
119151
Parameters
120152
----------
121153
path : str
122154
name of the file without extension where data should be saved.
155+
start : tuple, optional
156+
start of the coordinates.
157+
Used in the distributed context where each process
158+
writes its own vtk file. Default is (0, 0, 0).
123159
origin : tuple, optional
124160
grid origin.
125161
The default is (0.0, 0.0, 0.0).
@@ -138,17 +174,15 @@ def imageToVTK(
138174
Arrays must have same dimension in each direction and
139175
they should be equal to the dimensions of the cell data plus one and
140176
can contain scalar data ([n+1,n+1,n+1]) or
141-
+1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]).
177+
([n+1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]).
142178
The default is None.
143179
fieldData : dict, optional
144180
dictionary with variables associated with the field.
145181
Keys should be the names of the variable stored in each array.
146-
147182
Returns
148183
-------
149184
str
150185
Full path to saved file.
151-
152186
Notes
153187
-----
154188
At least, cellData or pointData must be present
@@ -157,7 +191,6 @@ def imageToVTK(
157191
assert cellData is not None or pointData is not None
158192

159193
# Extract dimensions
160-
start = (0, 0, 0)
161194
end = None
162195
if cellData is not None:
163196
keys = list(cellData.keys())
@@ -188,10 +221,11 @@ def imageToVTK(
188221

189222

190223
# ==============================================================================
191-
def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None):
224+
def gridToVTK(
225+
path, x, y, z, start=(0, 0, 0), cellData=None, pointData=None, fieldData=None
226+
):
192227
"""
193-
Write data values as a rectilinear or rectangular grid.
194-
228+
Write data values as a structured grid.
195229
Parameters
196230
----------
197231
path : str
@@ -202,6 +236,10 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None):
202236
y coordinate axis.
203237
z : array-like
204238
z coordinate axis.
239+
start : tuple, optional
240+
start of the coordinates.
241+
Used in the distributed conwhere each process
242+
writes its own vtk file. Default is (0, 0, 0).
205243
cellData : dict, optional
206244
dictionary containing arrays with cell centered data.
207245
Keys should be the names of the data arrays.
@@ -216,12 +254,10 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None):
216254
fieldData : dict, optional
217255
dictionary with variables associated with the field.
218256
Keys should be the names of the variable stored in each array.
219-
220257
Returns
221258
-------
222259
str
223260
Full path to saved file.
224-
225261
Notes
226262
-----
227263
coordinates of the nodes of the grid. They can be 1D or 3D depending if
@@ -235,8 +271,6 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None):
235271
In both cases the arrays dimensions should be
236272
equal to the number of nodes of the grid.
237273
"""
238-
# Extract dimensions
239-
start = (0, 0, 0)
240274
nx = ny = nz = 0
241275

242276
if x.ndim == 1 and y.ndim == 1 and z.ndim == 1:
@@ -249,13 +283,22 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None):
249283
isRect = False
250284
ftype = VtkStructuredGrid
251285
else:
252-
assert False
253-
end = (nx, ny, nz)
286+
raise ValueError(
287+
f"x, y and z should have ndim == 3 but they have ndim of {x.ndim}, {y.ndim}"
288+
f" and {z.ndim} respectively"
289+
)
290+
291+
# Write extent
292+
end = (start[0] + nx, start[1] + ny, start[2] + nz)
254293

294+
# Open File
255295
w = VtkFile(path, ftype)
296+
297+
# Open Grid part
256298
w.openGrid(start=start, end=end)
257299
w.openPiece(start=start, end=end)
258300

301+
# Add coordinates description
259302
if isRect:
260303
w.openElement("Coordinates")
261304
w.addData("x_coordinates", x)
@@ -267,16 +310,101 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None):
267310
w.addData("points", (x, y, z))
268311
w.closeElement("Points")
269312

313+
# Add data description
270314
_addDataToFile(w, cellData, pointData, fieldData)
315+
316+
# Close Grid part
271317
w.closePiece()
272318
w.closeGrid()
319+
273320
# Write coordinates
274321
if isRect:
275322
w.appendData(x).appendData(y).appendData(z)
276323
else:
277324
w.appendData((x, y, z))
278325
# Write data
279326
_appendDataToFile(w, cellData, pointData, fieldData)
327+
328+
# Close file
329+
w.save()
330+
331+
return w.getFileName()
332+
333+
334+
def writeParallelVTKGrid(
335+
path, coordsData, starts, ends, sources, ghostlevel=0, cellData=None, pointData=None
336+
):
337+
"""
338+
Writes a parallel vtk file from grid-like data:
339+
VTKStructuredGrid or VTKRectilinearGrid
340+
341+
Parameters
342+
----------
343+
path : str
344+
name of the file without extension.
345+
coordsData : tuple
346+
2-tuple (shape, dtype) where shape is the
347+
shape of the coordinates of the full mesh
348+
and dtype is the dtype of the coordinates.
349+
starts : list
350+
list of 3-tuple representing where each source file starts
351+
in each dimension
352+
source : list
353+
list of the relative paths of the source files where the actual data is found
354+
ghostlevel : int, optional
355+
Number of ghost-levels by which
356+
the extents in the individual source files overlap.
357+
pointData : dict
358+
dictionnary containing the information about the arrays
359+
containing node centered data.
360+
Keys shoud be the names of the arrays.
361+
Values are (dtype, number of components)
362+
cellData :
363+
dictionnary containing the information about the arrays
364+
containing cell centered data.
365+
Keys shoud be the names of the arrays.
366+
Values are (dtype, number of components)
367+
"""
368+
# Check that every source as a start and an end
369+
assert len(starts) == len(ends) == len(sources)
370+
371+
# Get the extension + check that it's consistent accros all source files
372+
common_ext = sources[0].split(".")[-1]
373+
assert all(s.split(".")[-1] == common_ext for s in sources)
374+
375+
if common_ext == "vts":
376+
ftype = VtkPStructuredGrid
377+
is_Rect = False
378+
elif common_ext == "vtr":
379+
ftype = VtkPRectilinearGrid
380+
is_Rect = True
381+
else:
382+
raise ValueError("This functions is meant to work only with ")
383+
384+
w = VtkParallelFile(path, ftype)
385+
start = (0, 0, 0)
386+
(s_x, s_y, s_z), dtype = coordsData
387+
end = s_x - 1, s_y - 1, s_z - 1
388+
389+
w.openGrid(start=start, end=end, ghostlevel=ghostlevel)
390+
391+
_addDataToParallelFile(w, cellData=cellData, pointData=pointData)
392+
393+
if is_Rect:
394+
w.openElement("PCoordinates")
395+
w.addHeader("x_coordinates", dtype=dtype, ncomp=1)
396+
w.addHeader("y_coordinates", dtype=dtype, ncomp=1)
397+
w.addHeader("z_coordinates", dtype=dtype, ncomp=1)
398+
w.closeElement("PCoordinates")
399+
else:
400+
w.openElement("PPoints")
401+
w.addHeader("points", dtype=dtype, ncomp=3)
402+
w.closeElement("PPoints")
403+
404+
for start_source, end_source, source in zip(starts, ends, sources):
405+
w.addPiece(start_source, end_source, source)
406+
407+
w.closeGrid()
280408
w.save()
281409
return w.getFileName()
282410

0 commit comments

Comments
 (0)