Category: PhysiCell

PhysiCell Tools : python-loader

The newest tool for PhysiCell provides an easy way to load your PhysiCell output data into python for analysis. This builds upon previous work on loading data into MATLAB. A post on that tool can be found at:

http://www.mathcancer.org/blog/working-with-physicell-snapshots-in-matlab/.

PhysiCell stores output data as a MultiCell Digital Snapshot (MultiCellDS) that consists of several files for each time step and is probably stored in your ./output directory. pyMCDS is a python object that is initialized with the .xml file

What you’ll need

Anatomy of a MultiCell Digital Snapshot

Each time PhysiCell’s internal time tracker passes a time step where data is to be saved, it generates a number of files of various types. Each of these files will have a number at the end that indicates where it belongs in the sequence of outputs. All of the files from the first round of output will end in 00000000.* and the second round will be 00000001.* and so on. Let’s say we’re interested in a set of output from partway through the run, the 88th set of output files. The files we care about most from this set consists of:

  • output00000087.xml: This file is the main organizer of the data. It contains an overview of the data stored in the MultiCellDS as well as some actual data including:
    • Metadata about the time and runtime for the current time step
    • Coordinates for the computational domain
    • Parameters for diffusing substrates in the microenvironment
    • Column labels for the cell data
    • File names for the files that contain microenvironment and cell data at this time step
  • output00000087_microenvironment0.mat: This is a MATLAB matrix file that contains all of the data about the microenvironment at this time step
  • output00000087_cells_physicell.mat: This is a MATLAB matrix file that contains all of the tracked information about the individual cells in the model. It tells us things like the cells’ position, volume, secretion, cell cycle status, and user-defined cell parameters.

Setup

Using pyMCDS

From the appropriate file in your PhysiCell directory, wherever pyMCDS.py lives, you can use the data loader in your own scripts or in an interactive session. To start you have to import the pyMCDS class

from pyMCDS import pyMCDS

Loading the data

Data is loaded into python from the MultiCellDS by initializing the pyMCDS object. The initialization function for pyMCDS takes one required and one optional argument.

__init__(xml_file, [output_path = '.'])
    '''
    xml_file : string
        String containing the name of the output xml file
    output_path : 
        String containing the path (relative or absolute) to the directory
        where PhysiCell output files are stored
    '''

We are interested in reading output00000087.xml that lives in ~/path/to/PhysiCell/output (don’t worry Windows paths work too). We would initialize our pyMCDS object using those names and the actual data would be stored in a member dictionary called data.

mcds = pyMCDS('output00000087.xml', '~/path/to/PhysiCell/output')
# Now our data lives in:
mcds.data

We’ve tried to keep everything organized inside of this dictionary but let’s take a look at what we actually have in here. Of course in real output, there will probably not be a chemical named my_chemical, this is simply there to illustrate how multiple chemicals are handled.

 

Overview of mcds.data dictionary-of-dictionaries structure
The data member dictionary is a dictionary of dictionaries whose child dictionaries can be accessed through normal python dictionary syntax.

mcds.data['metadata']
mcds.data['continuum_variables']['my_chemical']

Each of these subdictionaries contains data, we will take a look at exactly what that data is and how it can be accessed in the following sections.

Metadata

Expanded metadata subdictionary

The metadata dictionary contains information about the time of the simulation as well as units for both times and space. Here and in later sections blue boxes indicate scalars and green boxes indicate strings. We can access each of these things using normal dictionary syntax. We’ve also got access to a helper function get_time() for the common operation of retrieving the simulation time.

>>> mcds.data['metadata']['time_units']
'min'
>>> mcds.get_time()
5220.0

Mesh

Expanded mesh dictionary

The mesh dictionary has a lot more going on than the metadata dictionary. It contains three numpy arrays, indicated by orange boxes, as well as another dictionary. The three arrays contain \(x\), \(y\) and \(z\) coordinates for the centers of the voxels that constiture the computational domain in a meshgrid format. This means that each of those arrays is tensors of rank three. Together they identify the coordinates of each possible point in the space.

In contrast, the arrays in the voxel dictionary are stored linearly. If we know that we care about voxel number 42, we want to use the stuff in the voxels dictionary. If we want to make a contour plot, we want to use the x_coordinates, y_coordinates, and z_coordinates arrays.

# We can extract one of the meshgrid arrays as a numpy array
>>> y_coords = mcds.data['mesh']['y_coordinates']
>>> y_coords.shape
(75, 75, 75)
>>> y_coords[0, 0, :4]
array([-740., -740., -740., -740.])

# We can also extract the array of voxel centers
>>> centers = mcds.data['mesh']['voxels']['centers']
>>> centers.shape
(3, 421875)
>>> centers[:, :4]
array([[-740., -720., -700., -680.],
       [-740., -740., -740., -740.],
       [-740., -740., -740., -740.]])

# We have a handy function to quickly extract the components of the full meshgrid
>>> xx, yy, zz = mcds.get_mesh()
>>> yy.shape
(75, 75, 75)
>>> yy[0, 0, :4]
array([-740., -740., -740., -740.])

# We can also use this to return the meshgrid describing an x, y plane
>>> xx, yy = mcds.get_2D_mesh()
>>> yy.shape
(75, 75)

 

Continuum variables

Expanded microenvironment dictionaries

The continuum_variables dictionary is the most complicated of the four. It contains subdictionaries that we access using the names of each of the chemicals in the microenvironment. In our toy example above, these are oxygen and my_chemical. If our model tracked diffusing oxygen, VEGF, and glucose, then the continuum_variables dictionary would contain a subdirectory for each of them.

For a particular chemical species in the microenvironment we have two more dictionaries called decay_rate and diffusion_coefficient, and a numpy array called data. The diffusion and decay dictionaries each complete the value stored as a scalar and the unit stored as a string. The numpy array contains the concentrations of the chemical in each voxel at this time and is the same shape as the meshgrids of the computational domain stored in the .data[‘mesh’] arrays.

# we need to know the names of the substrates to work with
# this data. We have a function to help us find them.
>>> mcds.get_substrate_names()
['oxygen', 'my_chemical']

# The diffusable chemical dictionaries are messy
# if we need to do a lot with them it might be easier
# to put them into their own instance
>>> oxy_dict = mcds.data['continuum_variables']['oxygen']
>>> oxy_dict['decay_rate']
{'value': 0.1, 'units': '1/min'}

# What we care about most is probably the numpy 
# array of concentrations
>>> oxy_conc = oxy_dict['data']
>>> oxy_conc.shape
(75, 75, 75)

# Alternatively, we can get the same array with a function
>>> oxy_conc2 = mcds.get_concentrations('oxygen')
>>> oxy_conc2.shape
(75, 75, 75)

# We can also get the concentrations on a plane using the
# same function and supplying a z value to "slice through"
# note that right now the z_value must be an exact match
# for a plane of voxel centers, in the future we may add
# interpolation.
>>> oxy_plane = mcds.get_concentrations('oxygen', z_value=100.0)
>>> oxy_plane.shape
(75, 75)

# we can also find the concentration in a single voxel using the
# position of a point within that voxel. This will give us an
# array of all concentrations at that point.
>>> mcds.get_concentrations_at(x=0., y=550., z=0.)
array([17.94514446,  0.99113448])

 

Discrete Cells

expanded cells dictionary

The discrete cells dictionary is relatively straightforward. It contains a number of numpy arrays that contain information regarding individual cells.  These are all 1-dimensional arrays and each corresponds to one of the variables specified in the output*.xml file. With the default settings, these are:

  • ID: unique integer that will identify the cell throughout its lifetime in the simulation
  • position(_x, _y, _z): floating point positions for the cell in \(x\), \(y\), and \(z\) directions
  • total_volume: total volume of the cell
  • cell_type: integer label for the cell as used in PhysiCell
  • cycle_model: integer label for the cell cycle model as used in PhysiCell
  • current_phase: integer specification for which phase of the cycle model the cell is currently in
  • elapsed_time_in_phase: time that cell has been in current phase of cell cycle model
  • nuclear_volume: volume of cell nucleus
  • cytoplasmic_volume: volume of cell cytoplasm
  • fluid_fraction: proportion of the volume due to fliud
  • calcified_fraction: proportion of volume consisting of calcified material
  • orientation(_x, _y, _z): direction in which cell is pointing
  • polarity:
  • migration_speed: current speed of cell
  • motility_vector(_x, _y, _z): current direction of movement of cell
  • migration_bias: coefficient for stochastic movement (higher is “more deterministic”)
  • motility_bias_direction(_x, _y, _z): direction of movement bias
  • persistence_time: time in-between direction changes for cell
  • motility_reserved:
# Extracting single variables is just like before
>>> cell_ids = mcds.data['discrete_cells']['ID']
>>> cell_ids.shape
(18595,)
>>> cell_ids[:4]
array([0., 1., 2., 3.])

# If we're clever we can extract 2D arrays
>>> cell_vec = np.zeros((cell_ids.shape[0], 3))
>>> vec_list = ['position_x', 'position_y', 'position_z']
>>> for i, lab in enumerate(vec_list):
...     cell_vec[:, i] = mcds.data['discrete_cells'][lab]
...
array([[ -69.72657128,  -39.02046405, -233.63178904],
       [ -69.84507464,  -22.71693265, -233.59277388],
       [ -69.84891462,   -6.04070516, -233.61816711],
       [ -69.845265  ,   10.80035554, -233.61667313]])

# We can get the list of all of the variables stored in this dictionary
>>> mcds.get_cell_variables()
['ID',
 'position_x',
 'position_y',
 'position_z',
 'total_volume',
 'cell_type',
 'cycle_model',
 'current_phase',
 'elapsed_time_in_phase',
 'nuclear_volume',
 'cytoplasmic_volume',
 'fluid_fraction',
 'calcified_fraction',
 'orientation_x',
 'orientation_y',
 'orientation_z',
 'polarity',
 'migration_speed',
 'motility_vector_x',
 'motility_vector_y',
 'motility_vector_z',
 'migration_bias',
 'motility_bias_direction_x',
 'motility_bias_direction_y',
 'motility_bias_direction_z',
 'persistence_time',
 'motility_reserved',
 'oncoprotein',
 'elastic_coefficient',
 'kill_rate',
 'attachment_lifetime',
 'attachment_rate']
# We can also get all of the cell data as a pandas DataFrame 
>>> cell_df = mcds.get_cell_df() 
>>> cell_df.head() 
ID     position_x   position_y    position_z total_volume cell_type cycle_model ... 
0.0   - 69.726571  - 39.020464  - 233.631789       2494.0       0.0         5.0 ... 
1.0   - 69.845075  - 22.716933  - 233.592774       2494.0       0.0         5.0 ... 
2.0   - 69.848915  - 6.040705   - 233.618167       2494.0       0.0         5.0 ... 
3.0   - 69.845265    10.800356  - 233.616673       2494.0       0.0         5.0 ... 
4.0   - 69.828161    27.324530  - 233.631579       2494.0       0.0         5.0 ... 

# if we want to we can also get just the subset of cells that
# are in a specific voxel
>>> vox_df = mcds.get_cell_df_at(x=0.0, y=550.0, z=0.0)
>>> vox_df.iloc[:, :5]
             ID  position_x  position_y  position_z  total_volume
26718  228761.0    6.623617  536.709341   -1.282934   2454.814507
52736  270274.0   -7.990034  538.184921    9.648955   1523.386488

Examples

These examples will not be made using our toy dataset described above but will instead be made using a single timepoint dataset that can be found at:

https://sourceforge.net/projects/physicell/files/Tutorials/MultiCellDS/3D_PhysiCell_matlab_sample.zip/download

Substrate contour plot

One of the big advantages of working with PhysiCell data in python is that we have access to its plotting tools. For the sake of example let’s plot the partial pressure of oxygen throughout the computational domain along the \(z = 0\) plane. Once we’ve loaded our data by initializing a pyMCDS object, we can work entirely within python to produce the plot.

from pyMCDS import pyMCDS
import numpy as np
import matplotlib.pyplot as plt

# load data
mcds = pyMCDS('output00003696.xml', '../output')

# Set our z plane and get our substrate values along it
z_val = 0.00
plane_oxy = mcds.get_concentrations('oxygen', z_slice=z_val)

# Get the 2D mesh for contour plotting
xx, yy = mcds.get_mesh()

# We want to be able to control the number of contour levels so we
# need to do a little set up
num_levels = 21
min_conc = plane_oxy.min()
max_conc = plane_oxy.max()
my_levels = np.linspace(min_conc, max_conc, num_levels)

# set up the figure area and add data layers
fig, ax = plt.subplot()
cs = ax.contourf(xx, yy, plane_oxy, levels=my_levels)
ax.contour(xx, yy, plane_oxy, color='black', levels = my_levels,
           linewidths=0.5)

# Now we need to add our color bar
cbar1 = fig.colorbar(cs, shrink=0.75)
cbar1.set_label('mmHg')

# Let's put the time in to make these look nice
ax.set_aspect('equal')
ax.set_xlabel('x (micron)')
ax.set_ylabel('y (micron)')
ax.set_title('oxygen (mmHg) at t = {:.1f} {:s}, z = {:.2f} {:s}'.format(
                                        mcds.get_time(),
                                        mcds.data['metadata']['time_units'],
                                        z_val,
                                        mcds.data['metadata']['spatial_units'])

plt.show()
oxygen partial pressures over z=0

Adding a cells layer

We can also use pandas to do fairly complex selections of cells to add to our plots. Below we use pandas and the previous plot to add a cells layer.

from pyMCDS import pyMCDS
import numpy as np
import matplotlib.pyplot as plt

# load data
mcds = pyMCDS('output00003696.xml', '../output')

# Set our z plane and get our substrate values along it
z_val = 0.00
plane_oxy = mcds.get_concentrations('oxygen', z_slice=z_val)

# Get the 2D mesh for contour plotting
xx, yy = mcds.get_mesh()

# We want to be able to control the number of contour levels so we
# need to do a little set up
num_levels = 21
min_conc = plane_oxy.min()
max_conc = plane_oxy.max()
my_levels = np.linspace(min_conc, max_conc, num_levels)

# get our cells data and figure out which cells are in the plane
cell_df = mcds.get_cell_df()
ds = mcds.get_mesh_spacing()
inside_plane = (cell_df['position_z'] < z_val + ds) \ & (cell_df['position_z'] > z_val - ds)
plane_cells = cell_df[inside_plane]

# We're going to plot two types of cells and we want it to look nice
colors = ['black', 'grey']
sizes = [20, 8]
labels = ['Alive', 'Dead']

# set up the figure area and add microenvironment layer
fig, ax = plt.subplot()
cs = ax.contourf(xx, yy, plane_oxy, levels=my_levels)

# get our cells of interest
alive_cells = plane_cells[plane_cells['cycle_model'] < 6]
dead_cells = plane_cells[plane_cells['cycle_model'] > 6]

# plot the cell layer
for i, plot_cells in enumerate((alive_cells, dead_cells)):
    ax.scatter(plot_cells['position_x'].values, 
            plot_cells['position_y'].values, 
            facecolor='none', 
            edgecolors=colors[i],
            alpha=0.6,
            s=sizes[i],
            label=labels[i])

# Now we need to add our color bar
cbar1 = fig.colorbar(cs, shrink=0.75)
cbar1.set_label('mmHg')

# Let's put the time in to make these look nice
ax.set_aspect('equal')
ax.set_xlabel('x (micron)')
ax.set_ylabel('y (micron)')
ax.set_title('oxygen (mmHg) at t = {:.1f} {:s}, z = {:.2f} {:s}'.format(
                                        mcds.get_time(),
                                        mcds.data['metadata']['time_units'],
                                        z_val,
                                        mcds.data['metadata']['spatial_units'])
ax.legend(loc='upper right')

plt.show()

adding a cell layer to the oxygen plot

Future Direction

The first extension of this project will be timeseries functionality. This will provide similar data loading functionality but for a time series of MultiCell Digital Snapshots instead of simply one point in time.

Share this:
Tags : , , , , ,

PhysiCell Tools : PhysiCell-povwriter

As PhysiCell matures, we are starting to turn our attention to better training materials and an ecosystem of open source PhysiCell tools. PhysiCell-povwriter is is designed to help transform your 3-D simulation results into 3-D visualizations like this one:

PhysiCell-povwriter transforms simulation snapshots into 3-D scenes that can be rendered into still images using POV-ray: an open source software package that uses raytracing to mimic the path of light from a source of illumination to a single viewpoint (a camera or an eye). The result is a beautifully rendered scene (at any resolution you choose) with very nice shading and lighting.

If you repeat this on many simulation snapshots, you can create an animation of your work.

What you’ll need

This workflow is entirely based on open source software:

Setup

Building PhysiCell-povwriter

After you clone PhysiCell-povwriter or download its source from a release, you’ll need to compile it. In the project’s root directory, compile the project by:

make

(If you need to set up a C++ PhysiCell development environment, click here for OSX or here for Windows.)

Next, copy povwriter (povwriter.exe in Windows) to either the root directory of your PhysiCell project, or somewhere in your path. Copy ./config/povwriter-settings.xml to the ./config directory of your PhysiCell project.

Editing resolutions in POV-ray

PhysiCell-povwriter is intended for creating “square” images, but POV-ray does not have any pre-created square rendering resolutions out-of-the-box. However, this is straightforward to fix.

  1. Open POV-Ray
  2. Go to the “tools” menu and select “edit resolution INI file”
  3. At the top of the INI file (which opens for editing in POV-ray), make a new profile:
    [1080x1080, AA]
    Width=480
    Height=480
    Antialias=On
    

  4. Make similar profiles (with unique names) to suit your preferences. I suggest one at 480×480 (as a fast preview), another at 2160×2160, and another at 5000×5000 (because they will be absurdly high resolution). For example:
    [2160x2160 no AA]
    Width=2160
    Height=2160
    Antialias=Off
    

    You can optionally make more profiles with antialiasing on (which provides some smoothing for areas of high detail), but you’re probably better off just rendering without antialiasing at higher resolutions and the scaling the image down as needed. Also, rendering without antialiasing will be faster.

  5. Once done making profiles, save and exit POV-Ray.
  6. The next time you open POV-Ray, your new resolution profiles will be available in the lefthand dropdown box.

Configuring PhysiCell-povwriter

Once you have copied povwriter-settings.xml to your project’s config file, open it in a text editor. Below, we’ll show the different settings.

Camera settings

<camera>
	<distance_from_origin units="micron">1500</distance_from_origin>
	<xy_angle>3.92699081699</xy_angle> <!-- 5*pi/4 -->
	<yz_angle>1.0471975512</yz_angle> <!-- pi/3 -->
</camera>

For simplicity, PhysiCell-POVray (currently) always aims the camera towards the origin (0,0,0), with “up” towards the positive z-axis. distance_from_origin sets how far the camera is placed from the origin. xy_angle sets the angle \(\theta\) from the positive x-axis in the xy-plane. yz_angle sets the angle \(\phi\) from the positive z-axis in the yz-plane. Both angles are in radians.

Options

<options>
	<use_standard_colors>true</use_standard_colors>
	<nuclear_offset units="micron">0.1</nuclear_offset>
	<cell_bound units="micron">750</cell_bound>
	<threads>8</threads>
</options>

use_standard_colors (if set to true) uses a built-in “paint-by-numbers” color scheme, where each cell type (identified with an integer) gets XML-defined colors for live, apoptotic, and dead cells. More on this below. If use_standard_colors is set to false, then PhysiCell-povwriter uses the my_pigment_and_finish_function in ./custom_modules/povwriter.cpp to color cells.

The nuclear_offset is a small additional height given to nuclei when cropping to avoid visual artifacts when rendering (which can cause some “tearing” or “bleeding” between the rendered nucleus and cytoplasm). cell_bound is used for leaving some cells out of bound: any cell with |x|, |y|, or |z| exceeding cell_bound will not be rendered. threads is used for parallelizing on multicore processors; note that it only speeds up povwriter if you are converting multiple PhysiCell outputs to povray files.

Save

<save> <!-- done -->
	<folder>output</folder> <!-- use . for root -->
	<filebase>output</filebase>
	<time_index>3696</time_index>
</save>

Use folder to tell PhysiCell-povwriter where the data files are stored. Use filebase to tell how the outputs are named. Typically, they have the form output########_cells_physicell.mat; in this case, the filebase is output. Lastly, use time_index to set the output number. For example if your file is output00000182_cells_physicell.mat, then filebase = output and time_index = 182.

Below, we’ll see how to specify ranges of indices at the command line, which would supersede the time_index given here in the XML.

Clipping planes

PhysiCell-povwriter uses clipping planes to help create cutaway views of the simulations. By default, 3 clipping planes are used to cut out an octant of the viewing area.

Recall that a plane can be defined by its normal vector and a point p on the plane. With these, the plane can be defined as all points satisfying

\[  \left( \vec{x} -\vec{p} \right) \cdot \vec{n} = 0 \]

These are then written out as a plane equation

\[ a x + by + cz + d = 0, \]

where

\[ (a,b,c) = \vec{n} \hspace{.5in} \textrm{ and  } \hspace{0.5in} d = \: – \vec{n} \cdot \vec{p}. \]

As of Version 1.0.0, we are having some difficulties with clipping planes that do not pass through the origin (0,0,0), for which \( d = 0 \).

In the config file, these planes are written as \( (a,b,c,d) \):

<clipping_planes> <!-- done --> 
	<clipping_plane>0,-1,0,0</clipping_plane>
	<clipping_plane>-1,0,0,0</clipping_plane>
	<clipping_plane>0,0,1,0</clipping_plane>
</clipping_planes>

Note that cells “behind” the plane (where \( ( \vec{x} – \vec{p} ) \cdot \vec{n} \le 0 \)) are rendered, and cells in “front” of the plane (where \( (\vec{x}-\vec{p}) \cdot \vec{n} > 0 \)) are not rendered. Cells that intersect the plane are partially rendered (using constructive geometry via union and intersection commands in POV-ray).

Cell color definitions

Within <cell_color_definitions>, you’ll find multiple <cell_colors> blocks, each of which defines the live, dead, and necrotic colors for a specific cell type (with the type ID indicated in the attribute). These colors are only applied if use_standard_colors is set to true in options. See above.

The live colors are given as two rgb (red,green,blue) colors for the cytoplasm and nucleus of live cells. Each element of this triple can range from 0 to 1, and not from 0 to 255 as in many raw image formats. Next, finish specifies ambient (how much highly-scattered background ambient light illuminates the cell), diffuse (how well light rays can illuminate the surface), and specular (how much of a shiny reflective splotch the cell gets).

See the POV-ray documentation for for information on the finish.

This is repeated to give the apoptotic and necrotic colors for the cell type.

<cell_colors type="0">
	<live>
		<cytoplasm>.25,1,.25</cytoplasm> <!-- red,green,blue --> 
		<nuclear>0.03,0.125</nuclear>
		<finish>0.05,1,0.1</finish> <!-- ambient,diffuse,specular -->
	</live>
	<apoptotic>
		<cytoplasm>1,0,0</cytoplasm> <!-- red,green,blue --> 
		<nuclear>0.125,0,0</nuclear>
		<finish>0.05,1,0.1</finish> <!-- ambient,diffuse,specular -->
	</apoptotic>
	<necrotic>
		<cytoplasm>1,0.5412,0.1490</cytoplasm> <!-- red,green,blue --> 
		<nuclear>0.125,0.06765,0.018625</nuclear>
		<finish>0.01,0.5,0.1</finish> <!-- ambient,diffuse,specular -->
	</necrotic>
</cell_colors>

Use multiple cell_colors blocks (each with type corresponding to the integer cell type) to define the colors of multiple cell types.

Using PhysiCell-povwriter

Use by the XML configuration file alone

The simplest syntax:

physicell$ ./povwriter

(Windows users: povwriter or povwriter.exe) will process ./config/povwriter-settings.xml and convert the single indicated PhysiCell snapshot to a .pov file.

If you run POV-writer with the default configuration file in the povwriter structure (with the supplied sample data), it will render time index 3696 from the immunotherapy example in our 2018 PhysiCell Method Paper:

physicell$ ./povwriter

povwriter version 1.0.0
================================================================================

Copyright (c) Paul Macklin 2019, on behalf of the PhysiCell project
OSI License: BSD-3-Clause (see LICENSE.txt)

Usage:
================================================================================
povwriter : run povwriter with config file ./config/settings.xml

povwriter FILENAME.xml : run povwriter with config file FILENAME.xml

povwriter x:y:z : run povwriter on data in FOLDER with indices from x
to y in incremenets of z

Example: ./povwriter 0:2:10 processes files:
./FOLDER/FILEBASE00000000_physicell_cells.mat
./FOLDER/FILEBASE00000002_physicell_cells.mat
...
./FOLDER/FILEBASE00000010_physicell_cells.mat
(See the config file to set FOLDER and FILEBASE)

povwriter x1,...,xn : run povwriter on data in FOLDER with indices x1,...,xn

Example: ./povwriter 1,3,17 processes files:
./FOLDER/FILEBASE00000001_physicell_cells.mat
./FOLDER/FILEBASE00000003_physicell_cells.mat
./FOLDER/FILEBASE00000017_physicell_cells.mat
(Note that there are no spaces.)
(See the config file to set FOLDER and FILEBASE)

Code updates at https://github.com/PhysiCell-Tools/PhysiCell-povwriter

Tutorial & documentation at http://MathCancer.org/blog/povwriter
================================================================================

Using config file ./config/povwriter-settings.xml ...
Using standard coloring function ...
Found 3 clipping planes ...
Found 2 cell color definitions ...
Processing file ./output/output00003696_cells_physicell.mat...
Matrix size: 32 x 66978
Creating file pov00003696.pov for output ...
Writing 66978 cells ...
done!

Done processing all 1 files!

The result is a single POV-ray file (pov00003696.pov) in the root directory.

Now, open that file in POV-ray (double-click the file if you are in Windows), choose one of your resolutions in your lefthand dropdown (I’ll choose 2160×2160 no antialiasing), and click the green “run” button.

You can watch the image as it renders. The result should be a PNG file (named pov00003696.png) that looks like this:

Cancer immunotherapy sample image, at time index 3696

Using command-line options to process multiple times (option #1)

Now, suppose we have more outputs to process. We still state most of the options in the XML file as above, but now we also supply a command-line argument in the form of start:interval:end. If you’re still in the povwriter project, note that we have some more sample data there. Let’s grab and process it:

physicell$ cd output
physicell$ unzip more_samples.zip
Archive: more_samples.zip
inflating: output00000000_cells_physicell.mat
inflating: output00000001_cells_physicell.mat
inflating: output00000250_cells_physicell.mat
inflating: output00000300_cells_physicell.mat
inflating: output00000500_cells_physicell.mat
inflating: output00000750_cells_physicell.mat
inflating: output00001000_cells_physicell.mat
inflating: output00001250_cells_physicell.mat
inflating: output00001500_cells_physicell.mat
inflating: output00001750_cells_physicell.mat
inflating: output00002000_cells_physicell.mat
inflating: output00002250_cells_physicell.mat
inflating: output00002500_cells_physicell.mat
inflating: output00002750_cells_physicell.mat
inflating: output00003000_cells_physicell.mat
inflating: output00003250_cells_physicell.mat
inflating: output00003500_cells_physicell.mat
inflating: output00003696_cells_physicell.mat

physicell$ ls

citation and license.txt
more_samples.zip
output00000000_cells_physicell.mat
output00000001_cells_physicell.mat
output00000250_cells_physicell.mat
output00000300_cells_physicell.mat
output00000500_cells_physicell.mat
output00000750_cells_physicell.mat
output00001000_cells_physicell.mat
output00001250_cells_physicell.mat
output00001500_cells_physicell.mat
output00001750_cells_physicell.mat
output00002000_cells_physicell.mat
output00002250_cells_physicell.mat
output00002500_cells_physicell.mat
output00002750_cells_physicell.mat
output00003000_cells_physicell.mat
output00003250_cells_physicell.mat
output00003500_cells_physicell.mat
output00003696.xml
output00003696_cells_physicell.mat

Let’s go back to the parent directory and run povwriter:

physicell$ ./povwriter 0:250:3500

povwriter version 1.0.0
================================================================================

Copyright (c) Paul Macklin 2019, on behalf of the PhysiCell project
OSI License: BSD-3-Clause (see LICENSE.txt)

Usage:
================================================================================
povwriter : run povwriter with config file ./config/settings.xml

povwriter FILENAME.xml : run povwriter with config file FILENAME.xml

povwriter x:y:z : run povwriter on data in FOLDER with indices from x
to y in incremenets of z

Example: ./povwriter 0:2:10 processes files:
./FOLDER/FILEBASE00000000_physicell_cells.mat
./FOLDER/FILEBASE00000002_physicell_cells.mat
...
./FOLDER/FILEBASE00000010_physicell_cells.mat
(See the config file to set FOLDER and FILEBASE)

povwriter x1,...,xn : run povwriter on data in FOLDER with indices x1,...,xn

Example: ./povwriter 1,3,17 processes files:
./FOLDER/FILEBASE00000001_physicell_cells.mat
./FOLDER/FILEBASE00000003_physicell_cells.mat
./FOLDER/FILEBASE00000017_physicell_cells.mat
(Note that there are no spaces.)
(See the config file to set FOLDER and FILEBASE)

Code updates at https://github.com/PhysiCell-Tools/PhysiCell-povwriter

Tutorial & documentation at http://MathCancer.org/blog/povwriter
================================================================================

Using config file ./config/povwriter-settings.xml ...
Using standard coloring function ...
Found 3 clipping planes ...
Found 2 cell color definitions ...
Matrix size: 32 x 18317
Processing file ./output/output00000000_cells_physicell.mat...
Creating file pov00000000.pov for output ...
Writing 18317 cells ...
Processing file ./output/output00002000_cells_physicell.mat...
Matrix size: 32 x 33551
Creating file pov00002000.pov for output ...
Writing 33551 cells ...
Processing file ./output/output00002500_cells_physicell.mat...
Matrix size: 32 x 43440
Creating file pov00002500.pov for output ...
Writing 43440 cells ...
Processing file ./output/output00001500_cells_physicell.mat...
Matrix size: 32 x 40267
Creating file pov00001500.pov for output ...
Writing 40267 cells ...
Processing file ./output/output00003000_cells_physicell.mat...
Matrix size: 32 x 56659
Creating file pov00003000.pov for output ...
Writing 56659 cells ...
Processing file ./output/output00001000_cells_physicell.mat...
Matrix size: 32 x 74057
Creating file pov00001000.pov for output ...
Writing 74057 cells ...
Processing file ./output/output00003500_cells_physicell.mat...
Matrix size: 32 x 66791
Creating file pov00003500.pov for output ...
Writing 66791 cells ...
Processing file ./output/output00000500_cells_physicell.mat...
Matrix size: 32 x 114316
Creating file pov00000500.pov for output ...
Writing 114316 cells ...
done!

Processing file ./output/output00000250_cells_physicell.mat...
Matrix size: 32 x 75352
Creating file pov00000250.pov for output ...
Writing 75352 cells ...
done!

Processing file ./output/output00002250_cells_physicell.mat...
Matrix size: 32 x 37959
Creating file pov00002250.pov for output ...
Writing 37959 cells ...
done!

Processing file ./output/output00001750_cells_physicell.mat...
Matrix size: 32 x 32358
Creating file pov00001750.pov for output ...
Writing 32358 cells ...
done!

Processing file ./output/output00002750_cells_physicell.mat...
Matrix size: 32 x 49658
Creating file pov00002750.pov for output ...
Writing 49658 cells ...
done!

Processing file ./output/output00003250_cells_physicell.mat...
Matrix size: 32 x 63546
Creating file pov00003250.pov for output ...
Writing 63546 cells ...
done!

done!

done!

done!

Processing file ./output/output00001250_cells_physicell.mat...
Matrix size: 32 x 54771
Creating file pov00001250.pov for output ...
Writing 54771 cells ...
done!

done!

done!

done!

Processing file ./output/output00000750_cells_physicell.mat...
Matrix size: 32 x 97642
Creating file pov00000750.pov for output ...
Writing 97642 cells ...
done!

done!

Done processing all 15 files!

Notice that the output appears a bit out of order. This is normal: povwriter is using 8 threads to process 8 files at the same time, and sending some output to the single screen. Since this is all happening simultaneously, it’s a bit jumbled (and non-sequential). Don’t panic. You should now have created pov00000000.povpov00000250.pov, … , pov00003500.pov.

Now, go into POV-ray, and choose “queue.” Click “Add File” and select all 15 .pov files you just created:

Hit “OK” to let it render all the povray files to create PNG files (pov00000000.png, … , pov00003500.png).

Using command-line options to process multiple times (option #2)

You can also give a list of indices. Here’s how we render time indices 250, 1000, and 2250:

physicell$ ./povwriter 250,1000,2250

povwriter version 1.0.0
================================================================================

Copyright (c) Paul Macklin 2019, on behalf of the PhysiCell project
OSI License: BSD-3-Clause (see LICENSE.txt)

Usage:
================================================================================
povwriter : run povwriter with config file ./config/settings.xml

povwriter FILENAME.xml : run povwriter with config file FILENAME.xml

povwriter x:y:z : run povwriter on data in FOLDER with indices from x
to y in incremenets of z

Example: ./povwriter 0:2:10 processes files:
./FOLDER/FILEBASE00000000_physicell_cells.mat
./FOLDER/FILEBASE00000002_physicell_cells.mat
...
./FOLDER/FILEBASE00000010_physicell_cells.mat
(See the config file to set FOLDER and FILEBASE)

povwriter x1,...,xn : run povwriter on data in FOLDER with indices x1,...,xn

Example: ./povwriter 1,3,17 processes files:
./FOLDER/FILEBASE00000001_physicell_cells.mat
./FOLDER/FILEBASE00000003_physicell_cells.mat
./FOLDER/FILEBASE00000017_physicell_cells.mat
(Note that there are no spaces.)
(See the config file to set FOLDER and FILEBASE)

Code updates at https://github.com/PhysiCell-Tools/PhysiCell-povwriter

Tutorial & documentation at http://MathCancer.org/blog/povwriter
================================================================================

Using config file ./config/povwriter-settings.xml ...
Using standard coloring function ...
Found 3 clipping planes ...
Found 2 cell color definitions ...
Processing file ./output/output00002250_cells_physicell.mat...
Matrix size: 32 x 37959
Creating file pov00002250.pov for output ...
Writing 37959 cells ...
Processing file ./output/output00001000_cells_physicell.mat...
Matrix size: 32 x 74057
Creating file pov00001000.pov for output ...
Processing file ./output/output00000250_cells_physicell.mat...
Matrix size: 32 x 75352
Writing 74057 cells ...
Creating file pov00000250.pov for output ...
Writing 75352 cells ...
done!

done!

done!

Done processing all 3 files!

This will create files pov00000250.povpov00001000.pov, and pov00002250.pov. Render them in POV-ray just as before.

Advanced options (at the source code level)

If you set use_standard_colors to false, povwriter uses the function my_pigment_and_finish_function (at the end of  ./custom_modules/povwriter.cpp). Make sure that you set colors.cyto_pigment (RGB) and colors.nuclear_pigment (also RGB). The source file in povwriter has some hinting on how to write this. Note that the XML files saved by PhysiCell have a legend section that helps you do determine what is stored in each column of the matlab file.

Optional postprocessing

Image conversion / manipulation with ImageMagick

Suppose you want to convert the PNG files to JPEGs, and scale them down to 60% of original size. That’s very straightforward in ImageMagick:

physicell$ magick mogrify -format jpg -resize 60% pov*.png

Creating an animated GIF with ImageMagick

Suppose you want to create an animated GIF based on your images. I suggest first converting to JPG (see above) and then using ImageMagick again. Here, I’m adding a 20 ms delay between frames:

physicell$ magick convert -delay 20 *.jpg out.gif

Here’s the result:

Animated GIF created from raytraced still images. (You have to click the image to see the animation.)

Creating a compressed movie with Mencoder

Syntax coming later.

Closing thoughts and future work

In the future, we will probably allow more control over the clipping planes and a bit more debugging on how to handle planes that don’t pass through the origin. (First thoughts: we need to change how we use union and intersection commands in the POV-ray outputs.)

We should also look at adding some transparency for the cells. I’d prefer something like rgba (red-green-blue-alpha), but POV-ray uses filters and transmission, and we want to make sure to get it right.

Lastly, it would be nice to find a balance between the current very simple camera setup and better control.

Thanks for reading this PhysiCell Friday tutorial! Please do give PhysiCell at try (at http://PhysiCell.org) and read the method paper at PLoS Computational Biology.

Share this:
Tags : , , , , ,

Setting up the PhysiCell microenvironment with XML

As of release 1.6.0, users can define all the chemical substrates in the microenvironment with an XML configuration file. (These are stored by default in ./config/. The default parameter file is ./config/PhysiCell_settings.xml.) This should make it much easier to set up the microenvironment (previously required a lot of manual C++), as well as make it easier to change parameters and initial conditions.

This tutorial will show you the key techniques to use these features. (See the User_Guide for full documentation.) First, let’s create a barebones 2D project by populating the 2D template project. In a terminal shell in your root PhysiCell directory, do this:

make template2D

We will use this 2D project template for the remainder of the tutorial. We assume you already have a working copy of PhysiCell installed, version 1.6.0 or later. (If not, visit the PhysiCell tutorials to find installation instructions for your operating system.)

You can download the latest version of PhysiCell at:

Microenvironment setup in the XML configuration file

Next, let’s look at the parameter file. In your text editor of choice, open up ./config/PhysiCell_settings.xml, and browse down to <microenvironment_setup>:

<microenvironment_setup>
	<variable name="oxygen" units="mmHg" ID="0">
		<physical_parameter_set>
			<diffusion_coefficient units="micron^2/min">100000.0</diffusion_coefficient>
			<decay_rate units="1/min">0.1</decay_rate>  
		</physical_parameter_set>
		<initial_condition units="mmHg">38.0</initial_condition>
		<Dirichlet_boundary_condition units="mmHg" enabled="true">38.0</Dirichlet_boundary_condition>
	</variable>
	
	<options>
		<calculate_gradients>false</calculate_gradients>
		<track_internalized_substrates_in_each_agent>false</track_internalized_substrates_in_each_agent>
		<!-- not yet supported --> 
		<initial_condition type="matlab" enabled="false">
			<filename>./config/initial.mat</filename>
		</initial_condition>
		<!-- not yet supported --> 
		<dirichlet_nodes type="matlab" enabled="false">
			<filename>./config/dirichlet.mat</filename>
		</dirichlet_nodes>
	</options>
</microenvironment_setup>

Notice a few trends:

  • The <variable> XML element (tag) is used to define a chemical substrate in the microenvironment. The attributes say that it is named oxygen, and the units of measurement are mmHg. Notice also that the ID is 0: this unique integer identifier helps for finding and accessing the substrate within your PhysiCell project. Make sure your first substrate ID is 0, since C++ starts indexing at 0.
  • Within the <variable> block, we set the properties of this substrate:
    • <diffusion_coefficient> sets the (uniform) diffusion constant for the substrate.
    • <decay_rate> is the substrate’s background decay rate.
    • <initial_condition> is the value the substrate will be (uniformly) initialized to throughout the domain.
    • <Dirichlet_boundary_condition> is the value the substrate will be set to along the outer computational boundary throughout the simulation, if you set enabled=true. If enabled=false, then PhysiCell (via BioFVM) will use Neumann (zero flux) conditions for that substrate.
  • The <options> element helps configure other simulation behaviors:
    • Use <calculate_gradients> to control whether PhysiCell computes all chemical gradients at each time step. Set this to true to enable accurate gradients (e.g., for chemotaxis).
    • Use <track_internalized_substrates_in_each_agent> to enable or disable the PhysiCell feature of actively tracking the total amount of internalized substrates in each individual agent. Set this to true to enable the feature.
    • <initial_condition> is reserved for a future release where we can specify non-uniform initial conditions as an external file (e.g., a CSV or Matlab file). This is not yet supported.
    • <dirichlet_nodes> is reserved for a future release where we can specify Dirchlet nodes at any location in the simulation domain with an external file. This will be useful for irregular domains, but it is not yet implemented.

Note that PhysiCell does not convert units. The units attributes are helpful for clarity between users and developers, to ensure that you have worked in consistent length and time units. By default, PhysiCell uses minutes for all time units, and microns for all spatial units.

Changing an existing substrate

Let’s modify the oxygen variable to do the following:

  • Change the diffusion coefficient to 120000 \(\mu\mathrm{m}^2 / \mathrm{min}\)
  • Change the initial condition to 40 mmHg
  • Change the oxygen Dirichlet boundary condition to 42.7 mmHg
  • Enable gradient calculations

If you modify the appropriate fields in the <microenvironment_setup> block, it should look like this:

<microenvironment_setup>
	<variable name="oxygen" units="mmHg" ID="0">
		<physical_parameter_set>
			<diffusion_coefficient units="micron^2/min">120000.0</diffusion_coefficient>
			<decay_rate units="1/min">0.1</decay_rate>  
		</physical_parameter_set>
		<initial_condition units="mmHg">40.0</initial_condition>
		<Dirichlet_boundary_condition units="mmHg" enabled="true">42.7</Dirichlet_boundary_condition>
	</variable>
	
	<options>
		<calculate_gradients>true</calculate_gradients>
		<track_internalized_substrates_in_each_agent>false</track_internalized_substrates_in_each_agent>
		<!-- not yet supported --> 
		<initial_condition type="matlab" enabled="false">
			<filename>./config/initial.mat</filename>
		</initial_condition>
		<!-- not yet supported --> 
		<dirichlet_nodes type="matlab" enabled="false">
			<filename>./config/dirichlet.mat</filename>
		</dirichlet_nodes>
	</options>
</microenvironment_setup>

Adding a new diffusing substrate

Let’s add a new dimensionless substrate glucose with the following:

  • Diffusion coefficient is 18000 \(\mu\mathrm{m}^2 / \mathrm{min}\)
  • No decay rate
  • The initial condition is 1 (dimensionless)
  • Neumann (no flux) boundary conditions

To add the new variable, I suggest copying an existing variable (in this case, oxygen) and modifying to:

  • change the name and units throughout
  • increase the ID by one
  • write in the appropriate initial and boundary conditions

If you modify the appropriate fields in the <microenvironment_setup> block, it should look like this:

<microenvironment_setup>
	<variable name="oxygen" units="mmHg" ID="0">
		<physical_parameter_set>
			<diffusion_coefficient units="micron^2/min">120000.0</diffusion_coefficient>
			<decay_rate units="1/min">0.1</decay_rate>  
		</physical_parameter_set>
		<initial_condition units="mmHg">40.0</initial_condition>
		<Dirichlet_boundary_condition units="mmHg" enabled="true">42.7</Dirichlet_boundary_condition>
	</variable>
	
	<variable name="glucose" units="dimensionless" ID="1">
		<physical_parameter_set>
			<diffusion_coefficient units="micron^2/min">18000.0</diffusion_coefficient>
			<decay_rate units="1/min">0.0</decay_rate>  
		</physical_parameter_set>
		<initial_condition units="dimensionless">1</initial_condition>
		<Dirichlet_boundary_condition units="dimensionless" enabled="false">0</Dirichlet_boundary_condition>
	</variable>

	<options>
		<calculate_gradients>true</calculate_gradients>
		<track_internalized_substrates_in_each_agent>false</track_internalized_substrates_in_each_agent>
		<!-- not yet supported --> 
		<initial_condition type="matlab" enabled="false">
			<filename>./config/initial.mat</filename>
		</initial_condition>
		<!-- not yet supported --> 
		<dirichlet_nodes type="matlab" enabled="false">
			<filename>./config/dirichlet.mat</filename>
		</dirichlet_nodes>
	</options>
</microenvironment_setup>

Closing thoughts and future work

In the future, we plan to develop more of the options to allow users to set set the initial conditions externally and import them (via an external file), and to allow them to set up more complex domains by importing Dirichlet nodes.

More broadly, we are working to push more model specification from raw C++ to imported XML. It is our hope that this will vastly simplify model development, facilitate creation of graphical model editing tools, and ultimately broaden the class of developers who can use and contribute to PhysiCell. Thanks for giving it a try!

Share this:
Tags : , , ,

User parameters in PhysiCell

As of release 1.4.0, users can add any number of Boolean, integer, double, and string parameters to an XML configuration file. (These are stored by default in ./config/. The default parameter file is ./config/PhysiCell_settings.xml.) These parameters are automatically parsed into a parameters data structure, and accessible throughout a PhysiCell project.

This tutorial will show you the key techniques to use these features. (See the User_Guide for full documentation.) First, let’s create a barebones 2D project by populating the 2D template project. In a terminal shell in your root PhysiCell directory, do this:

make template2D

We will use this 2D project template for the remainder of the tutorial. We assume you already have a working copy of PhysiCell installed, version 1.4.0 or later. (If not, visit the PhysiCell tutorials to find installation instructions for your operating system.)

User parameters in the XML configuration file

Next, let’s look at the parameter file. In your text editor of choice, open up ./config/PhysiCell_settings.xml, and browse down to <user_parameters>, which will have some sample parameters from the 2D template project.

	<user_parameters>
		<random_seed type="int" units="dimensionless">0</random_seed> 
		<!-- example parameters from the template --> 
		
		<!-- motile cell type parameters --> 
		<motile_cell_persistence_time type="double" units="min">15</motile_cell_persistence_time>
		<motile_cell_migration_speed type="double" units="micron/min">0.5</motile_cell_migration_speed>
		<motile_cell_relative_adhesion type="double" units="dimensionless">0.05</motile_cell_relative_adhesion>
		<motile_cell_apoptosis_rate type="double" units="1/min">0.0</motile_cell_apoptosis_rate> 
		<motile_cell_relative_cycle_entry_rate type="double" units="dimensionless">0.1</motile_cell_relative_cycle_entry_rate>
	</user_parameters>

Notice a few trends:

  • Each XML element (tag) under <user_parameters> is a user parameter, whose name is the element name.
  • Each variable requires an attribute named “type”, with one of the following four values:
    • bool for a Boolean parameter
    • int for an integer parameter
    • double for a double (floating point) parameter
    • string for text string parameter

    While we do not encourage it, if no valid type is supplied, PhysiCell will attempt to interpret the parameter as a double.

  • Each variable here has an (optional) attribute “units”. PhysiCell does not convert units, but these are helpful for clarity between users and developers. By default, PhysiCell uses minutes for all time units, and microns for all spatial units.
  • Then, between the tags, you list the value of your parameter.

Let’s add the following parameters to the configuration file:

  • A string parameter called motile_color that sets the color of the motile_cell type in SVG outputs. Please refer to the User Guide (in the documentation folder) for more information on allowed color formats, including rgb values and named colors. Let’s use the value darkorange.
  • A double parameter called base_cycle_entry_rate that will give the rate of entry to the S cycle phase from the G1 phase for the default cell type in the code. Let’s use a ridiculously high value of 0.01 min-1.
  • A double parameter called base_apoptosis_rate for the default cell type. Let’s set the value at 1e-7 min-1.
  • A double parameter that sets the (relative) maximum cell-cell adhesion sensing distance, relative to the cell’s radius. Let’s set it at 2.5 (dimensionless). (The default is 1.25.)
  • A bool parameter that enables or disables placing a single motile cell in the initial setup. Let’s set it at true.

If you edit the <user_parameters> to include these, it should look like this:

	<user_parameters>
		<random_seed type="int" units="dimensionless">0</random_seed> 
		<!-- example parameters from the template --> 
		
		<!-- motile cell type parameters --> 
		<motile_cell_persistence_time type="double" units="min">15</motile_cell_persistence_time>
		<motile_cell_migration_speed type="double" units="micron/min">0.5</motile_cell_migration_speed>
		<motile_cell_relative_adhesion type="double" units="dimensionless">0.05</motile_cell_relative_adhesion>
		<motile_cell_apoptosis_rate type="double" units="1/min">0.0</motile_cell_apoptosis_rate> 
		<motile_cell_relative_cycle_entry_rate type="double" units="dimensionless">0.1</motile_cell_relative_cycle_entry_rate>
		
		<!-- for the tutorial --> 
		<motile_color type="string" units="dimensionless">darkorange</motile_color>
		
		<base_cycle_entry_rate type="double" units="1/min">0.01</base_cycle_entry_rate> 
		<base_apoptosis_rate type="double" units="1/min">1e-7</base_apoptosis_rate>
		<base_cell_adhesion_distance type="double" units="dimensionless">2.5</base_cell_adhesion_distance> 
		
		<include_motile_cell type="bool" units="dimensionless">true</include_motile_cell>
	</user_parameters>

Viewing the loaded parameters

Let’s compile and run the project.

make 
./project2D

At the beginning of the simulation, PhysiCell parses the <user_parameters> block into a global data structure called parameters, with sub-parts bools, ints, doubles, and strings. It displays these loaded parameters at the start of the simulation. Here’s what it looks like:

shell$  ./project2D
Using config file ./config/PhysiCell_settings.xml ...
User parameters in XML config file:
Bool parameters::
include_motile_cell: 1 [dimensionless]

Int parameters::
random_seed: 0 [dimensionless]

Double parameters::
motile_cell_persistence_time: 15 [min]
motile_cell_migration_speed: 0.5 [micron/min]
motile_cell_relative_adhesion: 0.05 [dimensionless]
motile_cell_apoptosis_rate: 0 [1/min]
motile_cell_relative_cycle_entry_rate: 0.1 [dimensionless]
base_cycle_entry_rate: 0.01 [1/min]
base_apoptosis_rate: 1e-007 [1/min]
base_cell_adhesion_distance: 2.5 [dimensionless]

String parameters::
motile_color: darkorange [dimensionless]

Getting parameter values

Within a PhysiCell project, you can access the value of any parameter by either its index or its name, so long as you know its type. Here’s an example of accessing the base_cell_adhesion_distance by its name:

/* this directly accesses the value of the parameter */ 
double temp = parameters.doubles( "base_cell_adhesion_distance" ); 
std::cout << temp << std::endl; 

/* this streams a formatted output including the parameter name and units */ 
std::cout << parameters.doubles[ "base_cell_adhesion_distance" ] << std::endl; 

std::cout << parameters.doubles["base_cell_adhesion_distance"].name << " " 
     << parameters.doubles["base_cell_adhesion_distance"].value << " " 
     << parameters.doubles["base_cell_adhesion_distance"].units << std::endl; 

Notice that accessing by () gets the value of the parameter in a user-friendly way, whereas accessing by [] gets the entire parameter, including its name, value, and units.

You can more efficiently access the parameter by first finding its integer index, and accessing by index:

/* this directly accesses the value of the parameter */ 
int my_index = parameters.doubles.find_index( "base_cell_adhesion_distance" ); 
double temp = parameters.doubles( my_index ); 
std::cout << temp << std::endl; 

/* this streams a formatted output including the parameter name and units */ 
std::cout << parameters.doubles[ my_index ] << std::endl; 

std::cout << parameters.doubles[ my_index ].name << " " 
     << parameters.doubles[ my_index ].value << " " 
     << parameters.doubles[ my_index ].units << std::endl; 

Similarly, we can access string and Boolean parameters. For example:

if( parameters.bools("include_motile_cell") == true )
{ std::cout << "I shall include a motile cell." << std::endl; }

int rand_ind = parameters.ints.find_index( "random_seed" ); 
std::cout << parameters.ints[rand_ind].name << " is at index " << rand_ind << std::endl; 

std::cout << "We'll use this nice color: " << parameters.strings( "motile_color" ); 

Using the parameters in custom functions

Let’s use these new parameters when setting up the parameter values of the simulation. For this project, all custom code is in ./custom_modules/custom.cpp. Open that source file in your favorite text editor. Look for the function called “create_cell_types“. In the code snipped below, we access the parameter values to set the appropriate parameters in the default cell definition, rather than hard-coding them.

	// add custom data here, if any 
	
	/* for the tutorial */ 
	cell_defaults.phenotype.cycle.data.transition_rate(G0G1_index,S_index) = 
		parameters.doubles("base_cycle_entry_rate"); 
	cell_defaults.phenotype.death.rates[apoptosis_model_index] = 
		parameters.doubles("base_apoptosis_rate"); 
		
	cell_defaults.phenotype.mechanics.set_relative_maximum_adhesion_distance( 
		parameters.doubles("base_cell_adhesion_distance") ); 

Next, let’s change the tissue setup (“setup_tissue“) to check our Boolean variable before placing the initial motile cell.

     // now create a motile cell 
     /*  remove this conditional for the normal project */ 
     if( parameters.bools("include_motile_cell") == true )
     {
           pC = create_cell( motile_cell ); 
           pC->assign_position( 15.0, -18.0, 0.0 );
     }

Lastly, let’s make use of the string parameter to change the plotting. Search for my_coloring_function and edit the source file to use the new color:

	// if the cell is motile and not dead, paint it black 
	
	static std::string motile_color = parameters.strings( "motile_color" );  // tutorial 
		
	if( pCell->phenotype.death.dead == false && pCell->type == 1 )
	{
		 output[0] = motile_color; 
		 output[2] = motile_color; 
	}

Notice the static here: We intend to call this function many, many times. For performance reasons, we don’t want to declare a string, instantiate it with motile_color, pass it to parameters.strings(), and then deallocate it once done. Instead, we store the search statically within the function, so that all future function calls will have access to that search result.

And that’s it! Compile your code, and give it a go.

make 
./project2D

This should create a lot of data in the ./output directory, including SVG files that color motile cells as darkorange, like this one below.

Now that this project is parsing the XML file to get parameter values, we don’t need to recompile to change a model parameter. For example, change motile_color to mediumpurple, set motile_cell_migration_speed to 0.25, and set motile_cell_relative_cycle_entry_rate to 2.0. Rerun the code (without compiling):

./project2D

And let’s look at the change in the final SVG output (output00000120.svg):

More notes on configuration files

You may notice other sections in the XML configuration file. I encourage you to explore them, but the meanings should be evident: you can set the computational domain size, the number of threads (for OpenMP parallelization), and how frequently (and where) data are stored. In future PhysiCell releases, we will continue adding more and more options to these XML files to simplify setup and configuration of PhysiCell models.

Share this:

Adding a directory to your Windows path

When you’re setting your BioFVM / PhysiCell g++ development environment, you’ll need to add the compiler, MSYS, and your text editor (like Notepad++) to your system path. For example, you may need to add folders like these to your system PATH variable:

  1. c:\Program Files\mingw-w64\x86_64-5.3.0-win32-seh-rt_v4_rev0\mingw64\bin\
  2. c:\Program Files (x86)\Notepad++\
  3. C:\MinGW\msys\1.0\bin\

Here’s how to do that in various versions of Windows.

Windows XP, 7, and 8

First, open up a text editor, and concatenate your three paths into a single block of text, separated by semicolons (;):

  1. Open notepad ([Windows]+R, notepad)
  2. Type a semicolon, paste in the first path, and append a semicolon. It should look like this:
    ;c:\Program Files\mingw-w64\x86_64-5.3.0-win32-seh-rt_v4_rev0\mingw64\bin\;
  3. Paste in the next path, and append a semicolon. It should look like this:
    ;c:\Program Files\mingw-w64\x86_64-5.3.0-win32-seh-rt_v4_rev0\mingw64\bin\;C:\Program Files (x86)\Notepad++\;
  4. Paste in the last path, and append a semicolon. It should look something like this:
    ;c:\Program Files\mingw-w64\x86_64-5.3.0-win32-seh-rt_v4_rev0\mingw64\bin\;C:\Program Files (x86)\Notepad++\;c:\MinGW\msys\1.0\bin\;

Lastly, add these paths to the system path:

  1. Go the Start Menu, the right-click “This PC” or “My Computer”, and choose “Properties.”
  2. Click on “Advanced system settings”
  3. Click on “Environment Variables…” in the “Advanced” tab
  4. Scroll through the “System Variables” below until you find Path.
  5. Select “Path”, then click “Edit…”
  6. At the very end of “Variable Value”, paste what you made in Notepad in the prior steps. Make sure to paste at the end of the existing value, rather than overwriting it!
  7. Hit OK, OK, and OK to completely exit the “Advanced system settings.”

Windows 10:

Windows 10 has made it harder to find these settings, but easier to edit them. First, let’s find the system path:

  1. At the “run / search / Cortana” box next to the start menu, type “view advanced”, and you should see “view advanced system settings” auto-complete:
  2. Click to enter the advanced system settings, then choose environment variables … at the bottom of this box, and scroll down the list of user variables to Path
  3. Click on edit, then click New to add a new path. In the new entry (a new line), paste in your first new path (the compiler):
  4. Repeat this for the other two paths, then click OK, OK, Apply, OK to apply the new paths and exit.
Share this:

Working with PhysiCell MultiCellDS digital snapshots in Matlab


PhysiCell 1.2.1 and later saves data as a specialized MultiCellDS digital snapshot, which includes chemical substrate fields, mesh information, and a readout of the cells and their phenotypes at single simulation time point. This tutorial will help you learn to use the matlab processing files included with PhysiCell.

This tutorial assumes you know (1) how to work at the shell / command line of your operating system, and (2) basic plotting and other functions in Matlab.

Key elements of a PhysiCell digital snapshot

A PhysiCell digital snapshot (a customized form of the MultiCellDS digital simulation snapshot) includes the following elements saved as XML and MAT files:

  1. output12345678.xml : This is the “base” output file, in MultiCellDS format. It includes key metadata such as when the file was created, the software, microenvironment information, and custom data saved at the simulation time. The Matlab files read this base file to find other related files (listed next). Example: output00003696.xml
  2. initial_mesh0.mat : This is the computational mesh information for BioFVM at time 0.0. Because BioFVM and PhysiCell do not use moving meshes, we do not save this data at any subsequent time.
  3. output12345678_microenvironment0.mat : This saves each biochemical substrate in the microenvironment at the computational voxels defined in the mesh (see above). Example: output00003696_microenvironment0.mat
  4. output12345678_cells.mat : This saves very basic cellular information related to BioFVM, including cell positions, volumes, secretion rates, uptake rates, and secretion saturation densities. Example: output00003696_cells.mat
  5. output12345678_cells_physicell.mat : This saves extra PhysiCell data for each cell agent, including volume information, cell cycle status, motility information, cell death information, basic mechanics, and any user-defined custom data. Example: output00003696_cells_physicell.mat

These snapshots make extensive use of Matlab Level 4 .mat files, for fast, compact, and well-supported saving of array data. Note that even if you cannot ready MultiCellDS XML files, you can work to parse the .mat files themselves.

The PhysiCell Matlab .m files

Every PhysiCell distribution includes some matlab functions to work with PhysiCell digital simulation snapshots, stored in the matlab subdirectory. The main ones are:

  1. composite_cutaway_plot.m : provides a quick, coarse 3-D cutaway plot of the discrete cells, with different colors for live (red), apoptotic (b), and necrotic (black) cells.
  2. read_MultiCellDS_xml.m : reads the “base” PhysiCell snapshot and its associated matlab files.
  3. set_MCDS_constants.m : creates a data structure MCDS_constants that has the same constants as PhysiCell_constants.h. This is useful for identifying cell cycle phases, etc.
  4. simple_cutaway_plot.m : provides a quick, coarse 3-D cutaway plot of user-specified cells.
  5. simple_plot.m : provides, a quick, coarse 3-D plot of the user-specified cells, without a cutaway or cross-sectional clipping plane.

A note on GNU Octave

Unfortunately, GNU octave does not include XML file parsing without some significant user tinkering. And one you’re done, it is approximately one order of magnitude slower than Matlab. Octave users can directly import the .mat files described above, but without the helpful metadata in the XML file. We’ll provide more information on the structure of these MAT files in a future blog post. Moreover, we plan to provide python and other tools for users without access to Matlab.

A sample digital snapshot

We provide a 3-D simulation snapshot from the final simulation time of the cancer-immune example in Ghaffarizadeh et al. (2017, in review) at:

https://sourceforge.net/projects/physicell/files/Tutorials/MultiCellDS/3D_PhysiCell_matlab_sample.zip/download

The corresponding SVG cross-section for that time (through = 0 μm) looks like this:

Unzip the sample dataset in any directory, and make sure the matlab files above are in the same directory (or in your Matlab path). If you’re inside matlab:

!unzip 3D_PhysiCell_matlab_sample.zip

Loading a PhysiCell MultiCellDS digital snapshot

Now, load the snapshot:

MCDS = read_MultiCellDS_xml( 'output00003696.xml'); 

This will load the mesh, substrates, and discrete cells into the MCDS data structure, and give a basic summary:

Typing ‘MCDS’ and then hitting ‘tab’ (for auto-completion) shows the overall structure of MCDS, stored as metadata, mesh, continuum variables, and discrete cells:

To get simulation metadata, such as the current simulation time, look at MCDS.metadata.current_time

Here, we see that the current simulation time is 30240 minutes, or 21 days. MCDS.metadata.current_runtime gives the elapsed walltime to up to this point: about 53 hours (1.9e5 seconds), including file I/O time to write full simulation data once per 3 simulated minutes after the start of the adaptive immune response.

Plotting chemical substrates

Let’s make an oxygen contour plot through z = 0 μm. First, we find the index corresponding to this z-value:

k = find( MCDS.mesh.Z_coordinates == 0 ); 

Next, let’s figure out which variable is oxygen. Type “MCDS.continuum_variables.name”, which will show the array of variable names:

Here, oxygen is the first variable, (index 1). So, to make a filled contour plot:

contourf( MCDS.mesh.X(:,:,k), MCDS.mesh.Y(:,:,k), ...
     MCDS.continuum_variables(1).data(:,:,k) , 20 ) ;

Now, let’s set this to a correct aspect ratio (no stretching in x or y), add a colorbar, and set the axis labels, using
metadata to get labels:

axis image
colorbar 
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 

Lastly, let’s add an appropriate (time-based) title:

title( sprintf('%s (%s) at t = %3.2f %s, z = %3.2f %s', MCDS.continuum_variables(1).name , ...
     MCDS.continuum_variables(1).units , ...
     MCDS.metadata.current_time , ...
     MCDS.metadata.time_units, ... 
     MCDS.mesh.Z_coordinates(k), ...
     MCDS.metadata.spatial_units ) ); 

Here’s the end result:

We can easily export graphics, such as to PNG format:

print( '-dpng' , 'output_o2.png' );

For more on plotting BioFVM data, see the tutorial
at http://www.mathcancer.org/blog/saving-multicellds-data-from-biofvm/

Plotting cells in space

3-D point cloud

First, let’s plot all the cells in 3D:

plot3( MCDS.discrete_cells.state.position(:,1) , MCDS.discrete_cells.state.position(:,2), ...
	MCDS.discrete_cells.state.position(:,3) , 'bo' ); 

At first glance, this does not look good: some cells are far out of the simulation domain, distorting the automatic range of the plot:

This does not ordinarily happen in PhysiCell (the default cell mechanics functions have checks to prevent such behavior), but this example includes a simple Hookean elastic adhesion model for immune cell attachment to tumor cells. In rare circumstances, an attached tumor cell or immune cell can apoptose on its own (due to its background apoptosis rate),
without “knowing” to detach itself from the surviving cell in the pair. The remaining cell attempts to calculate its elastic velocity based upon an invalid cell position (no longer in memory), creating an artificially large velocity that “flings” it out of the simulation domain. Such cells  are not simulated any further, so this is effectively equivalent to an extra apoptosis event (only 3 cells are out of the simulation domain after tens of millions of cell-cell elastic adhesion calculations). Future versions of this example will include extra checks to prevent this rare behavior.

The plot can simply be fixed by changing the axis:

axis( 1000*[-1 1 -1 1 -1 1] )
axis square 

Notice that this is a very difficult plot to read, and very non-interactive (laggy) to rotation and scaling operations. We can make a slightly nicer plot by searching for different cell types and plotting them with different colors:

% make it easier to work with the cell positions; 
P = MCDS.discrete_cells.state.position;

% find type 1 cells
ind1 = find( MCDS.discrete_cells.metadata.type == 1 ); 
% better still, eliminate those out of the simulation domain 
ind1 = find( MCDS.discrete_cells.metadata.type == 1 & ...
    abs(P(:,1))' < 1000 & abs(P(:,2))' < 1000 & abs(P(:,3))' < 1000 );

% find type 0 cells
ind0 = find( MCDS.discrete_cells.metadata.type == 0 & ...
    abs(P(:,1))' < 1000 & abs(P(:,2))' < 1000 & abs(P(:,3))' < 1000 ); 

%now plot them
P = MCDS.discrete_cells.state.position;
plot3( P(ind0,1), P(ind0,2), P(ind0,3), 'bo' )
hold on
plot3( P(ind1,1), P(ind1,2), P(ind1,3), 'ro' )
hold off
axis( 1000*[-1 1 -1 1 -1 1] )
axis square

However, this isn’t much better. You can use the scatter3 function to gain more control on the size and color of the plotted cells, or even make macros to plot spheres in the cell locations (with shading and lighting), but Matlab is very slow when plotting beyond 103 cells. Instead, we recommend the faster preview functions below for data exploration, and higher-quality plotting (e.g., by POV-ray) for final publication-

Fast 3-D cell data previewers

Notice that plot3 and scatter3 are painfully slow for any nontrivial number of cells. We can use a few fast previewers to quickly get a sense of the data. First, let’s plot all the dead cells, and make them red:

clf
simple_plot( MCDS,  MCDS, MCDS.discrete_cells.dead_cells , 'r' )


This function creates a coarse-grained 3-D indicator function (0 if no cells are present; 1 if they are), and plots a 3-D level surface. It is very responsive to rotations and other operations to explore the data. You may notice the second argument is a list of indices: only these cells are plotted. This gives you a method to select cells with specific characteristics when plotting. (More on that below.) If you want to get a sense of the interior structure, use a cutaway plot:

clf
simple_cutaway_plot( MCDS, MCDS, MCDS.discrete_cells.dead_cells , 'r' )

We also provide a fast “composite” cutaway which plots all live cells as red, apoptotic cells as blue (without the cutaway), and all necrotic cells as black:

clf
composite_cutaway_plot( MCDS )


Lastly, we show an improved plot that uses different colors for the immune cells, and Matlab’s “find” function to help set up the indexing:

constants = set_MCDS_constants

% find the type 0 necrotic cells
ind0_necrotic = find( MCDS.discrete_cells.metadata.type == 0 & ...
    (MCDS.discrete_cells.phenotype.cycle.current_phase == constants.necrotic_swelling | ...
    MCDS.discrete_cells.phenotype.cycle.current_phase == constants.necrotic_lysed | ...
    MCDS.discrete_cells.phenotype.cycle.current_phase == constants.necrotic) ); 

% find the live type 0 cells
ind0_live = find( MCDS.discrete_cells.metadata.type == 0 & ...
    (MCDS.discrete_cells.phenotype.cycle.current_phase ~= constants.necrotic_swelling & ...
    MCDS.discrete_cells.phenotype.cycle.current_phase ~= constants.necrotic_lysed & ...
    MCDS.discrete_cells.phenotype.cycle.current_phase ~= constants.necrotic & ...
    MCDS.discrete_cells.phenotype.cycle.current_phase ~= constants.apoptotic) ); 

clf
% plot live tumor cells red, in cutaway view
simple_cutaway_plot( MCDS, ind0_live , 'r' ); 
hold on 
% plot dead tumor cells black, in cutaway view 
simple_cutaway_plot( MCDS, ind0_necrotic , 'k' ) 
% plot all immune cells, but without cutaway (to show how they infiltrate)
simple_plot( MCDS, ind1, 'g' ) 
hold off

A small cautionary note on future compatibility

PhysiCell 1.2.1 uses the <custom> data tag (allowed as part of the MultiCellDS specification) to encode its cell data, to allow a more compact data representation, because the current PhysiCell daft does not support such a formulation, and Matlab is painfully slow at parsing XML files larger than ~50 MB. Thus, PhysiCell snapshots are not yet fully compatible with general MultiCellDS tools, which would by default ignore custom data. In the future, we will make available converter utilities to transform “native” custom PhysiCell snapshots to MultiCellDS snapshots that encode all the cellular information in a more verbose but compatible XML format.

Closing words and future work

Because Octave is not a great option for parsing XML files (with critical MultiCellDS metadata), we plan to write similar functions to read and plot PhysiCell snapshots in Python, as an open source alternative. Moreover, our lab in the next year will focus on creating further MultiCellDS configuration, analysis, and visualization routines. We also plan to provide additional 3-D functions for plotting the discrete cells and varying color with their properties.

In the longer term, we will develop open source, stand-alone analysis and visualization tools for MultiCellDS snapshots (including PhysiCell snapshots). Please stay tuned!

Share this:
Tags :

Running the PhysiCell sample projects

Introduction

In PhysiCell 1.2.1 and later, we include four sample projects on cancer heterogeneity, bioengineered multicellular systems, and cancer immunology. This post will walk you through the steps to build and run the examples.

If you are new to PhysiCell, you should first make sure you’re ready to run it. (Please note that this applies in particular for OSX users, as Xcode’s g++ is not compatible out-of-the-box.) Here are tutorials on getting ready to Run PhysiCell:

  1. Setting up a 64-bit gcc environment in Windows.
  2. Setting up gcc / OpenMP on OSX (MacPorts edition)
  3. Setting up gcc / OpenMP on OSX (Homebrew edition)
    Note: This is the preferred method for Mac OSX.
  4. Getting started with a PhysiCell Virtual Appliance (for virtual machines like VirtualBox)
    Note: The “native” setups above are preferred, but the Virtual Appliance is a great “plan B” if you run into trouble

Please note that we expect to expand this tutorial.

Building, running, and viewing the sample projects

All of these projects will create data of the following forms:

  1. Scalable vector graphics (SVG) cross-section plots through = 0.0 μm at each output time. Filenames will look like snapshot00000000.svg.
  2. Matlab (Level 4) .mat files to store raw BioFVM data. Filenames will look like output00000000_microenvironment0.mat (for the chemical substrates) and output00000000_cells.mat (for basic agent data).
  3. Matlab .mat files to store additional PhysiCell agent data. Filenames will look like output00000000_cells_physicell.mat.
  4. MultiCellDS .xml files that give further metadata and structure for the .mat files. Filenames will look like output00000000.xml.

You can read the combined data in the XML and MAT files with the read_MultiCellDS_xml function, stored in the matlab directory of every PhysiCell download. (Copy the read_MultiCellDS_xml.m and set_MultiCelLDS_constants.m files to the same directory as your data for the greatest simplicity.)

(If you are using Mac OSX and PhysiCell version > 1.2.1, remember to set the PHYSICELL_CPP environment variable to be an OpenMP-capable compiler – rf. Homebrew setup.)

 Biorobots (2D)

Type the following from a terminal window in your root PhysiCell directory:

make biorobots-sample
make 
./biorobots
make reset # optional -- gets a clean slate to try other samples

Because this is a 2-D example, the SVG snapshot files will provide the simplest method of visualizing these outputs. You can use utilities like ImageMagick to convert them into other formats for publications, such as PNG or EPS.

Anti-cancer biorobots (2D)

make cancer-biorobots-sample
make 
./cancer_biorobots
make reset # optional -- gets a clean slate to try other samples 

Cancer heterogeneity (2D)

make heterogeneity-sample
make project
./heterogeneity
make reset # optional -- gets a clean slate to try other samples 

Cancer immunology (3D)

make cancer-immune-sample
make 
./cancer_immune_3D
make reset # optional -- gets a clean slate to try other samples 

 

Share this:

Getting started with a PhysiCell Virtual Appliance

Note: This is part of a series of “how-to” blog posts to help new users and developers of BioFVM and PhysiCell. This guide is for for users in OSX, Linux, or Windows using the VirtualBox virtualization software to run a PhysiCell virtual appliance.

These instructions should get you up and running without needed to install a compiler, makefile capabilities, or any other software (beyond the virtual machine and the PhysiCell virtual appliance). We note that using the PhysiCell source with your own compiler is still the preferred / ideal way to get started, but the virtual appliance option is a fast way to start even if you’re having troubles setting up your development environment.

What’s a Virtual Machine? What’s a Virtual Appliance?

A virtual machine is a full simulated computer (with its own disk space, operating system, etc.) running on another. They are designed to let a user test on a completely different environment, without affecting the host (main) environment. They also allow a very robust way of taking and reproducing the state of a full working environment.

A virtual appliance is just this: a full image of an installed system (and often its saved state) on a virtual machine, which can easily be installed on a new virtual machine. In this tutorial, you will download our PhysiCell virtual appliance and use its pre-configured compiler and other tools.

What you’ll need:

  • VirtualBox: This is a free, cross-platform program to run virtual machines on OSX, Linux, Windows, and other platforms. It is a safe and easy way to install one full operating (a client system) on your main operating system (the host system). For us, this means that we can distribute a fully working Linux environment with a working copy of all the tools you need to compile and run PhysiCell. As of August 1, 2017, this will download Version 5.1.26.
  • PhysiCell Virtual Appliance: This is a single-file distribution of a virtual machine running Alpine Linux, including all key tools needed to compile and run PhysiCell. As of July 31, 2017, this will download PhysiCell 1.2.2 with g++ 6.3.0.
  • A computer with hardware support for virtualization: Your CPU needs to have hardware support for virtualization (almost all of them do now), and it has to be enabled in your BIOS. Consult your computer maker on how to turn this on if you get error messages later.

Main steps:

1) Install VirtualBox.

Double-click / open the VirtualBox download. Go ahead and accept all the default choices. If asked, go ahead and download/install the extensions pack.

2) Import the PhysiCell Virtual Appliance

Go the “File” menu and choose “Import Virtual Appliance”. Browse to find the .ova file you just downloaded.

Click on “Next,” and import with all the default options. That’s it!

3) [Optional] Change settings

You most likely won’t need this step, but you can increase/decrease the amount of RAM used for the virtual machine if you select the PhysiCell VM, click the Settings button (orange gear), and choose “System”:We set the Virtual Machine to have 4 GB of RAM. If you have a machine with lots of RAM (16 GB or more), you may want to set this to 8 GB.

Also, you can choose how many virtual CPUs to give to your VM: 

We selected 4 when we set up the Virtual Appliance, but you should match the number of physical processor cores on your machine. In my case, I have a quad core processor with hyperthreading. This means 4 real cores, 8 virtual cores, so I select 4 here.

4) Start the Virtual Machine and log in

Select the PhysiCell machine, and click the green “start” button. After the virtual machine boots (with the good old LILO boot manager that I’ve missed), you should see this:

Click the "More ..." button, and log in with username: physicell, password: physicell

5) Test the compiler and run your first simulation

Notice that PhysiCell is already there on the desktop in the PhysiCell folder. Right-click, and choose “open terminal here.” You’ll already be in the main PhysiCell root directory. 

Now, let’s compile your first project! Type “make template2D && make” And run your project! Type “./project” and let it go!Go ahead and run either the first few days of the simulation (until about 7200 minutes), then hit <control>-C to cancel out. Or run the whole simulation–that’s fine, too.

6) Look at the results

We bundled a few tools to easily look at results. First, ristretto is a very fast image viewer. Let’s view the SVG files: As a nice tip, you can press the left and right arrows to advance through the SVG images, or hold the right arrow down to advance through quickly.

Now, let’s use ImageMagick to convert the SVG files into JPG file: call “magick mogrify -format jpg snap*.svg”

Next, let’s turn those images into a movie. I generally create moves that are 24 frames pers se, so that 1 second of the movie is 1 hour of simulations time. We’ll use mencoder, with options below given to help get a good quality vs. size tradeoff:

When you’re done, view the movie with mplayer. The options below scale the window to fit within the virtual monitor:

If you want to loop the movie, add “-loop 999” to your command.

7) Get familiar with other tools

