Skip to content

Adding a New Dimension

LuoLei Zhao edited this page May 30, 2017 · 1 revision

This page is intended to document a step by step process for how to add a new attribute to filter through in Firefly. All changes will be made in the Loader File. The path to the loader file must be specified in the Configuration File as the variable: loaderScript. The variable must be set as a string.

  1. Identify the variables that you will need to load/Calculate the variables needed.

Firefly takes numpy arrays as input for color mapping and filtering. These variables can be loaded from an HDF5 file using the readsnap function, which returns a numpy array. If you need to map on a variable that is a function of multiple attributes of the numpy array, you can calculate the result directly using numpy functions.

  1. Add the numpy array to the loader.

Next, you will need to set the loader file to load the dimension. To do this, call the addDimension(string,numpyArray) function from the NumpyLoader class. An example for adding a few new dimensions can be found below:

# Prepare the numpy arrays
res = readsnap(datasetBase, snapshotNumber, 0)

#Calculate the weights from the original numpy array
weight = 2 * radius * res['rho']

# Initialize a loader. You only need one numpy Loader class for all variables loaded.
l = NumpyLoader()
#adding a new dimension
l.addDimension('Coordinates', res['p'])

l.addDimension('Mass', weight)
# To set this as a default colormap, copy this line and replace with the correct dimension
colormapMin, colormapMax = setDefaultRanges(weight) 
  1. Get a dimension variable from the Dataset class.

Now that we have the NumpyLoader set to load the correct variables, we now need to specify how we want this data to be used. To do this, we first need to get a dimension variable. Dimension variables are versatile classes that can be used whenever you want to specify a certain variable to be the subject of a colormap, size, or position setting.

We can add new variables using the addDimension(string loaderID, DimensionType type, int index, string internalID) function from the Dataset class.

An example, including what all arguments mean is shown below:


# Here, I am copying the Dimensions we added to the loader from the last step.
l = NumpyLoader()
l.addDimension('Coordinates', res['p'])
l.addDimension('Mass', weight)

#We initialize a new dataset variable 
ds0 = Dataset.create('PartType0')
#Then, we link this new dataset to the loader we created last step.
ds0.setLoader(l)

#For this first example, we want to be able to colormap by the mass of an object.
m0 = ds0.addDimension('Mass', DimensionType.Float, 0, 'm')
# Here, the first argument of the addDimension function, in this case "Mass", must
# match the string we specified as the first argument of the addDimension function.

# The second argument represents the type of data that is stored in this dimension.

#The third argument represents the index of the numpy array you are reading from. 

#The final argument is a string that is simply used for internal representation. It
# currently does not effect any visualization directly. However, make sure that each
# internal representation string is distinct.

# For this second example, we show how to specify a numpy array with multiple dimensions.
# In this case, we have the coordinate array, which can be split along 3 different axises. 
# We use the index argument to get each dimension individually.
x0 = ds0.addDimension('Coordinates', DimensionType.Float, 0, 'x')
y0 = ds0.addDimension('Coordinates', DimensionType.Float, 1, 'y')
z0 = ds0.addDimension('Coordinates', DimensionType.Float, 2, 'z')

The variables that you receive from the addDimension functions (In the example above, they are the variables m0, x0, y0 and z0), can then be passed to other functions, where they can be specified as the basis of a colormap, particle size, or particle position.

These functions must be called from the PointCloud object. A few examples are shown below:

# Only one pointCloud object is needed for each hdf5 file. This pointcloud object only
# needs to be initialized once and can be reused.
pc0 = PointCloud.create('pc0')

# Here, we are setting each point to be placed in the world based off of the 
# coordinate variables we specified earlier. This function takes 3 arguments, one for
# each axis. 
pc0.setDimensions(x0, y0, z0)

#The setData function allows us to specify which dimension we want to use for our colormaps.
# In this case, we are mapping by the mass variable 'm0', which we specified in the previous step.
pc0.setData(m0)        

# Here, we are specifying the size of each point based off of the dimension variable s0
pc0.setSize(s0)

  1. Add a new Entry to the dataModes Array

If you want to add an additional options to the interface under dataModes, simply add another string entry to the dataModes array. This array hold strings that will list the options that appear in dropdown menu on the User interface.

  1. Add a new condition to the setDataMode(string modeName) function

When the user selects an option from user interface, it will call the setDataMode function. The argument passed to the function is identical to the strings that you specified in the dataModes array. This will allow you to quickly switch between different dimension options.

An example of steps 4 and 5 are shown below:

#To add a new option in the UI, add a new string to this array.
# In this case, we only have two simple options. SingleColor, which displays
# all the points as a fixed color, and Masses, which performs colormapping relative to Mass.
dataModes = [
    'SingleColor',
    'Masses']

#The argument: mode, passed here is one of the entries specified in dataModes
# You need to add an additional if condition for each additional mode, along with 
# the display conditions.

def setDataMode(mode):
    global dataMode
    dataMode = mode
    dm = dataModes[mode]

    if(dm == 'SingleColor'):
#setting setData to none will disable all colormaps, and display the default color.
        pc0.setData(None)
        pc0.setSize(s0)
#The function setVisible can be used to hide a dataset
        pc0.setVisible(True) 
        pc0.setProgram(prog_fixedColor)
        pc0.setColor(Color(0.2, 0.2, 1, 0.1))
    elif(dm == 'Masses'):
#Here, we are specifying that we want to map around the variable m0
        pc0.setData(m0)        
        pc0.setSize(s0)
        pc0.setVisible(True)
        pc0.setProgram(prog_channel)
# If you want, you can change other settings. These settings are actually
# found in fireflyUi.py and can also be changed from the user interface.
        updateColormapBounds(colormapMin, colormapMax )
        enableSmoothingLength(False)
        enableColormapper(True)
        enableLogScale(True)
    redraw()

Sometimes you may want to change the shader options. In general, you will need to use prog_fixedColor for fixed pointColor options and prog_channel for colormaps. For more information, see Shader Files

Clone this wiki locally