Category: XML

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:

Saving MultiCellDS data from BioFVM

Note: This is part of a series of “how-to” blog posts to help new users and developers of BioFVM

Introduction

A major initiative for my lab has been MultiCellDS: a standard for multicellular data. The project aims to create model-neutral representations of simulation data (for both discrete and continuum models), which can also work for segmented experimental and clinical data. A single-time output is called a digital snapshot. An interdisciplinary, multi-institutional review panel has been hard at work to nail down the draft standard.

A BioFVM MultiCellDS digital snapshot includes program and user metadata (more information to be included in a forthcoming publication), an output of the microenvironment, and any cells that are secreting or uptaking substrates.

As of Version 1.1.0, BioFVM supports output saved to MultiCellDS XML files. Each download also includes a matlab function for importing MultiCellDS snapshots saved by BioFVM programs. This tutorial will get you going.

BioFVM (finite volume method for biological problems) is an open source code for solving 3-D diffusion of 1 or more substrates. It was recently published as open access in Bioinformatics here:

http://dx.doi.org/10.1093/bioinformatics/btv730

The project website is at http://BioFVM.MathCancer.org, and downloads are at http://BioFVM.sf.net.

Working with MultiCellDS in BioFVM programs

We include a MultiCellDS_test.cpp file in the examples directory of every BioFVM download (Version 1.1.0 or later). Create a new project directory, copy the following files to it:

  1. BioFVM*.cpp and BioFVM*.h (from the main BioFVM directory)
  2. pugixml.* (from the main BioFVM directory)
  3. Makefile and MultiCellDS_test.cpp (from the examples directory)

Open the MultiCellDS_test.cpp file to see the syntax as you read the rest of this post.

See earlier tutorials (below) if you have troubles with this.

Setting metadata values

There are few key bits of metadata. First, the program used for the simulation (all these fields are optional):

// the program name, version, and project website:
BioFVM_metadata.program.program_name = "BioFVM MultiCellDS Test";
BioFVM_metadata.program.program_version = "1.0";
BioFVM_metadata.program.program_URL = "http://BioFVM.MathCancer.org";
 
// who created the program (if known)
BioFVM_metadata.program.creator.surname = "Macklin";
BioFVM_metadata.program.creator.given_names = "Paul";
BioFVM_metadata.program.creator.email = "Paul.Macklin@usc.edu";
BioFVM_metadata.program.creator.URL = "http://BioFVM.MathCancer.org";
BioFVM_metadata.program.creator.organization = "University of Southern California";
BioFVM_metadata.program.creator.department = "Center for Applied Molecular Medicine";
BioFVM_metadata.program.creator.ORCID = "0000-0002-9925-0151";
 
// (generally peer-reviewed) citation information for the program
BioFVM_metadata.program.citation.DOI = "10.1093/bioinformatics/btv730";
BioFVM_metadata.program.citation.PMID = "26656933";
BioFVM_metadata.program.citation.PMCID = "PMC1234567";
BioFVM_metadata.program.citation.text = "A. Ghaffarizadeh, S.H. Friedman, and P. Macklin, 
    BioFVM: an efficient parallelized diffusive transport solver for 3-D biological 
    simulations, Bioinformatics, 2015. DOI: 10.1093/bioinformatics/btv730.";
BioFVM_metadata.program.citation.notes = "notes here";
BioFVM_metadata.program.citation.URL = "http://dx.doi.org/10.1093/bioinformatics/btv730";
 
// user information: who ran the program
BioFVM_metadata.program.user.surname = "Kirk";
BioFVM_metadata.program.user.given_names = "James T.";
BioFVM_metadata.program.user.email = "Jimmy.Kirk@starfleet.mil";
BioFVM_metadata.program.user.organization = "Starfleet";
BioFVM_metadata.program.user.department = "U.S.S. Enterprise (NCC 1701)";
BioFVM_metadata.program.user.ORCID = "0000-0000-0000-0000";
 
// And finally, data citation information (the publication where this simulation snapshot appeared)
BioFVM_metadata.data_citation.DOI = "10.1093/bioinformatics/btv730";
BioFVM_metadata.data_citation.PMID = "12345678";
BioFVM_metadata.data_citation.PMCID = "PMC1234567";
BioFVM_metadata.data_citation.text = "A. Ghaffarizadeh, S.H. Friedman, and P. Macklin, BioFVM: 
    an efficient parallelized diffusive transport solver for 3-D biological simulations, Bioinformatics, 
    2015. DOI: 10.1093/bioinformatics/btv730.";
BioFVM_metadata.data_citation.notes = "notes here";
BioFVM_metadata.data_citation.URL = "http://dx.doi.org/10.1093/bioinformatics/btv730";

You can sync the metadata current time, program runtime (wall time), and dimensional units using the following command. (This command is automatically run whenever you use the save command below.)

BioFVM_metadata.sync_to_microenvironment( M ); 

You can display a basic summary of the metadata via:

BioFVM_metadata.display_information( std::cout ); 

Setting options

By default (to save time and disk space), BioFVM saves the mesh as a Level 3 matlab file, whose location is embedded into the MultiCellDS XML file. You can disable this feature and revert to full XML (e.g., for human-readable cross-model reporting) via:

set_save_biofvm_mesh_as_matlab( false );

Similarly, BioFVM defaults to saving the values of the substrates in a compact Level 3 matlab file. You can override this with:

set_save_biofvm_data_as_matlab( false ); 

BioFVM by default saves the cell-centered sources and sinks. These take a lot of time to parse because they require very hierarchical data structures. You can disable saving the cells (basic_agents) via:

set_save_biofvm_cell_data( false );

Lastly, when you do save the cells, we default to a customized, minimal matlab format. You can revert to a more standard (but much larger) XML format with:

set_save_biofvm_cell_data_as_custom_matlab( false )

Saving a file

Saving the data is very straightforward:

save_BioFVM_to_MultiCellDS_xml_pugi( "sample" , M , current_simulation_time );

Your data will be saved in sample.xml. (Depending upon your options, it may generate several .mat files beginning with “sample”.)

If you’d like the filename to depend upon the simulation time, use something more like this:

double current_simulation_time = 10.347; 
char filename_base [1024]; 
sprintf( &filename_base , "sample_%f", current_simulation_time ); 
save_BioFVM_to_MultiCellDS_xml_pugi( filename_base , M,
   current_simulation_time ); 

Your data will be saved in sample_10.347000.xml. (Depending upon your options, it may generate several .mat files beginning with “sample_10.347000”.)

Compiling and running the program:

Edit the Makefile as below:

PROGRAM_NAME := MCDS_test

all: $(BioFVM_OBJECTS) $(pugixml_OBJECTS) MultiCellDS_test.cpp

$(COMPILE_COMMAND) -o $(PROGRAM_NAME) $(BioFVM_OBJECTS) $(pugixml_OBJECTS) MultiCellDS_test.cpp

If you’re running OSX, you’ll probably need to update the compiler from “g++”. See these tutorials.

Then, at the command prompt:

make
./MCDS_test

On Windows, you’ll need to run without the ./:

make
MCDS_test

Working with MultiCellDS data in Matlab

Reading data in Matlab

Copy the read_MultiCellDS_xml.m file from the matlab directory (included in every MultiCellDS download). To read the data, just do this:

MCDS = read_MultiCellDS_xml( 'sample.xml' );

This should take around 30 seconds for larger data files (500,000 to 1,000,000 voxels with a few substrates, and around 250,000 cells). The long execution time is primarily because Matlab is ghastly inefficient at loops over hierarchical data structures. Increasing to 1,000,000 cells requires around 80-90 seconds to parse in matlab.

Plotting data in Matlab

Plotting the 3-D substrate data

First, let’s do some basic contour and surface plotting:

mid_index = round( length(MCDS.mesh.Z_coordinates)/2 ); 

contourf( MCDS.mesh.X(:,:,mid_index), ...
	MCDS.mesh.Y(:,:,mid_index), ... 
	MCDS.continuum_variables(2).data(:,:,mid_index) , 20 ) ; 
axis image
colorbar 
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
title( sprintf('%s (%s) at t = %f %s, z = %f %s', MCDS.continuum_variables(2).name , ...
	MCDS.continuum_variables(2).units , ...
	MCDS.metadata.current_time , ...
	MCDS.metadata.time_units, ... 
	MCDS.mesh.Z_coordinates(mid_index), ...
	MCDS.metadata.spatial_units ) ); 

OR

contourf( MCDS.mesh.X_coordinates , MCDS.mesh.Y_coordinates, ... 
	MCDS.continuum_variables(2).data(:,:,mid_index) , 20 ) ; 
axis image
colorbar 
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
title( sprintf('%s (%s) at t = %f %s, z = %f %s', ...
	MCDS.continuum_variables(2).name , ...
	MCDS.continuum_variables(2).units , ...
	MCDS.metadata.current_time , ...
	MCDS.metadata.time_units, ... 
	MCDS.mesh.Z_coordinates(mid_index), ...
	MCDS.metadata.spatial_units ) );  

Here’s a surface plot:

surf( MCDS.mesh.X_coordinates , MCDS.mesh.Y_coordinates, ... 
	MCDS.continuum_variables(1).data(:,:,mid_index) ) ; 
colorbar 
axis tight
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
zlabel( sprintf( '%s (%s)', MCDS.continuum_variables(1).name, ...
	MCDS.continuum_variables(1).units ) ); 
title( sprintf('%s (%s) at t = %f %s, z = %f %s', MCDS.continuum_variables(1).name , ...
	MCDS.continuum_variables(1).units , ...
	MCDS.metadata.current_time , ...
	MCDS.metadata.time_units, ...
	MCDS.mesh.Z_coordinates(mid_index), ...
	MCDS.metadata.spatial_units ) );

Finally, here are some more advanced plots. The first is an “exploded” stack of contour plots:

clf
contourslice( MCDS.mesh.X , MCDS.mesh.Y, MCDS.mesh.Z , ...
	MCDS.continuum_variables(2).data , [],[], ...
	MCDS.mesh.Z_coordinates(1:15:length(MCDS.mesh.Z_coordinates)),20);
view([-45 10]);
axis tight; 
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
zlabel( sprintf( 'z (%s)' , MCDS.metadata.spatial_units) ); 
title( sprintf('%s (%s) at t = %f %s', ...
	MCDS.continuum_variables(2).name , ...
	MCDS.continuum_variables(2).units , ...
	MCDS.metadata.current_time, ... 
	MCDS.metadata.time_units ) ); 

Next, we show how to use isosurfaces with transparency

clf
patch( isosurface( MCDS.mesh.X , MCDS.mesh.Y, MCDS.mesh.Z, ...
	MCDS.continuum_variables(1).data, 1000 ), 'edgecolor', ...
	'none', 'facecolor', 'r' , 'facealpha' , 1 ); 
hold on
patch( isosurface( MCDS.mesh.X , MCDS.mesh.Y, MCDS.mesh.Z, ...
MCDS.continuum_variables(1).data, 5000 ), 'edgecolor', ...
	'none', 'facecolor', 'b' , 'facealpha' , 0.7 ); 
patch( isosurface( MCDS.mesh.X , MCDS.mesh.Y, MCDS.mesh.Z, ...
	MCDS.continuum_variables(1).data, 10000 ), 'edgecolor', ...
	'none', 'facecolor', 'g' , 'facealpha' , 0.5 ); 
hold off
% shading interp 
camlight
view(3)
axis image 
axis tightcamlight lighting gouraud
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
zlabel( sprintf( 'z (%s)' , MCDS.metadata.spatial_units) );
title( sprintf('%s (%s) at t = %f %s', ... 
	MCDS.continuum_variables(1).name , ...
	MCDS.continuum_variables(1).units , ...
	MCDS.metadata.current_time, ... 
	MCDS.metadata.time_units ) );

You can get more 3-D volumetric visualization ideas at Matlab’s website. This visualization post at MIT also has some great tips.

Plotting the cells

Here is a basic 3-D plot for the cells:

plot3( MCDS.discrete_cells.state.position(:,1) , ...
	MCDS.discrete_cells.state.position(:,2) , ...
	MCDS.discrete_cells.state.position(:,3) , 'bo' );
view(3)
axis tight
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
zlabel( sprintf( 'z (%s)' , MCDS.metadata.spatial_units) );
title( sprintf('Cells at t = %f %s', MCDS.metadata.current_time, ...
	MCDS.metadata.time_units ) );

plot3 is more efficient than scatter3, but scatter3 will give more coloring options. Here is the syntax:

scatter3( MCDS.discrete_cells.state.position(:,1), ...
	MCDS.discrete_cells.state.position(:,2), ...
	MCDS.discrete_cells.state.position(:,3) , 'bo' );
view(3)
axis tight
xlabel( sprintf( 'x (%s)' , MCDS.metadata.spatial_units) ); 
ylabel( sprintf( 'y (%s)' , MCDS.metadata.spatial_units) ); 
zlabel( sprintf( 'z (%s)' , MCDS.metadata.spatial_units) ); 
title( sprintf('Cells at t = %f %s', MCDS.metadata.current_time, ...
	MCDS.metadata.time_units ) );

Jan Poleszczuk gives some great insights on plotting many cells in 3D at his blog. I’d recommend checking out his post on visualizing a cellular automaton model. At some point, I’ll update this post with prettier plotting based on his methods.

What’s next

Future releases of BioFVM will support reading MultiCellDS snapshots (for model initialization).

Matlab is pretty slow at parsing and visualizing large amounts of data. We also plan to include resources for accessing MultiCellDS data in VTK / Paraview and Python.


Return to News • Return to MathCancer

 

Share this: