From f428374183c0523a5ac43db6dcf264400c3b4afc Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:34:18 +0200 Subject: [PATCH 01/52] evtk: reformat with black --- evtk/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/evtk/__init__.py b/evtk/__init__.py index 7816d09..3086cf2 100644 --- a/evtk/__init__.py +++ b/evtk/__init__.py @@ -1,4 +1,8 @@ from pyevtk import * import warnings -warnings.warn('the "evtk" package is deprecated, use "pyevtk" instead', DeprecationWarning) \ No newline at end of file + +warnings.warn( + 'the "evtk" package is deprecated, use "pyevtk" instead', + DeprecationWarning, +) From d59bc12c034123bef624357a84b1a0f6025afd22 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:35:23 +0200 Subject: [PATCH 02/52] evtk-subpackage: reformat with black --- pyevtk/evtk.py | 136 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 44 deletions(-) diff --git a/pyevtk/evtk.py b/pyevtk/evtk.py index 335322c..421b9dd 100644 --- a/pyevtk/evtk.py +++ b/pyevtk/evtk.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,75 +22,123 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""Export routines.""" +import sys import struct +# import base64 import numpy as np -import sys # Map numpy dtype to struct format -np_to_struct = { 'int8' : 'b', - 'uint8' : 'B', - 'int16' : 'h', - 'uint16' : 'H', - 'int32' : 'i', - 'uint32' : 'I', - 'int64' : 'q', - 'uint64' : 'Q', - 'float32' : 'f', - 'float64' : 'd' } - +np_to_struct = { + "int8": "b", + "uint8": "B", + "int16": "h", + "uint16": "H", + "int32": "i", + "uint32": "I", + "int64": "q", + "uint64": "Q", + "float32": "f", + "float64": "d", +} + + def _get_byte_order_char(): -# Check format in https://docs.python.org/3.5/library/struct.html + # Check format in https://docs.python.org/3.5/library/struct.html if sys.byteorder == "little": - return '<' - else: - return '>' - + return "<" + return ">" + + # ================================ # Python interface -# ================================ +# ================================ def writeBlockSize(stream, block_size): - fmt = _get_byte_order_char() + 'Q' # Write size as unsigned long long == 64 bits unsigned integer + """ + Write block size to a given stream. + + Parameters + ---------- + stream : stream + open stream. + block_size : int + block size. + """ + fmt = ( + _get_byte_order_char() + "Q" + ) # Write size as unsigned long long == 64 bits unsigned integer stream.write(struct.pack(fmt, block_size)) + def writeArrayToFile(stream, data): - #stream.flush() # this should not be necessary - assert (data.ndim == 1 or data.ndim == 3) - fmt = _get_byte_order_char() + str(data.size) + np_to_struct[data.dtype.name] # > for big endian + """ + Write array to a given stream. + + Parameters + ---------- + stream : stream + open stream. + data : array-like + data array to be saved. + """ + # stream.flush() # this should not be necessary + assert data.ndim == 1 or data.ndim == 3 + fmt = ( + _get_byte_order_char() + str(data.size) + np_to_struct[data.dtype.name] + ) # > for big endian # Check if array is contiguous - assert (data.flags['C_CONTIGUOUS'] or data.flags['F_CONTIGUOUS']) - + assert data.flags["C_CONTIGUOUS"] or data.flags["F_CONTIGUOUS"] + # NOTE: VTK expects data in FORTRAN order # This is only needed when a multidimensional array has C-layout - dd = np.ravel(data, order='F') + dd = np.ravel(data, order="F") + + bin_pack = struct.pack(fmt, *dd) + stream.write(bin_pack) + - bin = struct.pack(fmt, *dd) - stream.write(bin) - # ============================================================================== def writeArraysToFile(stream, x, y, z): + """ + Write multiple array to a given stream. + + Parameters + ---------- + stream : stream + open stream. + x : array-like + x array to be saved. + y : array-like + y array to be saved. + z : array-like + z array to be saved. + """ # Check if arrays have same shape and data type - assert ( x.size == y.size == z.size ), "Different array sizes." - assert ( x.dtype.itemsize == y.dtype.itemsize == z.dtype.itemsize ), "Different item sizes." - + assert x.size == y.size == z.size, "Different array sizes." + assert ( + x.dtype.itemsize == y.dtype.itemsize == z.dtype.itemsize + ), "Different item sizes." + nitems = x.size - itemsize = x.dtype.itemsize + # itemsize = x.dtype.itemsize + + fmt = ( + _get_byte_order_char() + str(1) + np_to_struct[x.dtype.name] + ) # > for big endian - fmt = _get_byte_order_char() + str(1) + np_to_struct[x.dtype.name] # > for big endian - # Check if arrays are contiguous - assert (x.flags['C_CONTIGUOUS'] or x.flags['F_CONTIGUOUS']) - assert (y.flags['C_CONTIGUOUS'] or y.flags['F_CONTIGUOUS']) - assert (z.flags['C_CONTIGUOUS'] or z.flags['F_CONTIGUOUS']) - - + assert x.flags["C_CONTIGUOUS"] or x.flags["F_CONTIGUOUS"] + assert y.flags["C_CONTIGUOUS"] or y.flags["F_CONTIGUOUS"] + assert z.flags["C_CONTIGUOUS"] or z.flags["F_CONTIGUOUS"] + # NOTE: VTK expects data in FORTRAN order # This is only needed when a multidimensional array has C-layout - xx = np.ravel(x, order='F') - yy = np.ravel(y, order='F') - zz = np.ravel(z, order='F') - + xx = np.ravel(x, order="F") + yy = np.ravel(y, order="F") + zz = np.ravel(z, order="F") + # eliminate this loop by creating a composed array. for i in range(nitems): bx = struct.pack(fmt, xx[i]) From ce2224e8b294b2b95c0c3273e48504094ef4559b Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:36:15 +0200 Subject: [PATCH 03/52] xml: add doc; reformat with black --- pyevtk/xml.py | 86 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/pyevtk/xml.py b/pyevtk/xml.py index 6d92f32..1c76832 100644 --- a/pyevtk/xml.py +++ b/pyevtk/xml.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,38 +22,65 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""Simple class to generate a well-formed XML file.""" -# ************************************** -# * Simple class to generate a * -# * well-formed XML file. * -# ************************************** class XmlWriter: - def __init__(self, filepath, addDeclaration = True): + """ + xml writer class. + + Parameters + ---------- + filepath : TYPE + DESCRIPTION. + addDeclaration : bool, optional + DESCRIPTION. The default is True. + """ + + def __init__(self, filepath, addDeclaration=True): self.stream = open(filepath, "wb") self.openTag = False self.current = [] - if (addDeclaration): self.addDeclaration() + if addDeclaration: + self.addDeclaration() def close(self): - assert(not self.openTag) + """Close the file.""" + assert not self.openTag self.stream.close() def addDeclaration(self): + """Add xml declaration.""" self.stream.write(b'') - + def openElement(self, tag): - if self.openTag: self.stream.write(b">") + """Open a new xml element.""" + if self.openTag: + self.stream.write(b">") st = "\n<%s" % tag self.stream.write(str.encode(st)) self.openTag = True self.current.append(tag) return self - def closeElement(self, tag = None): + def closeElement(self, tag=None): + """ + Close the current element. + + Parameters + ---------- + tag : str, optional + Tag of the element. + The default is None. + + Returns + ------- + XmlWriter + The XmlWriter itself for chained calles. + """ if tag: - assert(self.current.pop() == tag) - if (self.openTag): + assert self.current.pop() == tag + if self.openTag: self.stream.write(b">") self.openTag = False st = "\n" % tag @@ -65,16 +92,41 @@ def closeElement(self, tag = None): return self def addText(self, text): - if (self.openTag): + """ + Add text. + + Parameters + ---------- + text : str + Text to add. + + Returns + ------- + XmlWriter + The XmlWriter itself for chained calles. + """ + if self.openTag: self.stream.write(b">\n") self.openTag = False self.stream.write(str.encode(text)) return self def addAttributes(self, **kwargs): - assert (self.openTag) + """ + Add attributes. + + Parameters + ---------- + **kwargs + keys as attribute names. + + Returns + ------- + XmlWriter + The XmlWriter itself for chained calles. + """ + assert self.openTag for key in kwargs: - st = ' %s="%s"'%(key, kwargs[key]) + st = ' %s="%s"' % (key, kwargs[key]) self.stream.write(str.encode(st)) return self - From 5388d6061994c112d0ee876a6da60a4b76e776ee Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:39:24 +0200 Subject: [PATCH 04/52] vtk: update doc; reformat with black; remove largefile attribute; remove ref to non-existing routine (64bit) --- pyevtk/vtk.py | 669 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 424 insertions(+), 245 deletions(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 2b6d9f0..3015b85 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,16 +22,14 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""Low level Python library to export data to binary VTK file.""" -# ************************************** -# * Low level Python library to * -# * export data to binary VTK file. * -# ************************************** +import sys +import os from .evtk import writeBlockSize, writeArrayToFile, writeArraysToFile from .xml import XmlWriter -import sys -import os + # ================================ # VTK Types @@ -39,22 +37,44 @@ # FILE TYPES class VtkFileType: + """ + Wrapper class for VTK file types. + + Parameters + ---------- + name : str + Data name. + ext : str + File extension. + """ def __init__(self, name, ext): self.name = name - self.ext = ext + self.ext = ext def __str__(self): return "Name: %s Ext: %s \n" % (self.name, self.ext) -VtkImageData = VtkFileType("ImageData", ".vti") -VtkPolyData = VtkFileType("PolyData", ".vtp") -VtkRectilinearGrid = VtkFileType("RectilinearGrid", ".vtr") -VtkStructuredGrid = VtkFileType("StructuredGrid", ".vts") + +VtkImageData = VtkFileType("ImageData", ".vti") +VtkPolyData = VtkFileType("PolyData", ".vtp") +VtkRectilinearGrid = VtkFileType("RectilinearGrid", ".vtr") +VtkStructuredGrid = VtkFileType("StructuredGrid", ".vts") VtkUnstructuredGrid = VtkFileType("UnstructuredGrid", ".vtu") + # DATA TYPES class VtkDataType: + """ + Wrapper class for VTK data types. + + Parameters + ---------- + size : int + Size in byte. + name : str + Type name. + """ def __init__(self, size, name): self.size = size @@ -63,38 +83,53 @@ def __init__(self, size, name): def __str__(self): return "Type: %s Size: %d \n" % (self.name, self.size) -VtkInt8 = VtkDataType(1, "Int8") -VtkUInt8 = VtkDataType(1, "UInt8") -VtkInt16 = VtkDataType(2, "Int16") -VtkUInt16 = VtkDataType(2, "UInt16") -VtkInt32 = VtkDataType(4, "Int32") -VtkUInt32 = VtkDataType(4, "UInt32") -VtkInt64 = VtkDataType(8, "Int64") -VtkUInt64 = VtkDataType(8, "UInt64") + +VtkInt8 = VtkDataType(1, "Int8") +VtkUInt8 = VtkDataType(1, "UInt8") +VtkInt16 = VtkDataType(2, "Int16") +VtkUInt16 = VtkDataType(2, "UInt16") +VtkInt32 = VtkDataType(4, "Int32") +VtkUInt32 = VtkDataType(4, "UInt32") +VtkInt64 = VtkDataType(8, "Int64") +VtkUInt64 = VtkDataType(8, "UInt64") VtkFloat32 = VtkDataType(4, "Float32") VtkFloat64 = VtkDataType(8, "Float64") # Map numpy to VTK data types -np_to_vtk = { 'int8' : VtkInt8, - 'uint8' : VtkUInt8, - 'int16' : VtkInt16, - 'uint16' : VtkUInt16, - 'int32' : VtkInt32, - 'uint32' : VtkUInt32, - 'int64' : VtkInt64, - 'uint64' : VtkUInt64, - 'float32' : VtkFloat32, - 'float64' : VtkFloat64 } +np_to_vtk = { + "int8": VtkInt8, + "uint8": VtkUInt8, + "int16": VtkInt16, + "uint16": VtkUInt16, + "int32": VtkInt32, + "uint32": VtkUInt32, + "int64": VtkInt64, + "uint64": VtkUInt64, + "float32": VtkFloat32, + "float64": VtkFloat64, +} + # CELL TYPES class VtkCellType: + """ + Wrapper class for VTK cell types. + + Parameters + ---------- + tid : int + Type ID. + name : TYPE + Cell type name. + """ def __init__(self, tid, name): self.tid = tid self.name = name def __str__(self): - return "VtkCellType( %s ) \n" % ( self.name ) + return "VtkCellType( %s ) \n" % (self.name) + VtkVertex = VtkCellType(1, "Vertex") VtkPolyVertex = VtkCellType(2, "PolyVertex") @@ -116,271 +151,387 @@ def __str__(self): VtkQuadraticTetra = VtkCellType(24, "Quadratic_Tetra") VtkQuadraticHexahedron = VtkCellType(25, "Quadratic_Hexahedron") + # ============================== # Helper functions # ============================== def _mix_extents(start, end): - assert (len(start) == len(end) == 3) - string = "%d %d %d %d %d %d" % (start[0], end[0], start[1], end[1], start[2], end[2]) + assert len(start) == len(end) == 3 + string = "%d %d %d %d %d %d" % ( + start[0], + end[0], + start[1], + end[1], + start[2], + end[2], + ) return string + def _array_to_string(a): s = "".join([repr(num) + " " for num in a]) return s + def _get_byte_order(): if sys.byteorder == "little": return "LittleEndian" - else: - return "BigEndian" + return "BigEndian" + # ================================ # VtkGroup class # ================================ class VtkGroup: - + """ + Creates a VtkGroup file that is stored in filepath. + + Parameters + ---------- + filepath : str + filename without extension. + """ + def __init__(self, filepath): - """ Creates a VtkGroup file that is stored in filepath. - - PARAMETERS: - filepath: filename without extension. - """ self.xml = XmlWriter(filepath + ".pvd") self.xml.openElement("VTKFile") - self.xml.addAttributes(type = "Collection", version = "0.1", byte_order = _get_byte_order()) + self.xml.addAttributes( + type="Collection", version="0.1", byte_order=_get_byte_order() + ) self.xml.openElement("Collection") self.root = os.path.dirname(filepath) def save(self): - """ Closes this VtkGroup. """ + """Close this VtkGroup.""" self.xml.closeElement("Collection") self.xml.closeElement("VTKFile") self.xml.close() - - def addFile(self, filepath, sim_time, group = "", part = "0"): - """ Adds file to this VTK group. - - PARAMETERS: - filepath: full path to VTK file. - sim_time: simulated time. - group: This attribute is not required; it is only for informational purposes. - part: It is an integer value greater than or equal to 0. - - See: http://www.paraview.org/Wiki/ParaView/Data_formats#PVD_File_Format for details. + + def addFile(self, filepath, sim_time, group="", part="0"): + """ + Add a file to this VTK group. + + Parameters + ---------- + filepath : str + full path to VTK file. + sim_time : float + simulated time. + group : str, optional + This attribute is not required; + it is only for informational purposes. + The default is "". + part : int, optional + It is an integer value greater than or equal to 0. + The default is "0". + + Notes + ----- + See: http://www.paraview.org/Wiki/ParaView/Data_formats#PVD_File_Format for details. """ # TODO: Check what the other attributes are for. - filename = os.path.relpath(filepath, start = self.root) + filename = os.path.relpath(filepath, start=self.root) self.xml.openElement("DataSet") - self.xml.addAttributes(timestep = sim_time, group = group, part = part, file = filename) + self.xml.addAttributes( + timestep=sim_time, group=group, part=part, file=filename + ) self.xml.closeElement() - # ================================ -# VtkFile class +# VtkFile class # ================================ class VtkFile: - - def __init__(self, filepath, ftype, largeFile = False): - """ - PARAMETERS: - filepath: filename without extension. - ftype: file type, e.g. VtkImageData, etc. - largeFile: If size of the stored data cannot be represented by a UInt32. - """ + """ + Class for a VTK file. + + Parameters + ---------- + filepath : str + filename without extension. + ftype : str + file type, e.g. VtkImageData, etc. + largeFile : bool, optional + If size of the stored data cannot be represented by a UInt32. + The default is False. + """ + + def __init__(self, filepath, ftype): self.ftype = ftype self.filename = filepath + ftype.ext self.xml = XmlWriter(self.filename) self.offset = 0 # offset in bytes after beginning of binary section self.appendedDataIsOpen = False -# self.largeFile = largeFile - -# if largeFile == False: -# self.xml.openElement("VTKFile").addAttributes(type = ftype.name, -# version = "0.1", -# byte_order = _get_byte_order()) -# else: -# print "WARNING: output file only compatible with VTK 6.0 and later." - self.xml.openElement("VTKFile").addAttributes(type = ftype.name, - version = "1.0", - byte_order = _get_byte_order(), - header_type = "UInt64") + self.xml.openElement("VTKFile").addAttributes( + type=ftype.name, + version="1.0", + byte_order=_get_byte_order(), + header_type="UInt64", + ) def getFileName(self): - """ Returns absolute path to this file. """ - return os.path.abspath(self.filename) - - def openPiece(self, start = None, end = None, - npoints = None, ncells = None, - nverts = None, nlines = None, nstrips = None, npolys = None): - """ Open piece section. - - PARAMETERS: - Next two parameters must be given together. - start: array or list with start indexes in each direction. - end: array or list with end indexes in each direction. - - npoints: number of points in piece (int). - ncells: number of cells in piece (int). If present, - npoints must also be given. - - All the following parameters must be given together with npoints. - They should all be integer values. - nverts: number of vertices. - nlines: number of lines. - nstrips: number of strips. - npolys: number of . - - RETURNS: - this VtkFile to allow chained calls. + """Return absolute path to this file.""" + return os.path.abspath(self.filename) + + def openPiece( + self, + start=None, + end=None, + npoints=None, + ncells=None, + nverts=None, + nlines=None, + nstrips=None, + npolys=None, + ): + """ + Open piece section. + + Parameters + ---------- + start : array-like, optional + array or list with start indexes in each direction. + Must be given with end. + end : array-like, optional + array or list with end indexes in each direction. + Must be given with start. + npoints : int, optional + number of points in piece + ncells : int, optional + number of cells in piece. + If present, npoints must also be given. + nverts : int, optional + number of vertices. + nlines : int, optional + number of lines. + nstrips : int, optional + number of stripes. + npolys : int, optional + number of poly. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ # TODO: Check what are the requirements for each type of grid. self.xml.openElement("Piece") - if (start and end): + if start and end: ext = _mix_extents(start, end) - self.xml.addAttributes( Extent = ext) - - elif (ncells and npoints): - self.xml.addAttributes(NumberOfPoints = npoints, NumberOfCells = ncells) - + self.xml.addAttributes(Extent=ext) + + elif ncells and npoints: + self.xml.addAttributes( + NumberOfPoints=npoints, NumberOfCells=ncells + ) + elif npoints or nverts or nlines or nstrips or npolys: - if npoints is None: npoints = str(0) - if nverts is None: nverts = str(0) - if nlines is None: nlines = str(0) - if nstrips is None: nstrips = str(0) - if npolys is None: npolys = str(0) - self.xml.addAttributes(NumberOfPoints = npoints, NumberOfVerts = nverts, - NumberOfLines = nlines, NumberOfStrips = nstrips, NumberOfPolys = npolys) + if npoints is None: + npoints = str(0) + if nverts is None: + nverts = str(0) + if nlines is None: + nlines = str(0) + if nstrips is None: + nstrips = str(0) + if npolys is None: + npolys = str(0) + self.xml.addAttributes( + NumberOfPoints=npoints, + NumberOfVerts=nverts, + NumberOfLines=nlines, + NumberOfStrips=nstrips, + NumberOfPolys=npolys, + ) else: - assert(False) + assert False return self def closePiece(self): + """Close Piece.""" self.xml.closeElement("Piece") - def openData(self, nodeType, scalars=None, vectors=None, normals=None, tensors=None, tcoords=None): - """ Open data section. - - PARAMETERS: - nodeType: Point or Cell. - scalars: default data array name for scalar data. - vectors: default data array name for vector data. - normals: default data array name for normals data. - tensors: default data array name for tensors data. - tcoords: dafault data array name for tcoords data. - - RETURNS: - this VtkFile to allow chained calls. + def openData( + self, + nodeType, + scalars=None, + vectors=None, + normals=None, + tensors=None, + tcoords=None, + ): + """ + Open data section. + + Parameters + ---------- + nodeType : str + DESCRIPTION. + scalars : str, optional + default data array name for scalar data. + vectors : str, optional + default data array name for vector data. + normals : str, optional + default data array name for normals data. + tensors : str, optional + default data array name for tensors data. + tcoords : str, optional + default data array name for tcoords data. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ self.xml.openElement(nodeType + "Data") if scalars: - self.xml.addAttributes(scalars = scalars) + self.xml.addAttributes(scalars=scalars) if vectors: - self.xml.addAttributes(vectors = vectors) + self.xml.addAttributes(vectors=vectors) if normals: - self.xml.addAttributes(normals = normals) + self.xml.addAttributes(normals=normals) if tensors: - self.xml.addAttributes(tensors = tensors) + self.xml.addAttributes(tensors=tensors) if tcoords: - self.xml.addAttributes(tcoords = tcoords) + self.xml.addAttributes(tcoords=tcoords) return self def closeData(self, nodeType): - """ Close data section. - - PARAMETERS: - nodeType: Point or Cell. - - RETURNS: - this VtkFile to allow chained calls. """ - self.xml.closeElement(nodeType + "Data") + Close data section. + Parameters + ---------- + nodeType : str + "Point", "Cell" or "Field". - def openGrid(self, start = None, end = None, origin = None, spacing = None): - """ Open grid section. - - PARAMETERS: - start: array or list of start indexes. Required for Structured, Rectilinear and ImageData grids. - end: array or list of end indexes. Required for Structured, Rectilinear and ImageData grids. - origin: 3D array or list with grid origin. Only required for ImageData grids. - spacing: 3D array or list with grid spacing. Only required for ImageData grids. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + self.xml.closeElement(nodeType + "Data") - RETURNS: - this VtkFile to allow chained calls. + def openGrid(self, start=None, end=None, origin=None, spacing=None): + """ + Open grid section. + + Parameters + ---------- + start : array-like, optional + array or list of start indexes. + Required for Structured, Rectilinear and ImageData grids. + The default is None. + end : array-like, optional + array or list of end indexes. + Required for Structured, Rectilinear and ImageData grids. + The default is None. + origin : array-like, optional + 3D array or list with grid origin. + Only required for ImageData grids. + The default is None. + spacing : array-like, optional + 3D array or list with grid spacing. + Only required for ImageData grids. + The default is None. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ gType = self.ftype.name self.xml.openElement(gType) - if (gType == VtkImageData.name): - if (not start or not end or not origin or not spacing): assert(False) + if gType == VtkImageData.name: + if not start or not end or not origin or not spacing: + assert False ext = _mix_extents(start, end) - self.xml.addAttributes(WholeExtent = ext, - Origin = _array_to_string(origin), - Spacing = _array_to_string(spacing)) - - elif (gType == VtkStructuredGrid.name or gType == VtkRectilinearGrid.name): - if (not start or not end): assert (False) + self.xml.addAttributes( + WholeExtent=ext, + Origin=_array_to_string(origin), + Spacing=_array_to_string(spacing), + ) + + elif gType in [VtkStructuredGrid.name, VtkRectilinearGrid.name]: + if not start or not end: + assert False ext = _mix_extents(start, end) - self.xml.addAttributes(WholeExtent = ext) - + self.xml.addAttributes(WholeExtent=ext) + return self def closeGrid(self): - """ Closes grid element. + """ + Close grid element. - RETURNS: - this VtkFile to allow chained calls. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ self.xml.closeElement(self.ftype.name) - def addHeader(self, name, dtype, nelem, ncomp): - """ Adds data array description to xml header section. - - PARAMETERS: - name: data array name. - dtype: string describing type of the data. - Format is the same as used by numpy, e.g. 'float64'. - nelem: number of elements in the array. - ncomp: number of components, 1 (=scalar) and 3 (=vector). - - RETURNS: - This VtkFile to allow chained calls. - - NOTE: This is a low level function. Use addData if you want - to add a numpy array. + """ + Add data array description to xml header section. + + Parameters + ---------- + name : str + data array name. + dtype : str + data type. + nelem : int + number of elements in array. + ncomp : int + number of components, 1 (=scalar) and 3 (=vector). + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + + Notes + ----- + This is a low level function. + Use addData if you want to add a numpy array. """ dtype = np_to_vtk[dtype] - self.xml.openElement( "DataArray") - self.xml.addAttributes( Name = name, - NumberOfComponents = ncomp, - type = dtype.name, - format = "appended", - offset = self.offset) + self.xml.openElement("DataArray") + self.xml.addAttributes( + Name=name, + NumberOfComponents=ncomp, + type=dtype.name, + format="appended", + offset=self.offset, + ) self.xml.closeElement() - #TODO: Check if 4/8 is platform independent - #if self.largeFile == False: - # self.offset += nelem * ncomp * dtype.size + 4 # add 4 to indicate array size - #else: - self.offset += nelem * ncomp * dtype.size + 8 # add 8 to indicate array size + self.offset += ( + nelem * ncomp * dtype.size + 8 + ) # add 8 to indicate array size return self def addData(self, name, data): - """ Adds array description to xml header section. - - PARAMETERS: - name: data array name. - data: one numpy array or a tuple with 3 numpy arrays. If a tuple, the individual - arrays must represent the components of a vector field. - All arrays must be one dimensional or three-dimensional. """ - if type(data).__name__ == "tuple": # vector data - assert (len(data) == 3) + Add array description to xml header section. + + Parameters + ---------- + name : str + data array name. + data : array-like + one numpy array or a tuple with 3 numpy arrays. + If a tuple, the individual arrays must represent the components + of a vector field. + All arrays must be one dimensional or three-dimensional. + """ + if type(data).__name__ == "tuple": # vector data + assert len(data) == 3 x = data[0] self.addHeader(name, x.dtype.name, x.size, 3) elif type(data).__name__ == "ndarray": @@ -392,96 +543,124 @@ def addData(self, name, data): assert False, "Argument must be a Numpy array" def appendHeader(self, dtype, nelem, ncomp): - """ This function only writes the size of the data block that will be appended. - The data itself must be written immediately after calling this function. - - PARAMETERS: - dtype: string with data type representation (same as numpy). For example, 'float64' - nelem: number of elements. - ncomp: number of components, 1 (=scalar) or 3 (=vector). + """ + Append size of data block to header. + + This function only writes the size of the data block + that will be appended. + The data itself must be written immediately after + calling this function. + + Parameters + ---------- + dtype : str + string with data type representation (same as numpy). + For example, 'float64'. + nelem : int + number of elements. + ncomp : int + number of components, 1 (=scalar) or 3 (=vector).. """ self.openAppendedData() dsize = np_to_vtk[dtype].size block_size = dsize * ncomp * nelem - if self.largeFile == False: - writeBlockSize(self.xml.stream, block_size) - else: - writeBlockSize64Bit(self.xml.stream, block_size) + writeBlockSize(self.xml.stream, block_size) + # else: # this routine does not exist! + # writeBlockSize64Bit(self.xml.stream, block_size) - def appendData(self, data): - """ Append data to binary section. - This function writes the header section and the data to the binary file. - - PARAMETERS: - data: one numpy array or a tuple with 3 numpy arrays. If a tuple, the individual - arrays must represent the components of a vector field. - All arrays must be one dimensional or three-dimensional. - The order of the arrays must coincide with the numbering scheme of the grid. - - RETURNS: - This VtkFile to allow chained calls - - TODO: Extend this function to accept contiguous C order arrays. """ + Append data to binary section. + + This function writes the header section + and the data to the binary file. + + Parameters + ---------- + data : array-like + one numpy array or a tuple with 3 numpy arrays. + If a tuple, the individual + arrays must represent the components of a vector field. + All arrays must be one dimensional or three-dimensional. + The order of the arrays must coincide with + the numbering scheme of the grid. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + # TODO: Extend this function to accept contiguous C order arrays. self.openAppendedData() - if type(data).__name__ == 'tuple': # 3 numpy arrays + if type(data).__name__ == "tuple": # 3 numpy arrays ncomp = len(data) - assert (ncomp == 3) + assert ncomp == 3 dsize = data[0].dtype.itemsize nelem = data[0].size block_size = ncomp * nelem * dsize - #if self.largeFile == False: + # if self.largeFile == False: writeBlockSize(self.xml.stream, block_size) - #else: + # else: # writeBlockSize64Bit(self.xml.stream, block_size) x, y, z = data[0], data[1], data[2] writeArraysToFile(self.xml.stream, x, y, z) - - elif type(data).__name__ == 'ndarray' and (data.ndim == 1 or data.ndim == 3): # single numpy array - ncomp = 1 + + elif type(data).__name__ == "ndarray" and ( + data.ndim == 1 or data.ndim == 3 + ): # single numpy array + ncomp = 1 dsize = data.dtype.itemsize nelem = data.size block_size = ncomp * nelem * dsize - #if self.largeFile == False: + # if self.largeFile == False: writeBlockSize(self.xml.stream, block_size) - #else: + # else: # writeBlockSize64Bit(self.xml.stream, block_size) writeArrayToFile(self.xml.stream, data) - + else: assert False return self def openAppendedData(self): - """ Opens binary section. + """ + Open binary section. - It is not necessary to explicitly call this function from an external library. + It is not necessary to explicitly call this function + from an external library. """ if not self.appendedDataIsOpen: - self.xml.openElement("AppendedData").addAttributes(encoding = "raw").addText("_") + self.xml.openElement("AppendedData").addAttributes( + encoding="base64" + ).addText("_") self.appendedDataIsOpen = True def closeAppendedData(self): - """ Closes binary section. + """ + Close binary section. - It is not necessary to explicitly call this function from an external library. + It is not necessary to explicitly call this function + from an external library. """ self.xml.closeElement("AppendedData") def openElement(self, tagName): - """ Useful to add elements such as: Coordinates, Points, Verts, etc. """ + """ + Open an element. + + Useful to add elements such as: Coordinates, Points, Verts, etc. + """ self.xml.openElement(tagName) def closeElement(self, tagName): + """Close an element.""" self.xml.closeElement(tagName) def save(self): - """ Closes file """ + """Close file.""" if self.appendedDataIsOpen: self.xml.closeElement("AppendedData") self.xml.closeElement("VTKFile") self.xml.close() - From 3b3fcf0a22c5e282d1dac6f10934e2fd6a5e4c32 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:40:12 +0200 Subject: [PATCH 05/52] hl: add field_data export option; update doc; reformat with black --- pyevtk/hl.py | 727 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 475 insertions(+), 252 deletions(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 7f6383b..c9c1b66 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,23 +22,30 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""High level Python library to export data to binary VTK file.""" -# ************************************** -# * High level Python library to * -# * export data to binary VTK file. * -# ************************************** - -from .vtk import * # VtkFile, VtkUnstructuredGrid, etc. import numpy as np +from .vtk import ( + VtkFile, + VtkUnstructuredGrid, + VtkImageData, + VtkRectilinearGrid, + VtkStructuredGrid, + VtkVertex, + VtkLine, + VtkPolyLine, + VtkPixel, +) + # ================================= # Helper functions # ================================= -def _addDataToFile(vtkFile, cellData, pointData): +def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): # Point data if pointData: keys = list(pointData.keys()) - vtkFile.openData("Point", scalars = keys[0]) + vtkFile.openData("Point", scalars=keys[0]) for key in keys: data = pointData[key] vtkFile.addData(key, data) @@ -47,132 +54,197 @@ def _addDataToFile(vtkFile, cellData, pointData): # Cell data if cellData: keys = list(cellData.keys()) - vtkFile.openData("Cell", scalars = keys[0]) + vtkFile.openData("Cell", scalars=keys[0]) for key in keys: data = cellData[key] vtkFile.addData(key, data) vtkFile.closeData("Cell") -def _appendDataToFile(vtkFile, cellData, pointData): + # Field data + # https://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files#XML_VTK_files + if fieldData: + keys = list(fieldData.keys()) + vtkFile.openData("Field") # no attributes in FieldData + for key in keys: + data = fieldData[key] + vtkFile.addData(key, data) + vtkFile.closeData("Field") + + +def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None): # Append data to binary section - if pointData != None: + if pointData is not None: keys = list(pointData.keys()) for key in keys: data = pointData[key] vtkFile.appendData(data) - if cellData != None: + if cellData is not None: keys = list(cellData.keys()) for key in keys: data = cellData[key] vtkFile.appendData(data) + if fieldData is not None: + keys = list(fieldData.keys()) + for key in keys: + data = fieldData[key] + vtkFile.appendData(data) + + # ================================= -# High level functions +# High level functions # ================================= -def imageToVTK(path, origin = (0.0,0.0,0.0), spacing = (1.0,1.0,1.0), cellData = None, pointData = None ): - """ Exports data values as a rectangular image. - - PARAMETERS: - path: name of the file without extension where data should be saved. - origin: grid origin (default = (0,0,0)) - spacing: grid spacing (default = (1,1,1)) - cellData: dictionary containing arrays with cell centered data. - Keys should be the names of the data arrays. - Arrays must have the same dimensions in all directions and can contain - scalar data ([n,n,n]) or vector data ([n,n,n],[n,n,n],[n,n,n]). - nodeData: dictionary containing arrays with node centered data. - Keys should be the names of the data arrays. - Arrays must have same dimension in each direction and - they should be equal to the dimensions of the cell data plus one and - can contain scalar data ([n+1,n+1,n+1]) or - vector data ([n+1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]). - - RETURNS: - Full path to saved file. - - NOTE: At least, cellData or pointData must be present to infer the dimensions of the image. +def imageToVTK( + path, + origin=(0.0, 0.0, 0.0), + spacing=(1.0, 1.0, 1.0), + cellData=None, + pointData=None, + fieldData=None, +): + """ + Export data values as a rectangular image. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + origin : tuple, optional + grid origin. + The default is (0.0, 0.0, 0.0). + spacing : tuple, optional + grid spacing. + The default is (1.0, 1.0, 1.0). + cellData : dict, optional + dictionary containing arrays with cell centered data. + Keys should be the names of the data arrays. + Arrays must have the same dimensions in all directions and can contain + scalar data ([n,n,n]) or vector data ([n,n,n],[n,n,n],[n,n,n]). + The default is None. + pointData : dict, optional + dictionary containing arrays with node centered data. + Keys should be the names of the data arrays. + Arrays must have same dimension in each direction and + they should be equal to the dimensions of the cell data plus one and + can contain scalar data ([n+1,n+1,n+1]) or + +1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]). + The default is None. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + At least, cellData or pointData must be present + to infer the dimensions of the image. """ - assert (cellData != None or pointData != None) - + assert cellData is not None or pointData is not None + # Extract dimensions - start = (0,0,0) + start = (0, 0, 0) end = None - if cellData != None: + if cellData is not None: keys = list(cellData.keys()) data = cellData[keys[0]] - if hasattr(data,'shape'): + if hasattr(data, "shape"): end = data.shape - elif(data[0].ndim==3 and data[1].ndim==3 and data[2].ndim==3): + elif data[0].ndim == 3 and data[1].ndim == 3 and data[2].ndim == 3: end = data[0].shape - elif pointData != None: + elif pointData is not None: keys = list(pointData.keys()) data = pointData[keys[0]] - if hasattr(data,'shape'): + if hasattr(data, "shape"): end = data.shape - elif(data[0].ndim==3 and data[1].ndim==3 and data[2].ndim==3): + elif data[0].ndim == 3 and data[1].ndim == 3 and data[2].ndim == 3: end = data[0].shape end = (end[0] - 1, end[1] - 1, end[2] - 1) # Write data to file w = VtkFile(path, VtkImageData) - w.openGrid(start = start, end = end, origin = origin, spacing = spacing) - w.openPiece(start = start, end = end) - _addDataToFile(w, cellData, pointData) + w.openGrid(start=start, end=end, origin=origin, spacing=spacing) + w.openPiece(start=start, end=end) + _addDataToFile(w, cellData, pointData, fieldData) w.closePiece() w.closeGrid() - _appendDataToFile(w, cellData, pointData) + _appendDataToFile(w, cellData, pointData, fieldData) w.save() return w.getFileName() + # ============================================================================== -def gridToVTK(path, x, y, z, cellData = None, pointData = None): +def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): """ - Writes data values as a rectilinear or rectangular grid. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: coordinates of the nodes of the grid. They can be 1D or 3D depending if - the grid should be saved as a rectilinear or logically structured grid, respectively. - Arrays should contain coordinates of the nodes of the grid. - If arrays are 1D, then the grid should be Cartesian, i.e. faces in all cells are orthogonal. - If arrays are 3D, then the grid should be logically structured with hexahedral cells. - In both cases the arrays dimensions should be equal to the number of nodes of the grid. - cellData: dictionary containing arrays with cell centered data. - Keys should be the names of the data arrays. - Arrays must have the same dimensions in all directions and must contain - only scalar data. - pointData: dictionary containing arrays with node centered data. - Keys should be the names of the data arrays. - Arrays must have same dimension in each direction and - they should be equal to the dimensions of the cell data plus one and - must contain only scalar data. - - RETURNS: - Full path to saved file. - + Write data values as a rectilinear or rectangular grid. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinate axis. + y : array-like + y coordinate axis. + z : array-like + z coordinate axis. + cellData : dict, optional + dictionary containing arrays with cell centered data. + Keys should be the names of the data arrays. + Arrays must have the same dimensions in all directions and must contain + only scalar data. + pointData : dict, optional + dictionary containing arrays with node centered data. + Keys should be the names of the data arrays. + Arrays must have same dimension in each direction and + they should be equal to the dimensions of the cell data plus one and + must contain only scalar data. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + coordinates of the nodes of the grid. They can be 1D or 3D depending if + the grid should be saved as a rectilinear or logically structured grid, + respectively. + Arrays should contain coordinates of the nodes of the grid. + If arrays are 1D, then the grid should be Cartesian, + i.e. faces in all cells are orthogonal. + If arrays are 3D, then the grid should be logically structured + with hexahedral cells. + In both cases the arrays dimensions should be + equal to the number of nodes of the grid. """ # Extract dimensions - start = (0,0,0) + start = (0, 0, 0) nx = ny = nz = 0 - if (x.ndim == 1 and y.ndim == 1 and z.ndim == 1): + if x.ndim == 1 and y.ndim == 1 and z.ndim == 1: nx, ny, nz = x.size - 1, y.size - 1, z.size - 1 isRect = True ftype = VtkRectilinearGrid - elif (x.ndim == 3 and y.ndim == 3 and z.ndim == 3): + elif x.ndim == 3 and y.ndim == 3 and z.ndim == 3: s = x.shape nx, ny, nz = s[0] - 1, s[1] - 1, s[2] - 1 isRect = False ftype = VtkStructuredGrid else: - assert(False) + assert False end = (nx, ny, nz) - - w = VtkFile(path, ftype) - w.openGrid(start = start, end = end) - w.openPiece(start = start, end = end) + w = VtkFile(path, ftype) + w.openGrid(start=start, end=end) + w.openPiece(start=start, end=end) if isRect: w.openElement("Coordinates") @@ -182,282 +254,422 @@ def gridToVTK(path, x, y, z, cellData = None, pointData = None): w.closeElement("Coordinates") else: w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") - _addDataToFile(w, cellData, pointData) + _addDataToFile(w, cellData, pointData, fieldData) w.closePiece() w.closeGrid() # Write coordinates if isRect: w.appendData(x).appendData(y).appendData(z) else: - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) # Write data - _appendDataToFile(w, cellData, pointData) + _appendDataToFile(w, cellData, pointData, fieldData) w.save() return w.getFileName() # ============================================================================== -def pointsToVTK(path, x, y, z, data = None): +def pointsToVTK(path, x, y, z, data=None, fieldData=None): """ - Export points and associated data as an unstructured grid. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the points. - data: dictionary with variables associated to each point. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export points and associated data as an unstructured grid. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the points. + y : array-like + y coordinates of the points. + z : array-like + z coordinates of the points. + data : dict, optional + dictionary with variables associated to each point. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. """ - assert (x.size == y.size == z.size) + assert x.size == y.size == z.size npoints = x.size - + # create some temporary arrays to write grid topology - offsets = np.arange(start = 1, stop = npoints + 1, dtype = 'int32') # index of last node in each cell - connectivity = np.arange(npoints, dtype = 'int32') # each point is only connected to itself - cell_types = np.empty(npoints, dtype = 'uint8') - + offsets = np.arange( + start=1, stop=npoints + 1, dtype="int32" + ) # index of last node in each cell + connectivity = np.arange( + npoints, dtype="int32" + ) # each point is only connected to itself + cell_types = np.empty(npoints, dtype="uint8") + cell_types[:] = VtkVertex.tid w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = npoints, npoints = npoints) - + w.openPiece(ncells=npoints, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = None, pointData = data) + + _addDataToFile(w, cellData=None, pointData=data, fieldData=fieldData) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = None, pointData = data) + _appendDataToFile(w, cellData=None, pointData=data, fieldData=fieldData) w.save() return w.getFileName() + # ============================================================================== -def linesToVTK(path, x, y, z, cellData = None, pointData = None): +def linesToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): """ - Export line segments that joint 2 points and associated data. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the vertex of the lines. It is assumed that each line. - is defined by two points, then the lenght of the arrays should be equal to 2 * number of lines. - cellData: dictionary with variables associated to each line. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - pointData: dictionary with variables associated to each vertex. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export line segments that joint 2 points and associated data. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the points in lines. + y : array-like + y coordinates of the points in lines. + z : array-like + z coordinates of the points in lines. + cellData : dict, optional + dictionary with variables associated to each line. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + x, y, z are 1D arrays with coordinates of the vertex of the lines. + It is assumed that each line. + is defined by two points, + then the lenght of the arrays should be equal to 2 * number of lines. """ - assert (x.size == y.size == z.size) - assert (x.size % 2 == 0) + assert x.size == y.size == z.size + assert x.size % 2 == 0 npoints = x.size ncells = x.size / 2 - + # Check cellData has the same size that the number of cells - + # create some temporary arrays to write grid topology - offsets = np.arange(start = 2, step = 2, stop = npoints + 1, dtype = 'int32') # index of last node in each cell - connectivity = np.arange(npoints, dtype = 'int32') # each point is only connected to itself - cell_types = np.empty(npoints, dtype = 'uint8') - + offsets = np.arange( + start=2, step=2, stop=npoints + 1, dtype="int32" + ) # index of last node in each cell + connectivity = np.arange( + npoints, dtype="int32" + ) # each point is only connected to itself + cell_types = np.empty(npoints, dtype="uint8") + cell_types[:] = VtkLine.tid w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = ncells, npoints = npoints) - + w.openPiece(ncells=ncells, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = cellData, pointData = pointData) + + _addDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = cellData, pointData = pointData) + _appendDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.save() return w.getFileName() + # ============================================================================== -def polyLinesToVTK(path, x, y, z, pointsPerLine, cellData = None, pointData = None): +def polyLinesToVTK( + path, x, y, z, pointsPerLine, cellData=None, pointData=None, fieldData=None +): """ - Export line segments that joint 2 points and associated data. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the vertices of the lines. It is assumed that each line. - has diffent number of points. - pointsPerLine: 1D array that defines the number of points associated to each line. Thus, - the length of this array define the number of lines. It also implicitly - defines the connectivity or topology of the set of lines. It is assumed - that points that define a line are consecutive in the x, y and z arrays. - cellData: Dictionary with variables associated to each line. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - pointData: Dictionary with variables associated to each vertex. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export line segments that joint n points and associated data. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the points in lines. + y : array-like + y coordinates of the points in lines. + z : array-like + z coordinates of the points in lines. + pointsPerLine : array-like + Points in each poly-line. + cellData : dict, optional + dictionary with variables associated to each line. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. """ - assert (x.size == y.size == z.size) + assert x.size == y.size == z.size npoints = x.size ncells = pointsPerLine.size - + # create some temporary arrays to write grid topology - offsets = np.zeros(ncells, dtype = 'int32') # index of last node in each cell + offsets = np.zeros( + ncells, dtype="int32" + ) # index of last node in each cell ii = 0 for i in range(ncells): ii += pointsPerLine[i] offsets[i] = ii - - connectivity = np.arange(npoints, dtype = 'int32') # each line connects points that are consecutive - - cell_types = np.empty(npoints, dtype = 'uint8') + + connectivity = np.arange( + npoints, dtype="int32" + ) # each line connects points that are consecutive + + cell_types = np.empty(npoints, dtype="uint8") cell_types[:] = VtkPolyLine.tid w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = ncells, npoints = npoints) - + w.openPiece(ncells=ncells, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = cellData, pointData = pointData) + + _addDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = cellData, pointData = pointData) + _appendDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.save() return w.getFileName() + # ============================================================================== -def unstructuredGridToVTK(path, x, y, z, connectivity, offsets, cell_types, cellData = None, pointData = None): +def unstructuredGridToVTK( + path, + x, + y, + z, + connectivity, + offsets, + cell_types, + cellData=None, + pointData=None, + fieldData=None, +): """ - Export unstructured grid and associated data. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the vertices of cells. It is assumed that each element - has diffent number of vertices. - connectivity: 1D array that defines the vertices associated to each element. - Together with offset define the connectivity or topology of the grid. - It is assumed that vertices in an element are listed consecutively. - offsets: 1D array with the index of the last vertex of each element in the connectivity array. - It should have length nelem, where nelem is the number of cells or elements in the grid. - cell_types: 1D array with an integer that defines the cell type of each element in the grid. - It should have size nelem. This should be assigned from evtk.vtk.VtkXXXX.tid, where XXXX represent - the type of cell. Please check the VTK file format specification for allowed cell types. - cellData: Dictionary with variables associated to each line. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - pointData: Dictionary with variables associated to each vertex. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export unstructured grid and associated data. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the vertices. + y : array-like + y coordinates of the vertices. + z : array-like + z coordinates of the vertices. + connectivity : array-like + 1D array that defines the vertices associated to each element. + Together with offset define the connectivity or topology of the grid. + It is assumed that vertices in an element are listed consecutively. + offsets : array-like + 1D array with the index of the last vertex of each element + in the connectivity array. + It should have length nelem, + where nelem is the number of cells or elements in the grid.. + cell_types : TYPE + 1D array with an integer that defines the cell type of + each element in the grid. + It should have size nelem. + This should be assigned from evtk.vtk.VtkXXXX.tid, where XXXX represent + the type of cell. + Please check the VTK file format specification for allowed cell types. + cellData : dict, optional + dictionary with variables associated to each cell. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. """ - assert (x.size == y.size == z.size) + assert x.size == y.size == z.size npoints = x.size ncells = cell_types.size - assert (offsets.size == ncells) - + assert offsets.size == ncells + w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = ncells, npoints = npoints) - + w.openPiece(ncells=ncells, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = cellData, pointData = pointData) + + _addDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = cellData, pointData = pointData) + _appendDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.save() return w.getFileName() - + + # ============================================================================== -def cylinderToVTK(path, x0, y0, z0, z1, radius, nlayers, npilars = 16, cellData=None, pointData=None): +def cylinderToVTK( + path, + x0, + y0, + z0, + z1, + radius, + nlayers, + npilars=16, + cellData=None, + pointData=None, + fieldData=None, +): """ - Export cylinder as VTK unstructured grid. - - PARAMETERS: - path: path to file without extension. - x0, yo: center of cylinder. - z0, z1: lower and top elevation of the cylinder. - radius: radius of cylinder. - nlayers: Number of layers in z direction to divide the cylinder. - npilars: Number of points around the diameter of the cylinder. - Higher value gives higher resolution to represent the curved shape. - cellData: dictionary with 1D arrays that store cell data. - Arrays should have number of elements equal to ncells = npilars * nlayers. - pointData: dictionary with 1D arrays that store point data. - Arrays should have number of elements equal to npoints = npilars * (nlayers + 1). - - RETURNS: - Full path to saved file. - - NOTE: This function only export vertical shapes for now. However, it should be easy to - rotate the cylinder to represent other orientations. + Export cylinder as VTK unstructured grid. + + Parameters + ---------- + path : TYPE + DESCRIPTION. + x0 : float + x-center of the cylinder. + y0 : float + y-center of the cylinder. + z0 : float + lower end of the cylinder. + z1 : float + upper end of the cylinder. + radius : float + radius of the cylinder. + nlayers : int + Number of layers in z direction to divide the cylinder.. + npilars : int, optional + Number of points around the diameter of the cylinder. + Higher value gives higher resolution to represent the curved shape. + The default is 16. + cellData : dict, optional + dictionary with variables associated to each cell. + Keys should be the names of the variable stored in each array. + Arrays should have number of elements equal to + ncells = npilars * nlayers. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + Arrays should have number of elements equal to + npoints = npilars * (nlayers + 1). + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + This function only export vertical shapes for now. + However, it should be easy to + rotate the cylinder to represent other orientations. """ import math as m - + # Define x, y coordinates from polar coordinates. dpi = 2.0 * m.pi / npilars angles = np.arange(0.0, 2.0 * m.pi, dpi) @@ -466,10 +678,10 @@ def cylinderToVTK(path, x0, y0, z0, z1, radius, nlayers, npilars = 16, cellData= y = radius * np.sin(angles) + y0 dz = (z1 - z0) / nlayers - z = np.arange(z0, z1+dz, step = dz) + z = np.arange(z0, z1 + dz, step=dz) npoints = npilars * (nlayers + 1) - ncells = npilars * nlayers + ncells = npilars * nlayers xx = np.zeros(npoints) yy = np.zeros(npoints) @@ -484,33 +696,44 @@ def cylinderToVTK(path, x0, y0, z0, z1, radius, nlayers, npilars = 16, cellData= ii = ii + 1 # Define connectivity - conn = np.zeros(4 * ncells, dtype = np.int64) + conn = np.zeros(4 * ncells, dtype=np.int64) ii = 0 for l in range(nlayers): for p in range(npilars): p0 = p - if(p + 1 == npilars): - p1 = 0 - else: - p1 = p + 1 # circular loop - + if p + 1 == npilars: + p1 = 0 + else: + p1 = p + 1 # circular loop + n0 = p0 + l * npilars - n1 = p1 + l * npilars + n1 = p1 + l * npilars n2 = n0 + npilars n3 = n1 + npilars - + conn[ii + 0] = n0 conn[ii + 1] = n1 conn[ii + 2] = n3 - conn[ii + 3] = n2 + conn[ii + 3] = n2 ii = ii + 4 - - # Define offsets - offsets = np.zeros(ncells, dtype = np.int64) + + # Define offsets + offsets = np.zeros(ncells, dtype=np.int64) for i in range(ncells): offsets[i] = (i + 1) * 4 # Define cell types ctype = np.ones(ncells) + VtkPixel.tid - - return unstructuredGridToVTK(path, xx, yy, zz, connectivity = conn, offsets = offsets, cell_types = ctype, cellData = cellData, pointData = pointData) + + return unstructuredGridToVTK( + path, + xx, + yy, + zz, + connectivity=conn, + offsets=offsets, + cell_types=ctype, + cellData=cellData, + pointData=pointData, + fieldData=fieldData, + ) From e10458ba92f3998454dbf8ab686c810ba6b3d8f4 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:41:17 +0200 Subject: [PATCH 06/52] examples: reformat wit black; add field data example --- examples/group.py | 10 +++++----- examples/image.py | 26 +++++++++++++------------ examples/lines.py | 11 ++++++++--- examples/lowlevel.py | 41 ++++++++++++++++++++-------------------- examples/points.py | 13 ++++++------- examples/poly_lines.py | 12 +++++++++--- examples/rectilinear.py | 25 +++++++++++++++--------- examples/structured.py | 29 +++++++++++++++++----------- examples/unstructured.py | 20 +++++++++++++++++--- 9 files changed, 113 insertions(+), 74 deletions(-) diff --git a/examples/group.py b/examples/group.py index 070e9db..ba619f5 100644 --- a/examples/group.py +++ b/examples/group.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -33,8 +33,8 @@ from pyevtk.vtk import VtkGroup g = VtkGroup("./group") -g.addFile(filepath = "sim0000.vtu", sim_time = 0.0) -g.addFile(filepath = "sim0001.vtu", sim_time = 1.0) -g.addFile(filepath = "sim0002.vtu", sim_time = 2.0) -g.addFile(filepath = "sim0003.vtu", sim_time = 3.0) +g.addFile(filepath="sim0000.vtu", sim_time=0.0) +g.addFile(filepath="sim0001.vtu", sim_time=1.0) +g.addFile(filepath="sim0002.vtu", sim_time=2.0) +g.addFile(filepath="sim0003.vtu", sim_time=3.0) g.save() diff --git a/examples/image.py b/examples/image.py index 82425c3..41f15c0 100644 --- a/examples/image.py +++ b/examples/image.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -38,19 +38,21 @@ npoints = (nx + 1) * (ny + 1) * (nz + 1) # Variables -pressure = np.random.rand(ncells).reshape( (nx, ny, nz), order = 'C') -temp = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1)) +pressure = np.random.rand(ncells).reshape((nx, ny, nz), order="C") +temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) -imageToVTK("./image", cellData = {"pressure" : pressure}, pointData = {"temp" : temp} ) +imageToVTK( + "./image", cellData={"pressure": pressure}, pointData={"temp": temp} +) -fluxx = np.random.rand(ncells).reshape( (nx, ny, nz), order='F') -fluxy = np.random.rand(ncells).reshape( (nx, ny, nz), order='F') -fluxz = np.random.rand(ncells).reshape( (nx, ny, nz), order='F') +fluxx = np.random.rand(ncells).reshape((nx, ny, nz), order="F") +fluxy = np.random.rand(ncells).reshape((nx, ny, nz), order="F") +fluxz = np.random.rand(ncells).reshape((nx, ny, nz), order="F") flux = (fluxx, fluxy, fluxz) -Efieldx = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1), order='F') -Efieldy = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1), order='F') -Efieldz = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1), order='F') -Efield = (Efieldx,Efieldy,Efieldz) +Efieldx = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1), order="F") +Efieldy = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1), order="F") +Efieldz = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1), order="F") +Efield = (Efieldx, Efieldy, Efieldz) -imageToVTK("./image", cellData={"flux" : flux}, pointData = {"Efield" : Efieldx} ) \ No newline at end of file +imageToVTK("./image", cellData={"flux": flux}, pointData={"Efield": Efieldx}) diff --git a/examples/lines.py b/examples/lines.py index 6e5ff61..d83f174 100644 --- a/examples/lines.py +++ b/examples/lines.py @@ -48,6 +48,11 @@ x[2], y[2], z[2] = 0.0, 0.0, 0.0 x[3], y[3], z[3] = -1.0, 1.0, 1.0 -linesToVTK("./lines", x, y, z, cellData = {"vel" : vel}, pointData = {"temp" : temp, "pressure" : pressure}) - - +linesToVTK( + "./lines", + x, + y, + z, + cellData={"vel": vel}, + pointData={"temp": temp, "pressure": pressure}, +) diff --git a/examples/lowlevel.py b/examples/lowlevel.py index 13a5661..d727b52 100644 --- a/examples/lowlevel.py +++ b/examples/lowlevel.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -34,45 +34,44 @@ nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 -dx, dy, dz = lx/nx, ly/ny, lz/nz +dx, dy, dz = lx / nx, ly / ny, lz / nz ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) -x = np.arange(0, lx + 0.1*dx, dx, dtype='float64') -y = np.arange(0, ly + 0.1*dy, dy, dtype='float64') -z = np.arange(0, lz + 0.1*dz, dz, dtype='float64') -start, end = (0,0,0), (nx, ny, nz) +x = np.arange(0, lx + 0.1 * dx, dx, dtype="float64") +y = np.arange(0, ly + 0.1 * dy, dy, dtype="float64") +z = np.arange(0, lz + 0.1 * dz, dz, dtype="float64") +start, end = (0, 0, 0), (nx, ny, nz) w = VtkFile("./evtk_test", VtkRectilinearGrid) -w.openGrid(start = start, end = end) -w.openPiece( start = start, end = end) +w.openGrid(start=start, end=end) +w.openPiece(start=start, end=end) # Point data temp = np.random.rand(npoints) -vx = vy = vz = np.zeros([nx + 1, ny + 1, nz + 1], dtype="float64", order = 'F') -w.openData("Point", scalars = "Temperature", vectors = "Velocity") +vx = vy = vz = np.zeros([nx + 1, ny + 1, nz + 1], dtype="float64", order="F") +w.openData("Point", scalars="Temperature", vectors="Velocity") w.addData("Temperature", temp) -w.addData("Velocity", (vx,vy,vz)) +w.addData("Velocity", (vx, vy, vz)) w.closeData("Point") # Cell data -pressure = np.zeros([nx, ny, nz], dtype="float64", order='F') -w.openData("Cell", scalars = "Pressure") +pressure = np.zeros([nx, ny, nz], dtype="float64", order="F") +w.openData("Cell", scalars="Pressure") w.addData("Pressure", pressure) w.closeData("Cell") # Coordinates of cell vertices w.openElement("Coordinates") -w.addData("x_coordinates", x); -w.addData("y_coordinates", y); -w.addData("z_coordinates", z); -w.closeElement("Coordinates"); +w.addData("x_coordinates", x) +w.addData("y_coordinates", y) +w.addData("z_coordinates", z) +w.closeElement("Coordinates") w.closePiece() w.closeGrid() -w.appendData(data = temp) -w.appendData(data = (vx,vy,vz)) -w.appendData(data = pressure) +w.appendData(data=temp) +w.appendData(data=(vx, vy, vz)) +w.appendData(data=pressure) w.appendData(x).appendData(y).appendData(z) w.save() - diff --git a/examples/points.py b/examples/points.py index 8b6f0d1..30ad3ab 100644 --- a/examples/points.py +++ b/examples/points.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -39,11 +39,10 @@ z = np.random.rand(npoints) pressure = np.random.rand(npoints) temp = np.random.rand(npoints) -pointsToVTK("./rnd_points", x, y, z, data = {"temp" : temp, "pressure" : pressure}) +pointsToVTK("./rnd_points", x, y, z, data={"temp": temp, "pressure": pressure}) # Example 2 -x = np.arange(1.0,10.0,0.1) -y = np.arange(1.0,10.0,0.1) -z = np.arange(1.0,10.0,0.1) -pointsToVTK("./line_points", x, y, z, data = {"elev" : z}) - +x = np.arange(1.0, 10.0, 0.1) +y = np.arange(1.0, 10.0, 0.1) +z = np.arange(1.0, 10.0, 0.1) +pointsToVTK("./line_points", x, y, z, data={"elev": z}) diff --git a/examples/poly_lines.py b/examples/poly_lines.py index 05e66ff..22962a1 100644 --- a/examples/poly_lines.py +++ b/examples/poly_lines.py @@ -61,6 +61,12 @@ vel[0:3] = 1.0 vel[4:6] = 5.0 -polyLinesToVTK("./poly_lines", x, y, z, pointsPerLine = pointsPerLine, cellData = {"vel" : vel}, pointData = {"temp" : temp, "pressure" : pressure}) - - +polyLinesToVTK( + "./poly_lines", + x, + y, + z, + pointsPerLine=pointsPerLine, + cellData={"vel": vel}, + pointData={"temp": temp, "pressure": pressure}, +) diff --git a/examples/rectilinear.py b/examples/rectilinear.py index 14f9ae5..4f83660 100644 --- a/examples/rectilinear.py +++ b/examples/rectilinear.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -36,18 +36,25 @@ # Dimensions nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 -dx, dy, dz = lx/nx, ly/ny, lz/nz +dx, dy, dz = lx / nx, ly / ny, lz / nz ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) # Coordinates -x = np.arange(0, lx + 0.1*dx, dx, dtype='float64') -y = np.arange(0, ly + 0.1*dy, dy, dtype='float64') -z = np.arange(0, lz + 0.1*dz, dz, dtype='float64') +x = np.arange(0, lx + 0.1 * dx, dx, dtype="float64") +y = np.arange(0, ly + 0.1 * dy, dy, dtype="float64") +z = np.arange(0, lz + 0.1 * dz, dz, dtype="float64") # Variables -pressure = np.random.rand(ncells).reshape( (nx, ny, nz)) -temp = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1)) - -gridToVTK("./rectilinear", x, y, z, cellData = {"pressure" : pressure}, pointData = {"temp" : temp}) +pressure = np.random.rand(ncells).reshape((nx, ny, nz)) +temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) + +gridToVTK( + "./rectilinear", + x, + y, + z, + cellData={"pressure": pressure}, + pointData={"temp": temp}, +) diff --git a/examples/structured.py b/examples/structured.py index ec65376..40de3bc 100644 --- a/examples/structured.py +++ b/examples/structured.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -37,15 +37,15 @@ # Dimensions nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 -dx, dy, dz = lx/nx, ly/ny, lz/nz +dx, dy, dz = lx / nx, ly / ny, lz / nz ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) # Coordinates -X = np.arange(0, lx + 0.1*dx, dx, dtype='float64') -Y = np.arange(0, ly + 0.1*dy, dy, dtype='float64') -Z = np.arange(0, lz + 0.1*dz, dz, dtype='float64') +X = np.arange(0, lx + 0.1 * dx, dx, dtype="float64") +Y = np.arange(0, ly + 0.1 * dy, dy, dtype="float64") +Z = np.arange(0, lz + 0.1 * dz, dz, dtype="float64") x = np.zeros((nx + 1, ny + 1, nz + 1)) y = np.zeros((nx + 1, ny + 1, nz + 1)) @@ -56,12 +56,19 @@ for k in range(nz + 1): for j in range(ny + 1): for i in range(nx + 1): - x[i,j,k] = X[i] + (0.5 - rnd.random()) * 0.1 * dx - y[i,j,k] = Y[j] + (0.5 - rnd.random()) * 0.1 * dy - z[i,j,k] = Z[k] + (0.5 - rnd.random()) * 0.1 * dz + x[i, j, k] = X[i] + (0.5 - rnd.random()) * 0.1 * dx + y[i, j, k] = Y[j] + (0.5 - rnd.random()) * 0.1 * dy + z[i, j, k] = Z[k] + (0.5 - rnd.random()) * 0.1 * dz # Variables -pressure = np.random.rand(ncells).reshape( (nx, ny, nz)) -temp = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1)) +pressure = np.random.rand(ncells).reshape((nx, ny, nz)) +temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) -gridToVTK("./structured", x, y, z, cellData = {"pressure" : pressure}, pointData = {"temp" : temp}) +gridToVTK( + "./structured", + x, + y, + z, + cellData={"pressure": pressure}, + pointData={"temp": temp}, +) diff --git a/examples/unstructured.py b/examples/unstructured.py index d28f536..d9ff10a 100644 --- a/examples/unstructured.py +++ b/examples/unstructured.py @@ -48,11 +48,14 @@ x[4], y[4], z[4] = 1.0, 1.0, 0.0 x[5], y[5], z[5] = 2.0, 1.0, 0.0 +point_data = {"test_pd": np.array([1, 2, 3, 4, 5, 6])} +cell_data = {"test_cd": np.array([1, 2, 3])} +field_data = {"test_fd": np.array([1.0, 2.0])} # Define connectivity or vertices that belongs to each element conn = np.zeros(10) -conn[0], conn[1], conn[2] = 0, 1, 3 # first triangle -conn[3], conn[4], conn[5] = 1, 4, 3 # second triangle +conn[0], conn[1], conn[2] = 0, 1, 3 # first triangle +conn[3], conn[4], conn[5] = 1, 4, 3 # second triangle conn[6], conn[7], conn[8], conn[9] = 1, 2, 5, 4 # rectangle # Define offset of last vertex of each element @@ -67,4 +70,15 @@ ctype[0], ctype[1] = VtkTriangle.tid, VtkTriangle.tid ctype[2] = VtkQuad.tid -unstructuredGridToVTK("unstructured", x, y, z, connectivity = conn, offsets = offset, cell_types = ctype, cellData = None, pointData = None) +unstructuredGridToVTK( + "unstructured", + x, + y, + z, + connectivity=conn, + offsets=offset, + cell_types=ctype, + cellData=cell_data, + pointData=point_data, + fieldData=field_data, +) From dcd0fc1b7185b767ef6126f697f4dcc580789684 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:43:00 +0200 Subject: [PATCH 07/52] vtk: reset appended encoding to raw --- pyevtk/vtk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 3015b85..98e0b4c 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -633,7 +633,7 @@ def openAppendedData(self): """ if not self.appendedDataIsOpen: self.xml.openElement("AppendedData").addAttributes( - encoding="base64" + encoding="raw" ).addText("_") self.appendedDataIsOpen = True From 71a9fabdf3350ec2988f52fc36b6f937091dd2da Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:57:37 +0200 Subject: [PATCH 08/52] minor doc corrections --- pyevtk/vtk.py | 2 +- pyevtk/xml.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 98e0b4c..39ae00e 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -119,7 +119,7 @@ class VtkCellType: ---------- tid : int Type ID. - name : TYPE + name : str Cell type name. """ diff --git a/pyevtk/xml.py b/pyevtk/xml.py index 1c76832..b4c1e37 100644 --- a/pyevtk/xml.py +++ b/pyevtk/xml.py @@ -31,10 +31,11 @@ class XmlWriter: Parameters ---------- - filepath : TYPE - DESCRIPTION. + filepath : str + Path to the xml file. addDeclaration : bool, optional - DESCRIPTION. The default is True. + Whether to add the declaration. + The default is True. """ def __init__(self, filepath, addDeclaration=True): From e4d7287a339841cd93e1c8ec70dabc622774778c Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:59:11 +0200 Subject: [PATCH 09/52] setup.py: reformat with black --- setup.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index 4257e0f..71524f6 100644 --- a/setup.py +++ b/setup.py @@ -30,28 +30,27 @@ def readme(fname): - with open(fname, 'r') as f: + """Open the readme file.""" + with open(fname, "r") as f: return f.read() setup( - name='pyevtk', - version='1.1.2', - description='Export data as binary VTK files', - long_description=readme('README.md'), - long_description_content_type='text/markdown', - author='Paulo Herrera', - author_email='pauloa.herrera@gmail.com', - maintainer='Adamos Kyriakou', - maintainer_email='somada141@gmail.com', - url = 'https://github.com/paulo-herrera/PyEVTK.git', - packages=['pyevtk', 'evtk'], - package_dir={'pyevtk': 'pyevtk'}, - package_data={'pyevtk': ['LICENSE.txt', 'examples/*.py']}, - install_requires=[ - "numpy >= 1.8.0", - ], + name="pyevtk", + version="1.1.2", + description="Export data as binary VTK files", + long_description=readme("README.md"), + long_description_content_type="text/markdown", + author="Paulo Herrera", + author_email="pauloa.herrera@gmail.com", + maintainer="Adamos Kyriakou", + maintainer_email="somada141@gmail.com", + url="https://github.com/paulo-herrera/PyEVTK.git", + packages=["pyevtk", "evtk"], + package_dir={"pyevtk": "pyevtk"}, + package_data={"pyevtk": ["LICENSE.txt", "examples/*.py"]}, + install_requires=["numpy >= 1.8.0"], # necessary for 'python setup.py test' - setup_requires=['pytest-runner'], - tests_require=['pytest>=3.1', 'pytest-cov', 'twine', 'check-manifest'], + setup_requires=["pytest-runner"], + tests_require=["pytest>=3.1", "pytest-cov", "twine", "check-manifest"], ) From fcfc3460e4f694271b76480e117c139c70332a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 23 Apr 2020 08:52:23 +0200 Subject: [PATCH 10/52] [ci] update travis setup - removes EOL'ed python versions - switches Travis's base image to the latest (ubuntu bionic) --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6b6ff70..003d14c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,15 @@ language: python -dist: xenial +dist: bionic python: - - "2.7" - - "3.4" + # EOL 2020-09-13 - "3.5" + # EOL 2021-12-23 - "3.6" + # EOL 2023-06-27 - "3.7" + # EOL 2024-10 + - "3.8" install: - pip install -r requirements.txt From 5150345f883dff33721e6a89daf07b2ec3821f0f Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:34:18 +0200 Subject: [PATCH 11/52] evtk: reformat with black --- evtk/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/evtk/__init__.py b/evtk/__init__.py index 7816d09..3086cf2 100644 --- a/evtk/__init__.py +++ b/evtk/__init__.py @@ -1,4 +1,8 @@ from pyevtk import * import warnings -warnings.warn('the "evtk" package is deprecated, use "pyevtk" instead', DeprecationWarning) \ No newline at end of file + +warnings.warn( + 'the "evtk" package is deprecated, use "pyevtk" instead', + DeprecationWarning, +) From 666c780f83070c6abe6ae08cde52b734b95f3568 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:35:23 +0200 Subject: [PATCH 12/52] evtk-subpackage: reformat with black --- pyevtk/evtk.py | 136 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 44 deletions(-) diff --git a/pyevtk/evtk.py b/pyevtk/evtk.py index 335322c..421b9dd 100644 --- a/pyevtk/evtk.py +++ b/pyevtk/evtk.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,75 +22,123 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""Export routines.""" +import sys import struct +# import base64 import numpy as np -import sys # Map numpy dtype to struct format -np_to_struct = { 'int8' : 'b', - 'uint8' : 'B', - 'int16' : 'h', - 'uint16' : 'H', - 'int32' : 'i', - 'uint32' : 'I', - 'int64' : 'q', - 'uint64' : 'Q', - 'float32' : 'f', - 'float64' : 'd' } - +np_to_struct = { + "int8": "b", + "uint8": "B", + "int16": "h", + "uint16": "H", + "int32": "i", + "uint32": "I", + "int64": "q", + "uint64": "Q", + "float32": "f", + "float64": "d", +} + + def _get_byte_order_char(): -# Check format in https://docs.python.org/3.5/library/struct.html + # Check format in https://docs.python.org/3.5/library/struct.html if sys.byteorder == "little": - return '<' - else: - return '>' - + return "<" + return ">" + + # ================================ # Python interface -# ================================ +# ================================ def writeBlockSize(stream, block_size): - fmt = _get_byte_order_char() + 'Q' # Write size as unsigned long long == 64 bits unsigned integer + """ + Write block size to a given stream. + + Parameters + ---------- + stream : stream + open stream. + block_size : int + block size. + """ + fmt = ( + _get_byte_order_char() + "Q" + ) # Write size as unsigned long long == 64 bits unsigned integer stream.write(struct.pack(fmt, block_size)) + def writeArrayToFile(stream, data): - #stream.flush() # this should not be necessary - assert (data.ndim == 1 or data.ndim == 3) - fmt = _get_byte_order_char() + str(data.size) + np_to_struct[data.dtype.name] # > for big endian + """ + Write array to a given stream. + + Parameters + ---------- + stream : stream + open stream. + data : array-like + data array to be saved. + """ + # stream.flush() # this should not be necessary + assert data.ndim == 1 or data.ndim == 3 + fmt = ( + _get_byte_order_char() + str(data.size) + np_to_struct[data.dtype.name] + ) # > for big endian # Check if array is contiguous - assert (data.flags['C_CONTIGUOUS'] or data.flags['F_CONTIGUOUS']) - + assert data.flags["C_CONTIGUOUS"] or data.flags["F_CONTIGUOUS"] + # NOTE: VTK expects data in FORTRAN order # This is only needed when a multidimensional array has C-layout - dd = np.ravel(data, order='F') + dd = np.ravel(data, order="F") + + bin_pack = struct.pack(fmt, *dd) + stream.write(bin_pack) + - bin = struct.pack(fmt, *dd) - stream.write(bin) - # ============================================================================== def writeArraysToFile(stream, x, y, z): + """ + Write multiple array to a given stream. + + Parameters + ---------- + stream : stream + open stream. + x : array-like + x array to be saved. + y : array-like + y array to be saved. + z : array-like + z array to be saved. + """ # Check if arrays have same shape and data type - assert ( x.size == y.size == z.size ), "Different array sizes." - assert ( x.dtype.itemsize == y.dtype.itemsize == z.dtype.itemsize ), "Different item sizes." - + assert x.size == y.size == z.size, "Different array sizes." + assert ( + x.dtype.itemsize == y.dtype.itemsize == z.dtype.itemsize + ), "Different item sizes." + nitems = x.size - itemsize = x.dtype.itemsize + # itemsize = x.dtype.itemsize + + fmt = ( + _get_byte_order_char() + str(1) + np_to_struct[x.dtype.name] + ) # > for big endian - fmt = _get_byte_order_char() + str(1) + np_to_struct[x.dtype.name] # > for big endian - # Check if arrays are contiguous - assert (x.flags['C_CONTIGUOUS'] or x.flags['F_CONTIGUOUS']) - assert (y.flags['C_CONTIGUOUS'] or y.flags['F_CONTIGUOUS']) - assert (z.flags['C_CONTIGUOUS'] or z.flags['F_CONTIGUOUS']) - - + assert x.flags["C_CONTIGUOUS"] or x.flags["F_CONTIGUOUS"] + assert y.flags["C_CONTIGUOUS"] or y.flags["F_CONTIGUOUS"] + assert z.flags["C_CONTIGUOUS"] or z.flags["F_CONTIGUOUS"] + # NOTE: VTK expects data in FORTRAN order # This is only needed when a multidimensional array has C-layout - xx = np.ravel(x, order='F') - yy = np.ravel(y, order='F') - zz = np.ravel(z, order='F') - + xx = np.ravel(x, order="F") + yy = np.ravel(y, order="F") + zz = np.ravel(z, order="F") + # eliminate this loop by creating a composed array. for i in range(nitems): bx = struct.pack(fmt, xx[i]) From 6da811f45c114f176b840d29afaca0cfb9eb8f48 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:36:15 +0200 Subject: [PATCH 13/52] xml: add doc; reformat with black --- pyevtk/xml.py | 86 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/pyevtk/xml.py b/pyevtk/xml.py index 6d92f32..1c76832 100644 --- a/pyevtk/xml.py +++ b/pyevtk/xml.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,38 +22,65 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""Simple class to generate a well-formed XML file.""" -# ************************************** -# * Simple class to generate a * -# * well-formed XML file. * -# ************************************** class XmlWriter: - def __init__(self, filepath, addDeclaration = True): + """ + xml writer class. + + Parameters + ---------- + filepath : TYPE + DESCRIPTION. + addDeclaration : bool, optional + DESCRIPTION. The default is True. + """ + + def __init__(self, filepath, addDeclaration=True): self.stream = open(filepath, "wb") self.openTag = False self.current = [] - if (addDeclaration): self.addDeclaration() + if addDeclaration: + self.addDeclaration() def close(self): - assert(not self.openTag) + """Close the file.""" + assert not self.openTag self.stream.close() def addDeclaration(self): + """Add xml declaration.""" self.stream.write(b'') - + def openElement(self, tag): - if self.openTag: self.stream.write(b">") + """Open a new xml element.""" + if self.openTag: + self.stream.write(b">") st = "\n<%s" % tag self.stream.write(str.encode(st)) self.openTag = True self.current.append(tag) return self - def closeElement(self, tag = None): + def closeElement(self, tag=None): + """ + Close the current element. + + Parameters + ---------- + tag : str, optional + Tag of the element. + The default is None. + + Returns + ------- + XmlWriter + The XmlWriter itself for chained calles. + """ if tag: - assert(self.current.pop() == tag) - if (self.openTag): + assert self.current.pop() == tag + if self.openTag: self.stream.write(b">") self.openTag = False st = "\n" % tag @@ -65,16 +92,41 @@ def closeElement(self, tag = None): return self def addText(self, text): - if (self.openTag): + """ + Add text. + + Parameters + ---------- + text : str + Text to add. + + Returns + ------- + XmlWriter + The XmlWriter itself for chained calles. + """ + if self.openTag: self.stream.write(b">\n") self.openTag = False self.stream.write(str.encode(text)) return self def addAttributes(self, **kwargs): - assert (self.openTag) + """ + Add attributes. + + Parameters + ---------- + **kwargs + keys as attribute names. + + Returns + ------- + XmlWriter + The XmlWriter itself for chained calles. + """ + assert self.openTag for key in kwargs: - st = ' %s="%s"'%(key, kwargs[key]) + st = ' %s="%s"' % (key, kwargs[key]) self.stream.write(str.encode(st)) return self - From ab0b3afb14fd6503a7d8f759d45be4d535bcb936 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:39:24 +0200 Subject: [PATCH 14/52] vtk: update doc; reformat with black; remove largefile attribute; remove ref to non-existing routine (64bit) --- pyevtk/vtk.py | 669 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 424 insertions(+), 245 deletions(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 2b6d9f0..3015b85 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,16 +22,14 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""Low level Python library to export data to binary VTK file.""" -# ************************************** -# * Low level Python library to * -# * export data to binary VTK file. * -# ************************************** +import sys +import os from .evtk import writeBlockSize, writeArrayToFile, writeArraysToFile from .xml import XmlWriter -import sys -import os + # ================================ # VTK Types @@ -39,22 +37,44 @@ # FILE TYPES class VtkFileType: + """ + Wrapper class for VTK file types. + + Parameters + ---------- + name : str + Data name. + ext : str + File extension. + """ def __init__(self, name, ext): self.name = name - self.ext = ext + self.ext = ext def __str__(self): return "Name: %s Ext: %s \n" % (self.name, self.ext) -VtkImageData = VtkFileType("ImageData", ".vti") -VtkPolyData = VtkFileType("PolyData", ".vtp") -VtkRectilinearGrid = VtkFileType("RectilinearGrid", ".vtr") -VtkStructuredGrid = VtkFileType("StructuredGrid", ".vts") + +VtkImageData = VtkFileType("ImageData", ".vti") +VtkPolyData = VtkFileType("PolyData", ".vtp") +VtkRectilinearGrid = VtkFileType("RectilinearGrid", ".vtr") +VtkStructuredGrid = VtkFileType("StructuredGrid", ".vts") VtkUnstructuredGrid = VtkFileType("UnstructuredGrid", ".vtu") + # DATA TYPES class VtkDataType: + """ + Wrapper class for VTK data types. + + Parameters + ---------- + size : int + Size in byte. + name : str + Type name. + """ def __init__(self, size, name): self.size = size @@ -63,38 +83,53 @@ def __init__(self, size, name): def __str__(self): return "Type: %s Size: %d \n" % (self.name, self.size) -VtkInt8 = VtkDataType(1, "Int8") -VtkUInt8 = VtkDataType(1, "UInt8") -VtkInt16 = VtkDataType(2, "Int16") -VtkUInt16 = VtkDataType(2, "UInt16") -VtkInt32 = VtkDataType(4, "Int32") -VtkUInt32 = VtkDataType(4, "UInt32") -VtkInt64 = VtkDataType(8, "Int64") -VtkUInt64 = VtkDataType(8, "UInt64") + +VtkInt8 = VtkDataType(1, "Int8") +VtkUInt8 = VtkDataType(1, "UInt8") +VtkInt16 = VtkDataType(2, "Int16") +VtkUInt16 = VtkDataType(2, "UInt16") +VtkInt32 = VtkDataType(4, "Int32") +VtkUInt32 = VtkDataType(4, "UInt32") +VtkInt64 = VtkDataType(8, "Int64") +VtkUInt64 = VtkDataType(8, "UInt64") VtkFloat32 = VtkDataType(4, "Float32") VtkFloat64 = VtkDataType(8, "Float64") # Map numpy to VTK data types -np_to_vtk = { 'int8' : VtkInt8, - 'uint8' : VtkUInt8, - 'int16' : VtkInt16, - 'uint16' : VtkUInt16, - 'int32' : VtkInt32, - 'uint32' : VtkUInt32, - 'int64' : VtkInt64, - 'uint64' : VtkUInt64, - 'float32' : VtkFloat32, - 'float64' : VtkFloat64 } +np_to_vtk = { + "int8": VtkInt8, + "uint8": VtkUInt8, + "int16": VtkInt16, + "uint16": VtkUInt16, + "int32": VtkInt32, + "uint32": VtkUInt32, + "int64": VtkInt64, + "uint64": VtkUInt64, + "float32": VtkFloat32, + "float64": VtkFloat64, +} + # CELL TYPES class VtkCellType: + """ + Wrapper class for VTK cell types. + + Parameters + ---------- + tid : int + Type ID. + name : TYPE + Cell type name. + """ def __init__(self, tid, name): self.tid = tid self.name = name def __str__(self): - return "VtkCellType( %s ) \n" % ( self.name ) + return "VtkCellType( %s ) \n" % (self.name) + VtkVertex = VtkCellType(1, "Vertex") VtkPolyVertex = VtkCellType(2, "PolyVertex") @@ -116,271 +151,387 @@ def __str__(self): VtkQuadraticTetra = VtkCellType(24, "Quadratic_Tetra") VtkQuadraticHexahedron = VtkCellType(25, "Quadratic_Hexahedron") + # ============================== # Helper functions # ============================== def _mix_extents(start, end): - assert (len(start) == len(end) == 3) - string = "%d %d %d %d %d %d" % (start[0], end[0], start[1], end[1], start[2], end[2]) + assert len(start) == len(end) == 3 + string = "%d %d %d %d %d %d" % ( + start[0], + end[0], + start[1], + end[1], + start[2], + end[2], + ) return string + def _array_to_string(a): s = "".join([repr(num) + " " for num in a]) return s + def _get_byte_order(): if sys.byteorder == "little": return "LittleEndian" - else: - return "BigEndian" + return "BigEndian" + # ================================ # VtkGroup class # ================================ class VtkGroup: - + """ + Creates a VtkGroup file that is stored in filepath. + + Parameters + ---------- + filepath : str + filename without extension. + """ + def __init__(self, filepath): - """ Creates a VtkGroup file that is stored in filepath. - - PARAMETERS: - filepath: filename without extension. - """ self.xml = XmlWriter(filepath + ".pvd") self.xml.openElement("VTKFile") - self.xml.addAttributes(type = "Collection", version = "0.1", byte_order = _get_byte_order()) + self.xml.addAttributes( + type="Collection", version="0.1", byte_order=_get_byte_order() + ) self.xml.openElement("Collection") self.root = os.path.dirname(filepath) def save(self): - """ Closes this VtkGroup. """ + """Close this VtkGroup.""" self.xml.closeElement("Collection") self.xml.closeElement("VTKFile") self.xml.close() - - def addFile(self, filepath, sim_time, group = "", part = "0"): - """ Adds file to this VTK group. - - PARAMETERS: - filepath: full path to VTK file. - sim_time: simulated time. - group: This attribute is not required; it is only for informational purposes. - part: It is an integer value greater than or equal to 0. - - See: http://www.paraview.org/Wiki/ParaView/Data_formats#PVD_File_Format for details. + + def addFile(self, filepath, sim_time, group="", part="0"): + """ + Add a file to this VTK group. + + Parameters + ---------- + filepath : str + full path to VTK file. + sim_time : float + simulated time. + group : str, optional + This attribute is not required; + it is only for informational purposes. + The default is "". + part : int, optional + It is an integer value greater than or equal to 0. + The default is "0". + + Notes + ----- + See: http://www.paraview.org/Wiki/ParaView/Data_formats#PVD_File_Format for details. """ # TODO: Check what the other attributes are for. - filename = os.path.relpath(filepath, start = self.root) + filename = os.path.relpath(filepath, start=self.root) self.xml.openElement("DataSet") - self.xml.addAttributes(timestep = sim_time, group = group, part = part, file = filename) + self.xml.addAttributes( + timestep=sim_time, group=group, part=part, file=filename + ) self.xml.closeElement() - # ================================ -# VtkFile class +# VtkFile class # ================================ class VtkFile: - - def __init__(self, filepath, ftype, largeFile = False): - """ - PARAMETERS: - filepath: filename without extension. - ftype: file type, e.g. VtkImageData, etc. - largeFile: If size of the stored data cannot be represented by a UInt32. - """ + """ + Class for a VTK file. + + Parameters + ---------- + filepath : str + filename without extension. + ftype : str + file type, e.g. VtkImageData, etc. + largeFile : bool, optional + If size of the stored data cannot be represented by a UInt32. + The default is False. + """ + + def __init__(self, filepath, ftype): self.ftype = ftype self.filename = filepath + ftype.ext self.xml = XmlWriter(self.filename) self.offset = 0 # offset in bytes after beginning of binary section self.appendedDataIsOpen = False -# self.largeFile = largeFile - -# if largeFile == False: -# self.xml.openElement("VTKFile").addAttributes(type = ftype.name, -# version = "0.1", -# byte_order = _get_byte_order()) -# else: -# print "WARNING: output file only compatible with VTK 6.0 and later." - self.xml.openElement("VTKFile").addAttributes(type = ftype.name, - version = "1.0", - byte_order = _get_byte_order(), - header_type = "UInt64") + self.xml.openElement("VTKFile").addAttributes( + type=ftype.name, + version="1.0", + byte_order=_get_byte_order(), + header_type="UInt64", + ) def getFileName(self): - """ Returns absolute path to this file. """ - return os.path.abspath(self.filename) - - def openPiece(self, start = None, end = None, - npoints = None, ncells = None, - nverts = None, nlines = None, nstrips = None, npolys = None): - """ Open piece section. - - PARAMETERS: - Next two parameters must be given together. - start: array or list with start indexes in each direction. - end: array or list with end indexes in each direction. - - npoints: number of points in piece (int). - ncells: number of cells in piece (int). If present, - npoints must also be given. - - All the following parameters must be given together with npoints. - They should all be integer values. - nverts: number of vertices. - nlines: number of lines. - nstrips: number of strips. - npolys: number of . - - RETURNS: - this VtkFile to allow chained calls. + """Return absolute path to this file.""" + return os.path.abspath(self.filename) + + def openPiece( + self, + start=None, + end=None, + npoints=None, + ncells=None, + nverts=None, + nlines=None, + nstrips=None, + npolys=None, + ): + """ + Open piece section. + + Parameters + ---------- + start : array-like, optional + array or list with start indexes in each direction. + Must be given with end. + end : array-like, optional + array or list with end indexes in each direction. + Must be given with start. + npoints : int, optional + number of points in piece + ncells : int, optional + number of cells in piece. + If present, npoints must also be given. + nverts : int, optional + number of vertices. + nlines : int, optional + number of lines. + nstrips : int, optional + number of stripes. + npolys : int, optional + number of poly. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ # TODO: Check what are the requirements for each type of grid. self.xml.openElement("Piece") - if (start and end): + if start and end: ext = _mix_extents(start, end) - self.xml.addAttributes( Extent = ext) - - elif (ncells and npoints): - self.xml.addAttributes(NumberOfPoints = npoints, NumberOfCells = ncells) - + self.xml.addAttributes(Extent=ext) + + elif ncells and npoints: + self.xml.addAttributes( + NumberOfPoints=npoints, NumberOfCells=ncells + ) + elif npoints or nverts or nlines or nstrips or npolys: - if npoints is None: npoints = str(0) - if nverts is None: nverts = str(0) - if nlines is None: nlines = str(0) - if nstrips is None: nstrips = str(0) - if npolys is None: npolys = str(0) - self.xml.addAttributes(NumberOfPoints = npoints, NumberOfVerts = nverts, - NumberOfLines = nlines, NumberOfStrips = nstrips, NumberOfPolys = npolys) + if npoints is None: + npoints = str(0) + if nverts is None: + nverts = str(0) + if nlines is None: + nlines = str(0) + if nstrips is None: + nstrips = str(0) + if npolys is None: + npolys = str(0) + self.xml.addAttributes( + NumberOfPoints=npoints, + NumberOfVerts=nverts, + NumberOfLines=nlines, + NumberOfStrips=nstrips, + NumberOfPolys=npolys, + ) else: - assert(False) + assert False return self def closePiece(self): + """Close Piece.""" self.xml.closeElement("Piece") - def openData(self, nodeType, scalars=None, vectors=None, normals=None, tensors=None, tcoords=None): - """ Open data section. - - PARAMETERS: - nodeType: Point or Cell. - scalars: default data array name for scalar data. - vectors: default data array name for vector data. - normals: default data array name for normals data. - tensors: default data array name for tensors data. - tcoords: dafault data array name for tcoords data. - - RETURNS: - this VtkFile to allow chained calls. + def openData( + self, + nodeType, + scalars=None, + vectors=None, + normals=None, + tensors=None, + tcoords=None, + ): + """ + Open data section. + + Parameters + ---------- + nodeType : str + DESCRIPTION. + scalars : str, optional + default data array name for scalar data. + vectors : str, optional + default data array name for vector data. + normals : str, optional + default data array name for normals data. + tensors : str, optional + default data array name for tensors data. + tcoords : str, optional + default data array name for tcoords data. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ self.xml.openElement(nodeType + "Data") if scalars: - self.xml.addAttributes(scalars = scalars) + self.xml.addAttributes(scalars=scalars) if vectors: - self.xml.addAttributes(vectors = vectors) + self.xml.addAttributes(vectors=vectors) if normals: - self.xml.addAttributes(normals = normals) + self.xml.addAttributes(normals=normals) if tensors: - self.xml.addAttributes(tensors = tensors) + self.xml.addAttributes(tensors=tensors) if tcoords: - self.xml.addAttributes(tcoords = tcoords) + self.xml.addAttributes(tcoords=tcoords) return self def closeData(self, nodeType): - """ Close data section. - - PARAMETERS: - nodeType: Point or Cell. - - RETURNS: - this VtkFile to allow chained calls. """ - self.xml.closeElement(nodeType + "Data") + Close data section. + Parameters + ---------- + nodeType : str + "Point", "Cell" or "Field". - def openGrid(self, start = None, end = None, origin = None, spacing = None): - """ Open grid section. - - PARAMETERS: - start: array or list of start indexes. Required for Structured, Rectilinear and ImageData grids. - end: array or list of end indexes. Required for Structured, Rectilinear and ImageData grids. - origin: 3D array or list with grid origin. Only required for ImageData grids. - spacing: 3D array or list with grid spacing. Only required for ImageData grids. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + self.xml.closeElement(nodeType + "Data") - RETURNS: - this VtkFile to allow chained calls. + def openGrid(self, start=None, end=None, origin=None, spacing=None): + """ + Open grid section. + + Parameters + ---------- + start : array-like, optional + array or list of start indexes. + Required for Structured, Rectilinear and ImageData grids. + The default is None. + end : array-like, optional + array or list of end indexes. + Required for Structured, Rectilinear and ImageData grids. + The default is None. + origin : array-like, optional + 3D array or list with grid origin. + Only required for ImageData grids. + The default is None. + spacing : array-like, optional + 3D array or list with grid spacing. + Only required for ImageData grids. + The default is None. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ gType = self.ftype.name self.xml.openElement(gType) - if (gType == VtkImageData.name): - if (not start or not end or not origin or not spacing): assert(False) + if gType == VtkImageData.name: + if not start or not end or not origin or not spacing: + assert False ext = _mix_extents(start, end) - self.xml.addAttributes(WholeExtent = ext, - Origin = _array_to_string(origin), - Spacing = _array_to_string(spacing)) - - elif (gType == VtkStructuredGrid.name or gType == VtkRectilinearGrid.name): - if (not start or not end): assert (False) + self.xml.addAttributes( + WholeExtent=ext, + Origin=_array_to_string(origin), + Spacing=_array_to_string(spacing), + ) + + elif gType in [VtkStructuredGrid.name, VtkRectilinearGrid.name]: + if not start or not end: + assert False ext = _mix_extents(start, end) - self.xml.addAttributes(WholeExtent = ext) - + self.xml.addAttributes(WholeExtent=ext) + return self def closeGrid(self): - """ Closes grid element. + """ + Close grid element. - RETURNS: - this VtkFile to allow chained calls. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. """ self.xml.closeElement(self.ftype.name) - def addHeader(self, name, dtype, nelem, ncomp): - """ Adds data array description to xml header section. - - PARAMETERS: - name: data array name. - dtype: string describing type of the data. - Format is the same as used by numpy, e.g. 'float64'. - nelem: number of elements in the array. - ncomp: number of components, 1 (=scalar) and 3 (=vector). - - RETURNS: - This VtkFile to allow chained calls. - - NOTE: This is a low level function. Use addData if you want - to add a numpy array. + """ + Add data array description to xml header section. + + Parameters + ---------- + name : str + data array name. + dtype : str + data type. + nelem : int + number of elements in array. + ncomp : int + number of components, 1 (=scalar) and 3 (=vector). + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + + Notes + ----- + This is a low level function. + Use addData if you want to add a numpy array. """ dtype = np_to_vtk[dtype] - self.xml.openElement( "DataArray") - self.xml.addAttributes( Name = name, - NumberOfComponents = ncomp, - type = dtype.name, - format = "appended", - offset = self.offset) + self.xml.openElement("DataArray") + self.xml.addAttributes( + Name=name, + NumberOfComponents=ncomp, + type=dtype.name, + format="appended", + offset=self.offset, + ) self.xml.closeElement() - #TODO: Check if 4/8 is platform independent - #if self.largeFile == False: - # self.offset += nelem * ncomp * dtype.size + 4 # add 4 to indicate array size - #else: - self.offset += nelem * ncomp * dtype.size + 8 # add 8 to indicate array size + self.offset += ( + nelem * ncomp * dtype.size + 8 + ) # add 8 to indicate array size return self def addData(self, name, data): - """ Adds array description to xml header section. - - PARAMETERS: - name: data array name. - data: one numpy array or a tuple with 3 numpy arrays. If a tuple, the individual - arrays must represent the components of a vector field. - All arrays must be one dimensional or three-dimensional. """ - if type(data).__name__ == "tuple": # vector data - assert (len(data) == 3) + Add array description to xml header section. + + Parameters + ---------- + name : str + data array name. + data : array-like + one numpy array or a tuple with 3 numpy arrays. + If a tuple, the individual arrays must represent the components + of a vector field. + All arrays must be one dimensional or three-dimensional. + """ + if type(data).__name__ == "tuple": # vector data + assert len(data) == 3 x = data[0] self.addHeader(name, x.dtype.name, x.size, 3) elif type(data).__name__ == "ndarray": @@ -392,96 +543,124 @@ def addData(self, name, data): assert False, "Argument must be a Numpy array" def appendHeader(self, dtype, nelem, ncomp): - """ This function only writes the size of the data block that will be appended. - The data itself must be written immediately after calling this function. - - PARAMETERS: - dtype: string with data type representation (same as numpy). For example, 'float64' - nelem: number of elements. - ncomp: number of components, 1 (=scalar) or 3 (=vector). + """ + Append size of data block to header. + + This function only writes the size of the data block + that will be appended. + The data itself must be written immediately after + calling this function. + + Parameters + ---------- + dtype : str + string with data type representation (same as numpy). + For example, 'float64'. + nelem : int + number of elements. + ncomp : int + number of components, 1 (=scalar) or 3 (=vector).. """ self.openAppendedData() dsize = np_to_vtk[dtype].size block_size = dsize * ncomp * nelem - if self.largeFile == False: - writeBlockSize(self.xml.stream, block_size) - else: - writeBlockSize64Bit(self.xml.stream, block_size) + writeBlockSize(self.xml.stream, block_size) + # else: # this routine does not exist! + # writeBlockSize64Bit(self.xml.stream, block_size) - def appendData(self, data): - """ Append data to binary section. - This function writes the header section and the data to the binary file. - - PARAMETERS: - data: one numpy array or a tuple with 3 numpy arrays. If a tuple, the individual - arrays must represent the components of a vector field. - All arrays must be one dimensional or three-dimensional. - The order of the arrays must coincide with the numbering scheme of the grid. - - RETURNS: - This VtkFile to allow chained calls - - TODO: Extend this function to accept contiguous C order arrays. """ + Append data to binary section. + + This function writes the header section + and the data to the binary file. + + Parameters + ---------- + data : array-like + one numpy array or a tuple with 3 numpy arrays. + If a tuple, the individual + arrays must represent the components of a vector field. + All arrays must be one dimensional or three-dimensional. + The order of the arrays must coincide with + the numbering scheme of the grid. + + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + # TODO: Extend this function to accept contiguous C order arrays. self.openAppendedData() - if type(data).__name__ == 'tuple': # 3 numpy arrays + if type(data).__name__ == "tuple": # 3 numpy arrays ncomp = len(data) - assert (ncomp == 3) + assert ncomp == 3 dsize = data[0].dtype.itemsize nelem = data[0].size block_size = ncomp * nelem * dsize - #if self.largeFile == False: + # if self.largeFile == False: writeBlockSize(self.xml.stream, block_size) - #else: + # else: # writeBlockSize64Bit(self.xml.stream, block_size) x, y, z = data[0], data[1], data[2] writeArraysToFile(self.xml.stream, x, y, z) - - elif type(data).__name__ == 'ndarray' and (data.ndim == 1 or data.ndim == 3): # single numpy array - ncomp = 1 + + elif type(data).__name__ == "ndarray" and ( + data.ndim == 1 or data.ndim == 3 + ): # single numpy array + ncomp = 1 dsize = data.dtype.itemsize nelem = data.size block_size = ncomp * nelem * dsize - #if self.largeFile == False: + # if self.largeFile == False: writeBlockSize(self.xml.stream, block_size) - #else: + # else: # writeBlockSize64Bit(self.xml.stream, block_size) writeArrayToFile(self.xml.stream, data) - + else: assert False return self def openAppendedData(self): - """ Opens binary section. + """ + Open binary section. - It is not necessary to explicitly call this function from an external library. + It is not necessary to explicitly call this function + from an external library. """ if not self.appendedDataIsOpen: - self.xml.openElement("AppendedData").addAttributes(encoding = "raw").addText("_") + self.xml.openElement("AppendedData").addAttributes( + encoding="base64" + ).addText("_") self.appendedDataIsOpen = True def closeAppendedData(self): - """ Closes binary section. + """ + Close binary section. - It is not necessary to explicitly call this function from an external library. + It is not necessary to explicitly call this function + from an external library. """ self.xml.closeElement("AppendedData") def openElement(self, tagName): - """ Useful to add elements such as: Coordinates, Points, Verts, etc. """ + """ + Open an element. + + Useful to add elements such as: Coordinates, Points, Verts, etc. + """ self.xml.openElement(tagName) def closeElement(self, tagName): + """Close an element.""" self.xml.closeElement(tagName) def save(self): - """ Closes file """ + """Close file.""" if self.appendedDataIsOpen: self.xml.closeElement("AppendedData") self.xml.closeElement("VTKFile") self.xml.close() - From 4d0cdf280078c6e7c0fa594a088fbe5f9ecfca56 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:40:12 +0200 Subject: [PATCH 15/52] hl: add field_data export option; update doc; reformat with black --- pyevtk/hl.py | 727 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 475 insertions(+), 252 deletions(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 7f6383b..c9c1b66 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -1,5 +1,5 @@ # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -22,23 +22,30 @@ # * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * # * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * # *********************************************************************************** +"""High level Python library to export data to binary VTK file.""" -# ************************************** -# * High level Python library to * -# * export data to binary VTK file. * -# ************************************** - -from .vtk import * # VtkFile, VtkUnstructuredGrid, etc. import numpy as np +from .vtk import ( + VtkFile, + VtkUnstructuredGrid, + VtkImageData, + VtkRectilinearGrid, + VtkStructuredGrid, + VtkVertex, + VtkLine, + VtkPolyLine, + VtkPixel, +) + # ================================= # Helper functions # ================================= -def _addDataToFile(vtkFile, cellData, pointData): +def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): # Point data if pointData: keys = list(pointData.keys()) - vtkFile.openData("Point", scalars = keys[0]) + vtkFile.openData("Point", scalars=keys[0]) for key in keys: data = pointData[key] vtkFile.addData(key, data) @@ -47,132 +54,197 @@ def _addDataToFile(vtkFile, cellData, pointData): # Cell data if cellData: keys = list(cellData.keys()) - vtkFile.openData("Cell", scalars = keys[0]) + vtkFile.openData("Cell", scalars=keys[0]) for key in keys: data = cellData[key] vtkFile.addData(key, data) vtkFile.closeData("Cell") -def _appendDataToFile(vtkFile, cellData, pointData): + # Field data + # https://www.visitusers.org/index.php?title=Time_and_Cycle_in_VTK_files#XML_VTK_files + if fieldData: + keys = list(fieldData.keys()) + vtkFile.openData("Field") # no attributes in FieldData + for key in keys: + data = fieldData[key] + vtkFile.addData(key, data) + vtkFile.closeData("Field") + + +def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None): # Append data to binary section - if pointData != None: + if pointData is not None: keys = list(pointData.keys()) for key in keys: data = pointData[key] vtkFile.appendData(data) - if cellData != None: + if cellData is not None: keys = list(cellData.keys()) for key in keys: data = cellData[key] vtkFile.appendData(data) + if fieldData is not None: + keys = list(fieldData.keys()) + for key in keys: + data = fieldData[key] + vtkFile.appendData(data) + + # ================================= -# High level functions +# High level functions # ================================= -def imageToVTK(path, origin = (0.0,0.0,0.0), spacing = (1.0,1.0,1.0), cellData = None, pointData = None ): - """ Exports data values as a rectangular image. - - PARAMETERS: - path: name of the file without extension where data should be saved. - origin: grid origin (default = (0,0,0)) - spacing: grid spacing (default = (1,1,1)) - cellData: dictionary containing arrays with cell centered data. - Keys should be the names of the data arrays. - Arrays must have the same dimensions in all directions and can contain - scalar data ([n,n,n]) or vector data ([n,n,n],[n,n,n],[n,n,n]). - nodeData: dictionary containing arrays with node centered data. - Keys should be the names of the data arrays. - Arrays must have same dimension in each direction and - they should be equal to the dimensions of the cell data plus one and - can contain scalar data ([n+1,n+1,n+1]) or - vector data ([n+1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]). - - RETURNS: - Full path to saved file. - - NOTE: At least, cellData or pointData must be present to infer the dimensions of the image. +def imageToVTK( + path, + origin=(0.0, 0.0, 0.0), + spacing=(1.0, 1.0, 1.0), + cellData=None, + pointData=None, + fieldData=None, +): + """ + Export data values as a rectangular image. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + origin : tuple, optional + grid origin. + The default is (0.0, 0.0, 0.0). + spacing : tuple, optional + grid spacing. + The default is (1.0, 1.0, 1.0). + cellData : dict, optional + dictionary containing arrays with cell centered data. + Keys should be the names of the data arrays. + Arrays must have the same dimensions in all directions and can contain + scalar data ([n,n,n]) or vector data ([n,n,n],[n,n,n],[n,n,n]). + The default is None. + pointData : dict, optional + dictionary containing arrays with node centered data. + Keys should be the names of the data arrays. + Arrays must have same dimension in each direction and + they should be equal to the dimensions of the cell data plus one and + can contain scalar data ([n+1,n+1,n+1]) or + +1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]). + The default is None. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + At least, cellData or pointData must be present + to infer the dimensions of the image. """ - assert (cellData != None or pointData != None) - + assert cellData is not None or pointData is not None + # Extract dimensions - start = (0,0,0) + start = (0, 0, 0) end = None - if cellData != None: + if cellData is not None: keys = list(cellData.keys()) data = cellData[keys[0]] - if hasattr(data,'shape'): + if hasattr(data, "shape"): end = data.shape - elif(data[0].ndim==3 and data[1].ndim==3 and data[2].ndim==3): + elif data[0].ndim == 3 and data[1].ndim == 3 and data[2].ndim == 3: end = data[0].shape - elif pointData != None: + elif pointData is not None: keys = list(pointData.keys()) data = pointData[keys[0]] - if hasattr(data,'shape'): + if hasattr(data, "shape"): end = data.shape - elif(data[0].ndim==3 and data[1].ndim==3 and data[2].ndim==3): + elif data[0].ndim == 3 and data[1].ndim == 3 and data[2].ndim == 3: end = data[0].shape end = (end[0] - 1, end[1] - 1, end[2] - 1) # Write data to file w = VtkFile(path, VtkImageData) - w.openGrid(start = start, end = end, origin = origin, spacing = spacing) - w.openPiece(start = start, end = end) - _addDataToFile(w, cellData, pointData) + w.openGrid(start=start, end=end, origin=origin, spacing=spacing) + w.openPiece(start=start, end=end) + _addDataToFile(w, cellData, pointData, fieldData) w.closePiece() w.closeGrid() - _appendDataToFile(w, cellData, pointData) + _appendDataToFile(w, cellData, pointData, fieldData) w.save() return w.getFileName() + # ============================================================================== -def gridToVTK(path, x, y, z, cellData = None, pointData = None): +def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): """ - Writes data values as a rectilinear or rectangular grid. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: coordinates of the nodes of the grid. They can be 1D or 3D depending if - the grid should be saved as a rectilinear or logically structured grid, respectively. - Arrays should contain coordinates of the nodes of the grid. - If arrays are 1D, then the grid should be Cartesian, i.e. faces in all cells are orthogonal. - If arrays are 3D, then the grid should be logically structured with hexahedral cells. - In both cases the arrays dimensions should be equal to the number of nodes of the grid. - cellData: dictionary containing arrays with cell centered data. - Keys should be the names of the data arrays. - Arrays must have the same dimensions in all directions and must contain - only scalar data. - pointData: dictionary containing arrays with node centered data. - Keys should be the names of the data arrays. - Arrays must have same dimension in each direction and - they should be equal to the dimensions of the cell data plus one and - must contain only scalar data. - - RETURNS: - Full path to saved file. - + Write data values as a rectilinear or rectangular grid. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinate axis. + y : array-like + y coordinate axis. + z : array-like + z coordinate axis. + cellData : dict, optional + dictionary containing arrays with cell centered data. + Keys should be the names of the data arrays. + Arrays must have the same dimensions in all directions and must contain + only scalar data. + pointData : dict, optional + dictionary containing arrays with node centered data. + Keys should be the names of the data arrays. + Arrays must have same dimension in each direction and + they should be equal to the dimensions of the cell data plus one and + must contain only scalar data. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + coordinates of the nodes of the grid. They can be 1D or 3D depending if + the grid should be saved as a rectilinear or logically structured grid, + respectively. + Arrays should contain coordinates of the nodes of the grid. + If arrays are 1D, then the grid should be Cartesian, + i.e. faces in all cells are orthogonal. + If arrays are 3D, then the grid should be logically structured + with hexahedral cells. + In both cases the arrays dimensions should be + equal to the number of nodes of the grid. """ # Extract dimensions - start = (0,0,0) + start = (0, 0, 0) nx = ny = nz = 0 - if (x.ndim == 1 and y.ndim == 1 and z.ndim == 1): + if x.ndim == 1 and y.ndim == 1 and z.ndim == 1: nx, ny, nz = x.size - 1, y.size - 1, z.size - 1 isRect = True ftype = VtkRectilinearGrid - elif (x.ndim == 3 and y.ndim == 3 and z.ndim == 3): + elif x.ndim == 3 and y.ndim == 3 and z.ndim == 3: s = x.shape nx, ny, nz = s[0] - 1, s[1] - 1, s[2] - 1 isRect = False ftype = VtkStructuredGrid else: - assert(False) + assert False end = (nx, ny, nz) - - w = VtkFile(path, ftype) - w.openGrid(start = start, end = end) - w.openPiece(start = start, end = end) + w = VtkFile(path, ftype) + w.openGrid(start=start, end=end) + w.openPiece(start=start, end=end) if isRect: w.openElement("Coordinates") @@ -182,282 +254,422 @@ def gridToVTK(path, x, y, z, cellData = None, pointData = None): w.closeElement("Coordinates") else: w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") - _addDataToFile(w, cellData, pointData) + _addDataToFile(w, cellData, pointData, fieldData) w.closePiece() w.closeGrid() # Write coordinates if isRect: w.appendData(x).appendData(y).appendData(z) else: - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) # Write data - _appendDataToFile(w, cellData, pointData) + _appendDataToFile(w, cellData, pointData, fieldData) w.save() return w.getFileName() # ============================================================================== -def pointsToVTK(path, x, y, z, data = None): +def pointsToVTK(path, x, y, z, data=None, fieldData=None): """ - Export points and associated data as an unstructured grid. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the points. - data: dictionary with variables associated to each point. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export points and associated data as an unstructured grid. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the points. + y : array-like + y coordinates of the points. + z : array-like + z coordinates of the points. + data : dict, optional + dictionary with variables associated to each point. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. """ - assert (x.size == y.size == z.size) + assert x.size == y.size == z.size npoints = x.size - + # create some temporary arrays to write grid topology - offsets = np.arange(start = 1, stop = npoints + 1, dtype = 'int32') # index of last node in each cell - connectivity = np.arange(npoints, dtype = 'int32') # each point is only connected to itself - cell_types = np.empty(npoints, dtype = 'uint8') - + offsets = np.arange( + start=1, stop=npoints + 1, dtype="int32" + ) # index of last node in each cell + connectivity = np.arange( + npoints, dtype="int32" + ) # each point is only connected to itself + cell_types = np.empty(npoints, dtype="uint8") + cell_types[:] = VtkVertex.tid w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = npoints, npoints = npoints) - + w.openPiece(ncells=npoints, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = None, pointData = data) + + _addDataToFile(w, cellData=None, pointData=data, fieldData=fieldData) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = None, pointData = data) + _appendDataToFile(w, cellData=None, pointData=data, fieldData=fieldData) w.save() return w.getFileName() + # ============================================================================== -def linesToVTK(path, x, y, z, cellData = None, pointData = None): +def linesToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): """ - Export line segments that joint 2 points and associated data. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the vertex of the lines. It is assumed that each line. - is defined by two points, then the lenght of the arrays should be equal to 2 * number of lines. - cellData: dictionary with variables associated to each line. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - pointData: dictionary with variables associated to each vertex. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export line segments that joint 2 points and associated data. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the points in lines. + y : array-like + y coordinates of the points in lines. + z : array-like + z coordinates of the points in lines. + cellData : dict, optional + dictionary with variables associated to each line. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + x, y, z are 1D arrays with coordinates of the vertex of the lines. + It is assumed that each line. + is defined by two points, + then the lenght of the arrays should be equal to 2 * number of lines. """ - assert (x.size == y.size == z.size) - assert (x.size % 2 == 0) + assert x.size == y.size == z.size + assert x.size % 2 == 0 npoints = x.size ncells = x.size / 2 - + # Check cellData has the same size that the number of cells - + # create some temporary arrays to write grid topology - offsets = np.arange(start = 2, step = 2, stop = npoints + 1, dtype = 'int32') # index of last node in each cell - connectivity = np.arange(npoints, dtype = 'int32') # each point is only connected to itself - cell_types = np.empty(npoints, dtype = 'uint8') - + offsets = np.arange( + start=2, step=2, stop=npoints + 1, dtype="int32" + ) # index of last node in each cell + connectivity = np.arange( + npoints, dtype="int32" + ) # each point is only connected to itself + cell_types = np.empty(npoints, dtype="uint8") + cell_types[:] = VtkLine.tid w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = ncells, npoints = npoints) - + w.openPiece(ncells=ncells, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = cellData, pointData = pointData) + + _addDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = cellData, pointData = pointData) + _appendDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.save() return w.getFileName() + # ============================================================================== -def polyLinesToVTK(path, x, y, z, pointsPerLine, cellData = None, pointData = None): +def polyLinesToVTK( + path, x, y, z, pointsPerLine, cellData=None, pointData=None, fieldData=None +): """ - Export line segments that joint 2 points and associated data. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the vertices of the lines. It is assumed that each line. - has diffent number of points. - pointsPerLine: 1D array that defines the number of points associated to each line. Thus, - the length of this array define the number of lines. It also implicitly - defines the connectivity or topology of the set of lines. It is assumed - that points that define a line are consecutive in the x, y and z arrays. - cellData: Dictionary with variables associated to each line. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - pointData: Dictionary with variables associated to each vertex. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export line segments that joint n points and associated data. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the points in lines. + y : array-like + y coordinates of the points in lines. + z : array-like + z coordinates of the points in lines. + pointsPerLine : array-like + Points in each poly-line. + cellData : dict, optional + dictionary with variables associated to each line. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. """ - assert (x.size == y.size == z.size) + assert x.size == y.size == z.size npoints = x.size ncells = pointsPerLine.size - + # create some temporary arrays to write grid topology - offsets = np.zeros(ncells, dtype = 'int32') # index of last node in each cell + offsets = np.zeros( + ncells, dtype="int32" + ) # index of last node in each cell ii = 0 for i in range(ncells): ii += pointsPerLine[i] offsets[i] = ii - - connectivity = np.arange(npoints, dtype = 'int32') # each line connects points that are consecutive - - cell_types = np.empty(npoints, dtype = 'uint8') + + connectivity = np.arange( + npoints, dtype="int32" + ) # each line connects points that are consecutive + + cell_types = np.empty(npoints, dtype="uint8") cell_types[:] = VtkPolyLine.tid w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = ncells, npoints = npoints) - + w.openPiece(ncells=ncells, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = cellData, pointData = pointData) + + _addDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = cellData, pointData = pointData) + _appendDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.save() return w.getFileName() + # ============================================================================== -def unstructuredGridToVTK(path, x, y, z, connectivity, offsets, cell_types, cellData = None, pointData = None): +def unstructuredGridToVTK( + path, + x, + y, + z, + connectivity, + offsets, + cell_types, + cellData=None, + pointData=None, + fieldData=None, +): """ - Export unstructured grid and associated data. - - PARAMETERS: - path: name of the file without extension where data should be saved. - x, y, z: 1D arrays with coordinates of the vertices of cells. It is assumed that each element - has diffent number of vertices. - connectivity: 1D array that defines the vertices associated to each element. - Together with offset define the connectivity or topology of the grid. - It is assumed that vertices in an element are listed consecutively. - offsets: 1D array with the index of the last vertex of each element in the connectivity array. - It should have length nelem, where nelem is the number of cells or elements in the grid. - cell_types: 1D array with an integer that defines the cell type of each element in the grid. - It should have size nelem. This should be assigned from evtk.vtk.VtkXXXX.tid, where XXXX represent - the type of cell. Please check the VTK file format specification for allowed cell types. - cellData: Dictionary with variables associated to each line. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - pointData: Dictionary with variables associated to each vertex. - Keys should be the names of the variable stored in each array. - All arrays must have the same number of elements. - - RETURNS: - Full path to saved file. - + Export unstructured grid and associated data. + + Parameters + ---------- + path : str + name of the file without extension where data should be saved. + x : array-like + x coordinates of the vertices. + y : array-like + y coordinates of the vertices. + z : array-like + z coordinates of the vertices. + connectivity : array-like + 1D array that defines the vertices associated to each element. + Together with offset define the connectivity or topology of the grid. + It is assumed that vertices in an element are listed consecutively. + offsets : array-like + 1D array with the index of the last vertex of each element + in the connectivity array. + It should have length nelem, + where nelem is the number of cells or elements in the grid.. + cell_types : TYPE + 1D array with an integer that defines the cell type of + each element in the grid. + It should have size nelem. + This should be assigned from evtk.vtk.VtkXXXX.tid, where XXXX represent + the type of cell. + Please check the VTK file format specification for allowed cell types. + cellData : dict, optional + dictionary with variables associated to each cell. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + All arrays must have the same number of elements. + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. """ - assert (x.size == y.size == z.size) + assert x.size == y.size == z.size npoints = x.size ncells = cell_types.size - assert (offsets.size == ncells) - + assert offsets.size == ncells + w = VtkFile(path, VtkUnstructuredGrid) w.openGrid() - w.openPiece(ncells = ncells, npoints = npoints) - + w.openPiece(ncells=ncells, npoints=npoints) + w.openElement("Points") - w.addData("points", (x,y,z)) + w.addData("points", (x, y, z)) w.closeElement("Points") w.openElement("Cells") w.addData("connectivity", connectivity) w.addData("offsets", offsets) w.addData("types", cell_types) w.closeElement("Cells") - - _addDataToFile(w, cellData = cellData, pointData = pointData) + + _addDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.closePiece() w.closeGrid() - w.appendData( (x,y,z) ) + w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile(w, cellData = cellData, pointData = pointData) + _appendDataToFile( + w, cellData=cellData, pointData=pointData, fieldData=fieldData + ) w.save() return w.getFileName() - + + # ============================================================================== -def cylinderToVTK(path, x0, y0, z0, z1, radius, nlayers, npilars = 16, cellData=None, pointData=None): +def cylinderToVTK( + path, + x0, + y0, + z0, + z1, + radius, + nlayers, + npilars=16, + cellData=None, + pointData=None, + fieldData=None, +): """ - Export cylinder as VTK unstructured grid. - - PARAMETERS: - path: path to file without extension. - x0, yo: center of cylinder. - z0, z1: lower and top elevation of the cylinder. - radius: radius of cylinder. - nlayers: Number of layers in z direction to divide the cylinder. - npilars: Number of points around the diameter of the cylinder. - Higher value gives higher resolution to represent the curved shape. - cellData: dictionary with 1D arrays that store cell data. - Arrays should have number of elements equal to ncells = npilars * nlayers. - pointData: dictionary with 1D arrays that store point data. - Arrays should have number of elements equal to npoints = npilars * (nlayers + 1). - - RETURNS: - Full path to saved file. - - NOTE: This function only export vertical shapes for now. However, it should be easy to - rotate the cylinder to represent other orientations. + Export cylinder as VTK unstructured grid. + + Parameters + ---------- + path : TYPE + DESCRIPTION. + x0 : float + x-center of the cylinder. + y0 : float + y-center of the cylinder. + z0 : float + lower end of the cylinder. + z1 : float + upper end of the cylinder. + radius : float + radius of the cylinder. + nlayers : int + Number of layers in z direction to divide the cylinder.. + npilars : int, optional + Number of points around the diameter of the cylinder. + Higher value gives higher resolution to represent the curved shape. + The default is 16. + cellData : dict, optional + dictionary with variables associated to each cell. + Keys should be the names of the variable stored in each array. + Arrays should have number of elements equal to + ncells = npilars * nlayers. + pointData : dict, optional + dictionary with variables associated to each vertex. + Keys should be the names of the variable stored in each array. + Arrays should have number of elements equal to + npoints = npilars * (nlayers + 1). + fieldData : dict, optional + dictionary with variables associated with the field. + Keys should be the names of the variable stored in each array. + + Returns + ------- + str + Full path to saved file. + + Notes + ----- + This function only export vertical shapes for now. + However, it should be easy to + rotate the cylinder to represent other orientations. """ import math as m - + # Define x, y coordinates from polar coordinates. dpi = 2.0 * m.pi / npilars angles = np.arange(0.0, 2.0 * m.pi, dpi) @@ -466,10 +678,10 @@ def cylinderToVTK(path, x0, y0, z0, z1, radius, nlayers, npilars = 16, cellData= y = radius * np.sin(angles) + y0 dz = (z1 - z0) / nlayers - z = np.arange(z0, z1+dz, step = dz) + z = np.arange(z0, z1 + dz, step=dz) npoints = npilars * (nlayers + 1) - ncells = npilars * nlayers + ncells = npilars * nlayers xx = np.zeros(npoints) yy = np.zeros(npoints) @@ -484,33 +696,44 @@ def cylinderToVTK(path, x0, y0, z0, z1, radius, nlayers, npilars = 16, cellData= ii = ii + 1 # Define connectivity - conn = np.zeros(4 * ncells, dtype = np.int64) + conn = np.zeros(4 * ncells, dtype=np.int64) ii = 0 for l in range(nlayers): for p in range(npilars): p0 = p - if(p + 1 == npilars): - p1 = 0 - else: - p1 = p + 1 # circular loop - + if p + 1 == npilars: + p1 = 0 + else: + p1 = p + 1 # circular loop + n0 = p0 + l * npilars - n1 = p1 + l * npilars + n1 = p1 + l * npilars n2 = n0 + npilars n3 = n1 + npilars - + conn[ii + 0] = n0 conn[ii + 1] = n1 conn[ii + 2] = n3 - conn[ii + 3] = n2 + conn[ii + 3] = n2 ii = ii + 4 - - # Define offsets - offsets = np.zeros(ncells, dtype = np.int64) + + # Define offsets + offsets = np.zeros(ncells, dtype=np.int64) for i in range(ncells): offsets[i] = (i + 1) * 4 # Define cell types ctype = np.ones(ncells) + VtkPixel.tid - - return unstructuredGridToVTK(path, xx, yy, zz, connectivity = conn, offsets = offsets, cell_types = ctype, cellData = cellData, pointData = pointData) + + return unstructuredGridToVTK( + path, + xx, + yy, + zz, + connectivity=conn, + offsets=offsets, + cell_types=ctype, + cellData=cellData, + pointData=pointData, + fieldData=fieldData, + ) From 1f124ae141cc6bc440714c2842aae73b12b2d52e Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:41:17 +0200 Subject: [PATCH 16/52] examples: reformat wit black; add field data example --- examples/group.py | 10 +++++----- examples/image.py | 26 +++++++++++++------------ examples/lines.py | 11 ++++++++--- examples/lowlevel.py | 41 ++++++++++++++++++++-------------------- examples/points.py | 13 ++++++------- examples/poly_lines.py | 12 +++++++++--- examples/rectilinear.py | 25 +++++++++++++++--------- examples/structured.py | 29 +++++++++++++++++----------- examples/unstructured.py | 20 +++++++++++++++++--- 9 files changed, 113 insertions(+), 74 deletions(-) diff --git a/examples/group.py b/examples/group.py index 070e9db..ba619f5 100644 --- a/examples/group.py +++ b/examples/group.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -33,8 +33,8 @@ from pyevtk.vtk import VtkGroup g = VtkGroup("./group") -g.addFile(filepath = "sim0000.vtu", sim_time = 0.0) -g.addFile(filepath = "sim0001.vtu", sim_time = 1.0) -g.addFile(filepath = "sim0002.vtu", sim_time = 2.0) -g.addFile(filepath = "sim0003.vtu", sim_time = 3.0) +g.addFile(filepath="sim0000.vtu", sim_time=0.0) +g.addFile(filepath="sim0001.vtu", sim_time=1.0) +g.addFile(filepath="sim0002.vtu", sim_time=2.0) +g.addFile(filepath="sim0003.vtu", sim_time=3.0) g.save() diff --git a/examples/image.py b/examples/image.py index 82425c3..41f15c0 100644 --- a/examples/image.py +++ b/examples/image.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -38,19 +38,21 @@ npoints = (nx + 1) * (ny + 1) * (nz + 1) # Variables -pressure = np.random.rand(ncells).reshape( (nx, ny, nz), order = 'C') -temp = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1)) +pressure = np.random.rand(ncells).reshape((nx, ny, nz), order="C") +temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) -imageToVTK("./image", cellData = {"pressure" : pressure}, pointData = {"temp" : temp} ) +imageToVTK( + "./image", cellData={"pressure": pressure}, pointData={"temp": temp} +) -fluxx = np.random.rand(ncells).reshape( (nx, ny, nz), order='F') -fluxy = np.random.rand(ncells).reshape( (nx, ny, nz), order='F') -fluxz = np.random.rand(ncells).reshape( (nx, ny, nz), order='F') +fluxx = np.random.rand(ncells).reshape((nx, ny, nz), order="F") +fluxy = np.random.rand(ncells).reshape((nx, ny, nz), order="F") +fluxz = np.random.rand(ncells).reshape((nx, ny, nz), order="F") flux = (fluxx, fluxy, fluxz) -Efieldx = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1), order='F') -Efieldy = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1), order='F') -Efieldz = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1), order='F') -Efield = (Efieldx,Efieldy,Efieldz) +Efieldx = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1), order="F") +Efieldy = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1), order="F") +Efieldz = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1), order="F") +Efield = (Efieldx, Efieldy, Efieldz) -imageToVTK("./image", cellData={"flux" : flux}, pointData = {"Efield" : Efieldx} ) \ No newline at end of file +imageToVTK("./image", cellData={"flux": flux}, pointData={"Efield": Efieldx}) diff --git a/examples/lines.py b/examples/lines.py index 6e5ff61..d83f174 100644 --- a/examples/lines.py +++ b/examples/lines.py @@ -48,6 +48,11 @@ x[2], y[2], z[2] = 0.0, 0.0, 0.0 x[3], y[3], z[3] = -1.0, 1.0, 1.0 -linesToVTK("./lines", x, y, z, cellData = {"vel" : vel}, pointData = {"temp" : temp, "pressure" : pressure}) - - +linesToVTK( + "./lines", + x, + y, + z, + cellData={"vel": vel}, + pointData={"temp": temp, "pressure": pressure}, +) diff --git a/examples/lowlevel.py b/examples/lowlevel.py index 13a5661..d727b52 100644 --- a/examples/lowlevel.py +++ b/examples/lowlevel.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -34,45 +34,44 @@ nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 -dx, dy, dz = lx/nx, ly/ny, lz/nz +dx, dy, dz = lx / nx, ly / ny, lz / nz ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) -x = np.arange(0, lx + 0.1*dx, dx, dtype='float64') -y = np.arange(0, ly + 0.1*dy, dy, dtype='float64') -z = np.arange(0, lz + 0.1*dz, dz, dtype='float64') -start, end = (0,0,0), (nx, ny, nz) +x = np.arange(0, lx + 0.1 * dx, dx, dtype="float64") +y = np.arange(0, ly + 0.1 * dy, dy, dtype="float64") +z = np.arange(0, lz + 0.1 * dz, dz, dtype="float64") +start, end = (0, 0, 0), (nx, ny, nz) w = VtkFile("./evtk_test", VtkRectilinearGrid) -w.openGrid(start = start, end = end) -w.openPiece( start = start, end = end) +w.openGrid(start=start, end=end) +w.openPiece(start=start, end=end) # Point data temp = np.random.rand(npoints) -vx = vy = vz = np.zeros([nx + 1, ny + 1, nz + 1], dtype="float64", order = 'F') -w.openData("Point", scalars = "Temperature", vectors = "Velocity") +vx = vy = vz = np.zeros([nx + 1, ny + 1, nz + 1], dtype="float64", order="F") +w.openData("Point", scalars="Temperature", vectors="Velocity") w.addData("Temperature", temp) -w.addData("Velocity", (vx,vy,vz)) +w.addData("Velocity", (vx, vy, vz)) w.closeData("Point") # Cell data -pressure = np.zeros([nx, ny, nz], dtype="float64", order='F') -w.openData("Cell", scalars = "Pressure") +pressure = np.zeros([nx, ny, nz], dtype="float64", order="F") +w.openData("Cell", scalars="Pressure") w.addData("Pressure", pressure) w.closeData("Cell") # Coordinates of cell vertices w.openElement("Coordinates") -w.addData("x_coordinates", x); -w.addData("y_coordinates", y); -w.addData("z_coordinates", z); -w.closeElement("Coordinates"); +w.addData("x_coordinates", x) +w.addData("y_coordinates", y) +w.addData("z_coordinates", z) +w.closeElement("Coordinates") w.closePiece() w.closeGrid() -w.appendData(data = temp) -w.appendData(data = (vx,vy,vz)) -w.appendData(data = pressure) +w.appendData(data=temp) +w.appendData(data=(vx, vy, vz)) +w.appendData(data=pressure) w.appendData(x).appendData(y).appendData(z) w.save() - diff --git a/examples/points.py b/examples/points.py index 8b6f0d1..30ad3ab 100644 --- a/examples/points.py +++ b/examples/points.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -39,11 +39,10 @@ z = np.random.rand(npoints) pressure = np.random.rand(npoints) temp = np.random.rand(npoints) -pointsToVTK("./rnd_points", x, y, z, data = {"temp" : temp, "pressure" : pressure}) +pointsToVTK("./rnd_points", x, y, z, data={"temp": temp, "pressure": pressure}) # Example 2 -x = np.arange(1.0,10.0,0.1) -y = np.arange(1.0,10.0,0.1) -z = np.arange(1.0,10.0,0.1) -pointsToVTK("./line_points", x, y, z, data = {"elev" : z}) - +x = np.arange(1.0, 10.0, 0.1) +y = np.arange(1.0, 10.0, 0.1) +z = np.arange(1.0, 10.0, 0.1) +pointsToVTK("./line_points", x, y, z, data={"elev": z}) diff --git a/examples/poly_lines.py b/examples/poly_lines.py index 05e66ff..22962a1 100644 --- a/examples/poly_lines.py +++ b/examples/poly_lines.py @@ -61,6 +61,12 @@ vel[0:3] = 1.0 vel[4:6] = 5.0 -polyLinesToVTK("./poly_lines", x, y, z, pointsPerLine = pointsPerLine, cellData = {"vel" : vel}, pointData = {"temp" : temp, "pressure" : pressure}) - - +polyLinesToVTK( + "./poly_lines", + x, + y, + z, + pointsPerLine=pointsPerLine, + cellData={"vel": vel}, + pointData={"temp": temp, "pressure": pressure}, +) diff --git a/examples/rectilinear.py b/examples/rectilinear.py index 14f9ae5..4f83660 100644 --- a/examples/rectilinear.py +++ b/examples/rectilinear.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -36,18 +36,25 @@ # Dimensions nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 -dx, dy, dz = lx/nx, ly/ny, lz/nz +dx, dy, dz = lx / nx, ly / ny, lz / nz ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) # Coordinates -x = np.arange(0, lx + 0.1*dx, dx, dtype='float64') -y = np.arange(0, ly + 0.1*dy, dy, dtype='float64') -z = np.arange(0, lz + 0.1*dz, dz, dtype='float64') +x = np.arange(0, lx + 0.1 * dx, dx, dtype="float64") +y = np.arange(0, ly + 0.1 * dy, dy, dtype="float64") +z = np.arange(0, lz + 0.1 * dz, dz, dtype="float64") # Variables -pressure = np.random.rand(ncells).reshape( (nx, ny, nz)) -temp = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1)) - -gridToVTK("./rectilinear", x, y, z, cellData = {"pressure" : pressure}, pointData = {"temp" : temp}) +pressure = np.random.rand(ncells).reshape((nx, ny, nz)) +temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) + +gridToVTK( + "./rectilinear", + x, + y, + z, + cellData={"pressure": pressure}, + pointData={"temp": temp}, +) diff --git a/examples/structured.py b/examples/structured.py index ec65376..40de3bc 100644 --- a/examples/structured.py +++ b/examples/structured.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # *********************************************************************************** -# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +# * Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * # * * # * Redistribution and use in source and binary forms, with or without * # * modification, are permitted provided that the following conditions are met: * @@ -37,15 +37,15 @@ # Dimensions nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 -dx, dy, dz = lx/nx, ly/ny, lz/nz +dx, dy, dz = lx / nx, ly / ny, lz / nz ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) # Coordinates -X = np.arange(0, lx + 0.1*dx, dx, dtype='float64') -Y = np.arange(0, ly + 0.1*dy, dy, dtype='float64') -Z = np.arange(0, lz + 0.1*dz, dz, dtype='float64') +X = np.arange(0, lx + 0.1 * dx, dx, dtype="float64") +Y = np.arange(0, ly + 0.1 * dy, dy, dtype="float64") +Z = np.arange(0, lz + 0.1 * dz, dz, dtype="float64") x = np.zeros((nx + 1, ny + 1, nz + 1)) y = np.zeros((nx + 1, ny + 1, nz + 1)) @@ -56,12 +56,19 @@ for k in range(nz + 1): for j in range(ny + 1): for i in range(nx + 1): - x[i,j,k] = X[i] + (0.5 - rnd.random()) * 0.1 * dx - y[i,j,k] = Y[j] + (0.5 - rnd.random()) * 0.1 * dy - z[i,j,k] = Z[k] + (0.5 - rnd.random()) * 0.1 * dz + x[i, j, k] = X[i] + (0.5 - rnd.random()) * 0.1 * dx + y[i, j, k] = Y[j] + (0.5 - rnd.random()) * 0.1 * dy + z[i, j, k] = Z[k] + (0.5 - rnd.random()) * 0.1 * dz # Variables -pressure = np.random.rand(ncells).reshape( (nx, ny, nz)) -temp = np.random.rand(npoints).reshape( (nx + 1, ny + 1, nz + 1)) +pressure = np.random.rand(ncells).reshape((nx, ny, nz)) +temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) -gridToVTK("./structured", x, y, z, cellData = {"pressure" : pressure}, pointData = {"temp" : temp}) +gridToVTK( + "./structured", + x, + y, + z, + cellData={"pressure": pressure}, + pointData={"temp": temp}, +) diff --git a/examples/unstructured.py b/examples/unstructured.py index d28f536..d9ff10a 100644 --- a/examples/unstructured.py +++ b/examples/unstructured.py @@ -48,11 +48,14 @@ x[4], y[4], z[4] = 1.0, 1.0, 0.0 x[5], y[5], z[5] = 2.0, 1.0, 0.0 +point_data = {"test_pd": np.array([1, 2, 3, 4, 5, 6])} +cell_data = {"test_cd": np.array([1, 2, 3])} +field_data = {"test_fd": np.array([1.0, 2.0])} # Define connectivity or vertices that belongs to each element conn = np.zeros(10) -conn[0], conn[1], conn[2] = 0, 1, 3 # first triangle -conn[3], conn[4], conn[5] = 1, 4, 3 # second triangle +conn[0], conn[1], conn[2] = 0, 1, 3 # first triangle +conn[3], conn[4], conn[5] = 1, 4, 3 # second triangle conn[6], conn[7], conn[8], conn[9] = 1, 2, 5, 4 # rectangle # Define offset of last vertex of each element @@ -67,4 +70,15 @@ ctype[0], ctype[1] = VtkTriangle.tid, VtkTriangle.tid ctype[2] = VtkQuad.tid -unstructuredGridToVTK("unstructured", x, y, z, connectivity = conn, offsets = offset, cell_types = ctype, cellData = None, pointData = None) +unstructuredGridToVTK( + "unstructured", + x, + y, + z, + connectivity=conn, + offsets=offset, + cell_types=ctype, + cellData=cell_data, + pointData=point_data, + fieldData=field_data, +) From b61c10ec43b3cb4825aa1810acc0bf62d048f07f Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:43:00 +0200 Subject: [PATCH 17/52] vtk: reset appended encoding to raw --- pyevtk/vtk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 3015b85..98e0b4c 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -633,7 +633,7 @@ def openAppendedData(self): """ if not self.appendedDataIsOpen: self.xml.openElement("AppendedData").addAttributes( - encoding="base64" + encoding="raw" ).addText("_") self.appendedDataIsOpen = True From d1f1e3392bcdb94fe99aed511f1c715896bfa9d5 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:57:37 +0200 Subject: [PATCH 18/52] minor doc corrections --- pyevtk/vtk.py | 2 +- pyevtk/xml.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 98e0b4c..39ae00e 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -119,7 +119,7 @@ class VtkCellType: ---------- tid : int Type ID. - name : TYPE + name : str Cell type name. """ diff --git a/pyevtk/xml.py b/pyevtk/xml.py index 1c76832..b4c1e37 100644 --- a/pyevtk/xml.py +++ b/pyevtk/xml.py @@ -31,10 +31,11 @@ class XmlWriter: Parameters ---------- - filepath : TYPE - DESCRIPTION. + filepath : str + Path to the xml file. addDeclaration : bool, optional - DESCRIPTION. The default is True. + Whether to add the declaration. + The default is True. """ def __init__(self, filepath, addDeclaration=True): From 6e058a4380fb2b556e24708d82fa6ca43d430a25 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Wed, 22 Apr 2020 22:59:11 +0200 Subject: [PATCH 19/52] setup.py: reformat with black --- setup.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index 4257e0f..71524f6 100644 --- a/setup.py +++ b/setup.py @@ -30,28 +30,27 @@ def readme(fname): - with open(fname, 'r') as f: + """Open the readme file.""" + with open(fname, "r") as f: return f.read() setup( - name='pyevtk', - version='1.1.2', - description='Export data as binary VTK files', - long_description=readme('README.md'), - long_description_content_type='text/markdown', - author='Paulo Herrera', - author_email='pauloa.herrera@gmail.com', - maintainer='Adamos Kyriakou', - maintainer_email='somada141@gmail.com', - url = 'https://github.com/paulo-herrera/PyEVTK.git', - packages=['pyevtk', 'evtk'], - package_dir={'pyevtk': 'pyevtk'}, - package_data={'pyevtk': ['LICENSE.txt', 'examples/*.py']}, - install_requires=[ - "numpy >= 1.8.0", - ], + name="pyevtk", + version="1.1.2", + description="Export data as binary VTK files", + long_description=readme("README.md"), + long_description_content_type="text/markdown", + author="Paulo Herrera", + author_email="pauloa.herrera@gmail.com", + maintainer="Adamos Kyriakou", + maintainer_email="somada141@gmail.com", + url="https://github.com/paulo-herrera/PyEVTK.git", + packages=["pyevtk", "evtk"], + package_dir={"pyevtk": "pyevtk"}, + package_data={"pyevtk": ["LICENSE.txt", "examples/*.py"]}, + install_requires=["numpy >= 1.8.0"], # necessary for 'python setup.py test' - setup_requires=['pytest-runner'], - tests_require=['pytest>=3.1', 'pytest-cov', 'twine', 'check-manifest'], + setup_requires=["pytest-runner"], + tests_require=["pytest>=3.1", "pytest-cov", "twine", "check-manifest"], ) From db63f7597c2c1faa0a62779c3be855719a0e3d37 Mon Sep 17 00:00:00 2001 From: MuellerSeb Date: Thu, 23 Apr 2020 09:49:03 +0200 Subject: [PATCH 20/52] add some missing descriptions --- examples/unstructured.vtu | Bin 0 -> 1504 bytes pyevtk/hl.py | 4 ++-- pyevtk/vtk.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 examples/unstructured.vtu diff --git a/examples/unstructured.vtu b/examples/unstructured.vtu new file mode 100644 index 0000000000000000000000000000000000000000..8700ba40232bdcb4a41deccd78bc69f38ffc64e3 GIT binary patch literal 1504 zcmb_c%TB{E5G+q2PCano7+IW2OG^!z4btmwpFG@M4Osw|nnmD{dmbw}an(eEcOjWa zJ{MSzGX-NIJ&+E$W=aKc&OJssv7UkI=os5va@A=Q-M-vVheixsux=*dBgk8SBp6q* zLpmf6ok9@kNGqw&()#6$Dmsvo&a4|6YNb(HS11x;BsiqKS_H=#27)F+EMLfwDu>W0 z0`7sHOZa{aI?^Dv?r!HiDR~8-D>w&Nu_;rtYS!j2Seu6jDQ|hIOjZ1Mf`fK8SeYR4 z=RevXW%_NiUFrTMWLE6bfJ*J3f>@0sFLCjU3QWx{FSwWQV|IwX@vhZq7u-uoG*_ai zLWsLqh)(@gNC%IRA=f&F3<3`|RnG7KM7zZ4Z5^WzMK(LVVOoM+ar*{%1JW%5?h23b zCx=M-qKz8A)s}hQdka?e7sN4Kt~1{T^J?rfkB{BW@sWA{yRBb#+{qftj<`*Pa#)OgzRPc=rSO`g^DV literal 0 HcmV?d00001 diff --git a/pyevtk/hl.py b/pyevtk/hl.py index c9c1b66..19f4264 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -625,8 +625,8 @@ def cylinderToVTK( Parameters ---------- - path : TYPE - DESCRIPTION. + path : str + name of the file without extension where data should be saved. x0 : float x-center of the cylinder. y0 : float diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 39ae00e..061e321 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -368,7 +368,7 @@ def openData( Parameters ---------- nodeType : str - DESCRIPTION. + Either "Point", "Cell" or "Field". scalars : str, optional default data array name for scalar data. vectors : str, optional From 7cb294fa36969d67db3096674ce9f72d910ae80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 21 Jan 2021 17:49:03 +0100 Subject: [PATCH 21/52] update url in setup.py and add version classifiers --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 71524f6..1cf58c2 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ def readme(fname): author_email="pauloa.herrera@gmail.com", maintainer="Adamos Kyriakou", maintainer_email="somada141@gmail.com", - url="https://github.com/paulo-herrera/PyEVTK.git", + url="https://github.com/pyscience-projects/pyevtk", packages=["pyevtk", "evtk"], package_dir={"pyevtk": "pyevtk"}, package_data={"pyevtk": ["LICENSE.txt", "examples/*.py"]}, @@ -53,4 +53,8 @@ def readme(fname): # necessary for 'python setup.py test' setup_requires=["pytest-runner"], tests_require=["pytest>=3.1", "pytest-cov", "twine", "check-manifest"], + classifiers=['Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9',], ) From 2631d1236539756e75d26c4f061c1b630a540221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 21 Jan 2021 17:49:35 +0100 Subject: [PATCH 22/52] switches ci from travis to github actions --- .github/workflows/test.yml | 41 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 31 ---------------------------- requirements.txt | 1 + 3 files changed, 42 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..c6c3462 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,41 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Test Python + +on: + pull_request: + push: + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + + env: + SDIST_DIR: /tmp/sdist + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Test with pytest + run: | + python setup.py test + codecov + - name: Test with pytest + run: | + python setup.py bdist_wheel + python setup.py sdist -d ${SDIST_DIR}/ --format=gztar + twine check ${SDIST_DIR}/* + check-manifest -p python ${PWD} + cd ${SDIST_DIR} + pip install $(ls ${SDIST_DIR}) diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 003d14c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: python -dist: bionic - -python: - # EOL 2020-09-13 - - "3.5" - # EOL 2021-12-23 - - "3.6" - # EOL 2023-06-27 - - "3.7" - # EOL 2024-10 - - "3.8" - -install: - - pip install -r requirements.txt - -env: - - SDIST_DIR=/tmp/pyevtk_sdist/ - -before_script: - - pip install -U pip - -script: - - python setup.py test - - codecov - - python setup.py bdist_wheel - - python setup.py sdist -d ${SDIST_DIR}/ --format=gztar - - twine check ${SDIST_DIR}/* - - check-manifest -p python ${PWD} - - cd ${SDIST_DIR} - - pip install $(ls ${SDIST_DIR}) diff --git a/requirements.txt b/requirements.txt index e9106a4..0f25f38 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ pytest >= 3.1 pytest-cov numpy +wheel codecov twine check-manifest From 18084aca01828d71bf610c6617223061bc267877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 21 Jan 2021 18:06:00 +0100 Subject: [PATCH 23/52] [ci] add {test,}pypi upload --- .github/workflows/deploy.yml | 31 +++++++++++++++++++++++++++++++ .github/workflows/test_deploy.yml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/test_deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..fbb18dd --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,31 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml new file mode 100644 index 0000000..e7e2e92 --- /dev/null +++ b/.github/workflows/test_deploy.yml @@ -0,0 +1,31 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +name: Upload Python Package + +on: + push: + branches: [ master ] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.TEST_PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} + run: | + python setup.py sdist bdist_wheel + twine upload --repository testpypi dist/* From d7585ec06db0af0527c613b60bb0d2865f5178c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 21 Jan 2021 18:11:21 +0100 Subject: [PATCH 24/52] update gitignore for test artefacts --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 3a431f5..5e6063c 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,10 @@ ENV/ # pycharm .idea + +# test artefacts +*.vtu +*.vts +*.vtr +*.pvd +*.vti From 8f968ed885b3b5de7842034b2efdb4786a09ff1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 22 Jan 2021 15:51:52 +0100 Subject: [PATCH 25/52] [ci] switch to pypi gh action + add GH release draft on tag --- .github/workflows/deploy.yml | 43 ++++++++++++++++++++++--------- .github/workflows/test_deploy.yml | 17 ++++++------ 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fbb18dd..77472c4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,11 +4,12 @@ name: Upload Python Package on: - release: - types: [created] + push: + tags: + - "v*" jobs: - deploy: + pypi: runs-on: ubuntu-latest @@ -18,14 +19,32 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.x' - - name: Install dependencies + - name: Build dist artefacts run: | python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - run: | - python setup.py sdist bdist_wheel - twine upload dist/* + pip install setuptools wheel + python -m build --sdist --wheel --outdir dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{ secrets.PYPI_TOKEN }} + github: + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + body: | + Changes in this Release + - First Change + - Second Change + draft: true + prerelease: false diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index e7e2e92..cede9c6 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -18,14 +18,13 @@ jobs: uses: actions/setup-python@v2 with: python-version: '3.x' - - name: Install dependencies + - name: Build dist artefacts run: | python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.TEST_PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }} - run: | - python setup.py sdist bdist_wheel - twine upload --repository testpypi dist/* + pip install setuptools wheel + python -m build --sdist --wheel --outdir dist/ + - name: Publish distribution 📦 to Test PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{ secrets.TEST_PYPI_TOKEN }} + repository_url: https://test.pypi.org/legacy/ From 7fe597c8f3679f93aaa9c0bdc5f8d7b1cb1f0090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 22 Jan 2021 16:04:54 +0100 Subject: [PATCH 26/52] Mark v1.2.0 release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1cf58c2..2b7855c 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ def readme(fname): setup( name="pyevtk", - version="1.1.2", + version="1.2.0", description="Export data as binary VTK files", long_description=readme("README.md"), long_description_content_type="text/markdown", From df8cbb2e5b53412f18799999883fa96be64f53c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 22 Jan 2021 16:19:53 +0100 Subject: [PATCH 27/52] [ci] add missing build pacakge in actions --- .github/workflows/deploy.yml | 2 +- .github/workflows/test_deploy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 77472c4..1b86bde 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,7 +22,7 @@ jobs: - name: Build dist artefacts run: | python -m pip install --upgrade pip - pip install setuptools wheel + pip install build setuptools wheel python -m build --sdist --wheel --outdir dist/ - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@master diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index cede9c6..85ad068 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -21,7 +21,7 @@ jobs: - name: Build dist artefacts run: | python -m pip install --upgrade pip - pip install setuptools wheel + pip install build setuptools wheel python -m build --sdist --wheel --outdir dist/ - name: Publish distribution 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@master From a7bda4016763f06b4d7002aae9c909c3465bb3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 22 Jan 2021 15:56:16 +0100 Subject: [PATCH 28/52] [readme] switch build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 312bc5c..adfe031 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Coverage Status](https://codecov.io/gh/pyscience-projects/pyevtk/branch/master/graph/badge.svg)](https://codecov.io/gh/pyscience-projects/pyevtk) -[![Build Status](https://travis-ci.org/pyscience-projects/pyevtk.png?branch=master)](https://travis-ci.org/pyscience-projects/pyevtk) +[![Build Status](https://github.com/pyscience-projects/pyevtk/workflows/Test%20Python/badge.svg)](https://github.com/pyscience-projects/pyevtk/actions?query=workflow%3A%22Test+Python%22) PREAMBLE: ========= From 8fe2d5d749f1f0f6d6c372c83f92c9e5da818b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Tue, 16 Feb 2021 16:48:30 +0100 Subject: [PATCH 29/52] [ci] try to fix deploy from GH workflow --- .github/workflows/deploy.yml | 1 + .github/workflows/test_deploy.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1b86bde..41fbcd0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,6 +27,7 @@ jobs: - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@master with: + user: __token__ password: ${{ secrets.PYPI_TOKEN }} github: name: Create Release diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index 85ad068..7b02611 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -26,5 +26,6 @@ jobs: - name: Publish distribution 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@master with: + user: __token__ password: ${{ secrets.TEST_PYPI_TOKEN }} repository_url: https://test.pypi.org/legacy/ From a1651d8b7508fe001fffdf8a595b3c1c2cd6dbde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Tue, 16 Feb 2021 16:59:37 +0100 Subject: [PATCH 30/52] [ci] test deploy on all pushes --- .github/workflows/test_deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index 7b02611..903a59c 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -5,7 +5,7 @@ name: Upload Python Package on: push: - branches: [ master ] + branches: jobs: deploy: From 571426c4272a93c756b6aae0b8b27bed5cbde160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Wed, 17 Feb 2021 15:34:12 +0100 Subject: [PATCH 31/52] switch from manual versioning to using versioneer --- .gitattributes | 1 + .github/workflows/test_deploy.yml | 4 +- MANIFEST.in | 2 + pyevtk/__init__.py | 4 + pyevtk/_version.py | 525 ++++++++ setup.cfg | 12 + setup.py | 4 +- versioneer.py | 1855 +++++++++++++++++++++++++++++ 8 files changed, 2403 insertions(+), 4 deletions(-) create mode 100644 .gitattributes create mode 100644 pyevtk/_version.py create mode 100644 versioneer.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e3e670a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +pyevtk/_version.py export-subst diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index 903a59c..5747ada 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -3,9 +3,7 @@ name: Upload Python Package -on: - push: - branches: +on: [pull_request] jobs: deploy: diff --git a/MANIFEST.in b/MANIFEST.in index c898e78..a45bc29 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,3 +3,5 @@ include *.md include MANIFEST.in recursive-include examples *.py recursive-include tests *.py +include versioneer.py +include pyevtk/_version.py diff --git a/pyevtk/__init__.py b/pyevtk/__init__.py index ef7b834..3545368 100644 --- a/pyevtk/__init__.py +++ b/pyevtk/__init__.py @@ -21,3 +21,7 @@ from . import hl from . import vtk from . import xml + +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions diff --git a/pyevtk/_version.py b/pyevtk/_version.py new file mode 100644 index 0000000..7de3e0f --- /dev/null +++ b/pyevtk/_version.py @@ -0,0 +1,525 @@ + +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "$Format:%d$" + git_full = "$Format:%H$" + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "pep440-pre" + cfg.tag_prefix = "" + cfg.parentdir_prefix = "" + cfg.versionfile_source = "pyevtk/_version.py" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %s but none started with prefix %s" % + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %s" % r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", + "--always", "--long", + "--match", "%s*" % tag_prefix], + cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" + % (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], + cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post0.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post0.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post0.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} diff --git a/setup.cfg b/setup.cfg index 995a861..0960662 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,3 +26,15 @@ python_class = Test pep8maxlinelength = 120 pep8ignore = E221,E226,E241,E242 addopts= --cov pyevtk + +# See the docstring in versioneer.py for instructions. Note that you must +# re-run 'versioneer.py setup' after changing this section, and commit the +# resulting files. + +[versioneer] +VCS = git +style = pep440-pre +versionfile_source = pyevtk/_version.py +versionfile_build = pyevtk/_version.py +tag_prefix = +parentdir_prefix = diff --git a/setup.py b/setup.py index 2b7855c..d27ecbc 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ except ImportError: from distutils.core import setup +import versioneer def readme(fname): """Open the readme file.""" @@ -37,7 +38,8 @@ def readme(fname): setup( name="pyevtk", - version="1.2.0", + version=versioneer.get_version(), + cmdclass=versioneer.get_cmdclass(), description="Export data as binary VTK files", long_description=readme("README.md"), long_description_content_type="text/markdown", diff --git a/versioneer.py b/versioneer.py new file mode 100644 index 0000000..1040c21 --- /dev/null +++ b/versioneer.py @@ -0,0 +1,1855 @@ + +# Version: 0.19 + +"""The Versioneer - like a rocketeer, but for versions. + +The Versioneer +============== + +* like a rocketeer, but for versions! +* https://github.com/python-versioneer/python-versioneer +* Brian Warner +* License: Public Domain +* Compatible with: Python 3.6, 3.7, 3.8, 3.9 and pypy3 +* [![Latest Version][pypi-image]][pypi-url] +* [![Build Status][travis-image]][travis-url] + +This is a tool for managing a recorded version number in distutils-based +python projects. The goal is to remove the tedious and error-prone "update +the embedded version string" step from your release process. Making a new +release should be as easy as recording a new tag in your version-control +system, and maybe making new tarballs. + + +## Quick Install + +* `pip install versioneer` to somewhere in your $PATH +* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md)) +* run `versioneer install` in your source tree, commit the results +* Verify version information with `python setup.py version` + +## Version Identifiers + +Source trees come from a variety of places: + +* a version-control system checkout (mostly used by developers) +* a nightly tarball, produced by build automation +* a snapshot tarball, produced by a web-based VCS browser, like github's + "tarball from tag" feature +* a release tarball, produced by "setup.py sdist", distributed through PyPI + +Within each source tree, the version identifier (either a string or a number, +this tool is format-agnostic) can come from a variety of places: + +* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows + about recent "tags" and an absolute revision-id +* the name of the directory into which the tarball was unpacked +* an expanded VCS keyword ($Id$, etc) +* a `_version.py` created by some earlier build step + +For released software, the version identifier is closely related to a VCS +tag. Some projects use tag names that include more than just the version +string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool +needs to strip the tag prefix to extract the version identifier. For +unreleased software (between tags), the version identifier should provide +enough information to help developers recreate the same tree, while also +giving them an idea of roughly how old the tree is (after version 1.2, before +version 1.3). Many VCS systems can report a description that captures this, +for example `git describe --tags --dirty --always` reports things like +"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the +0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has +uncommitted changes). + +The version identifier is used for multiple purposes: + +* to allow the module to self-identify its version: `myproject.__version__` +* to choose a name and prefix for a 'setup.py sdist' tarball + +## Theory of Operation + +Versioneer works by adding a special `_version.py` file into your source +tree, where your `__init__.py` can import it. This `_version.py` knows how to +dynamically ask the VCS tool for version information at import time. + +`_version.py` also contains `$Revision$` markers, and the installation +process marks `_version.py` to have this marker rewritten with a tag name +during the `git archive` command. As a result, generated tarballs will +contain enough information to get the proper version. + +To allow `setup.py` to compute a version too, a `versioneer.py` is added to +the top level of your source tree, next to `setup.py` and the `setup.cfg` +that configures it. This overrides several distutils/setuptools commands to +compute the version when invoked, and changes `setup.py build` and `setup.py +sdist` to replace `_version.py` with a small static file that contains just +the generated version data. + +## Installation + +See [INSTALL.md](./INSTALL.md) for detailed installation instructions. + +## Version-String Flavors + +Code which uses Versioneer can learn about its version string at runtime by +importing `_version` from your main `__init__.py` file and running the +`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can +import the top-level `versioneer.py` and run `get_versions()`. + +Both functions return a dictionary with different flavors of version +information: + +* `['version']`: A condensed version string, rendered using the selected + style. This is the most commonly used value for the project's version + string. The default "pep440" style yields strings like `0.11`, + `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section + below for alternative styles. + +* `['full-revisionid']`: detailed revision identifier. For Git, this is the + full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". + +* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the + commit date in ISO 8601 format. This will be None if the date is not + available. + +* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that + this is only accurate if run in a VCS checkout, otherwise it is likely to + be False or None + +* `['error']`: if the version string could not be computed, this will be set + to a string describing the problem, otherwise it will be None. It may be + useful to throw an exception in setup.py if this is set, to avoid e.g. + creating tarballs with a version string of "unknown". + +Some variants are more useful than others. Including `full-revisionid` in a +bug report should allow developers to reconstruct the exact code being tested +(or indicate the presence of local changes that should be shared with the +developers). `version` is suitable for display in an "about" box or a CLI +`--version` output: it can be easily compared against release notes and lists +of bugs fixed in various releases. + +The installer adds the following text to your `__init__.py` to place a basic +version in `YOURPROJECT.__version__`: + + from ._version import get_versions + __version__ = get_versions()['version'] + del get_versions + +## Styles + +The setup.cfg `style=` configuration controls how the VCS information is +rendered into a version string. + +The default style, "pep440", produces a PEP440-compliant string, equal to the +un-prefixed tag name for actual releases, and containing an additional "local +version" section with more detail for in-between builds. For Git, this is +TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags +--dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the +tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and +that this commit is two revisions ("+2") beyond the "0.11" tag. For released +software (exactly equal to a known tag), the identifier will only contain the +stripped tag, e.g. "0.11". + +Other styles are available. See [details.md](details.md) in the Versioneer +source tree for descriptions. + +## Debugging + +Versioneer tries to avoid fatal errors: if something goes wrong, it will tend +to return a version of "0+unknown". To investigate the problem, run `setup.py +version`, which will run the version-lookup code in a verbose mode, and will +display the full contents of `get_versions()` (including the `error` string, +which may help identify what went wrong). + +## Known Limitations + +Some situations are known to cause problems for Versioneer. This details the +most significant ones. More can be found on Github +[issues page](https://github.com/python-versioneer/python-versioneer/issues). + +### Subprojects + +Versioneer has limited support for source trees in which `setup.py` is not in +the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are +two common reasons why `setup.py` might not be in the root: + +* Source trees which contain multiple subprojects, such as + [Buildbot](https://github.com/buildbot/buildbot), which contains both + "master" and "slave" subprojects, each with their own `setup.py`, + `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI + distributions (and upload multiple independently-installable tarballs). +* Source trees whose main purpose is to contain a C library, but which also + provide bindings to Python (and perhaps other languages) in subdirectories. + +Versioneer will look for `.git` in parent directories, and most operations +should get the right version string. However `pip` and `setuptools` have bugs +and implementation details which frequently cause `pip install .` from a +subproject directory to fail to find a correct version string (so it usually +defaults to `0+unknown`). + +`pip install --editable .` should work correctly. `setup.py install` might +work too. + +Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in +some later version. + +[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking +this issue. The discussion in +[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the +issue from the Versioneer side in more detail. +[pip PR#3176](https://github.com/pypa/pip/pull/3176) and +[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve +pip to let Versioneer work correctly. + +Versioneer-0.16 and earlier only looked for a `.git` directory next to the +`setup.cfg`, so subprojects were completely unsupported with those releases. + +### Editable installs with setuptools <= 18.5 + +`setup.py develop` and `pip install --editable .` allow you to install a +project into a virtualenv once, then continue editing the source code (and +test) without re-installing after every change. + +"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a +convenient way to specify executable scripts that should be installed along +with the python package. + +These both work as expected when using modern setuptools. When using +setuptools-18.5 or earlier, however, certain operations will cause +`pkg_resources.DistributionNotFound` errors when running the entrypoint +script, which must be resolved by re-installing the package. This happens +when the install happens with one version, then the egg_info data is +regenerated while a different version is checked out. Many setup.py commands +cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into +a different virtualenv), so this can be surprising. + +[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes +this one, but upgrading to a newer version of setuptools should probably +resolve it. + + +## Updating Versioneer + +To upgrade your project to a new release of Versioneer, do the following: + +* install the new Versioneer (`pip install -U versioneer` or equivalent) +* edit `setup.cfg`, if necessary, to include any new configuration settings + indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. +* re-run `versioneer install` in your source tree, to replace + `SRC/_version.py` +* commit any changed files + +## Future Directions + +This tool is designed to make it easily extended to other version-control +systems: all VCS-specific components are in separate directories like +src/git/ . The top-level `versioneer.py` script is assembled from these +components by running make-versioneer.py . In the future, make-versioneer.py +will take a VCS name as an argument, and will construct a version of +`versioneer.py` that is specific to the given VCS. It might also take the +configuration arguments that are currently provided manually during +installation by editing setup.py . Alternatively, it might go the other +direction and include code from all supported VCS systems, reducing the +number of intermediate scripts. + +## Similar projects + +* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time + dependency +* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of + versioneer + +## License + +To make Versioneer easier to embed, all its code is dedicated to the public +domain. The `_version.py` that it creates is also in the public domain. +Specifically, both are released under the Creative Commons "Public Domain +Dedication" license (CC0-1.0), as described in +https://creativecommons.org/publicdomain/zero/1.0/ . + +[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg +[pypi-url]: https://pypi.python.org/pypi/versioneer/ +[travis-image]: +https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg +[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer + +""" + +import configparser +import errno +import json +import os +import re +import subprocess +import sys + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_root(): + """Get the project root directory. + + We require that all commands are run from the project root, i.e. the + directory that contains setup.py, setup.cfg, and versioneer.py . + """ + root = os.path.realpath(os.path.abspath(os.getcwd())) + setup_py = os.path.join(root, "setup.py") + versioneer_py = os.path.join(root, "versioneer.py") + if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): + # allow 'python path/to/setup.py COMMAND' + root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) + setup_py = os.path.join(root, "setup.py") + versioneer_py = os.path.join(root, "versioneer.py") + if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): + err = ("Versioneer was unable to run the project root directory. " + "Versioneer requires setup.py to be executed from " + "its immediate directory (like 'python setup.py COMMAND'), " + "or in a way that lets it use sys.argv[0] to find the root " + "(like 'python path/to/setup.py COMMAND').") + raise VersioneerBadRootError(err) + try: + # Certain runtime workflows (setup.py install/develop in a setuptools + # tree) execute all dependencies in a single python process, so + # "versioneer" may be imported multiple times, and python's shared + # module-import table will cache the first one. So we can't use + # os.path.dirname(__file__), as that will find whichever + # versioneer.py was first imported, even in later projects. + me = os.path.realpath(os.path.abspath(__file__)) + me_dir = os.path.normcase(os.path.splitext(me)[0]) + vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) + if me_dir != vsr_dir: + print("Warning: build in %s is using versioneer.py from %s" + % (os.path.dirname(me), versioneer_py)) + except NameError: + pass + return root + + +def get_config_from_root(root): + """Read the project setup.cfg file to determine Versioneer config.""" + # This might raise EnvironmentError (if setup.cfg is missing), or + # configparser.NoSectionError (if it lacks a [versioneer] section), or + # configparser.NoOptionError (if it lacks "VCS="). See the docstring at + # the top of versioneer.py for instructions on writing your setup.cfg . + setup_cfg = os.path.join(root, "setup.cfg") + parser = configparser.ConfigParser() + with open(setup_cfg, "r") as f: + parser.read_file(f) + VCS = parser.get("versioneer", "VCS") # mandatory + + def get(parser, name): + if parser.has_option("versioneer", name): + return parser.get("versioneer", name) + return None + cfg = VersioneerConfig() + cfg.VCS = VCS + cfg.style = get(parser, "style") or "" + cfg.versionfile_source = get(parser, "versionfile_source") + cfg.versionfile_build = get(parser, "versionfile_build") + cfg.tag_prefix = get(parser, "tag_prefix") + if cfg.tag_prefix in ("''", '""'): + cfg.tag_prefix = "" + cfg.parentdir_prefix = get(parser, "parentdir_prefix") + cfg.verbose = get(parser, "verbose") + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +# these dictionaries contain VCS-specific tools +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +LONG_VERSION_PY['git'] = r''' +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" + git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" + git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "%(STYLE)s" + cfg.tag_prefix = "%(TAG_PREFIX)s" + cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" + cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + try: + dispcmd = str([c] + args) + # remember shell=False, so use git.cmd on windows, not just git + p = subprocess.Popen([c] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %%s" %% dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %%s" %% (commands,)) + return None, None + stdout = p.communicate()[0].strip().decode() + if p.returncode != 0: + if verbose: + print("unable to run %%s (error)" %% dispcmd) + print("stdout was %%s" %% stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %%s but none started with prefix %%s" %% + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %%d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%%s', no digits" %% ",".join(refs - tags)) + if verbose: + print("likely tags: %%s" %% ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %%s" %% r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %%s not under git control" %% root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", + "--always", "--long", + "--match", "%%s*" %% tag_prefix], + cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%%s'" + %% describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%%s' doesn't start with prefix '%%s'" + print(fmt %% (full_tag, tag_prefix)) + pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" + %% (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"], + cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post0.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post0.dev%%d" %% pieces["distance"] + else: + # exception #1 + rendered = "0.post0.dev%%d" %% pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%%s" %% pieces["short"] + else: + # exception #1 + rendered = "0.post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%%s" %% pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%%s'" %% style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} +''' + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = set([r for r in refs if re.search(r'\d', r)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + if verbose: + print("picking %s" % r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", + "--always", "--long", + "--match", "%s*" % tag_prefix], + cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" + % (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], + cwd=root) + pieces["distance"] = int(count_out) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], + cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def do_vcs_install(manifest_in, versionfile_source, ipy): + """Git-specific installation logic for Versioneer. + + For Git, this means creating/changing .gitattributes to mark _version.py + for export-subst keyword substitution. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + files = [manifest_in, versionfile_source] + if ipy: + files.append(ipy) + try: + me = __file__ + if me.endswith(".pyc") or me.endswith(".pyo"): + me = os.path.splitext(me)[0] + ".py" + versioneer_file = os.path.relpath(me) + except NameError: + versioneer_file = "versioneer.py" + files.append(versioneer_file) + present = False + try: + f = open(".gitattributes", "r") + for line in f.readlines(): + if line.strip().startswith(versionfile_source): + if "export-subst" in line.strip().split()[1:]: + present = True + f.close() + except EnvironmentError: + pass + if not present: + f = open(".gitattributes", "a+") + f.write("%s export-subst\n" % versionfile_source) + f.close() + files.append(".gitattributes") + run_command(GITS, ["add", "--"] + files) + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for i in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + else: + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %s but none started with prefix %s" % + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +SHORT_VERSION_PY = """ +# This file was generated by 'versioneer.py' (0.19) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json + +version_json = ''' +%s +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) +""" + + +def versions_from_file(filename): + """Try to determine the version from _version.py if present.""" + try: + with open(filename) as f: + contents = f.read() + except EnvironmentError: + raise NotThisMethod("unable to read _version.py") + mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", + contents, re.M | re.S) + if not mo: + mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", + contents, re.M | re.S) + if not mo: + raise NotThisMethod("no version_json in _version.py") + return json.loads(mo.group(1)) + + +def write_to_version_file(filename, versions): + """Write the given version number to the given _version.py file.""" + os.unlink(filename) + contents = json.dumps(versions, sort_keys=True, + indent=1, separators=(",", ": ")) + with open(filename, "w") as f: + f.write(SHORT_VERSION_PY % contents) + + print("set %s to '%s'" % (filename, versions["version"])) + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post0.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post0.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post0.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +class VersioneerBadRootError(Exception): + """The project root directory is unknown or missing key files.""" + + +def get_versions(verbose=False): + """Get the project version from whatever source is available. + + Returns dict with two keys: 'version' and 'full'. + """ + if "versioneer" in sys.modules: + # see the discussion in cmdclass.py:get_cmdclass() + del sys.modules["versioneer"] + + root = get_root() + cfg = get_config_from_root(root) + + assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" + handlers = HANDLERS.get(cfg.VCS) + assert handlers, "unrecognized VCS '%s'" % cfg.VCS + verbose = verbose or cfg.verbose + assert cfg.versionfile_source is not None, \ + "please set versioneer.versionfile_source" + assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" + + versionfile_abs = os.path.join(root, cfg.versionfile_source) + + # extract version from first of: _version.py, VCS command (e.g. 'git + # describe'), parentdir. This is meant to work for developers using a + # source checkout, for users of a tarball created by 'setup.py sdist', + # and for users of a tarball/zipball created by 'git archive' or github's + # download-from-tag feature or the equivalent in other VCSes. + + get_keywords_f = handlers.get("get_keywords") + from_keywords_f = handlers.get("keywords") + if get_keywords_f and from_keywords_f: + try: + keywords = get_keywords_f(versionfile_abs) + ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) + if verbose: + print("got version from expanded keyword %s" % ver) + return ver + except NotThisMethod: + pass + + try: + ver = versions_from_file(versionfile_abs) + if verbose: + print("got version from file %s %s" % (versionfile_abs, ver)) + return ver + except NotThisMethod: + pass + + from_vcs_f = handlers.get("pieces_from_vcs") + if from_vcs_f: + try: + pieces = from_vcs_f(cfg.tag_prefix, root, verbose) + ver = render(pieces, cfg.style) + if verbose: + print("got version from VCS %s" % ver) + return ver + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + if verbose: + print("got version from parentdir %s" % ver) + return ver + except NotThisMethod: + pass + + if verbose: + print("unable to compute version") + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, "error": "unable to compute version", + "date": None} + + +def get_version(): + """Get the short version string for this project.""" + return get_versions()["version"] + + +def get_cmdclass(cmdclass=None): + """Get the custom setuptools/distutils subclasses used by Versioneer. + + If the package uses a different cmdclass (e.g. one from numpy), it + should be provide as an argument. + """ + if "versioneer" in sys.modules: + del sys.modules["versioneer"] + # this fixes the "python setup.py develop" case (also 'install' and + # 'easy_install .'), in which subdependencies of the main project are + # built (using setup.py bdist_egg) in the same python process. Assume + # a main project A and a dependency B, which use different versions + # of Versioneer. A's setup.py imports A's Versioneer, leaving it in + # sys.modules by the time B's setup.py is executed, causing B to run + # with the wrong versioneer. Setuptools wraps the sub-dep builds in a + # sandbox that restores sys.modules to it's pre-build state, so the + # parent is protected against the child's "import versioneer". By + # removing ourselves from sys.modules here, before the child build + # happens, we protect the child from the parent's versioneer too. + # Also see https://github.com/python-versioneer/python-versioneer/issues/52 + + cmds = {} if cmdclass is None else cmdclass.copy() + + # we add "version" to both distutils and setuptools + from distutils.core import Command + + class cmd_version(Command): + description = "report generated version string" + user_options = [] + boolean_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + vers = get_versions(verbose=True) + print("Version: %s" % vers["version"]) + print(" full-revisionid: %s" % vers.get("full-revisionid")) + print(" dirty: %s" % vers.get("dirty")) + print(" date: %s" % vers.get("date")) + if vers["error"]: + print(" error: %s" % vers["error"]) + cmds["version"] = cmd_version + + # we override "build_py" in both distutils and setuptools + # + # most invocation pathways end up running build_py: + # distutils/build -> build_py + # distutils/install -> distutils/build ->.. + # setuptools/bdist_wheel -> distutils/install ->.. + # setuptools/bdist_egg -> distutils/install_lib -> build_py + # setuptools/install -> bdist_egg ->.. + # setuptools/develop -> ? + # pip install: + # copies source tree to a tempdir before running egg_info/etc + # if .git isn't copied too, 'git describe' will fail + # then does setup.py bdist_wheel, or sometimes setup.py install + # setup.py egg_info -> ? + + # we override different "build_py" commands for both environments + if 'build_py' in cmds: + _build_py = cmds['build_py'] + elif "setuptools" in sys.modules: + from setuptools.command.build_py import build_py as _build_py + else: + from distutils.command.build_py import build_py as _build_py + + class cmd_build_py(_build_py): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + _build_py.run(self) + # now locate _version.py in the new build/ directory and replace + # it with an updated value + if cfg.versionfile_build: + target_versionfile = os.path.join(self.build_lib, + cfg.versionfile_build) + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + cmds["build_py"] = cmd_build_py + + if "setuptools" in sys.modules: + from setuptools.command.build_ext import build_ext as _build_ext + else: + from distutils.command.build_ext import build_ext as _build_ext + + class cmd_build_ext(_build_ext): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + _build_ext.run(self) + if self.inplace: + # build_ext --inplace will only build extensions in + # build/lib<..> dir with no _version.py to write to. + # As in place builds will already have a _version.py + # in the module dir, we do not need to write one. + return + # now locate _version.py in the new build/ directory and replace + # it with an updated value + target_versionfile = os.path.join(self.build_lib, + cfg.versionfile_source) + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + cmds["build_ext"] = cmd_build_ext + + if "cx_Freeze" in sys.modules: # cx_freeze enabled? + from cx_Freeze.dist import build_exe as _build_exe + # nczeczulin reports that py2exe won't like the pep440-style string + # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. + # setup(console=[{ + # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION + # "product_version": versioneer.get_version(), + # ... + + class cmd_build_exe(_build_exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _build_exe.run(self) + os.unlink(target_versionfile) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write(LONG % + {"DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + }) + cmds["build_exe"] = cmd_build_exe + del cmds["build_py"] + + if 'py2exe' in sys.modules: # py2exe enabled? + from py2exe.distutils_buildexe import py2exe as _py2exe + + class cmd_py2exe(_py2exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _py2exe.run(self) + os.unlink(target_versionfile) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write(LONG % + {"DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + }) + cmds["py2exe"] = cmd_py2exe + + # we override different "sdist" commands for both environments + if 'sdist' in cmds: + _sdist = cmds['sdist'] + elif "setuptools" in sys.modules: + from setuptools.command.sdist import sdist as _sdist + else: + from distutils.command.sdist import sdist as _sdist + + class cmd_sdist(_sdist): + def run(self): + versions = get_versions() + self._versioneer_generated_versions = versions + # unless we update this, the command will keep using the old + # version + self.distribution.metadata.version = versions["version"] + return _sdist.run(self) + + def make_release_tree(self, base_dir, files): + root = get_root() + cfg = get_config_from_root(root) + _sdist.make_release_tree(self, base_dir, files) + # now locate _version.py in the new base_dir directory + # (remembering that it may be a hardlink) and replace it with an + # updated value + target_versionfile = os.path.join(base_dir, cfg.versionfile_source) + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, + self._versioneer_generated_versions) + cmds["sdist"] = cmd_sdist + + return cmds + + +CONFIG_ERROR = """ +setup.cfg is missing the necessary Versioneer configuration. You need +a section like: + + [versioneer] + VCS = git + style = pep440 + versionfile_source = src/myproject/_version.py + versionfile_build = myproject/_version.py + tag_prefix = + parentdir_prefix = myproject- + +You will also need to edit your setup.py to use the results: + + import versioneer + setup(version=versioneer.get_version(), + cmdclass=versioneer.get_cmdclass(), ...) + +Please read the docstring in ./versioneer.py for configuration instructions, +edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. +""" + +SAMPLE_CONFIG = """ +# See the docstring in versioneer.py for instructions. Note that you must +# re-run 'versioneer.py setup' after changing this section, and commit the +# resulting files. + +[versioneer] +#VCS = git +#style = pep440 +#versionfile_source = +#versionfile_build = +#tag_prefix = +#parentdir_prefix = + +""" + +INIT_PY_SNIPPET = """ +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions +""" + + +def do_setup(): + """Do main VCS-independent setup function for installing Versioneer.""" + root = get_root() + try: + cfg = get_config_from_root(root) + except (EnvironmentError, configparser.NoSectionError, + configparser.NoOptionError) as e: + if isinstance(e, (EnvironmentError, configparser.NoSectionError)): + print("Adding sample versioneer config to setup.cfg", + file=sys.stderr) + with open(os.path.join(root, "setup.cfg"), "a") as f: + f.write(SAMPLE_CONFIG) + print(CONFIG_ERROR, file=sys.stderr) + return 1 + + print(" creating %s" % cfg.versionfile_source) + with open(cfg.versionfile_source, "w") as f: + LONG = LONG_VERSION_PY[cfg.VCS] + f.write(LONG % {"DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + }) + + ipy = os.path.join(os.path.dirname(cfg.versionfile_source), + "__init__.py") + if os.path.exists(ipy): + try: + with open(ipy, "r") as f: + old = f.read() + except EnvironmentError: + old = "" + if INIT_PY_SNIPPET not in old: + print(" appending to %s" % ipy) + with open(ipy, "a") as f: + f.write(INIT_PY_SNIPPET) + else: + print(" %s unmodified" % ipy) + else: + print(" %s doesn't exist, ok" % ipy) + ipy = None + + # Make sure both the top-level "versioneer.py" and versionfile_source + # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so + # they'll be copied into source distributions. Pip won't be able to + # install the package without this. + manifest_in = os.path.join(root, "MANIFEST.in") + simple_includes = set() + try: + with open(manifest_in, "r") as f: + for line in f: + if line.startswith("include "): + for include in line.split()[1:]: + simple_includes.add(include) + except EnvironmentError: + pass + # That doesn't cover everything MANIFEST.in can do + # (http://docs.python.org/2/distutils/sourcedist.html#commands), so + # it might give some false negatives. Appending redundant 'include' + # lines is safe, though. + if "versioneer.py" not in simple_includes: + print(" appending 'versioneer.py' to MANIFEST.in") + with open(manifest_in, "a") as f: + f.write("include versioneer.py\n") + else: + print(" 'versioneer.py' already in MANIFEST.in") + if cfg.versionfile_source not in simple_includes: + print(" appending versionfile_source ('%s') to MANIFEST.in" % + cfg.versionfile_source) + with open(manifest_in, "a") as f: + f.write("include %s\n" % cfg.versionfile_source) + else: + print(" versionfile_source already in MANIFEST.in") + + # Make VCS-specific changes. For git, this means creating/changing + # .gitattributes to mark _version.py for export-subst keyword + # substitution. + do_vcs_install(manifest_in, cfg.versionfile_source, ipy) + return 0 + + +def scan_setup_py(): + """Validate the contents of setup.py against Versioneer's expectations.""" + found = set() + setters = False + errors = 0 + with open("setup.py", "r") as f: + for line in f.readlines(): + if "import versioneer" in line: + found.add("import") + if "versioneer.get_cmdclass()" in line: + found.add("cmdclass") + if "versioneer.get_version()" in line: + found.add("get_version") + if "versioneer.VCS" in line: + setters = True + if "versioneer.versionfile_source" in line: + setters = True + if len(found) != 3: + print("") + print("Your setup.py appears to be missing some important items") + print("(but I might be wrong). Please make sure it has something") + print("roughly like the following:") + print("") + print(" import versioneer") + print(" setup( version=versioneer.get_version(),") + print(" cmdclass=versioneer.get_cmdclass(), ...)") + print("") + errors += 1 + if setters: + print("You should remove lines like 'versioneer.VCS = ' and") + print("'versioneer.versionfile_source = ' . This configuration") + print("now lives in setup.cfg, and should be removed from setup.py") + print("") + errors += 1 + return errors + + +if __name__ == "__main__": + cmd = sys.argv[1] + if cmd == "setup": + errors = do_setup() + errors += scan_setup_py() + if errors: + sys.exit(1) From f7b5b779826d306f85f698342f3d1ebf148b1ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 18 Feb 2021 13:50:33 +0100 Subject: [PATCH 32/52] [ci] fix versioneer version in test deploy workflow --- .github/workflows/test_deploy.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index 5747ada..5f63697 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -12,6 +12,9 @@ jobs: steps: - uses: actions/checkout@v2 + with: + # to guarantee proper git tag pickup for versioneer + fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v2 with: From 3d508de81d690b6abf5fe7aaa353c7951e9194c0 Mon Sep 17 00:00:00 2001 From: Moritz Heinemann Date: Tue, 22 Jun 2021 19:48:44 +0200 Subject: [PATCH 33/52] fix xml data attributes typo --- pyevtk/vtk.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 061e321..77a4055 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -387,15 +387,15 @@ def openData( """ self.xml.openElement(nodeType + "Data") if scalars: - self.xml.addAttributes(scalars=scalars) + self.xml.addAttributes(Scalars=scalars) if vectors: - self.xml.addAttributes(vectors=vectors) + self.xml.addAttributes(Vectors=vectors) if normals: - self.xml.addAttributes(normals=normals) + self.xml.addAttributes(Normals=normals) if tensors: - self.xml.addAttributes(tensors=tensors) + self.xml.addAttributes(Tensors=tensors) if tcoords: - self.xml.addAttributes(tcoords=tcoords) + self.xml.addAttributes(TCoords=tcoords) return self From fa50435cddf6aae6df9c32d9f25cd21059b9d209 Mon Sep 17 00:00:00 2001 From: Moritz Heinemann Date: Tue, 22 Jun 2021 19:56:28 +0200 Subject: [PATCH 34/52] set a valid scalar/vector data key as attribute --- pyevtk/hl.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 19f4264..4d6685d 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -45,7 +45,10 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): # Point data if pointData: keys = list(pointData.keys()) - vtkFile.openData("Point", scalars=keys[0]) + # find first scalar and vector data key to set it as attribute + scalars = next((key for key in keys if type(pointData[key]).__name__ != "tuple"), None) + vectors = next((key for key in keys if type(pointData[key]).__name__ == "tuple"), None) + vtkFile.openData("Point", scalars=scalars, vectors=vectors) for key in keys: data = pointData[key] vtkFile.addData(key, data) @@ -54,7 +57,10 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): # Cell data if cellData: keys = list(cellData.keys()) - vtkFile.openData("Cell", scalars=keys[0]) + # find first scalar and vector data key to set it as attribute + scalars = next((key for key in keys if type(cellData[key]).__name__ != "tuple"), None) + vectors = next((key for key in keys if type(cellData[key]).__name__ == "tuple"), None) + vtkFile.openData("Cell", scalars=scalars, vectors=vectors) for key in keys: data = cellData[key] vtkFile.addData(key, data) From 123185ce178d9c45675442711ad769aecbd1993b Mon Sep 17 00:00:00 2001 From: Moritz Heinemann Date: Mon, 28 Jun 2021 14:30:07 +0200 Subject: [PATCH 35/52] update type checks --- pyevtk/hl.py | 8 ++++---- pyevtk/vtk.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 4d6685d..3786744 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -46,8 +46,8 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): if pointData: keys = list(pointData.keys()) # find first scalar and vector data key to set it as attribute - scalars = next((key for key in keys if type(pointData[key]).__name__ != "tuple"), None) - vectors = next((key for key in keys if type(pointData[key]).__name__ == "tuple"), None) + scalars = next((key for key in keys if isinstance(pointData[key], np.ndarray)), None) + vectors = next((key for key in keys if isinstance(pointData[key], tuple)), None) vtkFile.openData("Point", scalars=scalars, vectors=vectors) for key in keys: data = pointData[key] @@ -58,8 +58,8 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): if cellData: keys = list(cellData.keys()) # find first scalar and vector data key to set it as attribute - scalars = next((key for key in keys if type(cellData[key]).__name__ != "tuple"), None) - vectors = next((key for key in keys if type(cellData[key]).__name__ == "tuple"), None) + scalars = next((key for key in keys if isinstance(cellData[key], np.ndarray)), None) + vectors = next((key for key in keys if isinstance(cellData[key], tuple)), None) vtkFile.openData("Cell", scalars=scalars, vectors=vectors) for key in keys: data = cellData[key] diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 77a4055..376d73e 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -26,6 +26,7 @@ import sys import os +import numpy as np from .evtk import writeBlockSize, writeArrayToFile, writeArraysToFile from .xml import XmlWriter @@ -530,11 +531,11 @@ def addData(self, name, data): of a vector field. All arrays must be one dimensional or three-dimensional. """ - if type(data).__name__ == "tuple": # vector data + if isinstance(data, tuple): # vector data assert len(data) == 3 x = data[0] self.addHeader(name, x.dtype.name, x.size, 3) - elif type(data).__name__ == "ndarray": + elif isinstance(data, np.ndarray): if data.ndim == 1 or data.ndim == 3: self.addHeader(name, data.dtype.name, data.size, 1) else: @@ -593,7 +594,7 @@ def appendData(self, data): # TODO: Extend this function to accept contiguous C order arrays. self.openAppendedData() - if type(data).__name__ == "tuple": # 3 numpy arrays + if isinstance(data, tuple): # 3 numpy arrays ncomp = len(data) assert ncomp == 3 dsize = data[0].dtype.itemsize @@ -606,7 +607,7 @@ def appendData(self, data): x, y, z = data[0], data[1], data[2] writeArraysToFile(self.xml.stream, x, y, z) - elif type(data).__name__ == "ndarray" and ( + elif isinstance(data, np.ndarray) and ( data.ndim == 1 or data.ndim == 3 ): # single numpy array ncomp = 1 From 5cb3370a2ef1b6549ab5bde2d39df0e19f7ffabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Tue, 29 Jun 2021 09:20:16 +0200 Subject: [PATCH 36/52] [ci] revert wrong run rule for test deploy action --- .github/workflows/test_deploy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index 5f63697..a449a6b 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -3,7 +3,9 @@ name: Upload Python Package -on: [pull_request] +on: + push: + branches: jobs: deploy: From 3f6a5138e64599b1fd0ed3dfd3ddcb3d9d111e28 Mon Sep 17 00:00:00 2001 From: StRuoff Date: Tue, 20 Jul 2021 10:25:57 +0200 Subject: [PATCH 37/52] added keyword for name of dataset in a collection --- pyevtk/vtk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 376d73e..682b5a5 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -208,7 +208,7 @@ def save(self): self.xml.closeElement("VTKFile") self.xml.close() - def addFile(self, filepath, sim_time, group="", part="0"): + def addFile(self, filepath, sim_time, group="", part="0", name=""): """ Add a file to this VTK group. @@ -234,7 +234,7 @@ def addFile(self, filepath, sim_time, group="", part="0"): filename = os.path.relpath(filepath, start=self.root) self.xml.openElement("DataSet") self.xml.addAttributes( - timestep=sim_time, group=group, part=part, file=filename + timestep=sim_time, group=group, part=part, file=filename, name=name ) self.xml.closeElement() From cc42c916fbb4d640e31d7b77312f5f60c4f15cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 1 Apr 2022 17:23:58 +0200 Subject: [PATCH 38/52] [tests] call high level funcs with positional args only --- tests/dummy.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/dummy.py b/tests/dummy.py index f4c730f..f3d70ec 100644 --- a/tests/dummy.py +++ b/tests/dummy.py @@ -2,6 +2,7 @@ import os import runpy +import numpy as np def test_imports(): import pyevtk @@ -26,3 +27,42 @@ def test_compat_lib(): assert pyevtk.vtk is evtk.vtk assert pyevtk.xml is evtk.xml + +def test_positional_args_only_image(): + from pyevtk.hl import imageToVTK + nx, ny, nz = 6, 6, 2 + ncells = nx * ny * nz + npoints = (nx + 1) * (ny + 1) * (nz + 1) + + # Variables + pressure = np.random.rand(ncells).reshape((nx, ny, nz), order="C") + temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) + + imageToVTK( + "./image", (0.0, 0.0, 0.0), (1.0, 1.0, 1.0), {"pressure": pressure}, {"temp": temp} + ) + + +def test_positional_args_only_grid(): + from pyevtk.hl import gridToVTK + nx, ny, nz = 6, 6, 2 + + ncells = nx * ny * nz + npoints = (nx + 1) * (ny + 1) * (nz + 1) + + x = np.zeros((nx + 1, ny + 1, nz + 1)) + y = np.zeros((nx + 1, ny + 1, nz + 1)) + z = np.zeros((nx + 1, ny + 1, nz + 1)) + + # Variables + pressure = np.random.rand(ncells).reshape((nx, ny, nz)) + temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) + + gridToVTK( + "./structured", + x, + y, + z, + {"pressure": pressure}, + {"temp": temp}, + ) \ No newline at end of file From d08befc3b14f01de675415217f5114641fa27dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 1 Apr 2022 17:04:18 +0200 Subject: [PATCH 39/52] [ci] add github action --- .github/workflows/black.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 0000000..b7dff1e --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,14 @@ + +# GitHub Action that uses Black to reformat the Python code in an incoming pull request. +# If all Python code in the pull request is compliant with Black then this Action does nothing. +# Othewrwise, Black is run and its changes are committed back to the incoming pull request. +# https://github.com/cclauss/autoblack + +name: autoblack +on: [pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: psf/black@stable From 2527f250c6197b966320077a9d4a2076608391c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 1 Apr 2022 17:04:43 +0200 Subject: [PATCH 40/52] format with black --- examples/image.py | 4 +- pyevtk/__init__.py | 3 +- pyevtk/_version.py | 154 +++++++++++++++---------- pyevtk/evtk.py | 1 + pyevtk/hl.py | 36 +++--- pyevtk/vtk.py | 14 +-- requirements.txt | 1 + setup.py | 11 +- tests/dummy.py | 17 ++- versioneer.py | 278 +++++++++++++++++++++++++++------------------ 10 files changed, 308 insertions(+), 211 deletions(-) diff --git a/examples/image.py b/examples/image.py index 41f15c0..d38d3fd 100644 --- a/examples/image.py +++ b/examples/image.py @@ -41,9 +41,7 @@ pressure = np.random.rand(ncells).reshape((nx, ny, nz), order="C") temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) -imageToVTK( - "./image", cellData={"pressure": pressure}, pointData={"temp": temp} -) +imageToVTK("./image", cellData={"pressure": pressure}, pointData={"temp": temp}) fluxx = np.random.rand(ncells).reshape((nx, ny, nz), order="F") fluxy = np.random.rand(ncells).reshape((nx, ny, nz), order="F") diff --git a/pyevtk/__init__.py b/pyevtk/__init__.py index 3545368..527b33f 100644 --- a/pyevtk/__init__.py +++ b/pyevtk/__init__.py @@ -23,5 +23,6 @@ from . import xml from ._version import get_versions -__version__ = get_versions()['version'] + +__version__ = get_versions()["version"] del get_versions diff --git a/pyevtk/_version.py b/pyevtk/_version.py index 7de3e0f..8320030 100644 --- a/pyevtk/_version.py +++ b/pyevtk/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -58,17 +57,18 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None @@ -76,10 +76,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -114,16 +117,22 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -183,7 +192,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -192,7 +201,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -200,19 +209,26 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") @@ -227,8 +243,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -236,10 +251,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -262,17 +286,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -281,10 +304,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -295,13 +320,13 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() # Use only the last line. Previous lines may contain GPG signature # information. date = date.splitlines()[-1] @@ -335,8 +360,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -450,11 +474,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -474,9 +500,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } def get_versions(): @@ -490,8 +520,7 @@ def get_versions(): verbose = cfg.verbose try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass @@ -500,13 +529,16 @@ def get_versions(): # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): + for i in cfg.versionfile_source.split("/"): root = os.path.dirname(root) except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -520,6 +552,10 @@ def get_versions(): except NotThisMethod: pass - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/pyevtk/evtk.py b/pyevtk/evtk.py index 421b9dd..41feffa 100644 --- a/pyevtk/evtk.py +++ b/pyevtk/evtk.py @@ -26,6 +26,7 @@ import sys import struct + # import base64 import numpy as np diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 3786744..822aa5f 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -46,7 +46,9 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): if pointData: keys = list(pointData.keys()) # find first scalar and vector data key to set it as attribute - scalars = next((key for key in keys if isinstance(pointData[key], np.ndarray)), None) + scalars = next( + (key for key in keys if isinstance(pointData[key], np.ndarray)), None + ) vectors = next((key for key in keys if isinstance(pointData[key], tuple)), None) vtkFile.openData("Point", scalars=scalars, vectors=vectors) for key in keys: @@ -58,7 +60,9 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): if cellData: keys = list(cellData.keys()) # find first scalar and vector data key to set it as attribute - scalars = next((key for key in keys if isinstance(cellData[key], np.ndarray)), None) + scalars = next( + (key for key in keys if isinstance(cellData[key], np.ndarray)), None + ) vectors = next((key for key in keys if isinstance(cellData[key], tuple)), None) vtkFile.openData("Cell", scalars=scalars, vectors=vectors) for key in keys: @@ -415,18 +419,14 @@ def linesToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): w.addData("types", cell_types) w.closeElement("Cells") - _addDataToFile( - w, cellData=cellData, pointData=pointData, fieldData=fieldData - ) + _addDataToFile(w, cellData=cellData, pointData=pointData, fieldData=fieldData) w.closePiece() w.closeGrid() w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile( - w, cellData=cellData, pointData=pointData, fieldData=fieldData - ) + _appendDataToFile(w, cellData=cellData, pointData=pointData, fieldData=fieldData) w.save() return w.getFileName() @@ -473,9 +473,7 @@ def polyLinesToVTK( ncells = pointsPerLine.size # create some temporary arrays to write grid topology - offsets = np.zeros( - ncells, dtype="int32" - ) # index of last node in each cell + offsets = np.zeros(ncells, dtype="int32") # index of last node in each cell ii = 0 for i in range(ncells): ii += pointsPerLine[i] @@ -501,18 +499,14 @@ def polyLinesToVTK( w.addData("types", cell_types) w.closeElement("Cells") - _addDataToFile( - w, cellData=cellData, pointData=pointData, fieldData=fieldData - ) + _addDataToFile(w, cellData=cellData, pointData=pointData, fieldData=fieldData) w.closePiece() w.closeGrid() w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile( - w, cellData=cellData, pointData=pointData, fieldData=fieldData - ) + _appendDataToFile(w, cellData=cellData, pointData=pointData, fieldData=fieldData) w.save() return w.getFileName() @@ -595,18 +589,14 @@ def unstructuredGridToVTK( w.addData("types", cell_types) w.closeElement("Cells") - _addDataToFile( - w, cellData=cellData, pointData=pointData, fieldData=fieldData - ) + _addDataToFile(w, cellData=cellData, pointData=pointData, fieldData=fieldData) w.closePiece() w.closeGrid() w.appendData((x, y, z)) w.appendData(connectivity).appendData(offsets).appendData(cell_types) - _appendDataToFile( - w, cellData=cellData, pointData=pointData, fieldData=fieldData - ) + _appendDataToFile(w, cellData=cellData, pointData=pointData, fieldData=fieldData) w.save() return w.getFileName() diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 682b5a5..a762b36 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -323,9 +323,7 @@ def openPiece( self.xml.addAttributes(Extent=ext) elif ncells and npoints: - self.xml.addAttributes( - NumberOfPoints=npoints, NumberOfCells=ncells - ) + self.xml.addAttributes(NumberOfPoints=npoints, NumberOfCells=ncells) elif npoints or nverts or nlines or nstrips or npolys: if npoints is None: @@ -512,9 +510,7 @@ def addHeader(self, name, dtype, nelem, ncomp): ) self.xml.closeElement() - self.offset += ( - nelem * ncomp * dtype.size + 8 - ) # add 8 to indicate array size + self.offset += nelem * ncomp * dtype.size + 8 # add 8 to indicate array size return self def addData(self, name, data): @@ -633,9 +629,9 @@ def openAppendedData(self): from an external library. """ if not self.appendedDataIsOpen: - self.xml.openElement("AppendedData").addAttributes( - encoding="raw" - ).addText("_") + self.xml.openElement("AppendedData").addAttributes(encoding="raw").addText( + "_" + ) self.appendedDataIsOpen = True def closeAppendedData(self): diff --git a/requirements.txt b/requirements.txt index 0f25f38..5a244eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ codecov twine check-manifest readme_renderer[md] +black~=22.1.0 diff --git a/setup.py b/setup.py index d27ecbc..1e70dce 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ import versioneer + def readme(fname): """Open the readme file.""" with open(fname, "r") as f: @@ -55,8 +56,10 @@ def readme(fname): # necessary for 'python setup.py test' setup_requires=["pytest-runner"], tests_require=["pytest>=3.1", "pytest-cov", "twine", "check-manifest"], - classifiers=['Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9',], + classifiers=[ + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + ], ) diff --git a/tests/dummy.py b/tests/dummy.py index f3d70ec..5ae0385 100644 --- a/tests/dummy.py +++ b/tests/dummy.py @@ -4,16 +4,18 @@ import numpy as np + def test_imports(): import pyevtk + print(pyevtk.evtk) def test_examples(): this_dir = os.path.dirname(os.path.abspath(__file__)) - example_dir = os.path.join(this_dir, '..', 'examples') + example_dir = os.path.join(this_dir, "..", "examples") for root, _, files in os.walk(example_dir): - examples = [os.path.join(root, f) for f in files if f.endswith('.py')] + examples = [os.path.join(root, f) for f in files if f.endswith(".py")] for ex in examples: runpy.run_path(ex) @@ -22,6 +24,7 @@ def test_compat_lib(): with pytest.warns(DeprecationWarning): import evtk import pyevtk + assert pyevtk.evtk is evtk.evtk assert pyevtk.hl is evtk.hl assert pyevtk.vtk is evtk.vtk @@ -30,6 +33,7 @@ def test_compat_lib(): def test_positional_args_only_image(): from pyevtk.hl import imageToVTK + nx, ny, nz = 6, 6, 2 ncells = nx * ny * nz npoints = (nx + 1) * (ny + 1) * (nz + 1) @@ -39,12 +43,17 @@ def test_positional_args_only_image(): temp = np.random.rand(npoints).reshape((nx + 1, ny + 1, nz + 1)) imageToVTK( - "./image", (0.0, 0.0, 0.0), (1.0, 1.0, 1.0), {"pressure": pressure}, {"temp": temp} + "./image", + (0.0, 0.0, 0.0), + (1.0, 1.0, 1.0), + {"pressure": pressure}, + {"temp": temp}, ) def test_positional_args_only_grid(): from pyevtk.hl import gridToVTK + nx, ny, nz = 6, 6, 2 ncells = nx * ny * nz @@ -65,4 +74,4 @@ def test_positional_args_only_grid(): z, {"pressure": pressure}, {"temp": temp}, - ) \ No newline at end of file + ) diff --git a/versioneer.py b/versioneer.py index 1040c21..d5e0eb5 100644 --- a/versioneer.py +++ b/versioneer.py @@ -1,4 +1,3 @@ - # Version: 0.19 """The Versioneer - like a rocketeer, but for versions. @@ -301,11 +300,13 @@ def get_root(): setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - err = ("Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND').") + err = ( + "Versioneer was unable to run the project root directory. " + "Versioneer requires setup.py to be executed from " + "its immediate directory (like 'python setup.py COMMAND'), " + "or in a way that lets it use sys.argv[0] to find the root " + "(like 'python path/to/setup.py COMMAND')." + ) raise VersioneerBadRootError(err) try: # Certain runtime workflows (setup.py install/develop in a setuptools @@ -318,8 +319,10 @@ def get_root(): me_dir = os.path.normcase(os.path.splitext(me)[0]) vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) if me_dir != vsr_dir: - print("Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(me), versioneer_py)) + print( + "Warning: build in %s is using versioneer.py from %s" + % (os.path.dirname(me), versioneer_py) + ) except NameError: pass return root @@ -341,6 +344,7 @@ def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) return None + cfg = VersioneerConfig() cfg.VCS = VCS cfg.style = get(parser, "style") or "" @@ -365,17 +369,18 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None @@ -383,10 +388,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -409,7 +417,9 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, return stdout, p.returncode -LONG_VERSION_PY['git'] = r''' +LONG_VERSION_PY[ + "git" +] = r''' # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -993,7 +1003,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -1002,7 +1012,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -1010,19 +1020,26 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") @@ -1037,8 +1054,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -1046,10 +1062,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -1072,17 +1097,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -1091,10 +1115,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -1105,13 +1131,13 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() # Use only the last line. Previous lines may contain GPG signature # information. date = date.splitlines()[-1] @@ -1170,16 +1196,22 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -1208,11 +1240,13 @@ def versions_from_file(filename): contents = f.read() except EnvironmentError: raise NotThisMethod("unable to read _version.py") - mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S + ) if not mo: - mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S + ) if not mo: raise NotThisMethod("no version_json in _version.py") return json.loads(mo.group(1)) @@ -1221,8 +1255,7 @@ def versions_from_file(filename): def write_to_version_file(filename, versions): """Write the given version number to the given _version.py file.""" os.unlink(filename) - contents = json.dumps(versions, sort_keys=True, - indent=1, separators=(",", ": ")) + contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": ")) with open(filename, "w") as f: f.write(SHORT_VERSION_PY % contents) @@ -1254,8 +1287,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -1369,11 +1401,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -1393,9 +1427,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } class VersioneerBadRootError(Exception): @@ -1418,8 +1456,9 @@ def get_versions(verbose=False): handlers = HANDLERS.get(cfg.VCS) assert handlers, "unrecognized VCS '%s'" % cfg.VCS verbose = verbose or cfg.verbose - assert cfg.versionfile_source is not None, \ - "please set versioneer.versionfile_source" + assert ( + cfg.versionfile_source is not None + ), "please set versioneer.versionfile_source" assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" versionfile_abs = os.path.join(root, cfg.versionfile_source) @@ -1473,9 +1512,13 @@ def get_versions(verbose=False): if verbose: print("unable to compute version") - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } def get_version(): @@ -1528,6 +1571,7 @@ def run(self): print(" date: %s" % vers.get("date")) if vers["error"]: print(" error: %s" % vers["error"]) + cmds["version"] = cmd_version # we override "build_py" in both distutils and setuptools @@ -1546,8 +1590,8 @@ def run(self): # setup.py egg_info -> ? # we override different "build_py" commands for both environments - if 'build_py' in cmds: - _build_py = cmds['build_py'] + if "build_py" in cmds: + _build_py = cmds["build_py"] elif "setuptools" in sys.modules: from setuptools.command.build_py import build_py as _build_py else: @@ -1562,10 +1606,10 @@ def run(self): # now locate _version.py in the new build/ directory and replace # it with an updated value if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) + target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) + cmds["build_py"] = cmd_build_py if "setuptools" in sys.modules: @@ -1587,14 +1631,15 @@ def run(self): return # now locate _version.py in the new build/ directory and replace # it with an updated value - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_source) + target_versionfile = os.path.join(self.build_lib, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) + cmds["build_ext"] = cmd_build_ext if "cx_Freeze" in sys.modules: # cx_freeze enabled? from cx_Freeze.dist import build_exe as _build_exe + # nczeczulin reports that py2exe won't like the pep440-style string # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. # setup(console=[{ @@ -1615,17 +1660,21 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["build_exe"] = cmd_build_exe del cmds["build_py"] - if 'py2exe' in sys.modules: # py2exe enabled? + if "py2exe" in sys.modules: # py2exe enabled? from py2exe.distutils_buildexe import py2exe as _py2exe class cmd_py2exe(_py2exe): @@ -1641,18 +1690,22 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["py2exe"] = cmd_py2exe # we override different "sdist" commands for both environments - if 'sdist' in cmds: - _sdist = cmds['sdist'] + if "sdist" in cmds: + _sdist = cmds["sdist"] elif "setuptools" in sys.modules: from setuptools.command.sdist import sdist as _sdist else: @@ -1676,8 +1729,10 @@ def make_release_tree(self, base_dir, files): # updated value target_versionfile = os.path.join(base_dir, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, - self._versioneer_generated_versions) + write_to_version_file( + target_versionfile, self._versioneer_generated_versions + ) + cmds["sdist"] = cmd_sdist return cmds @@ -1732,11 +1787,13 @@ def do_setup(): root = get_root() try: cfg = get_config_from_root(root) - except (EnvironmentError, configparser.NoSectionError, - configparser.NoOptionError) as e: + except ( + EnvironmentError, + configparser.NoSectionError, + configparser.NoOptionError, + ) as e: if isinstance(e, (EnvironmentError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", - file=sys.stderr) + print("Adding sample versioneer config to setup.cfg", file=sys.stderr) with open(os.path.join(root, "setup.cfg"), "a") as f: f.write(SAMPLE_CONFIG) print(CONFIG_ERROR, file=sys.stderr) @@ -1745,15 +1802,18 @@ def do_setup(): print(" creating %s" % cfg.versionfile_source) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), - "__init__.py") + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + + ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") if os.path.exists(ipy): try: with open(ipy, "r") as f: @@ -1795,8 +1855,10 @@ def do_setup(): else: print(" 'versioneer.py' already in MANIFEST.in") if cfg.versionfile_source not in simple_includes: - print(" appending versionfile_source ('%s') to MANIFEST.in" % - cfg.versionfile_source) + print( + " appending versionfile_source ('%s') to MANIFEST.in" + % cfg.versionfile_source + ) with open(manifest_in, "a") as f: f.write("include %s\n" % cfg.versionfile_source) else: From 5837c6a6f80e98408ff9a68e636a0bc894b8eea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 1 Apr 2022 17:07:03 +0200 Subject: [PATCH 41/52] apply pre-commit config --- .gitignore | 8 ++++---- .pre-commit-config.yaml | 13 +++++++++++++ LICENSE.txt | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.gitignore b/.gitignore index 5e6063c..1b53511 100644 --- a/.gitignore +++ b/.gitignore @@ -102,8 +102,8 @@ ENV/ .idea # test artefacts -*.vtu -*.vts -*.vtr -*.pvd +*.vtu +*.vts +*.vtr +*.pvd *.vti diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8450495 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,13 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: trailing-whitespace + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/psf/black + rev: 22.1.0 + hooks: + - id: black diff --git a/LICENSE.txt b/LICENSE.txt index a3f8e8a..df99fbc 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,5 +1,5 @@ *********************************************************************************** -* Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * +* Copyright 2010 - 2016 Paulo A. Herrera. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions are met: * From 0f37e3a1bed4deefdd82d784806b7644f3fd4fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 1 Apr 2022 17:28:30 +0200 Subject: [PATCH 42/52] update manifest to ignore CI files --- MANIFEST.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index a45bc29..60cbab1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,3 +5,5 @@ recursive-include examples *.py recursive-include tests *.py include versioneer.py include pyevtk/_version.py +exclude .pre-commit-config.yaml +prune .github From 178bf48cb7943c0ad8f59394ee687ed1e2f62040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Fri, 1 Apr 2022 17:32:29 +0200 Subject: [PATCH 43/52] [ci] add most current python version --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6c3462..53a45d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] env: SDIST_DIR: /tmp/sdist From 487cb94d066088879d304653060a6424a78a8a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 7 Apr 2022 19:11:11 +0200 Subject: [PATCH 44/52] [ci] disambiguate job names --- .github/workflows/black.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index b7dff1e..b818536 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -7,7 +7,7 @@ name: autoblack on: [pull_request] jobs: - build: + black: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 53a45d7..4547baa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ on: push: jobs: - build: + test: runs-on: ubuntu-latest strategy: From 20e8520b220a74056b2a9ce56fbb2b51cc0f5306 Mon Sep 17 00:00:00 2001 From: Antoine Lavandier Date: Thu, 31 Mar 2022 10:25:02 +0200 Subject: [PATCH 45/52] Adding VtkParallelFiles class, high level functions and examples with StructuredGrid and RectilinearGrid. --- examples/rectilinear.py | 29 ++++- examples/structured.py | 58 ++++++++- pyevtk/hl.py | 156 +++++++++++++++++++++--- pyevtk/vtk.py | 257 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 484 insertions(+), 16 deletions(-) diff --git a/examples/rectilinear.py b/examples/rectilinear.py index 4f83660..30696a0 100644 --- a/examples/rectilinear.py +++ b/examples/rectilinear.py @@ -30,9 +30,13 @@ # * This example shows how to export a rectilinear grid. * # ************************************************************** -from pyevtk.hl import gridToVTK +from pyevtk.hl import gridToVTK, writeParallelVTKGrid import numpy as np +# ================== +# Serial example +# ================== + # Dimensions nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 @@ -58,3 +62,26 @@ cellData={"pressure": pressure}, pointData={"temp": temp}, ) + + +# ================== +# Parallel example +# ================== + +# Dimensions +x1 = np.linspace(0, 1, 10) +x2 = np.linspace(1, 2, 20) + +y = np.linspace(0, 1, 10) +z = np.linspace(0, 1, 10) + +gridToVTK("test.0", x1, y, z, start=(0, 0, 0)) +gridToVTK("test.1", x2, y, z, start=(9, 0, 0)) + +writeParallelVTKGrid( + "test_full", + coordsData=((29, 10, 10), x.dtype), + starts=[(0, 0, 0), (9, 0, 0)], + ends=[(9, 9, 9), (28, 9, 9)], + sources=["test.0.vtr", "test.1.vtr"], +) diff --git a/examples/structured.py b/examples/structured.py index 40de3bc..acfeca3 100644 --- a/examples/structured.py +++ b/examples/structured.py @@ -30,10 +30,14 @@ # * This example shows how to export a structured grid. * # ************************************************************** -from pyevtk.hl import gridToVTK +from pyevtk.hl import gridToVTK, writeParallelVTKGrid import numpy as np import random as rnd +# =================== +# Serial Example +# =================== + # Dimensions nx, ny, nz = 6, 6, 2 lx, ly, lz = 1.0, 1.0, 1.0 @@ -72,3 +76,55 @@ cellData={"pressure": pressure}, pointData={"temp": temp}, ) + + +# =================== +# Parallel example +# =================== + +# Dimensions +x1 = np.linspace(0, 1, 20) + +x2_1 = np.linspace(0, 0.5, 10) +x2_2 = np.linspace(0.5, 1, 10) + +x3 = np.linspace(0, 2, 30) + +XX1_1, XX2_1, XX3_1 = np.meshgrid(x1, x2_1, x3, indexing="ij") +XX1_2, XX2_2, XX3_2 = np.meshgrid(x1, x2_2, x3, indexing="ij") + +pi = np.pi +sphere = lambda R, Th, Ph: ( + R * np.cos(pi * Ph) * np.sin(pi * Th), + R * np.sin(pi * Ph) * np.sin(pi * Th), + R * np.cos(pi * Th), +) + +# First Half sphere +gridToVTK( + "sphere.0", + *sphere(XX1_1, XX2_1, XX3_1), + start=(0, 0, 0), + pointData={"R": XX1_1, "Theta": XX2_1, "Phi": XX3_1} +) +# Second Half sphere +gridToVTK( + "sphere.1", + *sphere(XX1_2, XX2_2, XX3_2), + start=(0, 9, 0), + pointData={"R": XX1_2, "Theta": XX2_2, "Phi": XX3_2} +) + +# Write parallel file +writeParallelVTKGrid( + "sphere_full", + coordsData=((20, 19, 30), XX1_1.dtype), + starts=[(0, 0, 0), (0, 9, 0)], + ends=[(19, 9, 29), (19, 18, 29)], + sources=["sphere.0.vts", "sphere.1.vts"], + pointData={ + "R": (XX1_1.dtype, 1), + "Theta": (XX2_1.dtype, 1), + "Phi": (XX3_1.dtype, 1), + }, +) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 822aa5f..9ffb458 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -27,10 +27,15 @@ import numpy as np from .vtk import ( VtkFile, + VtkParallelFile, VtkUnstructuredGrid, VtkImageData, VtkRectilinearGrid, VtkStructuredGrid, + VtkPImageData, + VtkPRectilinearGrid, + VtkPStructuredGrid, + VtkUnstructuredGrid, VtkVertex, VtkLine, VtkPolyLine, @@ -81,6 +86,33 @@ def _addDataToFile(vtkFile, cellData, pointData, fieldData=None): vtkFile.closeData("Field") +def _addDataToParallelFile(vtkParallelFile, cellData, pointData): + assert isinstance(vtkParallelFile, VtkParallelFile) + # Point data + if pointData: + keys = list(pointData.keys()) + # find first scalar and vector data key to set it as attribute + scalars = next((key for key in keys if pointData[key][1] == 1), None) + vectors = next((key for key in keys if pointData[key][1] == 3), None) + vtkParallelFile.openData("PPoint", scalars=scalars, vectors=vectors) + for key in keys: + dtype, ncomp = pointData[key] + vtkParallelFile.addHeader(key, dtype=dtype, ncomp=ncomp) + vtkParallelFile.closeData("PPoint") + + # Cell data + if cellData: + keys = list(cellData.keys()) + # find first scalar and vector data key to set it as attribute + scalars = next((key for key in keys if cellData[key][1] == 1), None) + vectors = next((key for key in keys if cellData[key][1] == 3), None) + vtkParallelFile.openData("PCell", scalars=scalars, vectors=vectors) + for key in keys: + dtype, ncomp = pointData[key] + vtkParallelFile.addHeader(key, dtype=dtype, ncomp=ncomp) + vtkParallelFile.closeData("PCell") + + def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None): # Append data to binary section if pointData is not None: @@ -107,6 +139,7 @@ def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None): # ================================= def imageToVTK( path, + start=(0, 0, 0), origin=(0.0, 0.0, 0.0), spacing=(1.0, 1.0, 1.0), cellData=None, @@ -115,11 +148,14 @@ def imageToVTK( ): """ Export data values as a rectangular image. - Parameters ---------- path : str name of the file without extension where data should be saved. + start : tuple, optional + start of the coordinates. + Used in the distributed context where each process + writes its own vtk file. Default is (0, 0, 0). origin : tuple, optional grid origin. The default is (0.0, 0.0, 0.0). @@ -138,17 +174,15 @@ def imageToVTK( Arrays must have same dimension in each direction and they should be equal to the dimensions of the cell data plus one and can contain scalar data ([n+1,n+1,n+1]) or - +1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]). + ([n+1,n+1,n+1],[n+1,n+1,n+1],[n+1,n+1,n+1]). The default is None. fieldData : dict, optional dictionary with variables associated with the field. Keys should be the names of the variable stored in each array. - Returns ------- str Full path to saved file. - Notes ----- At least, cellData or pointData must be present @@ -157,7 +191,6 @@ def imageToVTK( assert cellData is not None or pointData is not None # Extract dimensions - start = (0, 0, 0) end = None if cellData is not None: keys = list(cellData.keys()) @@ -188,10 +221,11 @@ def imageToVTK( # ============================================================================== -def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): +def gridToVTK( + path, x, y, z, start=(0, 0, 0), cellData=None, pointData=None, fieldData=None +): """ - Write data values as a rectilinear or rectangular grid. - + Write data values as a structured grid. Parameters ---------- path : str @@ -202,6 +236,10 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): y coordinate axis. z : array-like z coordinate axis. + start : tuple, optional + start of the coordinates. + Used in the distributed conwhere each process + writes its own vtk file. Default is (0, 0, 0). cellData : dict, optional dictionary containing arrays with cell centered data. 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): fieldData : dict, optional dictionary with variables associated with the field. Keys should be the names of the variable stored in each array. - Returns ------- str Full path to saved file. - Notes ----- 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): In both cases the arrays dimensions should be equal to the number of nodes of the grid. """ - # Extract dimensions - start = (0, 0, 0) nx = ny = nz = 0 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): isRect = False ftype = VtkStructuredGrid else: - assert False - end = (nx, ny, nz) + raise ValueError( + f"x, y and z should have ndim == 3 but they have ndim of {x.ndim}, {y.ndim}" + f" and {z.ndim} respectively" + ) + + # Write extent + end = (start[0] + nx, start[1] + ny, start[2] + nz) + # Open File w = VtkFile(path, ftype) + + # Open Grid part w.openGrid(start=start, end=end) w.openPiece(start=start, end=end) + # Add coordinates description if isRect: w.openElement("Coordinates") w.addData("x_coordinates", x) @@ -267,9 +310,13 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): w.addData("points", (x, y, z)) w.closeElement("Points") + # Add data description _addDataToFile(w, cellData, pointData, fieldData) + + # Close Grid part w.closePiece() w.closeGrid() + # Write coordinates if isRect: w.appendData(x).appendData(y).appendData(z) @@ -277,6 +324,87 @@ def gridToVTK(path, x, y, z, cellData=None, pointData=None, fieldData=None): w.appendData((x, y, z)) # Write data _appendDataToFile(w, cellData, pointData, fieldData) + + # Close file + w.save() + + return w.getFileName() + + +def writeParallelVTKGrid( + path, coordsData, starts, ends, sources, ghostlevel=0, cellData=None, pointData=None +): + """ + Writes a parallel vtk file from grid-like data: + VTKStructuredGrid or VTKRectilinearGrid + + Parameters + ---------- + path : str + name of the file without extension. + coordsData : tuple + 2-tuple (shape, dtype) where shape is the + shape of the coordinates of the full mesh + and dtype is the dtype of the coordinates. + starts : list + list of 3-tuple representing where each source file starts + in each dimension + source : list + list of the relative paths of the source files where the actual data is found + ghostlevel : int, optional + Number of ghost-levels by which + the extents in the individual source files overlap. + pointData : dict + dictionnary containing the information about the arrays + containing node centered data. + Keys shoud be the names of the arrays. + Values are (dtype, number of components) + cellData : + dictionnary containing the information about the arrays + containing cell centered data. + Keys shoud be the names of the arrays. + Values are (dtype, number of components) + """ + # Check that every source as a start and an end + assert len(starts) == len(ends) == len(sources) + + # Get the extension + check that it's consistent accros all source files + common_ext = sources[0].split(".")[-1] + assert all(s.split(".")[-1] == common_ext for s in sources) + + if common_ext == "vts": + ftype = VtkPStructuredGrid + is_Rect = False + elif common_ext == "vtr": + ftype = VtkPRectilinearGrid + is_Rect = True + else: + raise ValueError("This functions is meant to work only with ") + + w = VtkParallelFile(path, ftype) + start = (0, 0, 0) + (s_x, s_y, s_z), dtype = coordsData + end = s_x - 1, s_y - 1, s_z - 1 + + w.openGrid(start=start, end=end, ghostlevel=ghostlevel) + + _addDataToParallelFile(w, cellData=cellData, pointData=pointData) + + if is_Rect: + w.openElement("PCoordinates") + w.addHeader("x_coordinates", dtype=dtype, ncomp=1) + w.addHeader("y_coordinates", dtype=dtype, ncomp=1) + w.addHeader("z_coordinates", dtype=dtype, ncomp=1) + w.closeElement("PCoordinates") + else: + w.openElement("PPoints") + w.addHeader("points", dtype=dtype, ncomp=3) + w.closeElement("PPoints") + + for start_source, end_source, source in zip(starts, ends, sources): + w.addPiece(start_source, end_source, source) + + w.closeGrid() w.save() return w.getFileName() diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index a762b36..2b19225 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -64,6 +64,29 @@ def __str__(self): VtkUnstructuredGrid = VtkFileType("UnstructuredGrid", ".vtu") +class VtkParallelFileType: + """ + A wrapper class for parallel vtk file types. + + Parameters + ---------- + vtkftype : VtkFileType + Vtk file type + """ + + def __init__(self, vtkftype): + self.name = "P" + vtkftype.name + ext = vtkftype.ext + self.ext = ext[0] + "p" + ext[1:] + + +VtkPImageData = VtkParallelFileType(VtkImageData) +VtkPPolyData = VtkParallelFileType(VtkPolyData) +VtkPRectilinearGrid = VtkParallelFileType(VtkRectilinearGrid) +VtkPStructuredGrid = VtkParallelFileType(VtkStructuredGrid) +VtkPUnstructuredGrid = VtkParallelFileType(VtkUnstructuredGrid) + + # DATA TYPES class VtkDataType: """ @@ -661,3 +684,237 @@ def save(self): self.xml.closeElement("AppendedData") self.xml.closeElement("VTKFile") self.xml.close() + + +# ================================ +# VtkParallelFile class +# ================================ +class VtkParallelFile: + """ + Class for a VTK parallel file. + + Parameters + ---------- + filepath : str + filename without extension + ftype : VtkParallelFileType + """ + + def __init__(self, filepath, ftype): + assert isinstance(ftype, VtkParallelFileType) + self.ftype = ftype + self.filename = filepath + ftype.ext + self.xml = XmlWriter(self.filename) + self.xml.openElement("VTKFile").addAttributes( + type=ftype.name, + version="1.0", + byte_order=_get_byte_order(), + header_type="UInt64", + ) + + def getFileName(self): + """Return absolute path to this file.""" + return os.path.abspath(self.filename) + + def addPiece( + self, + start=None, + end=None, + source=None, + ): + """ + Add piece section with extent and source. + Parameters + ---------- + start : array-like, optional + array or list with start indexes in each direction. + Must be given with end. + end : array-like, optional + array or list with end indexes in each direction. + Must be given with start. + source : str + Source of this piece + Returns + ------- + VtkParallelFile + This VtkFile to allow chained calls. + """ + # Check Source + assert source is not None + assert source.split(".")[-1] == self.ftype.ext[2:] + + self.xml.openElement("Piece") + if start and end: + ext = _mix_extents(start, end) + self.xml.addAttributes(Extent=ext) + self.xml.addAttributes(Source=source) + self.xml.closeElement() + return self + + def openData( + self, + nodeType, + scalars=None, + vectors=None, + normals=None, + tensors=None, + tcoords=None, + ): + """ + Open data section. + Parameters + ---------- + nodeType : str + Either "Point", "Cell" or "Field". + scalars : str, optional + default data array name for scalar data. + vectors : str, optional + default data array name for vector data. + normals : str, optional + default data array name for normals data. + tensors : str, optional + default data array name for tensors data. + tcoords : str, optional + default data array name for tcoords data. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + self.xml.openElement(nodeType + "Data") + if scalars: + self.xml.addAttributes(Scalars=scalars) + if vectors: + self.xml.addAttributes(Vectors=vectors) + if normals: + self.xml.addAttributes(Normals=normals) + if tensors: + self.xml.addAttributes(Tensors=tensors) + if tcoords: + self.xml.addAttributes(TCoords=tcoords) + + return self + + def closeData(self, nodeType): + """ + Close data section. + Parameters + ---------- + nodeType : str + "Point", "Cell" or "Field". + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + self.xml.closeElement(nodeType + "Data") + + def openGrid(self, start=None, end=None, origin=None, spacing=None, ghostlevel=0): + """ + Open grid section. + + Parameters + ---------- + start : array-like, optional + array or list of start indexes. + Required for Structured, Rectilinear and ImageData grids. + The default is None. + end : array-like, optional + array or list of end indexes. + Required for Structured, Rectilinear and ImageData grids. + The default is None. + origin : array-like, optional + 3D array or list with grid origin. + Only required for ImageData grids. + The default is None. + spacing : array-like, optional + 3D array or list with grid spacing. + Only required for ImageData grids. + The default is None. + ghostlevel : int + Number of ghost-levels by which + the extents in the individual pieces overlap. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + gType = self.ftype.name + self.xml.openElement(gType) + + if gType == VtkPImageData.name: + if not start or not end or not origin or not spacing: + raise ValueError(f"start, end, origin and spacing required for {gType}") + ext = _mix_extents(start, end) + self.xml.addAttributes( + WholeExtent=ext, + Origin=_array_to_string(origin), + Spacing=_array_to_string(spacing), + ) + + elif gType in [VtkPStructuredGrid.name, VtkPRectilinearGrid.name]: + if not start or not end: + raise ValueError(f"start and end required for {gType}.") + ext = _mix_extents(start, end) + self.xml.addAttributes(WholeExtent=ext) + + # Ghostlevel + self.xml.addAttributes(Ghostlevel=ghostlevel) + return self + + def closeGrid(self): + """ + Close grid element. + Returns + ------- + VtkFile + This VtkFile to allow chained calls. + """ + self.xml.closeElement(self.ftype.name) + + def addHeader(self, name, dtype, ncomp): + """ + Add data array description to xml header section. + Parameters + ---------- + name : str + data array name. + dtype : str + data type. + ncomp : int + number of components, 1 (=scalar) and 3 (=vector). + Returns + ------- + + VtkFile + This VtkFile to allow chained calls. + Notes + ----- + + This is a low level function. + Use addData if you want to add a numpy array. + """ + dtype = np_to_vtk[dtype.name] + + self.xml.openElement("DataArray") + self.xml.addAttributes( + Name=name, + NumberOfComponents=ncomp, + type=dtype.name, + ) + self.xml.closeElement() + + def openElement(self, tagName): + """ + Open an element. + Useful to add elements such as: Coordinates, Points, Verts, etc. + """ + self.xml.openElement(tagName) + + def closeElement(self, tagName): + self.xml.closeElement(tagName) + + def save(self): + """Close file.""" + self.xml.closeElement("VTKFile") + self.xml.close() From ba94befd8108290985c90410aac1baf1d970245c Mon Sep 17 00:00:00 2001 From: Antoine Lavandier Date: Thu, 31 Mar 2022 10:34:24 +0200 Subject: [PATCH 46/52] Fix docstring + add back line breaks --- pyevtk/hl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 9ffb458..b7b39f0 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -148,6 +148,7 @@ def imageToVTK( ): """ Export data values as a rectangular image. + Parameters ---------- path : str @@ -179,10 +180,12 @@ def imageToVTK( fieldData : dict, optional dictionary with variables associated with the field. Keys should be the names of the variable stored in each array. + Returns ------- str Full path to saved file. + Notes ----- At least, cellData or pointData must be present @@ -225,7 +228,8 @@ def gridToVTK( path, x, y, z, start=(0, 0, 0), cellData=None, pointData=None, fieldData=None ): """ - Write data values as a structured grid. + Write data values as a rectilinear or structured grid. + Parameters ---------- path : str From 5a421217ed8cf06635aeb0c6e864c64d42e3bd89 Mon Sep 17 00:00:00 2001 From: Antoine Lavandier Date: Fri, 1 Apr 2022 09:25:00 +0200 Subject: [PATCH 47/52] Typo in gridToVTK's docstring --- pyevtk/hl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index b7b39f0..d7aab33 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -242,7 +242,7 @@ def gridToVTK( z coordinate axis. start : tuple, optional start of the coordinates. - Used in the distributed conwhere each process + Used in the distributed context where each process writes its own vtk file. Default is (0, 0, 0). cellData : dict, optional dictionary containing arrays with cell centered data. From 229e2d035776d69903709705c1240ddc20a91576 Mon Sep 17 00:00:00 2001 From: Antoine Lavandier Date: Mon, 4 Apr 2022 16:59:46 +0200 Subject: [PATCH 48/52] Fixed pointData being used instead of cellData --- pyevtk/hl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index d7aab33..2a6f592 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -108,7 +108,7 @@ def _addDataToParallelFile(vtkParallelFile, cellData, pointData): vectors = next((key for key in keys if cellData[key][1] == 3), None) vtkParallelFile.openData("PCell", scalars=scalars, vectors=vectors) for key in keys: - dtype, ncomp = pointData[key] + dtype, ncomp = cellData[key] vtkParallelFile.addHeader(key, dtype=dtype, ncomp=ncomp) vtkParallelFile.closeData("PCell") From 77567f4b3fccf43dd5fb75eff3cee9a365128b37 Mon Sep 17 00:00:00 2001 From: Antoine Lavandier Date: Tue, 5 Apr 2022 15:22:18 +0200 Subject: [PATCH 49/52] Moved start kwarg at the end --- pyevtk/hl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index 2a6f592..d7f7dc8 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -139,12 +139,12 @@ def _appendDataToFile(vtkFile, cellData, pointData, fieldData=None): # ================================= def imageToVTK( path, - start=(0, 0, 0), origin=(0.0, 0.0, 0.0), spacing=(1.0, 1.0, 1.0), cellData=None, pointData=None, fieldData=None, + start=(0, 0, 0), ): """ Export data values as a rectangular image. @@ -225,7 +225,7 @@ def imageToVTK( # ============================================================================== def gridToVTK( - path, x, y, z, start=(0, 0, 0), cellData=None, pointData=None, fieldData=None + path, x, y, z, cellData=None, pointData=None, fieldData=None, start=(0, 0, 0) ): """ Write data values as a rectilinear or structured grid. From 191efb8e3467bce07a729df7fe53b79bd9ca7ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Mon, 5 Jun 2023 16:33:26 +0200 Subject: [PATCH 50/52] imageToVTK: determine low-dim grid from spacing --- pyevtk/hl.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index d7f7dc8..f4c3f65 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -202,6 +202,12 @@ def imageToVTK( end = data.shape elif data[0].ndim == 3 and data[1].ndim == 3 and data[2].ndim == 3: end = data[0].shape + for i, s in enumerate(spacing): + if np.isclose(s, 0.0): + if end[i] == 1: + end = end[:i] + (0,) + end[i+1:] + else: + raise ValueError("imageToVTK: grid has lower dimension than data") elif pointData is not None: keys = list(pointData.keys()) data = pointData[keys[0]] @@ -210,6 +216,9 @@ def imageToVTK( elif data[0].ndim == 3 and data[1].ndim == 3 and data[2].ndim == 3: end = data[0].shape end = (end[0] - 1, end[1] - 1, end[2] - 1) + for i, s in enumerate(spacing): + if np.isclose(s, 0.0) and end[i] > 0: + raise ValueError("imageToVTK: grid has lower dimension than data") # Write data to file w = VtkFile(path, VtkImageData) From a3a1983185ab8a65c1c492dccb245ba8a9c5c4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Mon, 5 Jun 2023 16:37:58 +0200 Subject: [PATCH 51/52] black fixes --- pyevtk/hl.py | 2 +- pyevtk/vtk.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyevtk/hl.py b/pyevtk/hl.py index f4c3f65..df2e96a 100644 --- a/pyevtk/hl.py +++ b/pyevtk/hl.py @@ -205,7 +205,7 @@ def imageToVTK( for i, s in enumerate(spacing): if np.isclose(s, 0.0): if end[i] == 1: - end = end[:i] + (0,) + end[i+1:] + end = end[:i] + (0,) + end[i + 1 :] else: raise ValueError("imageToVTK: grid has lower dimension than data") elif pointData is not None: diff --git a/pyevtk/vtk.py b/pyevtk/vtk.py index 2b19225..7b4bc0a 100644 --- a/pyevtk/vtk.py +++ b/pyevtk/vtk.py @@ -36,6 +36,7 @@ # VTK Types # ================================ + # FILE TYPES class VtkFileType: """ From a7e30e6d26bf79012b7dc0cc4ea3c8ac9efbe1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Mon, 5 Jun 2023 16:40:05 +0200 Subject: [PATCH 52/52] remove py36 test, add py311 test --- .github/workflows/test.yml | 2 +- setup.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4547baa..ef43906 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] env: SDIST_DIR: /tmp/sdist diff --git a/setup.py b/setup.py index 1e70dce..83e9796 100644 --- a/setup.py +++ b/setup.py @@ -57,9 +57,10 @@ def readme(fname): setup_requires=["pytest-runner"], tests_require=["pytest>=3.1", "pytest-cov", "twine", "check-manifest"], classifiers=[ - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ], )