# TODO: Update this file once we implement units properly
"""
This file contains a series of tests to ensure that the ODEs generated
by the PySB version of each model exactly match those of the original
publication. The procedure for validating the models (and creating the
tests) was as follows:
1. Generate the ODEs from the PySB model using pysb.bng.generate_equations
2. Programmatically rename all parameters and species to use the names
from the original publication.
3. (For :py:class:`TestChenBiophysJ` and :py:class:`TestHowells`)
Programmatically verify the values of all parameters against the values
used in the original publication.
4. Manually verify the transformed PySB ODEs against the ODEs from the
original publication.
5. Include the (verified) output in a test to ensure that the model
continues to produce the correct output in the face of any future
changes in macros, libraries, etc.
Note that the models in the earm.mito module, by default, have a
zero initial condition for the activator species (Bid). To generate
the ODEs used by the original publications, it is necessary to add
an additional non-zero initial condition for Bid. This is accomplished in the
setUp() method of each test.
"""
import unittest
from earm.mito import chen_biophys_j, chen_febs_direct, \
chen_febs_indirect, cui_direct, cui_direct1, cui_direct2, howells
from pysb.bng import generate_equations
from pysb.integrate import odesolve
from pysb import *
import numpy as np
import matplotlib.pyplot as plt
import re
from scipy.constants import N_A
from earm.shared import V
[docs]def convert_odes(model, p_name_map, s_name_map_by_pattern):
"""Substitutes species and parameter names using the given name mappings.
Parameters
----------
model : pysb.core.Model
The model to be converted.
p_name_map : dict
A dict where the keys are the parameter names of the PySB model and the
values are the parameter names of the original model, e.g.
{'bind_BidT_Bcl2_kf': 'ka_tBid_Bcl2'}
s_name_map : dict
A dict where the keys are the string representations of the species
from the PySB model, generated by calling str(species), and the values
are the species names in the original model, e.g.
{'Bid(bf=None, state=T)': 'Act'}
Returns
-------
List of strings
One string entry for each ODE in the model. Each ODE
is represented as a string, e.g. "d[Act]/dt = ..."
"""
generate_equations(model)
# Get the index of each species
s_name_map_by_num = {}
for i, s in enumerate(model.species):
for key in s_name_map_by_pattern:
if key == str(s):
s_name_map_by_num['s%d' % i] = s_name_map_by_pattern[key]
name_map = {}
name_map.update(p_name_map)
name_map.update(s_name_map_by_num)
# Substitute new names into the ODEs
ode_list = {}
for i, ode in enumerate(model.odes):
new_ode = ode.subs(name_map)
ode_species = s_name_map_by_pattern[str(model.species[i])]
ode_list[ode_species] = str(new_ode)
return ode_list
[docs]def odes_match(generated_odes, validated_odes):
"""Return True if the ODEs match.
Both args are dicts, where the key is the original species name,
and the value is the string representation of the ODE.
"""
# Will throw a KeyError if either list contains species that are not
# in the other
result_list1 = [generated_odes[species] == validated_odes[species]
for species in generated_odes]
result_list2 = [generated_odes[species] == validated_odes[species]
for species in validated_odes]
return np.all(result_list1) and np.all(result_list2)
[docs]def convert_parameters(model, p_name_map, original_units='micromolar'):
"""Convert the parameters from the PySB version of the model to have
names and units that match the original publication.
Used to test that the nominal parameter values of the PySB model match
those of the original publication.
Parameters
----------
model : pysb.core.Model
The model to be converted.
p_name_map : dict
A dict where the keys are the parameter names of the PySB model and the
values are the parameter names of the original model, e.g.
{'bind_BidT_Bcl2_kf': 'ka_tBid_Bcl2'}
original_units: string, 'micromolar' or 'nanomolar'
By convention, the units in the PySB versions of all models are
expressed as numbers of molecules with a default volume V, specified
in earm.shared. Convert parameters converts the parameter values
from numbers of molecules back into concentration units that
match the original publication (either micromolar or nanomolar).
"""
param_list = []
for p in model.parameters:
if p.name in p_name_map:
original_name = re.sub(p.name, p_name_map[p.name], p.name)
# Convert the units in the model, which have been converted
# into numbers of molecules, back to the units of the original
# paper for comparison.
#
# Set the scaling factor based on the units of the original paper
if original_units == 'nanomolar':
scaling_factor = 1e9
elif original_units == 'micromolar':
scaling_factor = 1e6
else:
raise Exception('Invalid units given.')
# Make sure to scale appropriately depending on whether the
# parameter is a concentration or a bimolecular rate constant:
if p.name.endswith('_0'): # i.e., an initial concentration
# First, convert from stochastic to deterministic (Molar):
original_value = p.value / (N_A * V)
# Then convert from molar to the particular original units
# (i.e., nanomolar or micromolar):
original_value *= scaling_factor
# Finally, round to 3 significant digits
original_value = round(original_value, 3)
elif p.name.endswith('kf') and not p.name.startswith('equilibrate'):
# Convert as above, but for bimolecular rate constants
original_value = p.value * N_A * V
original_value /= scaling_factor
original_value = round(original_value, 3)
else:
original_value = p.value
param_list.append((original_name, original_value))
return param_list
# Parameter mapping used by both Chen 2007 FEBS models
chenFEBS_p_name_map = {
'one_step_BidT_BaxC_to_BidT_BaxA_kf': 'k_InBax',
'reverse_BaxA_to_BaxC_kr': 'k_Bax',
'bind_BidT_Bcl2_kf': 'k_BH3_Bcl2',
'bind_BidT_Bcl2_kr': 'kr_BH3Bcl2',
'bind_BadM_Bcl2_kf': 'k_BH3_Bcl2',
'bind_BadM_Bcl2_kr': 'kr_BH3Bcl2',
'bind_BaxA_Bcl2_kf': 'k_Bax_Bcl2',
'bind_BaxA_Bcl2_kr': 'kr_BaxBcl2',
#'spontaneous_pore_BaxC_to_Bax4_kf': 'k_o',
#'spontaneous_pore_BaxC_to_Bax4_kr': 'kr_o',
'spontaneous_pore_BaxA_to_Bax4_kf': 'k_o',
'spontaneous_pore_BaxA_to_Bax4_kr': 'kr_o' }
# Parameter mapping used by all three Cui models
cui_p_name_map = {
'one_step_BidT_BaxC_to_BidT_BaxA_kf': 'k1',
'reverse_BaxA_to_BaxC_kr': 'k8',
'bind_BidT_Bcl2_kf': 'k4',
'bind_BidT_Bcl2_kr': 'k5',
'bind_BadM_Bcl2_kf': 'k9',
'bind_BadM_Bcl2_kr': 'k10',
'displace_BaxA_BidTBcl2_to_BaxABcl2_BidT_fwd_kf': 'k6',
'displace_BaxA_BidTBcl2_to_BaxABcl2_BidT_rev_kf': 'k7',
'displace_BadM_BidTBcl2_to_BadMBcl2_BidT_fwd_kf': 'k11',
'displace_BadM_BidTBcl2_to_BadMBcl2_BidT_rev_kf': 'k12',
'displace_BadM_BaxABcl2_to_BadMBcl2_BaxA_fwd_kf': 'k13',
'displace_BadM_BaxABcl2_to_BadMBcl2_BaxA_rev_kf': 'k14',
'assemble_pore_sequential_Bax_2_kf': 'k16',
'assemble_pore_sequential_Bax_2_kr': 'k17',
'synthesize_BaxC_k': 'p1',
'degrade_BaxC_k': 'u1',
'degrade_BaxA_k': 'u2',
'synthesize_BidT_k': 'p2',
'degrade_BidT_k': 'u3',
'synthesize_Bcl2_k': 'p3',
'degrade_Bcl2_k': 'u4',
'degrade_BidTBcl2_k': 'u5',
'degrade_BaxBcl2_k': 'u6',
'synthesize_BadMU_k': 'p4',
'degrade_BadMU_k': 'u7',
'degrade_BadBcl2_k': 'u8',
'degrade_BaxBax_k': 'u9',
'bind_BaxA_Bcl2_kf': 'k2',
'bind_BaxA_Bcl2_kr': 'k3',
'Bax_autoactivation_dimerization_k': 'k15' }
# Species mapping used by all three Cui models
cui_s_name_map = {
'Bid(bf=None, state=T)': 'Act',
'Bad(bf=None, state=M, serine=U)': 'Ena',
'Bax(bf=None, s1=None, s2=None, state=C)': 'InBax',
'Bcl2(bf=None)': 'Bcl2',
'__source()': '__source',
'Bax(bf=None, s1=None, s2=None, state=A)': 'AcBax',
'Bcl2(bf=1) % Bid(bf=1, state=T)': 'ActBcl2',
'Bad(bf=1, state=M, serine=U) % Bcl2(bf=1)': 'EnaBcl2',
'__sink()': '__sink',
'Bax(bf=None, s1=1, s2=None, state=A) % Bax(bf=None, s1=None, s2=1, state=A)': 'MAC',
'Bax(bf=1, s1=None, s2=None, state=A) % Bcl2(bf=1)': 'AcBaxBcl2'}
## TESTS ===============================================================
[docs]class TestChenBiophysJ(unittest.TestCase):
"""Test the PySB version of the model from [Chen2007biophysj]_."""
p_name_map = {
'one_step_BidT_BaxC_to_BidT_BaxA_kf': 'k1',
'reverse_BaxA_to_BaxC_kr': 'k2',
'bind_BidT_Bcl2_kf': 'k5',
'bind_BidT_Bcl2_kr': 'k6',
'bind_BaxA_Bcl2_kf': 'k3',
'bind_BaxA_Bcl2_kr': 'k4',
'displace_BaxA_BidTBcl2_to_BaxABcl2_BidT_kf': 'k7',
#'displace_BaxA_BidTBcl2_to_BaxABcl2_BidT_kr': 'k8',
'spontaneous_pore_BaxA_to_Bax4_kf': 'k9',
'spontaneous_pore_BaxA_to_Bax4_kr': 'k10' }
s_name_map = {
'Bid(bf=None, state=T)': 'Act',
'Bax(bf=None, s1=None, s2=None, state=C)': 'InBax',
'Bcl2(bf=None)': 'Bcl2',
'Bax(bf=None, s1=None, s2=None, state=A)': 'AcBax',
'Bcl2(bf=1) % Bid(bf=1, state=T)': 'ActBcl2',
'Bax(bf=1, s1=None, s2=None, state=A) % Bcl2(bf=1)': 'AcBaxBcl2',
'Bax(bf=None, s1=1, s2=2, state=A) % Bax(bf=None, s1=3, s2=1, state=A) % Bax(bf=None, s1=4, s2=3, state=A) % Bax(bf=None, s1=2, s2=4, state=A)': 'Bax4'
}
def setUp(self):
self.model = chen_biophys_j.model
if self.model.parameters.get('Bid_0') is None:
# Add initial condition for Bid
Bid_0 = Parameter('Bid_0', 1, _export=False)
self.model.add_component(Bid_0)
Bid = self.model.monomers['Bid']
self.model.initial(Bid(state='T', bf=None), Bid_0)
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs.
The ODEs generated by the PySB model match those of the paper with
the following two caveats:
1. In the equation for d[Bcl2]/dt, in the paper the authors group the
terms
+ AcBaxBcl2*k4 + ActBcl2*k6'
into the single term
+ k_Bcl2 * Bcl2_nonfree
with the comment that "Bcl2_nonfree indicates the total concentration
of Bcl2 associated with both activated Bax and Activator
([Bcl2_nonfree] = [AcBaxBcl2] + [ActBcl2]). We use k_bcl2 to
represent the rate of non-free Bcl2 shifting to free Bcl2, assuming
that free Bcl2 originates from both Bcl2 non-free forms at the same
rate."
In addition, in the legend for Table 1 (which lists parameters) they
state that: "We set k_bcl2 (the rate of non-free Bcl2 shifting to
free Bcl2)... equal to k6 assuming that free Bcl2 originate [sic]
from AcBaxBcl2 at the same rate with ActBcl2."
So, obviously this substitution of Bcl2_nonfree for AcBaxBcl2 and
ActBcl2 works if k4 and k6 are equal, which they claim as an
assumption; however, in their table of parameter values, they list k4
as having a value of 0.001 s^-1, and k6 as having a value of
0.04 s^-1.
2. It should also be noted that the parameter for spontaneous pore
formation, k9, has already been multiplied by 4 from its nominal
value listed in the paper. This accounts for BNG's (appropriate)
addition of the coefficients of 0.25 to the Bax polymerization
forward reaction, due to the reaction being a homomeric binding
reaction.
3. Because the rate of displacement of Bax from Bcl2 by tBid is set
to 0 in the original model, this reaction and its associated rate
parameter k8 have been eliminated from the model.
"""
ode_list = convert_odes(self.model, self.p_name_map, self.s_name_map)
self.assertTrue(odes_match(ode_list,
{'Act': 'AcBax*ActBcl2*k7 - Act*Bcl2*k5 + ActBcl2*k6',
'InBax': 'AcBax*k2 - Act*InBax*k1',
'Bcl2': '-AcBax*Bcl2*k3 + AcBaxBcl2*k4 - Act*Bcl2*k5 + ActBcl2*k6',
'AcBax': '-1.0*AcBax**4*k9 - AcBax*ActBcl2*k7 - AcBax*Bcl2*k3 - AcBax*k2 + AcBaxBcl2*k4 + Act*InBax*k1 + 4*Bax4*k10',
'ActBcl2': '-AcBax*ActBcl2*k7 + Act*Bcl2*k5 - ActBcl2*k6',
'AcBaxBcl2': 'AcBax*ActBcl2*k7 + AcBax*Bcl2*k3 - AcBaxBcl2*k4',
'Bax4': '0.25*AcBax**4*k9 - Bax4*k10'}))
[docs] def test_parameters(self):
"""Check that the values of the parameters in the PySB model
match those of the original.
The parameter values in the test below have been verified to match
the values listed in Table 1 of [Chen2007biophysj]_.
"""
param_list = convert_parameters(self.model, self.p_name_map,
original_units='micromolar')
self.assertEqual(param_list,
[('k1', 0.5),
('k2', 0.1),
('k5', 3.),
('k6', 0.04),
('k3', 2.),
('k4', 0.001),
('k7', 2.),
('k9', 8.),
('k10', 0.)])
[docs]class TestChenFEBS_Indirect(unittest.TestCase):
"""Test the PySB version of the "indirect" model from [Chen2007febs]_."""
s_name_map = {
'Bid(bf=None, state=T)': 'BH3',
'Bax(bf=None, s1=None, s2=None, state=A)': 'Bax',
'Bcl2(bf=None)': 'Bcl2',
'Bcl2(bf=1) % Bid(bf=1, state=T)': 'BH3Bcl2',
'Bax(bf=1, s1=None, s2=None, state=A) % Bcl2(bf=1)': 'BaxBcl2',
'Bax(bf=None, s1=1, s2=2, state=A) % Bax(bf=None, s1=3, s2=1, state=A) % Bax(bf=None, s1=4, s2=3, state=A) % Bax(bf=None, s1=2, s2=4, state=A)': 'MAC'}
def setUp(self):
self.model = chen_febs_indirect.model
if self.model.parameters.get('Bid_0') is None:
# Add initial condition for Bid
Bid_0 = Parameter('Bid_0', 1, _export=False)
self.model.add_component(Bid_0)
Bid = self.model.monomers['Bid']
self.model.initial(Bid(state='T', bf=None), Bid_0)
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs."""
ode_list = convert_odes(self.model, chenFEBS_p_name_map, self.s_name_map)
self.assertTrue(odes_match(ode_list,
{'BH3': '-BH3*Bcl2*k_BH3_Bcl2 + BH3Bcl2*kr_BH3Bcl2',
'Bax': '-1.0*Bax**4*k_o - Bax*Bcl2*k_Bax_Bcl2 + BaxBcl2*kr_BaxBcl2 + 4*MAC*kr_o',
'Bcl2': '-BH3*Bcl2*k_BH3_Bcl2 + BH3Bcl2*kr_BH3Bcl2 - Bax*Bcl2*k_Bax_Bcl2 + BaxBcl2*kr_BaxBcl2',
'BH3Bcl2': 'BH3*Bcl2*k_BH3_Bcl2 - BH3Bcl2*kr_BH3Bcl2',
'BaxBcl2': 'Bax*Bcl2*k_Bax_Bcl2 - BaxBcl2*kr_BaxBcl2',
'MAC': '0.25*Bax**4*k_o - MAC*kr_o'}))
[docs]class TestChenFEBS_Direct(unittest.TestCase):
"""Test the PySB version of the "direct" model from [Chen2007febs]_."""
s_name_map = {
'Bid(bf=None, state=T)': 'Act',
'Bad(bf=None, state=M, serine=U)': 'Ena',
'Bax(bf=None, s1=None, s2=None, state=C)': 'InBax',
'Bcl2(bf=None)': 'Bcl2',
'Bax(bf=None, s1=None, s2=None, state=A)': 'Bax',
'Bcl2(bf=1) % Bid(bf=1, state=T)': 'ActBcl2',
'Bad(bf=1, state=M, serine=U) % Bcl2(bf=1)': 'EnaBcl2',
'Bax(bf=None, s1=1, s2=2, state=A) % Bax(bf=None, s1=3, s2=1, state=A) % Bax(bf=None, s1=4, s2=3, state=A) % Bax(bf=None, s1=2, s2=4, state=A)': 'MAC'}
def setUp(self):
self.model = chen_febs_direct.model
if self.model.parameters.get('Bid_0') is None:
# Add initial condition for Bid
Bid_0 = Parameter('Bid_0', 1, _export=False)
self.model.add_component(Bid_0)
Bid = self.model.monomers['Bid']
self.model.initial(Bid(state='T', bf=None), Bid_0)
if self.model.parameters.get('Bad_0') is None:
# Add initial condition for Bad
Bad_0 = Parameter('Bad_0', 1, _export=False)
self.model.add_component(Bad_0)
Bad = self.model.monomers['Bad']
self.model.initial(Bad(state='M', bf=None, serine='U'), Bad_0)
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs."""
ode_list = convert_odes(self.model, chenFEBS_p_name_map, self.s_name_map)
self.assertTrue(odes_match(ode_list,
{'Act': '-Act*Bcl2*k_BH3_Bcl2 + ActBcl2*kr_BH3Bcl2',
'Ena': '-Bcl2*Ena*k_BH3_Bcl2 + EnaBcl2*kr_BH3Bcl2',
'InBax': '-Act*InBax*k_InBax + Bax*k_Bax',
'Bcl2': '-Act*Bcl2*k_BH3_Bcl2 + ActBcl2*kr_BH3Bcl2 - Bcl2*Ena*k_BH3_Bcl2 + EnaBcl2*kr_BH3Bcl2',
'Bax': 'Act*InBax*k_InBax - 1.0*Bax**4*k_o - Bax*k_Bax + 4*MAC*kr_o',
'ActBcl2': 'Act*Bcl2*k_BH3_Bcl2 - ActBcl2*kr_BH3Bcl2',
'EnaBcl2': 'Bcl2*Ena*k_BH3_Bcl2 - EnaBcl2*kr_BH3Bcl2',
'MAC': '0.25*Bax**4*k_o - MAC*kr_o'}))
[docs]class TestCui_Direct(unittest.TestCase):
"""Test the PySB version of the "direct" model from [Cui2008]_."""
def setUp(self):
self.model = cui_direct.model
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs."""
ode_list = convert_odes(self.model, cui_p_name_map, cui_s_name_map)
self.assertTrue(odes_match(ode_list,
{'Act': '-Act*Bcl2*k4 - Act*EnaBcl2*k12 - Act*u3 + ActBcl2*Ena*k11 + ActBcl2*k5 + __source*p2',
'Ena': 'Act*EnaBcl2*k12 - ActBcl2*Ena*k11 - Bcl2*Ena*k9 - Ena*u7 + EnaBcl2*k10 + __source*p4',
'InBax': 'AcBax*k8 - Act*InBax*k1 - InBax*u1 + __source*p1',
'Bcl2': '-Act*Bcl2*k4 + ActBcl2*k5 - Bcl2*Ena*k9 - Bcl2*u4 + EnaBcl2*k10 + __source*p3',
'__source': '0',
'AcBax': '-2*AcBax**2*k16 - AcBax*k8 - AcBax*u2 + Act*InBax*k1 + 2*MAC*k17',
'ActBcl2': 'Act*Bcl2*k4 + Act*EnaBcl2*k12 - ActBcl2*Ena*k11 - ActBcl2*k5 - ActBcl2*u5',
'EnaBcl2': '-Act*EnaBcl2*k12 + ActBcl2*Ena*k11 + Bcl2*Ena*k9 - EnaBcl2*k10 - EnaBcl2*u8',
'__sink': 'AcBax*u2 + Act*u3 + ActBcl2*u5 + Bcl2*u4 + Ena*u7 + EnaBcl2*u8 + InBax*u1 + MAC*u9',
'MAC': 'AcBax**2*k16 - MAC*k17 - MAC*u9'}))
[docs]class TestCui_Direct1(unittest.TestCase):
"""Test the PySB version of the "direct 1" model from [Cui2008]_."""
def setUp(self):
self.model = cui_direct1.model
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs."""
ode_list = convert_odes(self.model, cui_p_name_map, cui_s_name_map)
self.assertTrue(odes_match(ode_list,
{'Act': 'AcBax*ActBcl2*k6 - AcBaxBcl2*Act*k7 - Act*Bcl2*k4 - Act*EnaBcl2*k12 - Act*u3 + ActBcl2*Ena*k11 + ActBcl2*k5 + __source*p2',
'Ena': 'AcBax*EnaBcl2*k14 - AcBaxBcl2*Ena*k13 + Act*EnaBcl2*k12 - ActBcl2*Ena*k11 - Bcl2*Ena*k9 - Ena*u7 + EnaBcl2*k10 + __source*p4',
'InBax': 'AcBax*k8 - Act*InBax*k1 - InBax*u1 + __source*p1',
'Bcl2': '-AcBax*Bcl2*k2 + AcBaxBcl2*k3 - Act*Bcl2*k4 + ActBcl2*k5 - Bcl2*Ena*k9 - Bcl2*u4 + EnaBcl2*k10 + __source*p3',
'__source': '0',
'AcBax': '-2*AcBax**2*k16 - AcBax*ActBcl2*k6 - AcBax*Bcl2*k2 - AcBax*EnaBcl2*k14 - AcBax*k8 - AcBax*u2 + AcBaxBcl2*Act*k7 + AcBaxBcl2*Ena*k13 + AcBaxBcl2*k3 + Act*InBax*k1 + 2*MAC*k17',
'ActBcl2': '-AcBax*ActBcl2*k6 + AcBaxBcl2*Act*k7 + Act*Bcl2*k4 + Act*EnaBcl2*k12 - ActBcl2*Ena*k11 - ActBcl2*k5 - ActBcl2*u5',
'EnaBcl2': '-AcBax*EnaBcl2*k14 + AcBaxBcl2*Ena*k13 - Act*EnaBcl2*k12 + ActBcl2*Ena*k11 + Bcl2*Ena*k9 - EnaBcl2*k10 - EnaBcl2*u8',
'__sink': 'AcBax*u2 + AcBaxBcl2*u6 + Act*u3 + ActBcl2*u5 + Bcl2*u4 + Ena*u7 + EnaBcl2*u8 + InBax*u1 + MAC*u9',
'MAC': 'AcBax**2*k16 - MAC*k17 - MAC*u9',
'AcBaxBcl2': 'AcBax*ActBcl2*k6 + AcBax*Bcl2*k2 + AcBax*EnaBcl2*k14 - AcBaxBcl2*Act*k7 - AcBaxBcl2*Ena*k13 - AcBaxBcl2*k3 - AcBaxBcl2*u6'}))
[docs]class TestCui_Direct2(unittest.TestCase):
"""Test the PySB version of the "direct 2" model from [Cui2008]_."""
def setUp(self):
self.model = cui_direct2.model
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs."""
ode_list = convert_odes(self.model, cui_p_name_map, cui_s_name_map)
self.assertTrue(odes_match(ode_list,
{'Act': 'AcBax*ActBcl2*k6 - AcBaxBcl2*Act*k7 - Act*Bcl2*k4 - Act*EnaBcl2*k12 - Act*u3 + ActBcl2*Ena*k11 + ActBcl2*k5 + __source*p2',
'Ena': 'AcBax*EnaBcl2*k14 - AcBaxBcl2*Ena*k13 + Act*EnaBcl2*k12 - ActBcl2*Ena*k11 - Bcl2*Ena*k9 - Ena*u7 + EnaBcl2*k10 + __source*p4',
'InBax': '-AcBax*InBax*k15 + AcBax*k8 - Act*InBax*k1 - InBax*u1 + __source*p1',
'Bcl2': '-AcBax*Bcl2*k2 + AcBaxBcl2*k3 - Act*Bcl2*k4 + ActBcl2*k5 - Bcl2*Ena*k9 - Bcl2*u4 + EnaBcl2*k10 + __source*p3',
'__source': '0',
'AcBax': '-2*AcBax**2*k16 - AcBax*ActBcl2*k6 - AcBax*Bcl2*k2 - AcBax*EnaBcl2*k14 - AcBax*InBax*k15 - AcBax*k8 - AcBax*u2 + AcBaxBcl2*Act*k7 + AcBaxBcl2*Ena*k13 + AcBaxBcl2*k3 + Act*InBax*k1 + 2*MAC*k17',
'ActBcl2': '-AcBax*ActBcl2*k6 + AcBaxBcl2*Act*k7 + Act*Bcl2*k4 + Act*EnaBcl2*k12 - ActBcl2*Ena*k11 - ActBcl2*k5 - ActBcl2*u5',
'EnaBcl2': '-AcBax*EnaBcl2*k14 + AcBaxBcl2*Ena*k13 - Act*EnaBcl2*k12 + ActBcl2*Ena*k11 + Bcl2*Ena*k9 - EnaBcl2*k10 - EnaBcl2*u8',
'__sink': 'AcBax*u2 + AcBaxBcl2*u6 + Act*u3 + ActBcl2*u5 + Bcl2*u4 + Ena*u7 + EnaBcl2*u8 + InBax*u1 + MAC*u9',
'MAC': 'AcBax**2*k16 + AcBax*InBax*k15 - MAC*k17 - MAC*u9',
'AcBaxBcl2': 'AcBax*ActBcl2*k6 + AcBax*Bcl2*k2 + AcBax*EnaBcl2*k14 - AcBaxBcl2*Act*k7 - AcBaxBcl2*Ena*k13 - AcBaxBcl2*k3 - AcBaxBcl2*u6'}))
[docs]class TestHowells(unittest.TestCase):
"""Test the PySB version of the model from [Howells2011]_."""
# Mapping of parameter names
p_name_map = {
'one_step_BidT_BaxC_to_BidT_BaxA_kf': 'k_Bak_cat',
'reverse_BaxA_to_BaxC_kr': 'k_Bak_inac',
'bind_BidT_Bcl2_kf': 'ka_tBid_Bcl2',
'bind_BidT_Bcl2_kr': 'kd_tBid_Bcl2',
'bind_BaxA_Bcl2_kf': 'ka_Bak_Bcl2',
'bind_BaxA_Bcl2_kr': 'kd_Bak_Bcl2',
'displace_BaxA_BidTBcl2_to_BaxABcl2_BidT_kf': 'k_tBid_rel2',
'spontaneous_pore_BaxA_to_Bax4_kf': 'ka_Bak_poly',
'spontaneous_pore_BaxA_to_Bax4_kr': 'kd_Bak_poly',
'equilibrate_BadCU_to_BadMU_kf': 't_Bad_in',
'equilibrate_BadCU_to_BadMU_kr': 't_Bad_out',
'bind_BadM_Bcl2_kf': 'ka_Bad_Bcl2',
'bind_BadM_Bcl2_kr': 'kd_Bad_Bcl2',
'displace_BadM_BidTBcl2_to_BadMBcl2_BidT_kf': 'k_tBid_rel1',
'phosphorylate_Bad_k1': 'k_Bad_phos1',
'phosphorylate_Bad_k2': 'k_Bad_phos2',
'sequester_BadCP_to_BadC1433_k': 'k_Bad_seq',
'release_BadC1433_to_BadCU_k': 'k_Bad_rel'}
# Mapping of species names
s_name_map = {
'Bid(bf=None, state=T)': 'tBid',
'Bax(bf=None, s1=None, s2=None, state=C)': 'Bak_inac',
'Bcl2(bf=None)': 'Bcl2',
'Bad(bf=None, state=M, serine=U)': 'Bad_m',
'Bax(bf=None, s1=None, s2=None, state=A)': 'Bak',
'Bcl2(bf=1) % Bid(bf=1, state=T)': 'tBidBcl2',
'Bad(bf=None, state=C, serine=U)': 'Bad',
'Bad(bf=1, state=M, serine=U) % Bcl2(bf=1)': 'BadBcl2',
'Bad(bf=None, state=C, serine=P)': 'pBad',
'Bax(bf=1, s1=None, s2=None, state=A) % Bcl2(bf=1)': 'BakBcl2',
'Bax(bf=None, s1=1, s2=2, state=A) % Bax(bf=None, s1=3, s2=1, state=A) % Bax(bf=None, s1=4, s2=3, state=A) % Bax(bf=None, s1=2, s2=4, state=A)': 'Bak_poly',
'Bad(bf=None, state=C, serine=B)': 'pBad1433'}
def setUp(self):
self.model = howells.model
if self.model.parameters.get('Bid_0') is None:
# Add initial condition for Bid
Bid_0 = Parameter('Bid_0', 1, _export=False)
self.model.add_component(Bid_0)
Bid = self.model.monomers['Bid']
self.model.initial(Bid(state='T', bf=None), Bid_0)
if self.model.parameters.get('Bad_0') is None:
# Add initial condition for Bad
Bad_0 = Parameter('Bad_0', 1, _export=False)
self.model.add_component(Bad_0)
Bad = self.model.monomers['Bad']
self.model.initial(Bad(state='M', bf=None, serine='U'), Bad_0)
[docs] def test_odes(self):
"""Check the generated ODEs against manually validated ODEs.
The ODEs shown in the code match the ODEs listed in the paper, with the
note that the parameter for spontaneous pore formation, ka_Bak_poly,
has already been multiplied by 4 from its nominal value listed in the
paper. This accounts for BNG's (appropriate) addition of the
coefficients of 0.25 to the Bak polymerization forward reaction, due to
the reaction being a homomeric binding reaction.
"""
ode_list = convert_odes(self.model, self.p_name_map, self.s_name_map)
self.assertTrue(odes_match(ode_list,
{'tBid': 'Bad_m*k_tBid_rel1*tBidBcl2 + Bak*k_tBid_rel2*tBidBcl2 - Bcl2*ka_tBid_Bcl2*tBid + kd_tBid_Bcl2*tBidBcl2',
'Bak_inac': 'Bak*k_Bak_inac - Bak_inac*k_Bak_cat*tBid',
'Bcl2': 'BadBcl2*k_Bad_phos2 + BadBcl2*kd_Bad_Bcl2 - Bad_m*Bcl2*ka_Bad_Bcl2 - Bak*Bcl2*ka_Bak_Bcl2 + BakBcl2*kd_Bak_Bcl2 - Bcl2*ka_tBid_Bcl2*tBid + kd_tBid_Bcl2*tBidBcl2',
'Bad_m': 'Bad*t_Bad_in + BadBcl2*kd_Bad_Bcl2 - Bad_m*Bcl2*ka_Bad_Bcl2 - Bad_m*k_Bad_phos1 - Bad_m*k_tBid_rel1*tBidBcl2 - Bad_m*t_Bad_out',
'Bak': '-1.0*Bak**4*ka_Bak_poly - Bak*Bcl2*ka_Bak_Bcl2 - Bak*k_Bak_inac - Bak*k_tBid_rel2*tBidBcl2 + BakBcl2*kd_Bak_Bcl2 + Bak_inac*k_Bak_cat*tBid + 4*Bak_poly*kd_Bak_poly',
'tBidBcl2': '-Bad_m*k_tBid_rel1*tBidBcl2 - Bak*k_tBid_rel2*tBidBcl2 + Bcl2*ka_tBid_Bcl2*tBid - kd_tBid_Bcl2*tBidBcl2',
'Bad': '-Bad*k_Bad_phos1 - Bad*t_Bad_in + Bad_m*t_Bad_out + k_Bad_rel*pBad1433',
'BadBcl2': '-BadBcl2*k_Bad_phos2 - BadBcl2*kd_Bad_Bcl2 + Bad_m*Bcl2*ka_Bad_Bcl2 + Bad_m*k_tBid_rel1*tBidBcl2',
'pBad': 'Bad*k_Bad_phos1 + BadBcl2*k_Bad_phos2 + Bad_m*k_Bad_phos1 - k_Bad_seq*pBad',
'BakBcl2': 'Bak*Bcl2*ka_Bak_Bcl2 + Bak*k_tBid_rel2*tBidBcl2 - BakBcl2*kd_Bak_Bcl2',
'Bak_poly': '0.25*Bak**4*ka_Bak_poly - Bak_poly*kd_Bak_poly',
'pBad1433': '-k_Bad_rel*pBad1433 + k_Bad_seq*pBad'}))
[docs] def test_parameters(self):
"""Check that the values of the parameters in the PySB model
match those of the original.
The parameter values shown in the test below have been validated
against the list in Table 1 of Howells et al.
"""
param_list = convert_parameters(self.model, self.p_name_map,
original_units='micromolar')
self.assertEqual(param_list,
[('k_Bak_cat', 0.5),
('k_Bak_inac', 0.1),
('ka_tBid_Bcl2', 3),
('kd_tBid_Bcl2', 0.002),
('ka_Bak_Bcl2', 2),
('kd_Bak_Bcl2', 0.002),
('k_tBid_rel2', 2),
('ka_Bak_poly', 8000),
('kd_Bak_poly', 5e-05),
('t_Bad_in', 0.01),
('t_Bad_out', 0.002),
('ka_Bad_Bcl2', 15),
('kd_Bad_Bcl2', 0.002),
('k_tBid_rel1', 5),
('k_Bad_phos1', 0.001),
('k_Bad_phos2', 0.0001),
('k_Bad_seq', 0.001),
('k_Bad_rel', 0.00087)])
if __name__ == '__main__':
unittest.main()