Source code for earm.tests.test_shen_models

# 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()