Arrays

Introduction

pDynamo makes extensive use of various types of numerical array and has its own C library that implements them. It does not employ the Python NumPy library, principally because the latter does not provide a clear separation between Python and C code. However, there is no reason why users should not use NumPy in their pDynamo scripts and, indeed, it may be advantageous to do so if complicated array operations are to be performed.

Nevertheless, for many purposes, pDynamo's inbuilt arrays should be sufficient. Currently these consist of:

    • One, two and N-dimensional arrays of various types, including Boolean, Integer and Real. As examples, the arrays of the Real type are named RealArray1D, RealArray2D and RealArrayND, respectively.

    • Some specialized forms of Real matrix, including AntisymmetricMatrix, DoubleSymmetricMatrix and SymmetricMatrix.

    • Real arrays devoted to geometrical manipulations in three dimensions, notably Coordinates3, Matrix33 and Vector3.

Array types conform to the following notation:

    • extent is the number of items along a particular dimension of the array.

    • item is an individual element in the array.

    • rank is the number of dimensions in the array.

    • shape is the ensemble of extents for all the array's dimensions.

    • size is the total number of items in the array.

Building arrays

Each array type has its own constructors but the "non-geometrical" arrays can also be built via various functions in the Array package. A selection of these is as follows:

# . Import statements.

from pCore import DataType

from pScientific.Arrays import Array, StorageType


# . Construct initialized 1D arrays from an iterable.

integerArray1D = Array.FromIterable ( range ( 17 ), dataType = DataType.Integer )

realArray1D = Array.FromIterable ( [ -1.0, 30.0, 50.0 ] )


# . Construct uninitialized arrays which only require one extent.

booleanSkewMatrix = Array.WithExtent ( 4 , dataType = DataType.Boolean ,

storageType = StorageType.Antisymmetric )

realArray1D = Array.WithExtent ( 3 )


# . Construct uninitialized arrays which take many extents.

realArray2D = Array.WithExtents ( 7, 4 )

realArray4D = Array.WithExtents ( 9, 3, 5, 6 )

realSymmetricMatrix = Array.WithExtents ( 6, 6, storageType = StorageType.Symmetric )


# . Construct uninitialized arrays with explicit shapes.

realArray1D = Array.WithShape ( [ 3 ] )

realArray2D = Array.WithShape ( [ 7, 4 ] )

realArray4D = Array.WithShape ( [ 9, 3, 5, 6 ] )

In these examples, DataType and StorageType are Python enumerations. When the data and storage types are not specified for these functions they default to Real and Regular, respectively.

Instances of the "geometrical" arrays must be built using their specific constructors. Examples include:

# . Import statements.

from pScientific.Geometry3 import Coordinates3, Matrix33, Vector3


# . Uninitialized arrays.

direction = Vector3.Uninitialized ( )

xyz = Coordinates3.WithExtent ( 5 ) # . The second extent is always 3!


# . Arrays initialized to zero.

matrix = Matrix33.Null ( )

translation = Vector3.Null ( )


# . Other initialized arrays.

axis = Vector3.WithValues ( 1.0, 0.0, 0.0 )

rotation = Matrix33.MakeRotationAboutAxis ( angle, axis ) # . The angle is in radians.

Accessing array items

Individual items in an array can be accessed in standard Python fashion using square brackets. Indexing starts at 0, with negative indices denoting indexing from the end of an array dimension. For the specialized matrices, an integer is required for each dimension in the array, otherwise an error is raised. For regular Array1D, Array2D and ArrayND objects, however, slicing is also permitted. This follows standard Python terminology, with an integer or a slice specified for each array dimension. It is important to note that slicing does not generate a new array, but instead returns a minimal-rank view of the underlying parent object.

A Coordinates3 object is composite, behaving in some respects like a RealArray2D object of shape [n,3], and in others like a one-dimensional array of Vector3 objects. To reflect this dual behavior, Coordinates3 objects may be sliced like RealArray2D objects, but they may also be indexed with only a single integer or slice. If an integer is used a Vector3 view of the appropriate row in the Coordinates3 matrix will be returned, whereas a slice gives a Coordinates3 view of the underlying data.

Array views behave identically in most ways to arrays of the corresponding type, although there are some subtleties to consider when cloning and pickling. Thus, shallow cloning and pickling preserves the view, in the sense that the relation between the parent and view is maintained, whereas deep cloning generates a non-view array clone of the viewed data.

Array operations

Arrays undergo a wide range of operations. There are general operations, such as CopyTo, Set and Swap, that apply to all arrays, and more specialized ones that are only valid for certain array types. Examples of the latter include Sort for 1D arrays, and MatrixMultiply, Transpose and VectorMultiply which are implemented in the RealArray2D class.

Many array operations are delegated to an array's iterator. This is an iterator that loops over all the array's items in an order that is dependent on the array type. Both unary and binary iterator operations exist. The unary operations include All, Any, CountFalse, CountTrue and Not for Boolean iterators, Absolute, AbsoluteMaximum, CountSmall, DotSelf, FilterGreaterThan, FilterLessThan, Increment, Maximum, Minimum, Product, Scale, Sparsity and Sum for Integer and Real iterators, and a host of additional functions, such as Exponential, NaturalLogarithm, Norm2, Normalize, Power and Reciprocate for Real iterators. Examples of binary operations are And, Or and Xor for Boolean arrays, Add, Dot and Multiply for Integer and Real arrays, and Divide for Real arrays.

Finally, regular arrays can be flattened and reshaped using the Flatten and Reshape functions in the Array package. Flattening returns a 1D view of the original array (or the array itself if it is already 1D), whereas reshaping returns a view of the same size as the original array but with the new shape. Note that these operations are not always guaranteed to succeed as they can fail for certain array views.

Array printing

The Array package has two functions for printing arrays, both of which have an extensive series of options for tailoring how the printing is done. Simple examples of their use are:

# . Import statements.

from pScientific.Arrays import ArrayPrint, ArrayPrint2D


# . Print any type of array.

ArrayPrint ( realArray4D , itemFormat = "{:.3f}" ,

itemsPerRow = 20 ,

itemWidth = 5 ,

title = "4D Array" )


# . Print 2D arrays.

ArrayPrint2D ( booleanSkewMatrix , title = "Skew Matrix" )

ArrayPrint2D ( realArray2D , itemFormat = "{:.10f}" ,

title = "2D Array" )

In contrast to the general array constructors, these print functions can also be used to print the "geometrical" arrays.