# <<BEGIN-copyright>>
# <<END-copyright>>
"""
This module contains the endl3dmath class. Also see the supporting module endl3dmathmisc.py.
"""
import os
from fudge.core import fudgemisc
from fudge.core.utilities import brb, fudgeFileMisc, subprocessing
from fudge.vis.gnuplot import plotbase
from fudge.core.math import fudgemath
import endl1dmathClasses
import endl2dmathmisc
import endl3dmathmisc
__metaclass__ = type
[docs]class endl3dmath :
"""
This class is designed to support operations on a list of 3-column number data that may be useful to scientists.
(A number is an integer or a float.) In this documenation the three columns are referred to as x, y and z.
The data is to be stored as a python list of [ x, yz-data-for-x ], where yz-data-for-x is a list of
[ y, z ] data. For example, consider the 3 column data::
1 1 1
1 2 2
1 3 1
2 1 1
2 2 0
2 4 0
3 2 0
3 3 1
3 4 2
This data is stored using python lists as ::
[ [ 1, [ [ 1, 1 ], [ 2, 2 ], [ 3, 1 ] ] ],
[ 2, [ [ 1, 1 ], [ 2, 0 ], [ 4, 0 ] ] ]
[ 3, [ [ 2, 0 ], [ 3, 1 ], [ 4, 2 ] ] ] ]
Members::
data | A python list of numbers.
columns | This is always 3 as this is 1d data.
xLabel | Labelled used as the x-Label when plotting.
yLabel | Labelled used as the y-Label when plotting.
zLabel | Labelled used as the z-Label when plotting.
"""
def __init__( self, data = None, checkDataType = False, xLabel = None, yLabel = None, zLabel = None, label = "unknown", interpolation = 0, template = None ) :
"""Creates an endl3dmath instance. Data must be of type list[ number, list[ number, number ] ].
The default plotting labels can be set with xLabel, yLabel and zLabel."""
if( data is None ) : data = []
if( interpolation != 0 ) : raise Exception( "\nError in endl3dmath.__init__: Bad interpolation value = %s" % `interpolation` )
self.interpolation = interpolation
self.data = endl3dmathmisc.get3dmathData( data, 'endl3dmath.__init__', 'data' )
if ( template is not None ) : endl3dmathmisc.valid3dClassType( template, "endl3dmath.__init__", "template" )
self.columns = 3
self.xLabel = xLabel
if ( ( template is not None ) and ( xLabel is None ) ) : self.xLabel = template.xLabel
self.yLabel = yLabel
if ( ( template is not None ) and ( yLabel is None ) ) : self.yLabel = template.yLabel
self.zLabel = zLabel
if ( ( template is not None ) and ( zLabel is None ) ) : self.zLabel = template.zLabel
self.label = label
if( self.interpolation == 0 ) :
self.xInterpolation = 'linear,linear,linear'
else :
raise Exception( 'Unsupported interpolation = %d for 3d data' % self.interpolation )
def __getitem__( self, xy ) :
"""See getValue for current functioning. This method will be changed in version 4 to return
self.data[i] (i.e., the (i+1)^th element of self.data)."""
return( self.getValue( xy[0], xy[1] ) )
def __len__( self ) :
"""Returns the number of items in the top list of self (i.e. the number
of differce x values)."""
return len( self.data )
def __repr__( self ) :
"""Returns a printable string of the data in self. This method uses endl3dmathmisc.endl3d_repr_xFormat,
endl3dmathmisc.endl3d_repr_yFormat and endl3dmathmisc.endl3d_repr_zFormat to convert each point to a string."""
syz = "%%s %s %s" % ( endl3dmathmisc.endl3d_repr_yFormat, endl3dmathmisc.endl3d_repr_zFormat )
sxyz = []
for x_etal in self.data :
x = endl3dmathmisc.endl3d_repr_xFormat % x_etal[0]
for etal in x_etal[1] : sxyz.append( syz % ( x, etal[0], etal[1] ) )
s = '\n'.join( sxyz ) + '\n'
return s
def __add__( self, other ) :
"""Returns an endl3dmath instance that is the addition of self and other. If other is a number, then other is added
to all z-values. If other is an endl3dmath instance, then
1) a union of the x-values from self and other is formed,
2) for each x-value, the yz-values from self and other are obtained and added as two endl2dmath objects
which forms the addition at x.
Note that a renormalization of the returned instance may be needed for normalized distributions."""
if( fudgemath.isNumber( other ) ) :
other = float( other )
d = self.copyData( )
for x, yzs in d.data :
for yz in yzs : yz[1] = yz[1] + other
else :
data = endl3dmathmisc.valid3dClassType( other, "endl3dmath.__add__", "addition" )
if( len( self ) < 1 ) :
d = data.copyData( )
elif( len( data ) < 1 ) :
d = self.copyData( )
else :
try :
unitBaseSelf = self.unitbase
except :
unitBaseSelf = False
try :
unitBaseOther = other.unitbase
except :
unitBaseOther = False
xs = [ x for x, yz in self.data ]
for x, yz in other.data :
if( x not in xs ) : xs.append( x )
xs.sort( )
d = []
for x in xs :
yz = self.getAtX( x, unitBase = unitBaseSelf, endl2dmathObject = True ) + other.getAtX( x, unitBase = unitBaseOther,
endl2dmathObject = True )
d.append( [ x, yz.data ] )
d = endl3dmath( d, interpolation = min( self.interpolation, data.interpolation ) )
return( d )
def __sub__( self, other ) :
"""Returns an endl3dmath instance that is the subtraction of other from self. If other is a number, then other is subracted
from all z-values. If other is an endl3dmath instance, then
1) a union of the x-values from self and other is formed,
2) for each x-value, the yz-values from self and other are obtained and subtracted as two endl2dmath objects.
Note that a renormalization of the returned instance may be needed for normalized distributions."""
if( fudgemath.isNumber( other ) ) :
other = float( other )
d = self.copyData( )
for x, yzs in d.data :
for yz in yzs : yz[1] = yz[1] - other
else :
data = endl3dmathmisc.valid3dClassType( other, "endl3dmath.__sub__", "substraction" )
if( len( self ) < 1 ) :
d = data.copyData( )
elif( len( data ) < 1 ) :
d = self.copyData( )
else :
try :
unitBaseSelf = self.unitbase
except :
unitBaseSelf = False
try :
unitBaseOther = other.unitbase
except :
unitBaseOther = False
xs = [ x for x, yz in self.data ]
for x, yz in other.data :
if( x not in xs ) : xs.append( x )
xs.sort( )
d = []
for x in xs :
yz = self.getAtX( x, unitBase = unitBaseSelf, endl2dmathObject = True ) - other.getAtX( x, unitBase = unitBaseOther,
endl2dmathObject = True )
d.append( [ x, yz.data ] )
d = endl3dmath( d, interpolation = min( self.interpolation, data.interpolation ) )
return( d )
def __mul__( self, other ) :
"""Returns an endl3dmath instance that is the multiplication of self by other. Currently, other must be a number."""
if( fudgemath.isNumber( other ) ) :
d = self.copyData( )
for x, yzs in d.data :
for yz in yzs : yz[1] = yz[1] * other
else :
raise Exception( 'multiplier of an endl3dmath instance can only be a number, %s is not allowed' % brb.getType( other ) )
def __div__( self, other ) :
"""Returns an endl3dmath instance that is the division of self by other. Currently, other must be a number."""
if( fudgemath.isNumber( other ) ) :
d = self.copyData( )
for x, yzs in d.data :
for yz in yzs : yz[1] = yz[1] / other
else :
raise Exception( 'divisor of an endl3dmath instance can only be a number, %s is not allowed' % brb.getType( other ) )
[docs] def copyData( self ) :
"""Returns an endl3dmath instance that is a copy, and not a reference, of self."""
d3 = []
for x_yz in self.data :
d2 = []
for yz in x_yz[1] : d2.append( [ yz[0], yz[1] ] )
d3.append( [ x_yz[0], d2 ] )
return endl3dmath( d3, checkDataType = 0, template = self )
[docs] def copyDataToW_XYs( self, wUnit = None, xUnit = None, yUnit = None ) :
"""A function designed to work with fudgeMultiPlots.py. Not for general use."""
import copy
return( copy.copy( self.data ) )
[docs] def getAtX( self, x, unitBase = False, extrapolation = endl3dmathmisc.noExtrapolation, endl2dmathObject = False ) :
"""Calls endl3dmathmisc.interpolate3d to get self's yz data at x."""
return( endl3dmathmisc.interpolate3d( x, self.data, unitBase = unitBase, extrapolation = extrapolation, endl2dmathObject = endl2dmathObject ) )
[docs] def setAtX( self, x, data ) :
"""Sets self's yz data at x to data. Data can be a endl2dmath object."""
if( ( type( x ) == type( 1 ) ) or ( type( x ) == type( 1. ) ) ) :
points = endl2dmathmisc.get2dmathData( data, 'endl3dmathClasses.setAtX', 'points' )
i = 0
for xyz in self.data :
x_ = xyz[0]
if( x == x_ ) :
xyz[1] = points
return
elif( x < x_ ) :
break
i += 1
if( len( self.data ) == 0 ) :
self.data = [ [ x, points ] ]
else :
self.data.insert( i, [ x, points ] )
else :
raise Exception( '\nError in endl3dmath.setAtX: Bad x value. Must be a number.' )
[docs] def getDimensions( self ) :
"""Returns the dimensions (3 for endl3dmath) for this type of data."""
return( 3 )
@property
def dimension( self ) :
return( self.getDimensions( ) )
[docs] def getValue( self, x, y = None ) :
"""
Returns an endl2dmath instance of self evaluated at x if y is None. Otherwise, returns, as a float, the value of self at x, y.
If x (y) is outside the range of self's x (y) domain then 0. is returned.
"""
endl2dmath_atX = self.getAtX( x, endl2dmathObject = True )
if( y is None ) : return( endl2dmath_atX )
return( endl2dmath_atX.getValue( y ) )
[docs] def toString( self, format = None ) :
"""Returns the string returned by the endl3dmath's __repr__ function. This can be useful when endl3dmath is used as a base class."""
endl3d_repr_xFormat = endl3dmathmisc.endl3d_repr_xFormat
endl3d_repr_yFormat = endl3dmathmisc.endl3d_repr_yFormat
endl3d_repr_zFormat = endl3dmathmisc.endl3d_repr_zFormat
if( format is not None ) :
endl3dmathmisc.endl3d_repr_xFormat = format
endl3dmathmisc.endl3d_repr_yFormat = format
endl3dmathmisc.endl3d_repr_zFormat = format
s = endl3dmath.__repr__( self )
endl3dmathmisc.endl3d_repr_xFormat = endl3d_repr_xFormat
endl3dmathmisc.endl3d_repr_yFormat = endl3d_repr_yFormat
endl3dmathmisc.endl3d_repr_zFormat = endl3d_repr_zFormat
return( s )
[docs] def toStringWithPrefixSuffix( self, Prefix = "", Suffix = "" ) :
"""Returns a printable string of the data in self with Prefix and Suffix append to each line.
See __repr__ to change the output format."""
syz = "%%s %s %s" % ( endl3dmathmisc.endl3d_repr_yFormat, endl3dmathmisc.endl3d_repr_zFormat )
sxyz = []
for x_etal in self.data :
x = endl3dmathmisc.endl3d_repr_xFormat % x_etal[0]
for etal in x_etal[1] : sxyz.append( syz % ( x, etal[0], etal[1] ) )
return fudgemisc.stringWithPrefixSuffix( sxyz, Prefix, Suffix )
[docs] def plot( self, xMin = None, xMax = None, yMin = None, yMax = None, zMin = None, zMax = None, xyzlog = 0,
xLabel = None, yLabel = None, zLabel = None, title = None, xrot = None, zrot = None ) :
"""
Plots the data.
xyzlog values and meaning::
xyzlog plot-type for x-y-z axis
-----------------------------------
0 linear-linear-linear
1 log-linear-linear
2 linear-log-linear
3 log-log-linear
4 linear-linear-log
5 log-linear-log
6 linear-log-log
7 log-log-log
"""
if ( xLabel is None ) and ( self.xLabel is not None ) : xLabel = self.xLabel
if ( yLabel is None ) and ( self.yLabel is not None ) : yLabel = self.yLabel
if ( zLabel is None ) and ( self.zLabel is not None ) : zLabel = self.zLabel
dt = plotbase.parsePlotOptions( xMin, xMax, yMin, yMax, xLabel, yLabel, title, zMin = zMin, zMax = zMax, zLabel = zLabel, \
xrot = xrot, zrot = zrot )
f = fudgeFileMisc.fudgeTempFile( )
f.write( endl3dmath.__repr__( self ) )
f.close( )
p = os.path.join( __file__.split( 'fudge/legacy/' )[0], "fudge", "vis", "gnuplot", "endl3dplot.py" )
s = [ 'python', p, 'xyzlog', str( xyzlog ) ] + dt + [ f.getName( ) ]
subprocessing.spawn( s )
[docs] def xArray( self ) :
"""Returns an endl1dmath instances of the x values in self."""
xa = []
for xy in self.data : xa.append( xy[0] )
return endl1dmathClasses.endl1dmath( xa )
[docs] def xMax( self ) :
"""Returns the maximum x value of self if self contains data, otherwise it returns None."""
if ( len( self.data ) > 0 ) : return self.data[-1][0]
return None
[docs] def xMin( self ) :
"""Returns the minimum x value of self if self contains data, otherwise it returns None."""
if ( len( self.data ) > 0 ) : return self.data[0][0]
return None
[docs] def copyDataToGridWsAndXsAndYs( self ) :
"""A method to be like the W_XYs' method of the same name. Used in the vis.matplotlib.plot2d module."""
xs = [ x for x, yzs in self.data ]
ys = set( )
for x, yzs in self.data :
for y, z in yzs : ys.add( y )
ys = sorted( ys )
zs = []
for y in ys :
zs.append( [ self.getValue( x, y ) for x in xs ] )
return( xs, ys, zs )