Commit 8b32c4d3 authored by David Gobbi's avatar David Gobbi
Browse files

Fix numpy_interface compatibility with py3k.

This involved replacing map() with list comprehensions, changing the way
metaclasses are used, removing the __div__ method under python 3, and
using 'zip" instead of "izip" on python 3.
parent b7308b03
......@@ -8,6 +8,7 @@ guidelines. For details, see the documentation of individual
algorithms.
"""
from __future__ import absolute_import
import sys
try:
import numpy
......@@ -25,6 +26,11 @@ except ImportError:
vtkMultiProcessController = None
vtkMPI4PyCommunicator = None
if sys.hexversion < 0x03000000:
izip = itertools.izip
else:
izip = zip
def _apply_func2(func, array, args):
"""Apply a function to each member of a VTKCompositeDataArray.
Returns a list of arrays.
......@@ -67,7 +73,7 @@ def apply_dfunc(dfunc, array1, val2):
VTKArray and numpy arrays are also supported."""
if type(array1) == dsa.VTKCompositeDataArray and type(val2) == dsa.VTKCompositeDataArray:
res = []
for a1, a2 in itertools.izip(array1.Arrays, val2.Arrays):
for a1, a2 in izip(array1.Arrays, val2.Arrays):
if a1 is dsa.NoneArray or a2 is dsa.NoneArray:
res.append(dsa.NoneArray)
else:
......@@ -421,7 +427,7 @@ def _global_per_block(impl, array, axis=None, controller=None):
it.GoToNextItem()
# Fill the local array with available values.
for _id, _res in itertools.izip(ids, results):
for _id, _res in izip(ids, results):
success = True
try:
loc = reduce_ids.index(_id)
......@@ -770,13 +776,13 @@ def make_vector(arrayx, arrayy, arrayz=None):
if type(arrayx) == dsa.VTKCompositeDataArray and type(arrayy) == dsa.VTKCompositeDataArray and (type(arrayz) == dsa.VTKCompositeDataArray or arrayz is None):
res = []
if arrayz is None:
for ax, ay in itertools.izip(arrayx.Arrays, arrayy.Arrays):
for ax, ay in izip(arrayx.Arrays, arrayy.Arrays):
if ax is not dsa.NoneArray and ay is not dsa.NoneArray:
res.append(algs.make_vector(ax, ay))
else:
res.append(dsa.NoneArray)
else:
for ax, ay, az in itertools.izip(arrayx.Arrays, arrayy.Arrays, arrayz.Arrays):
for ax, ay, az in izip(arrayx.Arrays, arrayy.Arrays, arrayz.Arrays):
if ax is not dsa.NoneArray and ay is not dsa.NoneArray and az is not dsa.NoneArray:
res.append(algs.make_vector(ax, ay, az))
else:
......
......@@ -73,6 +73,11 @@ from vtk.util import numpy_support
from vtk.vtkCommonDataModel import vtkDataObject
import weakref
if sys.hexversion < 0x03000000:
izip = itertools.izip
else:
izip = zip
def reshape_append_ones (a1, a2):
"""Returns a list with the two arguments, any of them may be
processed. If the arguments are numpy.ndarrays, append 1s to the
......@@ -164,13 +169,17 @@ def _make_tensor_array_contiguous(array):
return array.transpose(0, 2, 1)
return array
class VTKArray(numpy.ndarray):
"""This is a sub-class of numpy ndarray that stores a
reference to a vtk array as well as the owning dataset.
The numpy array and vtk array should point to the same
memory location."""
def __metaclass__(name, parent, attr):
def _metaclass(mcs):
"""For compatibility between python 2 and python 3."""
def decorator(cls):
body = vars(cls).copy()
body.pop('__dict__', None)
body.pop('__weakref__', None)
return mcs(cls.__name__, cls.__bases__, body)
return decorator
class VTKArrayMetaClass(type):
def __new__(mcs, name, parent, attr):
"""We overwrite numerical/comparison operators because we might need
to reshape one of the arrays to perform the operation without
broadcast errors. For instace:
......@@ -219,7 +228,8 @@ class VTKArray(numpy.ndarray):
add_default_numeric_ops("add")
add_default_numeric_ops("sub")
add_default_numeric_ops("mul")
add_default_numeric_ops("div")
if sys.hexversion < 0x03000000:
add_default_numeric_ops("div")
add_default_numeric_ops("truediv")
add_default_numeric_ops("floordiv")
add_default_numeric_ops("mod")
......@@ -236,8 +246,14 @@ class VTKArray(numpy.ndarray):
add_default_numeric_op("ne")
add_default_numeric_op("ge")
add_default_numeric_op("gt")
return type(name, parent, attr)
return type.__new__(mcs, name, parent, attr)
@_metaclass(VTKArrayMetaClass)
class VTKArray(numpy.ndarray):
"""This is a sub-class of numpy ndarray that stores a
reference to a vtk array as well as the owning dataset.
The numpy array and vtk array should point to the same
memory location."""
def _numeric_op(self, other, attr_name):
"""Used to implement numpy-style numerical operations such as __add__,
......@@ -292,19 +308,8 @@ class VTKArray(numpy.ndarray):
raise AttributeError("class has no attribute %s" % name)
return getattr(self.VTKObject, name)
class VTKNoneArray(object):
"""VTKNoneArray is used to represent a "void" array. An instance
of this class (NoneArray) is returned instead of None when an
array that doesn't exist in a DataSetAttributes is requested.
All operations on the NoneArray return NoneArray. The main reason
for this is to support operations in parallel where one of the
processes may be working on an empty dataset. In such cases,
the process is still expected to evaluate a whole expression because
some of the functions may perform bulk MPI communication. None
cannot be used in these instances because it cannot properly override
operators such as __add__, __sub__ etc. This is the main raison
d'etre for VTKNoneArray."""
def __metaclass__(name, parent, attr):
class VTKNoneArrayMetaClass(type):
def __new__(mcs, name, parent, attr):
"""Simplify the implementation of the numeric/logical sequence API."""
def _add_op(attr_name, op):
"""Create an attribute named attr_name that calls
......@@ -330,7 +335,8 @@ class VTKNoneArray(object):
_add_default_ops("add")
_add_default_ops("sub")
_add_default_ops("mul")
_add_default_ops("div")
if sys.hexversion < 0x03000000:
_add_default_ops("div")
_add_default_ops("truediv")
_add_default_ops("floordiv")
_add_default_ops("mod")
......@@ -349,7 +355,21 @@ class VTKNoneArray(object):
_add_default_op("ne")
_add_default_op("ge")
_add_default_op("gt")
return type(name, parent, attr)
return type.__new__(mcs, name, parent, attr)
@_metaclass(VTKNoneArrayMetaClass)
class VTKNoneArray(object):
"""VTKNoneArray is used to represent a "void" array. An instance
of this class (NoneArray) is returned instead of None when an
array that doesn't exist in a DataSetAttributes is requested.
All operations on the NoneArray return NoneArray. The main reason
for this is to support operations in parallel where one of the
processes may be working on an empty dataset. In such cases,
the process is still expected to evaluate a whole expression because
some of the functions may perform bulk MPI communication. None
cannot be used in these instances because it cannot properly override
operators such as __add__, __sub__ etc. This is the main raison
d'etre for VTKNoneArray."""
def __getitem__(self, index):
return NoneArray
......@@ -361,18 +381,8 @@ class VTKNoneArray(object):
NoneArray = VTKNoneArray()
class VTKCompositeDataArray(object):
"""This class manages a set of arrays of the same name contained
within a composite dataset. Its main purpose is to provide a
Numpy-type interface to composite data arrays which are naturally
nothing but a collection of vtkDataArrays. A VTKCompositeDataArray
makes such a collection appear as a single Numpy array and support
all array operations that this module and the associated algorithm
module support. Note that this is not a subclass of a Numpy array
and as such cannot be passed to native Numpy functions. Instead
VTK modules should be used to process composite arrays.
"""
def __metaclass__(name, parent, attr):
class VTKCompositeDataArrayMetaClass(type):
def __new__(mcs, name, parent, attr):
"""Simplify the implementation of the numeric/logical sequence API."""
def add_numeric_op(attr_name, op):
"""Create an attribute named attr_name that calls
......@@ -406,7 +416,8 @@ class VTKCompositeDataArray(object):
add_default_numeric_ops("add")
add_default_numeric_ops("sub")
add_default_numeric_ops("mul")
add_default_numeric_ops("div")
if sys.hexversion < 0x03000000:
add_default_numeric_ops("div")
add_default_numeric_ops("truediv")
add_default_numeric_ops("floordiv")
add_default_numeric_ops("mod")
......@@ -425,7 +436,20 @@ class VTKCompositeDataArray(object):
add_default_numeric_op("ne")
add_default_numeric_op("ge")
add_default_numeric_op("gt")
return type(name, parent, attr)
return type.__new__(mcs, name, parent, attr)
@_metaclass(VTKCompositeDataArrayMetaClass)
class VTKCompositeDataArray(object):
"""This class manages a set of arrays of the same name contained
within a composite dataset. Its main purpose is to provide a
Numpy-type interface to composite data arrays which are naturally
nothing but a collection of vtkDataArrays. A VTKCompositeDataArray
makes such a collection appear as a single Numpy array and support
all array operations that this module and the associated algorithm
module support. Note that this is not a subclass of a Numpy array
and as such cannot be passed to native Numpy functions. Instead
VTK modules should be used to process composite arrays.
"""
def __init__(self, arrays = [], dataset = None, name = None,
association = ArrayAssociation.FIELD):
......@@ -487,7 +511,7 @@ class VTKCompositeDataArray(object):
self.__init_from_composite()
res = []
if type(index) == VTKCompositeDataArray:
for a, idx in itertools.izip(self._Arrays, index.Arrays):
for a, idx in izip(self._Arrays, index.Arrays):
if a is not NoneArray:
res.append(a.__getitem__(idx))
else:
......@@ -506,7 +530,7 @@ class VTKCompositeDataArray(object):
self.__init_from_composite()
res = []
if type(other) == VTKCompositeDataArray:
for a1, a2 in itertools.izip(self._Arrays, other.Arrays):
for a1, a2 in izip(self._Arrays, other.Arrays):
if a1 is not NoneArray and a2 is not NoneArray:
l = reshape_append_ones(a1, a2)
res.append(op(l[0],l[1]))
......@@ -527,7 +551,7 @@ class VTKCompositeDataArray(object):
self.__init_from_composite()
res = []
if type(other) == VTKCompositeDataArray:
for a1, a2 in itertools.izip(self._Arrays, other.Arrays):
for a1, a2 in izip(self._Arrays, other.Arrays):
if a1 is not NoneArray and a2 is notNoneArray:
l = reshape_append_ones(a2,a1)
res.append(op(l[0],l[1]))
......@@ -621,7 +645,9 @@ class DataSetAttributes(VTKObjectWrapper):
if not isinstance(narray, numpy.ndarray) or numpy.ndim(narray) == 0: # Scalar input
narray = narray * numpy.ones(arrLength)
elif narray.shape[0] != arrLength: # Vector input
components = reduce(operator.mul, narray.shape)
components = 1
for l in narray.shape:
components *= l
narray = narray.flatten() * numpy.ones((arrLength, components))
shape = narray.shape
......@@ -712,8 +738,8 @@ class CompositeDataSetAttributes():
# don't add the narray since it's a scalar. GetArray() will create a
# VTKCompositeArray on-demand.
else:
for ds, array in itertools.izip(self.DataSet, narray.Arrays):
if array != None:
for ds, array in izip(self.DataSet, narray.Arrays):
if array is not None:
ds.GetAttributes(self.Association).append(array, name)
added = True
if added:
......@@ -784,7 +810,7 @@ class MultiCompositeDataIterator(CompositeDataIterator):
CompositeDataIterator.__init__(self, cds[0])
self.Datasets = cds
def next(self):
def __next__(self):
if not self.Iterator:
raise StopIteration
......@@ -798,6 +824,9 @@ class MultiCompositeDataIterator(CompositeDataIterator):
self.Iterator.GoToNextItem()
return retVal
def next(self):
return self.__next__()
class DataObject(VTKObjectWrapper):
"""A wrapper for vtkDataObject that makes it easier to access FielData
arrays as VTKArrays
......@@ -905,7 +934,7 @@ class CompositeDataSet(DataObject):
pts.append(NoneArray)
else:
pts.append(_pts)
if len(pts) == 0 or all(map(lambda a : a is NoneArray, pts)):
if len(pts) == 0 or all([a is NoneArray for a in pts]):
cpts = NoneArray
else:
cpts = VTKCompositeDataArray(pts, dataset=self)
......
......@@ -522,7 +522,7 @@ def make_vector(ax, ay, az=None):
if ax is dsa.NoneArray or ay is dsa.NoneArray or ay is dsa.NoneArray:
return dsa.NoneArray
if len(ax.shape) != 1 or len(ay.shape) != 1 or (az != None and len(az.shape) != 1):
if len(ax.shape) != 1 or len(ay.shape) != 1 or (az is not None and len(az.shape) != 1):
raise ValueError("Can only merge 1D arrays")
if az is None:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment