Editing reaction kinetics

Previous examples showed how to create models using basico, and using kinetic functions from the reaction database. Here, I want to expand on that showing how to map kinetic functions to reactions involving modifiers as well. Lets start as usual by importing basico:

[1]:
from basico import *

now lets create a new model:

[2]:
new_model(name='Reactions');

we know we can create a reaction, by using the add_reaction command. It requires at the very least two arguments:

  • name: the name of the reaction

  • scheme: the reaction scheme

If nothing else is specified, this will create the reaction with the given name and reaction scheme, assigning it mass action kinetics with local parameters defaulting to a value of 0.1. All species will be created if they do not exist yet in the model. For example:

[3]:
add_reaction('R1', 'A -> B');

creates the reaction R1, species A and B and a local parameter (R1).k1. With get_reactions we can have a look at what was created:

[4]:
get_reactions()[['scheme', 'function', 'mapping']]
[4]:
scheme function mapping
name
R1 A -> B Mass action (irreversible) {'k1': 0.1, 'substrate': 'A'}

here I want to point out, the mapping column. It shows that the parameter k1 is a local one, as it is mapped to a value. And that the substrate of the function is mapped to A. We can specify the mapping directly in the add_reaction call, or we can specify it using set_reaction. So for example, if we wanted to modify the reaction, to map the reactions k1 parameter to a global quantity global_k, we could to that as follows:

[5]:
add_parameter(name='global_k', initial_value=0.2)
set_reaction('R1', mapping={'k1': 'global_k'})
[6]:
get_reactions()[['scheme', 'function', 'mapping']]
[6]:
scheme function mapping
name
R1 A -> B Mass action (irreversible) {'k1': 'global_k', 'substrate': 'A'}

next let us assume, i wanted to use a kinetic from the function database, that includes inhibition for the reaction. Using get_functions we can filter the functiondatabase, for suitable functions for our reaction, and then filter for ones that contain inhibition:

[7]:
suitable_functions = get_functions(suitable_for='R1')[['formula', 'mapping']]
suitable_inhibitions = suitable_functions[suitable_functions.index.str.contains('inhibition')]
suitable_inhibitions
[7]:
formula mapping
name
Allosteric inhibition (MWC) V*(substrate/Ks)*(1+(substrate/Ks))^(n-1)/(L*(... {'substrate': 'substrate', 'Inhibitor': 'modif...
Competitive inhibition (irr) V*substrate/(Km+substrate+Km*Inhibitor/Ki) {'substrate': 'substrate', 'Inhibitor': 'modif...
Mixed inhibition (irr) V*substrate/(Km*(1+Inhibitor/Kis)+substrate*(1... {'substrate': 'substrate', 'Inhibitor': 'modif...
Noncompetitive inhibition (irr) V*substrate/((Km+substrate)*(1+Inhibitor/Ki)) {'substrate': 'substrate', 'Inhibitor': 'modif...
Substrate inhibition (irr) V*substrate/(Km+substrate+Km*(substrate/Ki)^2) {'substrate': 'substrate', 'Km': 'parameter', ...
Uncompetitive inhibition (irr) V*substrate/(Km+substrate*(1+Inhibitor/Ki)) {'substrate': 'substrate', 'Inhibitor': 'modif...

let us use Allosteric inhibition (MWC) here, lets have a look at the formula and the mapping table:

[8]:
as_dict(suitable_inhibitions)[0]
[8]:
{'name': 'Allosteric inhibition (MWC)',
 'formula': 'V*(substrate/Ks)*(1+(substrate/Ks))^(n-1)/(L*(1+Inhibitor/Ki)^n+(1+(substrate/Ks))^n)',
 'mapping': {'substrate': 'substrate',
  'Inhibitor': 'modifier',
  'V': 'parameter',
  'Ks': 'parameter',
  'n': 'parameter',
  'L': 'parameter',
  'Ki': 'parameter'}}

since this function requires a modifier, we also change the reaction scheme to include a modifier. This is done by adding a semicolon at the end of the reaction scheme, and listing the modifiers space separated there. Then we an assign that function directly.

[9]:
set_reaction('R1', scheme='A -> B; C', function='Allosteric inhibition (MWC)')
get_reactions()[['scheme', 'function', 'mapping']]
[9]:
scheme function mapping
name
R1 A -> B; C Allosteric inhibition (MWC) {'substrate': 'A', 'Inhibitor': 'C', 'V': 0.1,...

Note: that here, the mapping is not necessary, as the function has only one modifier, had we multiple modifiers defined, then we’d want to specify the mapping dictionary and map the Inhibitor to the respective modifier in our reaction scheme:

[10]:
set_reaction('R1', scheme='A -> B; C D', function='Allosteric inhibition (MWC)', mapping={'Inhibitor': 'D'})
get_reactions()[['scheme', 'function', 'mapping']]
[10]:
scheme function mapping
name
R1 A -> B; D Allosteric inhibition (MWC) {'substrate': 'A', 'Inhibitor': 'D', 'V': 0.1,...

Note: Assigning a function that uses modifiers, requires that modifiers are present in the reaction scheme, or that all modifiers are specified in the mapping parameter. So assigning the function above would fail with error, if no modifier is declared:

[11]:
add_reaction('error', scheme='A -> B', function='Allosteric inhibition (MWC)');
get_reactions()[['scheme', 'function', 'mapping']]
ERROR:root:the mapping for reaction "error" with function "Allosteric inhibition (MWC)" is not valid and cannot be applied.
[11]:
scheme function mapping
name
R1 A -> B; D Allosteric inhibition (MWC) {'substrate': 'A', 'Inhibitor': 'D', 'V': 0.1,...
error A -> B Mass action (irreversible) {'k1': 0.1, 'substrate': 'A'}

However, it will succeeed, if the modifier is specified. in the mapping parameter:

[12]:
set_reaction('error', new_name='now_it_works', function='Allosteric inhibition (MWC)', mapping={'Inhibitor': 'D'})
get_reactions()[['scheme', 'function', 'mapping']]
[12]:
scheme function mapping
name
R1 A -> B; D Allosteric inhibition (MWC) {'substrate': 'A', 'Inhibitor': 'D', 'V': 0.1,...
now_it_works A -> B; D Allosteric inhibition (MWC) {'substrate': 'A', 'Inhibitor': 'D', 'V': 0.1,...