Use nano (useage: nano <filename>) to quickly change files at the command line. Hit <control>-O to save your results. Hit <control>-X to exit.  <control>-W will search within the file.

Use nedit (useage: nedit <filename> &) to open up one more text files in a graphical editor. This is a good way to edit multiple files at once.

Sometimes, you need to run commands at elevated (admin or root) privileges. Use sudo. Here’s an example, searching the Alpine Linux package manager apk for clang:

physicell:~$ sudo apk search gcc
[sudo] password for physicell:  
physicell:~$ sudo apk search clang
clang-analyzer-4.0.0-r0
clang-libs-4.0.0-r0
clang-dev-4.0.0-r0
clang-static-4.0.0-r0
emscripten-fastcomp-1.37.10-r0
clang-doc-4.0.0-r0
clang-4.0.0-r0
physicell:~/Desktop/PhysiCell$ 

If you want to install clang/llvm (as an alternative compiler):

physicell:~$ sudo apk add gcc
[sudo] password for physicell:  
physicell:~$ sudo apk search clang
clang-analyzer-4.0.0-r0
clang-libs-4.0.0-r0
clang-dev-4.0.0-r0
clang-static-4.0.0-r0
emscripten-fastcomp-1.37.10-r0
clang-doc-4.0.0-r0
clang-4.0.0-r0
physicell:~/Desktop/PhysiCell$ 

Notice that it asks for a password: use the password for root (which is physicell).

8) [Optional] Configure a shared folder

Coming soon.

Why both with zipped source, then?

Given that we can get a whole development environment by just downloading and importing a virtual appliance, why
bother with all the setup of a native development environment, like this tutorial (Windows) or this tutorial (Mac)?

One word: performance. In my testing, I still have not found the performance running inside a
virtual machine to match compiling and running directly on your system. So, the Virtual Appliance is a great
option to get up and running quickly while trying things out, but I still recommend setting up natively with
one of the tutorials I linked in the preceding paragraphs.

What’s next?

In the coming weeks, we’ll post further tutorials on using PhysiCell. In the meantime, have a look at the
PhysiCell project website, and these links as well:

  1. BioFVM on MathCancer.org: http://BioFVM.MathCancer.org
  2. BioFVM on SourceForge: http://BioFVM.sf.net
  3. BioFVM Method Paper in BioInformatics: http://dx.doi.org/10.1093/bioinformatics/btv730
  4. PhysiCell on MathCancer.org: http://PhysiCell.MathCancer.org
  5. PhysiCell on Sourceforge: http://PhysiCell.sf.net
  6. PhysiCell Method Paper (preprint): https://doi.org/10.1101/088773
  7. PhysiCell tutorials: [click here]

Return to NewsReturn to MathCancerFollow @MathCancer
Share this:

MathCancer C++ Style and Practices Guide

As PhysiCell, BioFVM, and other open source projects start to gain new users and contributors, it’s time to lay out a coding style. We have three goals here:

  1. Consistency: It’s easier to understand and contribute to the code if it’s written in a consistent way.
  2. Readability: We want the code to be as readable as possible.
  3. Reducing errors: We want to avoid coding styles that are more prone to errors. (e.g., code that can be broken by introducing whitespace).

So, here is the guide (revised June 2017). I expect to revise this guide from time to time.

Place braces on separate lines in functions and classes.

I find it much easier to read a class if the braces are on separate lines, with good use of whitespace. Remember: whitespace costs almost nothing, but reading and understanding (and time!) are expensive.

DON’T

class Cell{
public:
 double some_variable; 
 bool some_extra_variable;

 Cell(); };

class Phenotype{
public:
 double some_variable; 
 bool some_extra_variable;

 Phenotype();
};

DO:

class Cell
{
 public:
     double some_variable; 
     bool some_extra_variable;

     Cell(); 
};

class Phenotype
{
 public:
     double some_variable; 
     bool some_extra_variable;

     Phenotype();
};

Enclose all logic in braces, even when optional.

In C/C++, you can omit the curly braces in some cases. For example, this is legal

