The Azimuth Project
Experiments in Petri nets - Results from two different tools

This experiment compares the results obtained by running the same four Petri nets in two different tools. The four Petri nets are a series of chemical reactions, the first example from the Feinberg Lectures, the biochemical glycolysis pathway, and a simple climate model. The two tools are my Java-based Xholon software, and David Tanzer’s Python script.

Xholon is a general-purpose modeling and simulation tool, with a Petri net mechanism.

The Python script is a much simpler tool focused exclusively on executing Petri net specifications.

Xholon can use JFreeChart to generate charts. The Python script writes its results in comma-separated values (CSV) format, which can be read by most spreadsheet programs. I’m using LibreOffice Calc which comes with Ubuntu Linux, and is able to generate charts from the data. I’ve included the two charts (Xholon/JFreeChart and Python/LibreOffice) for each of the four Petri nets.

None of the four Petri nets on this page are particularly realistic at this point in their development. I’ve used them for learning about Petri nets. The glycolysis model and the simple climate model could both be improved to be more domain realistic.

I’m doing this comparison:

  • To test that both tools are working correctly. I’m more confident in the results from my own Xholon software by comparing with the results from David’s software.

  • To explore how their somewhat different runtime use of probablities impacts the results.

TODO add additional discussion on probabilities and differences between the results of the two tools

TODO add Petri net diagrams as used here, or bigraph diagrams as used here, for each of the four models

Chemical Reactions

Part 4 in the Network Theory series by John Baez describes a simple Petri net with three transitions (chemical reactions) and eight states (chemicals). I’ve implemented this model as an XML file so it will run using the Xholon software. The Xholon model is available here.

Xholon can export the model as a Python script, using the format described by David Tanzer for use with his Python software. The exported Python script is included below.

# To run this Petri net (in Ubuntu Linux):
# (1) Save this python script to a .py file, for example .
# (2) Ensure that David Tanzer's is in the same directory as this script.
# (3) Open a terminal window.
# (4) Run:
#     python
# (5) Or run it while redirecting the output to a CSV file:
#     python > pn_test.csv

from petri1 import *

# Petri net - test01
Rone = ("Rone", [["C"], ["O2"]], [["CO2"]])
Rtwo = ("Rtwo", [["NaOH"], ["CO2"]], [["NaHCO3"]])
Rthree = ("Rthree", [["HCl"], ["NaHCO3"]], [["H2O"], ["NaCl"], ["CO2"]])
petriNet = PetriNet(
    ["C", "O2", "NaOH", "CO2", "HCl", "NaHCO3", "H2O", "NaCl"], # states
    [Rone, Rtwo, Rthree] # transitions
initialLabelling = {"C":1.0, "O2":1.0, "NaOH":1.0, "CO2":0.0, "HCl":1.0, "NaHCO3":0.0, "H2O":0.0, "NaCl":0.0}
steps = 10
petriNet.RunSimulation(steps, initialLabelling)

The first chart below shows the results from the Xholon implementation, and the second chart shows the results from the Python tool. The number of tokens in each state is either 0 or 1, so there’s a lot of overlap in the lines.

Azimuth chemical reactions - Xholon Azimuth chemical reactions - Python with LibreOffice

Feinberg Lectures example 1.1

This is a simple model that appeared at the start of lecture 1 of Feinberg’s lectures on chemical reaction networks (CRN). CRNs are essentially the same as Petri nets. The Xholon version of this model is available here.

from petri1 import *

# Petri net - Feinberg Lectures 1.1
A_BB = ("A_BB", [["A"]], [["B",2.0]])
BB_A = ("BB_A", [["B",2.0]], [["A"]])
AC_D = ("AC_D", [["A"], ["C"]], [["D"]])
D_AC = ("D_AC", [["D"]], [["A"], ["C"]])
D_BE = ("D_BE", [["D"]], [["B"], ["E"]])
BE_AC = ("BE_AC", [["B"], ["E"]], [["A"], ["C"]])
petriNet = PetriNet(
    ["A", "B", "C", "D", "E"], # states
    [A_BB, BB_A, AC_D, D_AC, D_BE, BE_AC] # transitions
initialLabelling = {"A":140.0, "B":180.0, "C":200.0, "D":25.0, "E":80.0}
steps = 1200
petriNet.RunSimulation(steps, initialLabelling)
glycolyis - Xholon glycolyis - Python with LibreOffice

Glycolysis Pathway

The glycolysis pathway is a crucial part of metabolism in each of our cells. The ten enzymes in the pathway cooperate to convert glucose into pyruvate, an important cellular energy source. There are nine intermediate molecules along the pathway from glucose to pyruvate. Some of the enzymes use ATP for energy, converting it into ADP, while some of the enzymes create ATP from ADP.

Both charts (Xholon/JFreeChart and Python/LibreOffice) show the important features of the glycolysis pathway. The amount of glucose continuously decreases while the amount of pyruvate increases at roughly the same rate. ATP is converted into ADP throughout the process, and is then recovered at the end. So the amount of ATP is 500 at the start of a simulation run, and returns to 500 when the simulation is complete. The other intermediate chemicals exist in relatively small quantities throughout the simulation, and are completely used up at the end once the glucose runs out.

This modeling exercise is incomplete. In the cell, there’s a net gain of 2 ATP molecules per glucose molecule.

The Xholon version of this model is available here.

from petri1 import *

# Petri net - glycolysis
Hexokinase = ("Hexokinase", [["Glucose"]], [["Glucose_6_Phosphate"]])
PhosphoGlucoIsomerase = ("PhosphoGlucoIsomerase", [["Glucose_6_Phosphate"]], [["Fructose_6_Phosphate"]])
PhosphoFructokinase = ("PhosphoFructokinase", [["Fructose_6_Phosphate"], ["Atp"]], [["Fructose_1x6_Biphosphate"], ["Adp"]])
Aldolase = ("Aldolase", [["Fructose_1x6_Biphosphate"]], [["Glyceraldehyde_3_Phosphate"]])
Aldolase = ("Aldolase", [["Fructose_1x6_Biphosphate"]], [["DihydroxyacetonePhosphate"]])
TriosePhosphateIsomerase = ("TriosePhosphateIsomerase", [["DihydroxyacetonePhosphate"]], [["Glyceraldehyde_3_Phosphate"]])
Glyceraldehyde_3_phosphateDehydrogenase = ("Glyceraldehyde_3_phosphateDehydrogenase", [["Glyceraldehyde_3_Phosphate"]], [["X1x3_BisphosphoGlycerate"]])
PhosphoGlycerokinase = ("PhosphoGlycerokinase", [["X1x3_BisphosphoGlycerate"]], [["X3_PhosphoGlycerate"]])
PhosphoGlyceromutase = ("PhosphoGlyceromutase", [["X3_PhosphoGlycerate"]], [["X2_PhosphoGlycerate"]])
Enolase = ("Enolase", [["X2_PhosphoGlycerate"]], [["PhosphoEnolPyruvate"]])
PyruvateKinase = ("PyruvateKinase", [["PhosphoEnolPyruvate"], ["Adp"]], [["Pyruvate"], ["Atp"]])
petriNet = PetriNet(
    ["Glucose", "Glucose_6_Phosphate", "Fructose_6_Phosphate", "Fructose_1x6_Biphosphate", "DihydroxyacetonePhosphate", "Glyceraldehyde_3_Phosphate", "X1x3_BisphosphoGlycerate", "X3_PhosphoGlycerate", "X2_PhosphoGlycerate", "PhosphoEnolPyruvate", "Pyruvate", "Adp", "Atp"], # states
    [Hexokinase, PhosphoGlucoIsomerase, PhosphoFructokinase, Aldolase, Aldolase, TriosePhosphateIsomerase, Glyceraldehyde_3_phosphateDehydrogenase, PhosphoGlycerokinase, PhosphoGlyceromutase, Enolase, PyruvateKinase] # transitions
initialLabelling = {"Glucose":500.0, "Glucose_6_Phosphate":0.0, "Fructose_6_Phosphate":0.0, "Fructose_1x6_Biphosphate":0.0, "DihydroxyacetonePhosphate":0.0, "Glyceraldehyde_3_Phosphate":0.0, "X1x3_BisphosphoGlycerate":0.0, "X3_PhosphoGlycerate":0.0, "X2_PhosphoGlycerate":0.0, "PhosphoEnolPyruvate":0.0, "Pyruvate":0.0, "Adp":0.0, "Atp":500.0}
steps = 10000
petriNet.RunSimulation(steps, initialLabelling)
glycolyis - Xholon glycolyis - Python with LibreOffice

Simple Climate Model

This is a starting point for a simple climate model. It’s designed to be roughly qualitatively correct, and it demonstrates certain features such as the conservation of matter and energy. In this model, the Sun initially contains 1000 charged particles. Each time step, it converts one charged particle into one shortwave (SW) photon which is released into space. When the Earth’s atmosphere receives a shortwave photon, it either reflects it back into space or allows it to continue on to the surface. At the surface, the photon may also be reflected back into space or it may be absorbed as one unit of temperature. As the surface heats up, it emits longwave photons (LW) into the atmosphere, which are absorbed by the atmosphere as units of temperature. The atmosphere emits longwave photons, half of which return to the surface and half of which are lost to space. Eventually, all the energy in the system is dispersed into space as reflected shortwave photons from the atmosphere or surface, or as longwave photons emitted by the atmosphere. When this equilibrium is reached, the total amount of all energy lost to space from all three sources should equal the number of charged particles that were originally in the Sun.

If you sum all the counts in the two charts (Xholon/JFreeChart and Python/LibreOffice) at any time step, the total should always equal 1000. The total amount of energy in the system is always the same.

The Xholon version of this model is available here.

from petri1 import *

# Simple climate model
# the printed totals should always equal 1000
Sunbehavior_SW = ("Sunbehavior_SW", [["Sun_ChargedParticles",1.0]], [["Space_SunSpc_sw",1.0]])
Spacebehavior_SW = ("Spacebehavior_SW", [["Space_SunSpc_sw",1.0]], [["Atmosphere_SpcAtm_sw",1.0]])
Atmospherebehavior_SW = ("Atmospherebehavior_SW", [["Atmosphere_SpcAtm_sw",1.0]], [["Surface_AtmSrf_sw",1.0]])
Atmospherebehavior_SW_REFLECTED = ("Atmospherebehavior_SW_REFLECTED", [["Atmosphere_SpcAtm_sw",1.0]], [["Space_AtmSpc_sw",1.0]])
Atmospherebehavior_LW1 = ("Atmospherebehavior_LW1", [["Atmosphere_Temperature",1.0]], [["Space_AtmSpc_lw",1.0]])
Atmospherebehavior_LW2 = ("Atmospherebehavior_LW2", [["Atmosphere_Temperature",1.0]], [["Surface_Temperature",1.0]])
Surfacebehavior_SW = ("Surfacebehavior_SW", [["Surface_AtmSrf_sw",1.0]], [["Surface_Temperature",1.0]])
Surfacebehavior_SW_REFLECTED = ("Surfacebehavior_SW_REFLECTED", [["Surface_AtmSrf_sw",1.0]], [["Space_SrfSpc_sw",1.0]])
Surfacebehavior_LW = ("Surfacebehavior_LW", [["Surface_Temperature",1.0]], [["Atmosphere_Temperature",1.0]])
petriNet = PetriNet(
    ["Sun_ChargedParticles", "Space_SunSpc_sw", "Space_AtmSpc_sw", "Space_SrfSpc_sw", "Space_AtmSpc_lw", "Atmosphere_SpcAtm_sw", "Atmosphere_Temperature", "Surface_AtmSrf_sw", "Surface_Temperature"], # states
    [Sunbehavior_SW, Spacebehavior_SW, Atmospherebehavior_SW, Atmospherebehavior_SW_REFLECTED, Atmospherebehavior_LW1, Atmospherebehavior_LW2, Surfacebehavior_SW, Surfacebehavior_SW_REFLECTED, Surfacebehavior_LW] # transitions
initialLabelling = {"Sun_ChargedParticles":1000.0, "Space_SunSpc_sw":0.0, "Space_AtmSpc_sw":0.0, "Space_SrfSpc_sw":0.0, "Space_AtmSpc_lw":0.0, "Atmosphere_SpcAtm_sw":0.0, "Atmosphere_Temperature":0.0, "Surface_AtmSrf_sw":0.0, "Surface_Temperature":0.0}
steps = 20000
petriNet.RunSimulation(steps, initialLabelling)
glycolyis - Xholon glycolyis - Python with LibreOffice

category: experiments