if( distance > 1.5*cell_radius )
     interaction = false; 
force = 0.0; // is this part of the logic, or a separate statement?
error = false; 

However, this code is ambiguous to interpret. Moreover, small changes to whitespace–or small additions to the logic–could mess things up here. Use braces to make the logic crystal clear:

DON’T

if( distance > 1.5*cell_radius )  
     interaction = false; 
force = 0.0; // is this part of the logic, or a separate statement?
error = false; 

if( condition1 == true )
  do_something1 = true; 
elseif( condition2 == true )
  do_something2 = true;
else
  do_something3 = true; 

DO

if( distance > 1.5*cell_radius )  
{
     interaction = false; 
     force = 0.0;
}
error = false; 

if( condition1 == true )
{ do_something1 = true; }
elseif( condition2 == true )
{ do_something2 = true; }
else
{ do_something3 = true; }

Put braces on separate lines in logic, except for single-line logic.

This style rule relates to the previous point, to improve readability.

DON’T

if( distance > 1.5*cell_radius ){ 
     interaction = false;
     force = 0.0; } 

if( condition1 == true ){ do_something1 = true; }
elseif( condition2 == true ){ 
  do_something2 = true; }
else
{ do_something3 = true; error = true; }

DO

if( distance > 1.5*cell_radius )
{ 
     interaction = false;
     force = 0.0;
} 

if( condition1 == true )
{ do_something1 = true; } // this is fine
elseif( condition2 == true )
{ 
     do_something2 = true; // this is better
}
else
{
     do_something3 = true;
     error = true;
}

See how much easier that code is to read? The logical structure is crystal clear, and adding more to the logic is simple.

End all functions with a return, even if void.

For clarity, definitively state that a function is done by using return.

DON’T

void my_function( Cell& cell )
{
     cell.phenotype.volume.total *= 2.0; 
     cell.phenotype.death.rates[0] = 0.02;
     // Are we done, or did we forget something?
     // is somebody still working here? 
}

DO

void my_function( Cell& cell )
{
     cell.phenotype.volume.total *= 2.0; 
     cell.phenotype.death.rates[0] = 0.02;
     return; 
}

Use tabs to indent the contents of a class or function.

This is to make the code easier to read. (Unfortunately PHP/HTML makes me use five spaces here instead of tabs.)

DON’T

class Secretion
{
 public:
std::vector<double> secretion_rates;
std::vector<double> uptake_rates; 
std::vector<double> saturation_densities; 
};

void my_function( Cell& cell )
{
cell.phenotype.volume.total *= 2.0; 
cell.phenotype.death.rates[0] = 0.02;
return; 
}

DO

class Secretion
{
 public:
     std::vector<double> secretion_rates;
     std::vector<double> uptake_rates; 
     std::vector<double> saturation_densities; 
};

void my_function( Cell& cell )
{
     cell.phenotype.volume.total *= 2.0; 
     cell.phenotype.death.rates[0] = 0.02;
     return; 
}

Use a single space to indent public and other keywords in a class.

This gets us some nice formatting in classes, without needing two tabs everywhere.

DON’T

class Secretion
{
public:
std::vector<double> secretion_rates;
std::vector<double> uptake_rates; 
std::vector<double> saturation_densities; 
}; // not enough whitespace

class Errors
{
     private:
          std::string none_of_your_business
     public:
          std::string error_message;
          int error_code; 
}; // too much whitespace!

DO

class Secretion
{
 private:
 public:
     std::vector<double> secretion_rates;
     std::vector<double> uptake_rates; 
     std::vector<double> saturation_densities; 
}; 

class Errors
{
 private:
     std::string none_of_your_business
 public:
     std::string error_message;
     int error_code; 
};

Avoid arcane operators, when clear logic statements will do.

It can be difficult to decipher code with statements like this:

phenotype.volume.fluid=phenotype.volume.fluid<0?0:phenotype.volume.fluid;

Moreover, C and C++ can treat precedence of ternary operators very differently, so subtle bugs can creep in when using the “fancier” compact operators. Variations in how these operators work across languages are an additional source of error for programmers switching between languages in their daily scientific workflows. Wherever possible (and unless there is a significant performance reason to do so), use clear logical structures that are easy to read even if you only dabble in C/C++. Compiler-time optimizations will most likely eliminate any performance gains from these goofy operators.

DON’T

// if the fluid volume is negative, set it to zero
phenotype.volume.fluid=phenotype.volume.fluid<0.0?0.0:pCell->phenotype.volume.fluid;

DO

if( phenotype.volume.fluid < 0.0 )
{
     phenotype.volume.fluid = 0.0;
}

Here’s the funny thing: the second logic is much clearer, and it took fewer characters, even with extra whitespace for readability!

Pass by reference where possible.

Passing by reference is a great way to boost performance: we can avoid (1) allocating new temporary memory, (2) copying data into the temporary memory, (3) passing the temporary data to the function, and (4) deallocating the temporary memory once finished.

DON’T

double some_function( Cell cell )
{
     return = cell.phenotype.volume.total + 3.0; 
}
// This copies cell and all its member data!

DO

double some_function( Cell& cell )
{
     return = cell.phenotype.volume.total + 3.0; 
}
// This just accesses the original cell data without recopying it. 

Where possible, pass by reference instead of by pointer.

There is no performance advantage to passing by pointers over passing by reference, but the code is simpler / clearer when you can pass by reference. It makes code easier to write and understand if you can do so. (If nothing else, you save yourself character of typing each time you can replace “->” by “.”!)

DON’T

double some_function( Cell* pCell )
{
     return = pCell->phenotype.volume.total + 3.0; 
}
// Writing and debugging this code can be error-prone.

DO

double some_function( Cell& cell )
{
     return = cell.phenotype.volume.total + 3.0; 
}
// This is much easier to write. 

Be careful with static variables. Be thread safe!

PhysiCell relies heavily on parallelization by OpenMP, and so you should write functions under the assumption that they may be executed many times simultaneously. One easy source of errors is in static variables:

DON’T

double some_function( Cell& cell )
{
     static double four_pi = 12.566370614359172; 
     static double output; 
     output = cell.phenotype.geometry.radius; 
     output *= output; 
     output *= four_pi; 
     return output; 
}
// If two instances of some_function are running, they will both modify 
// the *same copy* of output 

DO

double some_function( Cell& cell )
{
     static double four_pi = 12.566370614359172; 
     double output; 
     output = cell.phenotype.geometry.radius; 
     output *= output; 
     output *= four_pi; 
     return output; 
}
// If two instances of some_function are running, they will both modify 
// the their own copy of output, but still use the more efficient, once-
// allocated copy of four_pi. This one is safe for OpenMP.

Use std:: instead of “using namespace std”

PhysiCell uses the BioFVM and PhysiCell namespaces to avoid potential collision with other codes. Other codes using PhysiCell may use functions that collide with the standard namespace. So, we formally use std:: whenever using functions in the standard namespace.

DON’T

using namespace std; 

cout << "Hi, Mom, I learned to code today!" << endl; 
string my_string = "Cheetos are good, but Doritos are better."; 
cout << my_string << endl; 

vector<double> my_vector;
vector.resize( 3, 0.0 ); 

DO

std::cout << "Hi, Mom, I learned to code today!" << std::endl; 
std::string my_string = "Cheetos are good, but Doritos are better."; 
std::cout << my_string << std::endl; 

std::vector<double> my_vector;
my_vector.resize( 3, 0.0 ); 

Camelcase is ugly. Use underscores.

This is purely an aesthetic distinction, but CamelCaseCodeIsUglyAndDoYouUseDNAorDna?

DON’T

double MyVariable1;
bool ProteinsInExosomes;
int RNAtranscriptionCount;

void MyFunctionDoesSomething( Cell& ImmuneCell );

DO

double my_variable1;
bool proteins_in_exosomes;
int RNA_transcription_count;

void my_function_does_something( Cell& immune_cell );

Use capital letters to declare a class. Use lowercase for instances.

To help in readability and consistency, declare classes with capital letters (but no camelcase), and use lowercase for instances of those classes.

DON’T

class phenotype; 

class cell
{
 public:
     std::vector<double> position; 
     phenotype Phenotype; 
}; 

class ImmuneCell : public cell
{
 public:
     std::vector<double> surface_receptors; 
};

void do_something( cell& MyCell , ImmuneCell& immuneCell ); 

cell Cell;
ImmuneCell MyImmune_cell;

do_something( Cell, MyImmune_cell ); 

DO

class Phenotype;

class Cell
{
 public:
     std::vector<double> position; 
     Phenotype phenotype; 
}; 

class Immune_Cell : public Cell
{
 public:
     std::vector<double> surface_receptors; 
};

void do_something( Cell& my_cell , Immune_Cell& immune_cell ); 

Cell cell;
Immune_Cell my_immune_cell;

do_something( cell, my_immune_cell ); 
Share this:

Setting up gcc / OpenMP on OSX (Homebrew edition)

Note: This is part of a series of “how-to” blog posts to help new users and developers of BioFVM and PhysiCell. This guide is for OSX users. Windows users should use this guide instead. A Linux guide is expected soon.

These instructions should get you up and running with a minimal environment for compiling 64-bit C++ projects with OpenMP (e.g., BioFVM and PhysiCell) using gcc. These instructions were tested with OSX 10.11 (El Capitan) and 10.12 (Sierra), but they should work on any reasonably recent version of OSX.

In the end result, you’ll have a compiler and key makefile capabilities. The entire toolchain is free and open source.

Of course, you can use other compilers and more sophisticated integrated desktop environments, but these instructions will get you a good baseline system with support for 64-bit binaries and OpenMP parallelization.

Note 1: OSX / Xcode appears to have gcc out of the box (you can type “gcc” in a Terminal window), but this really just maps back onto Apple’s build of clang. Alas, this will not support OpenMP for parallelization.

Note 2: In this post, we showed how to set up gcc using the popular MacPorts package manager. Because MacPorts builds gcc (and all its dependencies!) from source, it takes a very, very long time. On my 2012 Macbook Air, this step took 16 hours.  This tutorial uses Homebrew to dramatically speed up the process!

Note 3: This is an update over the previous version. It incorporates new information that Xcode command line tools can be installed without the full 4.41 GB download / installation of Xcode. Many thanks to Walter de Back and Tim at the Homebrew project for their help!


What you’ll need:

  • XCode Command Line Tools: These command line tools are needed for Homebrew and related package managers. Installation instructions are now very simple and included below. As of January 18, 2016, this will install Version 2343.
  • Homebrew: This is a package manager for OSX, which will let you easily download and install many linux utilities without building them from source. You’ll particularly need it for getting gcc. Installation is a simple command-line script, as detailed below. As of August 2, 2017, this will download Version 1.3.0.
  • gcc (from Homebrew): This will be an up-to-date 64-bit version of gcc, with support for OpenMP. As of August 2, 2017, this will download Version 7.1.0.

Main steps:

1) Install the XCode Command Line Tools

Open a terminal window (Open Launchpad, then “Other”, then “Terminal”), and run:

user$ xcode-select --install

A window should pop up asking you to either get Xcode or install. Choose the “install” option to avoid the huge 4+ GB Xcode download. It should only take a few minutes to complete.

2) Install Homebrew

Open a terminal window (Open Launchpad, then “Other”, then “Terminal”), and run:

user$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Let the script run, and answer “y” whenever asked. This will not take very long.

3) Get, install, and prepare gcc

Open a terminal window (see above), and search for gcc, version 7.x or above

user$ brew search gcc

You should see a list of packages, including gcc7. (In 2015, this looked like “gcc5”. In 2017, this looks like “gcc@7”.)

Then, download and install gcc:

user$ brew install gcc

This will download whatever dependencies are needed, generally already pre-compiled. The whole process should only take five or ten minutes.

Lastly, you need to get the exact name of your compiler. In your terminal window, type g++, and then hit tab twice to see a list. On my system, I see this:

Pauls-MBA:~ pmacklin$ g++
g++       g++-7     g++-mp-7

Look for the version of g++ without an “mp” (from MacPorts) in its name. In my case, it’s g++-7. Double-check that you have the right one by checking its version. It should look something like this:

Pauls-MBA:~ pmacklin$ g++-7 --version
g++-7 (Homebrew GCC 7.1.0) 7.1.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO warranty; not even 
for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Notice that Homebrew shows up in the information. The correct compiler is g++-7.

PhysiCell Version 1.2.2 and greater use a system variable to record your compiler version, so that you don’t need to modify the CC line in PhysiCell Makefiles. Set the PHYSICELL_CPP variable to record the compiler you just found above. For example, on the bash shell:

export PHYSICELL_CPP=g++-7
echo export PHYSICELL_CPP=g++-7 >> ~/.bash_profile

One last thing: If you don’t update your paths, make will may fail as it continues to combine Apple’s “gcc” toolchain with real gcc. (This seems to happen most often if you installed an older gcc like gcc5 with MacPorts earlier.) You may see errors like this:

user$ make
g++-7 -march=core2 -O3 -fomit-frame-pointer -fopenmp -std=c++11 -c BioFVM_vector.cpp
FATAL:/opt/local/bin/../libexec/as/x86_64/as: I don't understand 'm' flag!
make: *** [BIOFVM_vector.o] Error 1

To avoid this, run:

echo export PATH=/usr/local/bin:$PATH >> ~/.bash_profile

Note that you’ll need to open a new Terminal window for this fix to apply.

4) Test your setup

I wrote a sample C++ program that tests OpenMP parallelization (32 threads). If you can compile and run it, it means that everything (including make) is working! :-)

Make a new directory, and enter it

Open Terminal (see above). You should be in your user profile’s root directory. Make a new subdirectory called GCC_test, and enter it.

mkdir GCC_test
cd GCC_test
Grab a sample parallelized program:

Download a Makefile and C++ source file, and save them to the GCC_test directory. Here are the links:

  1. Makefile: [click here]
  2. C++ source: [click here]

Note: The Makefiles in PhysiCell (versions > 1.2.1) can use an environment variable to specify an OpenMP-capable g++ compiler. If you have not yet done so, you should go ahead and set that now, e.g., for the bash shell:

export PHYSICELL_CPP=g++-7
echo export PHYSICELL_CPP=g++-7 >> ~/.bash_profile
Compile and run the test:

Go back to your (still open) command prompt. Compile and run the program:

make 
./my_test

The output should look something like this:

Allocating 4096 MB of memory ...
Done!

Entering main loop ...
Done!

Note 1: If the make command gives errors like “**** missing separator”, then you need to replace the white space (e.g., one or more spaces) at the start of the “$(COMPILE_COMMAND)” and “rm -f” lines with a single tab character. 

Note 2: If the compiler gives an error like “fatal error: ‘omp.h’ not found”, you probably used Apple’s build of clang, which does not include OpenMP support. You’ll need to make sure that you set the environment variable PHYSICELL_CPP as above (for PhysiCell 1.2.2 or later), or specify your compiler on the CC line of your makefile (for PhysiCell 1.2.1 or earlier). 

Now, let’s verify that the code is using OpenMP.

Open another Terminal window. While the code is running, run top. Take a look at the performance, particularly CPU usage. While your program is running, you should see CPU usage fairly close to ‘100% user’. (This is a good indication that your code is running the OpenMP parallelization as expected.)

What’s next?

Download a copy of PhysiCell and try out the included examples! Visit BioFVM at MathCancer.org.

  1. PhysiCell links:
    1. PhysiCell Method Paper at bioRxiv: https://doi.org/10.1101/088773
    2. PhysiCell on MathCancer: http://PhysiCell.MathCancer.org
    3. PhysiCell on SourceForge: http://PhysiCell.sf.net
    4. PhysiCell on github: http://github.com/MathCancer/PhysiCell
    5. PhysiCell tutorials: [click here]
  2. BioFVM links:
    1. BioFVM announcement on this blog: [click here]
    2. BioFVM on MathCancer.org: http://BioFVM.MathCancer.org
    3. BioFVM on SourceForge: http://BioFVM.sf.net
    4. BioFVM Method Paper in BioInformatics: http://dx.doi.org/10.1093/bioinformatics/btv730
    5. BioFVM tutorials: [click here]

Return to NewsReturn to MathCancerFollow @MathCancer
Share this: