Sensitivities of OFMs in E. coli core

using ElementaryFluxModes

import AbstractFBCModels as A
import AbstractFBCModels.CanonicalModel as CM
using JSONFBCModels
import FastDifferentiation as F
const Ex = F.Node
import DifferentiableMetabolism as D
using COBREXA
import COBREXA as X
using JSON
import Tulip as T
using CairoMakie

It has been proven that enzyme constrained models with K constraints will use a maximum of K EFMs in their optimal solution (de Groot 2019).

Here, we take the E. coli core model with two enzyme constraints, find the optimal solution, and then find the EFMs in the optimal solution.

!isfile("e_coli_core.json") &&
    download("http://bigg.ucsd.edu/static/models/e_coli_core.json", "e_coli_core.json")

include("../../test/data_static.jl")

model = convert(CM.Model, X.load_model("e_coli_core.json"))
AbstractFBCModels.CanonicalModel.Model(
  reactions = Dict{String, AbstractFBCModels.CanonicalModel.Reaction}("ACALD" => AbstractFBCModels.CanonicalModel.Reaction("Acetaldehyde dehydrogenas…
  metabolites = Dict{String, AbstractFBCModels.CanonicalModel.Metabolite}("glu__L_c" => AbstractFBCModels.CanonicalModel.Metabolite("L-Glutamate", "c…
  genes = Dict{String, AbstractFBCModels.CanonicalModel.Gene}("b4301" => AbstractFBCModels.CanonicalModel.Gene("sgcE", Dict("sbo" => ["SBO:0000243"],…
  couplings = Dict{String, AbstractFBCModels.CanonicalModel.Coupling}(),
)

unconstrain glucose

model.reactions["EX_glc__D_e"].lower_bound = -1000.0
-1000.0

constrain PFL to zero

model.reactions["PFL"].upper_bound = 0.0

rid_kcat = Dict(k => Ex(Symbol(k)) for (k, _) in ecoli_core_reaction_kcats)
kcats = Symbol.(keys(ecoli_core_reaction_kcats))

float_reaction_isozymes = Dict{String,Dict{String,X.Isozyme}}()
for (rid, rxn) in model.reactions
    grrs = rxn.gene_association_dnf
    isnothing(grrs) && continue # skip if no grr available
    haskey(ecoli_core_reaction_kcats, rid) || continue # skip if no kcat data available
    for (i, grr) in enumerate(grrs)

        kcat = ecoli_core_reaction_kcats[rid] * 3.6 # change unit to k/h

        d = get!(float_reaction_isozymes, rid, Dict{String,X.Isozyme}())
        d["isozyme_$i"] = X.Isozyme(
            gene_product_stoichiometry=Dict(grr .=> fill(1.0, size(grr))), # assume subunit stoichiometry of 1 for all isozymes
            kcat_forward=kcat, # assume forward and reverse have the same kcat
            kcat_reverse=kcat,
        )
    end
end

gene_product_molar_masses = Dict(k => v for (k, v) in ecoli_core_gene_product_masses)

capacity = [
    (
        "membrane", # enzyme group names must be of type Symbol
        [x for (x, y) in ecoli_core_subcellular_location if y == "membrane"],
        15.0,
    ),
    ("cytosol", [x for (x, y) in ecoli_core_subcellular_location if y == "cytosol"], 35.0),
]

ec_solution = X.enzyme_constrained_flux_balance_analysis(
    model;
    reaction_isozymes=float_reaction_isozymes,
    gene_product_molar_masses,
    capacity,
    optimizer=T.Optimizer,
)

ec_solution
ConstraintTrees.Tree{Float64} with 12 elements:
  :coupling                     => ConstraintTrees.Tree{Float64}(#= 0 elements =#)
  :flux_stoichiometry           => ConstraintTrees.Tree{Float64}(#= 72 elements =#)
  :fluxes                       => ConstraintTrees.Tree{Float64}(#= 95 elements =#)
  :fluxes_forward               => ConstraintTrees.Tree{Float64}(#= 95 elements =#)
  :fluxes_reverse               => ConstraintTrees.Tree{Float64}(#= 95 elements =#)
  :gene_product_amounts         => ConstraintTrees.Tree{Float64}(#= 130 elements =#)
  :gene_product_capacity        => ConstraintTrees.Tree{Float64}(#= 2 elements =#)
  :isozyme_flux_forward_balance => ConstraintTrees.Tree{Float64}(#= 62 elements =#)
  :isozyme_flux_reverse_balance => ConstraintTrees.Tree{Float64}(#= 62 elements =#)
  :isozyme_forward_amounts      => ConstraintTrees.Tree{Float64}(#= 62 elements =#)
  :isozyme_reverse_amounts      => ConstraintTrees.Tree{Float64}(#= 62 elements =#)
  :objective                    => 0.740724

This solution is both producing acetate and consuming oxygen, therefore it looks like overflow metabolism.

ec_solution.fluxes["EX_ac_e"]
ec_solution.fluxes["EX_o2_e"]
-11.119604376947802

Since we had two enzyme pools, the membrane and the cytosol, this optimal solution will be composed of at most two EFMs. In order to find out how small parameter changes will affect this optimal composition, we must first remove inactive reactions, so that the optimal solution is a unique superposition of the two EFMs, and we can differentiate it.

This solution contains many inactive reactions

sort(collect(ec_solution.fluxes), by=ComposedFunction(abs, last))
95-element Vector{Pair{Symbol, Union{Float64, ConstraintTrees.Tree{Float64}}}}:
    :EX_fru_e => 0.0
    :EX_fum_e => 0.0
 :EX_gln__L_e => 0.0
 :EX_mal__L_e => 0.0
     :FRUpts2 => 0.0
     :FUMt2_2 => 0.0
      :GLNabc => 0.0
     :MALt2_2 => 0.0
         :PFL => 0.0
    :EX_for_e => 1.1751401296095953e-14
              ⋮
    :SUCCt2_2 => 45.1017515034113
      :SUCCt3 => 45.101751508128025
         :PDH => 48.64218610958069
         :ENO => 53.247640002202544
         :PGM => -53.24764000220255
    :EX_co2_e => 53.669000943520395
        :CO2t => -53.66900094352041
        :GAPD => 54.355763660340095
         :PGK => -54.355763660340095

And also many inactive gene products.

sort(collect(ec_solution.gene_product_amounts), by=last)
130-element Vector{Pair{Symbol, Union{Float64, ConstraintTrees.Tree{Float64}}}}:
 :b0902 => 0.0
 :b0903 => 0.0
 :b2579 => 0.0
 :b3114 => 0.0
 :b3951 => 0.0
 :b3952 => 0.0
 :b0809 => 2.9237052619941317e-16
 :b0810 => 2.9237052619941317e-16
 :b0811 => 2.9237052619941317e-16
 :b1702 => 3.9759214856671975e-12
        ⋮
 :b2415 => 0.0347023816798353
 :b2416 => 0.0347023816798353
 :b0978 => 0.040328743170719945
 :b0979 => 0.040328743170719945
 :b3528 => 0.053498438408503594
 :b2779 => 0.07065207134510235
 :b1779 => 0.11726330582717176
 :b1478 => 0.15248718085537333
 :b2926 => 0.2619504379992874

Let us start by pruning the model.

flux_zero_tol = 1e-6 # these bounds make a real difference!
gene_zero_tol = 1e-6
pruned_model, pruned_reaction_isozymes = D.prune_model(
    model,
    ec_solution.fluxes,
    ec_solution.gene_product_amounts,
    float_reaction_isozymes,
    ec_solution.isozyme_forward_amounts,
    ec_solution.isozyme_reverse_amounts,
    flux_zero_tol,
    gene_zero_tol,
)
(AbstractFBCModels.CanonicalModel.Model(Dict{String, AbstractFBCModels.CanonicalModel.Reaction}("ACALD" => AbstractFBCModels.CanonicalModel.Reaction("Acetaldehyde dehydrogenase (acetylating)", 0.0, 1000.0, Dict("coa_c" => 1.0, "nad_c" => 1.0, "accoa_c" => -1.0, "acald_c" => 1.0, "h_c" => -1.0, "nadh_c" => -1.0), 0.0, [["b0351"], ["b1241"]], Dict("bigg.reaction" => ["ACALD"], "sabiork" => ["163"], "metanetx.reaction" => ["MNXR95210"], "rhea" => ["23290", "23291", "23289", "23288"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn00171"], "kegg.reaction" => ["R00228"], "biocyc" => ["META:ACETALD-DEHYDROG-RXN"], "ec-code" => ["1.2.1.10"]), Dict("original_bigg_ids" => ["ACALD"])), "PTAr" => AbstractFBCModels.CanonicalModel.Reaction("Phosphotransacetylase", 0.0, 1000.0, Dict("coa_c" => 1.0, "accoa_c" => -1.0, "actp_c" => 1.0, "pi_c" => -1.0), 0.0, [["b2297"], ["b2458"]], Dict("bigg.reaction" => ["PTAr"], "sabiork" => ["72"], "metanetx.reaction" => ["MNXR103319"], "rhea" => ["19521", "19523", "19522", "19524"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn00173"], "kegg.reaction" => ["R00230"], "biocyc" => ["META:PHOSACETYLTRANS-RXN"], "ec-code" => ["2.3.1.8"]), Dict("original_bigg_ids" => ["PTAr"])), "ALCD2x" => AbstractFBCModels.CanonicalModel.Reaction("Alcohol dehydrogenase (ethanol)", 0.0, 1000.0, Dict("nad_c" => 1.0, "acald_c" => -1.0, "etoh_c" => 1.0, "h_c" => -1.0, "nadh_c" => -1.0), 0.0, [["b0356"], ["b1241"], ["b1478"]], Dict("bigg.reaction" => ["ALCD2x"], "sabiork" => ["597"], "metanetx.reaction" => ["MNXR95725"], "rhea" => ["25292", "25290", "25291", "25293"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn00543"], "kegg.reaction" => ["R00754"], "biocyc" => ["META:ALCOHOL-DEHYDROG-RXN"], "reactome.reaction" => ["R-MMU-71707", "R-XTR-71707", "R-CFA-71707", "R-OSA-71707", "R-RNO-71707", "R-HSA-71707", "R-SSC-71707", "R-GGA-71707", "R-SPO-71707", "R-DME-71707", "R-DRE-71707", "R-BTA-71707", "R-ATH-71707", "R-SCE-71707", "R-TGU-71707", "R-CEL-71707", "R-DDI-71707"], "ec-code" => ["1.1.1.71", "1.1.1.1"]…), Dict("original_bigg_ids" => ["ALCD2x"])), "PDH" => AbstractFBCModels.CanonicalModel.Reaction("Pyruvate dehydrogenase", 0.0, 1000.0, Dict("coa_c" => -1.0, "nad_c" => -1.0, "accoa_c" => 1.0, "co2_c" => 1.0, "pyr_c" => -1.0, "nadh_c" => 1.0), 0.0, [["b0114", "b0115", "b0116"]], Dict("bigg.reaction" => ["PDH"], "sabiork" => ["523"], "metanetx.reaction" => ["MNXR102425"], "rhea" => ["28043", "28045", "28044", "28042"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn00154"], "kegg.reaction" => ["R00209"], "biocyc" => ["META:PYRUVDEH-RXN"], "reactome.reaction" => ["R-RNO-71397", "R-OSA-71397", "R-GGA-373177", "R-DME-71397", "R-TGU-71397", "R-XTR-71397", "R-CFA-71397", "R-BTA-71397", "R-HSA-71397", "R-GGA-71397", "R-SPO-71397", "R-CEL-71397", "R-DDI-71397", "R-DRE-71397", "R-SSC-71397", "R-SCE-71397", "R-ATH-71397", "R-MMU-71397"], "ec-code" => ["1.2.1", "1.8.1.4", "1.2.1.51", "1.2.4.1", "2.3.1.12"]…), Dict("original_bigg_ids" => ["PDH"])), "CO2t" => AbstractFBCModels.CanonicalModel.Reaction("CO2 transporter via diffusion", 0.0, 1000.0, Dict("co2_e" => 1.0, "co2_c" => -1.0), 0.0, [["s0001"]], Dict("metanetx.reaction" => ["MNXR96810"], "sbo" => ["SBO:0000185"], "seed.reaction" => ["rxn05467", "rxn08237", "rxn09706", "rxn09821", "rxn09775", "rxn09876", "rxn08238", "rxn09860"], "biocyc" => ["META:TRANS-RXN0-545"], "reactome.reaction" => ["R-DDI-1247645", "R-CFA-1237069", "R-TGU-1247649", "R-CFA-1237042", "R-CEL-1247645", "R-XTR-1237069", "R-HSA-1247645", "R-OSA-1237042", "R-GGA-1237042", "R-BTA-1247645"  …  "R-MMU-1247645", "R-DME-1237069", "R-RNO-1237042", "R-TGU-1247645", "R-BTA-1237042", "R-GGA-1237069", "R-DME-1247649", "R-SSC-1237069", "R-DRE-1237069", "R-ATH-1247649"], "bigg.reaction" => ["CO2t"]), Dict("original_bigg_ids" => ["CO2t"])), "PYK" => AbstractFBCModels.CanonicalModel.Reaction("Pyruvate kinase", 0.0, 1000.0, Dict("adp_c" => -1.0, "atp_c" => 1.0, "pep_c" => -1.0, "pyr_c" => 1.0, "h_c" => -1.0), 0.0, [["b1676"], ["b1854"]], Dict("bigg.reaction" => ["PYK"], "sabiork" => ["9"], "metanetx.reaction" => ["MNXR103371"], "rhea" => ["18160", "18159", "18158", "18157"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn00148"], "kegg.reaction" => ["R00200"], "biocyc" => ["META:PEPDEPHOS-RXN"], "reactome.reaction" => ["R-SSC-71670", "R-GGA-353056", "R-DRE-71670", "R-OSA-71670", "R-SPO-71670", "R-DDI-71670", "R-SCE-71670", "R-CEL-71670", "R-PFA-71670", "R-HSA-71670", "R-TGU-71670", "R-DME-71670", "R-ATH-71670", "R-BTA-71670", "R-MMU-71670", "R-RNO-71670", "R-GGA-71670", "R-CFA-71670", "R-XTR-71670"], "ec-code" => ["2.7.1.40"]…), Dict("original_bigg_ids" => ["PYK"])), "EX_nh4_e" => AbstractFBCModels.CanonicalModel.Reaction("Ammonia exchange", 0.0, 1000.0, Dict("nh4_e" => 1.0), 0.0, nothing, Dict("sabiork" => ["11683"], "metanetx.reaction" => ["MNXR101950"], "rhea" => ["28749", "28748", "28750", "28747"], "sbo" => ["SBO:0000627"], "seed.reaction" => ["rxn13364", "rxn09835", "rxn05466", "rxn08987", "rxn08986", "rxn09736"], "biocyc" => ["META:TRANS-RXN0-206", "META:RXN-9615", "META:TRANS-RXN0-544"], "reactome.reaction" => ["R-CEL-444416", "R-SSC-444416", "R-DDI-444416", "R-MMU-444416", "R-DRE-444416", "R-GGA-444416", "R-CFA-444416", "R-HSA-444416", "R-XTR-444416", "R-BTA-444416", "R-RNO-444416", "R-TGU-444416", "R-DME-444416"], "bigg.reaction" => ["EX_nh4_e"]), Dict("original_bigg_ids" => ["EX_nh4_e"])), "CS" => AbstractFBCModels.CanonicalModel.Reaction("Citrate synthase", 0.0, 1000.0, Dict("coa_c" => 1.0, "h2o_c" => -1.0, "accoa_c" => -1.0, "cit_c" => 1.0, "oaa_c" => -1.0, "h_c" => 1.0), 0.0, [["b0720"]], Dict("bigg.reaction" => ["CS"], "sabiork" => ["267"], "metanetx.reaction" => ["MNXR96920"], "rhea" => ["16847", "16846", "16845", "16848"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn00256"], "kegg.reaction" => ["R00351"], "biocyc" => ["META:CITSYN-RXN", "META:RXN-14905"], "reactome.reaction" => ["R-GGA-373006", "R-GGA-70975", "R-CEL-70975", "R-HSA-70975", "R-DRE-70975", "R-MMU-70975", "R-ATH-70975", "R-SPO-70975", "R-OSA-70975", "R-SCE-70975", "R-SSC-70975", "R-DDI-70975", "R-RNO-70975", "R-DME-70975", "R-PFA-70975", "R-XTR-70975", "R-CFA-70975", "R-BTA-70975"], "ec-code" => ["2.3.3.16", "2.3.3.1", "2.3.3.3"]…), Dict("original_bigg_ids" => ["CS"])), "PGM" => AbstractFBCModels.CanonicalModel.Reaction("Phosphoglycerate mutase", 0.0, 1000.0, Dict("3pg_c" => -1.0, "2pg_c" => 1.0), 0.0, [["b0755"], ["b3612"], ["b4395"]], Dict("bigg.reaction" => ["PGM"], "sabiork" => ["7641"], "metanetx.reaction" => ["MNXR102547"], "rhea" => ["15902", "15903", "15901", "15904"], "sbo" => ["SBO:0000176"], "seed.reaction" => ["rxn01106"], "kegg.reaction" => ["R01518"], "biocyc" => ["META:3PGAREARR-RXN", "META:RXN-15513"], "reactome.reaction" => ["R-XTR-71654", "R-SSC-71654", "R-DME-71445", "R-XTR-71445", "R-RNO-71445", "R-GGA-71654", "R-SSC-71445", "R-GGA-71445", "R-DDI-71654", "R-SPO-71654"  …  "R-DDI-71445", "R-RNO-71654", "R-DRE-71445", "R-OSA-71445", "R-PFA-71654", "R-DME-71654", "R-TGU-71445", "R-SCE-71445", "R-ATH-71445", "R-SCE-71654"], "ec-code" => ["5.4.2.1", "5.4.2.11", "5.4.2.12"]…), Dict("original_bigg_ids" => ["PGM"])), "TKT1" => AbstractFBCModels.CanonicalModel.Reaction("Transketolase", 0.0, 1000.0, Dict("g3p_c" => 1.0, "r5p_c" => -1.0, "s7p_c" => 1.0, "xu5p__D_c" => -1.0), 0.0, [["b2465"], ["b2935"]], Dict("bigg.reaction" => ["TKT1"], "metanetx.reaction" => ["MNXR104868"], "sbo" => ["SBO:0000176"], "ec-code" => ["2.2.1.1"]), Dict("original_bigg_ids" => ["TKT1"]))…), Dict{String, AbstractFBCModels.CanonicalModel.Metabolite}("glu__L_c" => AbstractFBCModels.CanonicalModel.Metabolite("L-Glutamate", "c", Dict("C" => 5, "N" => 1, "H" => 8, "O" => 4), -1, 0.0, Dict("kegg.drug" => ["D00007", "D04341"], "kegg.compound" => ["C00025", "C00302"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:Glutamates", "META:GLT"], "chebi" => ["CHEBI:76051", "CHEBI:21301", "CHEBI:29985", "CHEBI:42825", "CHEBI:29987", "CHEBI:18237", "CHEBI:24314", "CHEBI:16015", "CHEBI:13107", "CHEBI:5431", "CHEBI:21304", "CHEBI:6224", "CHEBI:14321", "CHEBI:29988"], "bigg.metabolite" => ["glu__L"], "seed.compound" => ["cpd27177", "cpd00023", "cpd19002"], "sabiork" => ["73", "2010"], "metanetx.chemical" => ["MNXM89557"], "inchi_key" => ["WHUUTDBJXJRKMK-VKHMYHEASA-M"]…), Dict("original_bigg_ids" => ["glu_L_c"])), "icit_c" => AbstractFBCModels.CanonicalModel.Metabolite("Isocitrate", "c", Dict("C" => 6, "H" => 5, "O" => 7), -3, 0.0, Dict("chebi" => ["CHEBI:16087", "CHEBI:36454", "CHEBI:5998", "CHEBI:24886", "CHEBI:36453", "CHEBI:14465", "CHEBI:30887", "CHEBI:24884"], "metanetx.chemical" => ["MNXM89661"], "inchi_key" => ["ODBLHEXUDAPZAU-UHFFFAOYSA-K"], "kegg.compound" => ["C00311"], "hmdb" => ["HMDB00193"], "sabiork" => ["2013"], "sbo" => ["SBO:0000247"], "bigg.metabolite" => ["icit"], "seed.compound" => ["cpd00260"]), Dict("original_bigg_ids" => ["icit_c"])), "co2_c" => AbstractFBCModels.CanonicalModel.Metabolite("CO2 CO2", "c", Dict("C" => 1, "O" => 2), 0, 0.0, Dict("envipath" => ["650babc9-9d68-4b73-9332-11972ca26f7b/compound/2ec3da94-5f50-4525-81b1-5607c5c7a3d3", "32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/compound/05f60af4-0a3f-4ead-9a29-33bb0f123379"], "kegg.drug" => ["D00004"], "kegg.compound" => ["C00011"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:CARBON-DIOXIDE"], "chebi" => ["CHEBI:23011", "CHEBI:3283", "CHEBI:48829", "CHEBI:16526", "CHEBI:13283", "CHEBI:13285", "CHEBI:13284", "CHEBI:13282"], "bigg.metabolite" => ["co2"], "seed.compound" => ["cpd00011"], "sabiork" => ["1266"], "metanetx.chemical" => ["MNXM13"]…), Dict("original_bigg_ids" => ["co2_c"])), "6pgl_c" => AbstractFBCModels.CanonicalModel.Metabolite("6-phospho-D-glucono-1,5-lactone", "c", Dict("C" => 6, "P" => 1, "H" => 9, "O" => 9), -2, 0.0, Dict("kegg.compound" => ["C01236"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:D-6-P-GLUCONO-DELTA-LACTONE"], "chebi" => ["CHEBI:12233", "CHEBI:4160", "CHEBI:57955", "CHEBI:16938", "CHEBI:20989", "CHEBI:12958"], "bigg.metabolite" => ["6pgl"], "seed.compound" => ["cpd00911"], "sabiork" => ["1366"], "metanetx.chemical" => ["MNXM429"], "inchi_key" => ["IJOJIVNDFQSGAB-SQOUGZDYSA-L"], "hmdb" => ["HMDB62628", "HMDB01127"]…), Dict("original_bigg_ids" => ["6pgl_c"])), "cit_c" => AbstractFBCModels.CanonicalModel.Metabolite("Citrate", "c", Dict("C" => 6, "H" => 5, "O" => 7), -3, 0.0, Dict("kegg.drug" => ["D00037"], "kegg.compound" => ["C00158"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:CIT"], "chebi" => ["CHEBI:35802", "CHEBI:133748", "CHEBI:35809", "CHEBI:76049", "CHEBI:41523", "CHEBI:35810", "CHEBI:30769", "CHEBI:13999", "CHEBI:23322", "CHEBI:35808", "CHEBI:42563", "CHEBI:16947", "CHEBI:132362", "CHEBI:35804", "CHEBI:23321", "CHEBI:3727", "CHEBI:35806"], "bigg.metabolite" => ["cit"], "seed.compound" => ["cpd00137"], "sabiork" => ["1952"], "metanetx.chemical" => ["MNXM131"], "inchi_key" => ["KRKNYBCHXYNGOX-UHFFFAOYSA-K"]…), Dict("original_bigg_ids" => ["cit_c"])), "glc__D_e" => AbstractFBCModels.CanonicalModel.Metabolite("D-Glucose", "e", Dict("C" => 6, "H" => 12, "O" => 6), 0, 0.0, Dict("kegg.drug" => ["D00009"], "kegg.compound" => ["C00031"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:Glucopyranose"], "chebi" => ["CHEBI:12965", "CHEBI:20999", "CHEBI:4167", "CHEBI:17634"], "bigg.metabolite" => ["glc__D"], "seed.compound" => ["cpd26821", "cpd00027"], "sabiork" => ["1406", "1407"], "metanetx.chemical" => ["MNXM41"], "inchi_key" => ["WQZGKKKJIJFFOK-GASJEMHNSA-N"]…), Dict("original_bigg_ids" => ["glc_D_e"])), "nadh_c" => AbstractFBCModels.CanonicalModel.Metabolite("Nicotinamide adenine dinucleotide - reduced", "c", Dict("C" => 21, "N" => 7, "P" => 2, "H" => 27, "O" => 14), -2, 0.0, Dict("kegg.compound" => ["C00004"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:NADH"], "chebi" => ["CHEBI:13395", "CHEBI:21902", "CHEBI:7423", "CHEBI:44216", "CHEBI:57945", "CHEBI:16908", "CHEBI:13396"], "bigg.metabolite" => ["nadh"], "seed.compound" => ["cpd00004"], "sabiork" => ["38"], "metanetx.chemical" => ["MNXM10"], "inchi_key" => ["BOPGDPNILDQYTO-NNYOXOHSSA-L"], "hmdb" => ["HMDB01487"]…), Dict("original_bigg_ids" => ["nadh_c"])), "coa_c" => AbstractFBCModels.CanonicalModel.Metabolite("Coenzyme A", "c", Dict("S" => 1, "C" => 21, "N" => 7, "P" => 3, "H" => 32, "O" => 16), -4, 0.0, Dict("envipath" => ["32de3cf4-e3e6-4168-956e-32fa5ddb0ce1/compound/19310484-6aa5-4dcf-b1da-855a8c21ecfd"], "kegg.compound" => ["C00010"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:COA-GROUP", "META:CO-A"], "chebi" => ["CHEBI:41631", "CHEBI:41597", "CHEBI:741566", "CHEBI:23355", "CHEBI:13295", "CHEBI:13298", "CHEBI:57287", "CHEBI:15346", "CHEBI:3771", "CHEBI:13294"], "bigg.metabolite" => ["coa"], "seed.compound" => ["cpd22528", "cpd00010"], "sabiork" => ["1265"], "metanetx.chemical" => ["MNXM12"], "inchi_key" => ["RGJOEKWQDUBAIZ-IBOSZNHHSA-J"]…), Dict("original_bigg_ids" => ["coa_c"])), "g3p_c" => AbstractFBCModels.CanonicalModel.Metabolite("Glyceraldehyde 3-phosphate", "c", Dict("C" => 3, "P" => 1, "H" => 5, "O" => 6), -2, 0.0, Dict("kegg.compound" => ["C00661", "C00118"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:GAP"], "chebi" => ["CHEBI:5446", "CHEBI:14333", "CHEBI:12984", "CHEBI:181", "CHEBI:17138", "CHEBI:21026", "CHEBI:12983", "CHEBI:18324", "CHEBI:58027", "CHEBI:59776", "CHEBI:29052"], "bigg.metabolite" => ["g3p"], "seed.compound" => ["cpd00102", "cpd19005"], "sabiork" => ["27", "1687"], "metanetx.chemical" => ["MNXM74"], "inchi_key" => ["LXJXRIRHZLFYRP-VKHMYHEASA-L"], "hmdb" => ["HMDB01112"]…), Dict("original_bigg_ids" => ["g3p_c"])), "nad_c" => AbstractFBCModels.CanonicalModel.Metabolite("Nicotinamide adenine dinucleotide", "c", Dict("C" => 21, "N" => 7, "P" => 2, "H" => 26, "O" => 14), -1, 0.0, Dict("kegg.drug" => ["D00002"], "kegg.compound" => ["C00003"], "sbo" => ["SBO:0000247"], "biocyc" => ["META:NAD"], "chebi" => ["CHEBI:7422", "CHEBI:44215", "CHEBI:13394", "CHEBI:21901", "CHEBI:57540", "CHEBI:15846", "CHEBI:44281", "CHEBI:44214", "CHEBI:13393", "CHEBI:29867"], "bigg.metabolite" => ["nad"], "seed.compound" => ["cpd00003"], "sabiork" => ["37"], "metanetx.chemical" => ["MNXM8"], "inchi_key" => ["BAWFJGJZGIEFAR-NNYOXOHSSA-M"]…), Dict("original_bigg_ids" => ["nad_c"]))…), Dict{String, AbstractFBCModels.CanonicalModel.Gene}("b4301" => AbstractFBCModels.CanonicalModel.Gene("sgcE", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P39362"], "ecogene" => ["EG12553"], "ncbigene" => ["948829"], "ncbigi" => ["16132122"], "refseq_locus_tag" => ["b4301"], "refseq_name" => ["sgcE"], "asap" => ["ABE-0014097"], "refseq_synonym" => ["JW4263", "ECK4290", "yjhK"]), Dict("original_bigg_ids" => ["b4301"])), "b1779" => AbstractFBCModels.CanonicalModel.Gene("gapA", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P0A9B2"], "ecogene" => ["EG10367"], "ncbigene" => ["947679"], "ncbigi" => ["16129733"], "refseq_locus_tag" => ["b1779"], "refseq_name" => ["gapA"], "asap" => ["ABE-0005920"], "refseq_synonym" => ["ECK1777", "JW1768"]), Dict("original_bigg_ids" => ["b1779"])), "b2276" => AbstractFBCModels.CanonicalModel.Gene("nuoN", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P0AFF0"], "ecogene" => ["EG12093"], "ncbigene" => ["945136"], "ncbigi" => ["145698289"], "refseq_locus_tag" => ["b2276"], "refseq_name" => ["nuoN"], "asap" => ["ABE-0007526"], "refseq_synonym" => ["JW2271", "ECK2270"]), Dict("original_bigg_ids" => ["b2276"])), "b1761" => AbstractFBCModels.CanonicalModel.Gene("gdhA", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P00370"], "ecogene" => ["EG10372"], "ncbigene" => ["946802"], "ncbigi" => ["16129715"], "refseq_locus_tag" => ["b1761"], "refseq_name" => ["gdhA"], "asap" => ["ABE-0005865"], "refseq_synonym" => ["ECK1759", "JW1750"]), Dict("original_bigg_ids" => ["b1761"])), "b3493" => AbstractFBCModels.CanonicalModel.Gene("pitA", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P0AFJ7"], "ecogene" => ["EG12230"], "ncbigene" => ["948009"], "ncbigi" => ["16131365"], "refseq_locus_tag" => ["b3493"], "refseq_name" => ["pitA"], "asap" => ["ABE-0011407"], "refseq_synonym" => ["pit", "ECK3478", "JW3460"]), Dict("original_bigg_ids" => ["b3493"])), "b2926" => AbstractFBCModels.CanonicalModel.Gene("pgk", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P0A799"], "ecogene" => ["EG10703"], "ncbigene" => ["947414"], "ncbigi" => ["16130827"], "refseq_locus_tag" => ["b2926"], "refseq_name" => ["pgk"], "asap" => ["ABE-0009605"], "refseq_synonym" => ["ECK2922", "JW2893"]), Dict("original_bigg_ids" => ["b2926"])), "b0979" => AbstractFBCModels.CanonicalModel.Gene("cbdB", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P26458"], "ecogene" => ["EG11379"], "ncbigene" => ["947547"], "ncbigi" => ["16128945"], "refseq_locus_tag" => ["b0979"], "refseq_name" => ["cbdB"], "asap" => ["ABE-0003302"], "refseq_synonym" => ["JW0961", "appB", "cyxB", "ECK0970"]), Dict("original_bigg_ids" => ["b0979"])), "b2282" => AbstractFBCModels.CanonicalModel.Gene("nuoH", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P0AFD4"], "ecogene" => ["EG12088"], "ncbigene" => ["946761"], "ncbigi" => ["16130217"], "refseq_locus_tag" => ["b2282"], "refseq_name" => ["nuoH"], "asap" => ["ABE-0007541"], "refseq_synonym" => ["JW2277", "ECK2276"]), Dict("original_bigg_ids" => ["b2282"])), "b2283" => AbstractFBCModels.CanonicalModel.Gene("nuoG", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P33602"], "ecogene" => ["EG12087"], "ncbigene" => ["946762"], "ncbigi" => ["145698290"], "refseq_locus_tag" => ["b2283"], "refseq_name" => ["nuoG"], "asap" => ["ABE-0007543"], "refseq_synonym" => ["JW2278", "ECK2277"]), Dict("original_bigg_ids" => ["b2283"])), "b2416" => AbstractFBCModels.CanonicalModel.Gene("ptsI", Dict("sbo" => ["SBO:0000243"], "uniprot" => ["P08839"], "ecogene" => ["EG10789"], "ncbigene" => ["946879"], "ncbigi" => ["16130342"], "refseq_locus_tag" => ["b2416"], "refseq_name" => ["ptsI"], "asap" => ["ABE-0007967"], "refseq_synonym" => ["ECK2411", "ctr", "JW2409"]), Dict("original_bigg_ids" => ["b2416"]))…), Dict{String, AbstractFBCModels.CanonicalModel.Coupling}()), Dict{String, Dict{String, COBREXA.Isozyme}}("PFK" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b1723" => 1.0), 3601.6560000000004, nothing)), "PTAr" => Dict("isozyme_2" => COBREXA.Isozyme(Dict("b2458" => 1.0), 4219.092000000001, nothing)), "TKT2" => Dict("isozyme_2" => COBREXA.Isozyme(Dict("b2935" => 1.0), 1682.712, nothing)), "PGL" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b0767" => 1.0), 7633.512000000001, nothing)), "ACALD" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b0351" => 1.0), 2045.1960000000001, nothing)), "PGI" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b4025" => 1.0), 1685.1960000000001, nothing)), "ALCD2x" => Dict("isozyme_3" => COBREXA.Isozyme(Dict("b1478" => 1.0), 273.42, nothing)), "PGK" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b2926" => 1.0), 207.50400000000002, nothing)), "ICDHyr" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b1136" => 1.0), 142.632, nothing)), "PDH" => Dict("isozyme_1" => COBREXA.Isozyme(Dict("b0114" => 1.0, "b0115" => 1.0, "b0116" => 1.0), 1907.136, nothing))…))

Since we will later be differentiating our solution, we need to make parameter isozymes and a kinetic model

parameter_values = Dict(
    Symbol(x) => iso.kcat_forward for (x, y) in pruned_reaction_isozymes for (_, iso) in y
)

rid_kcat = Dict(k => Ex(Symbol(k)) for (k, _) in parameter_values)
parameter_isozymes = Dict(
    x => Dict(
        "isozyme" => X.IsozymeT{Ex}(
            iso.gene_product_stoichiometry,
            rid_kcat[Symbol(x)],
            nothing,
        ) for (_, iso) in y
    ) for (x, y) in pruned_reaction_isozymes
)

pkm = X.enzyme_constrained_flux_balance_constraints( # kinetic model
    pruned_model;
    reaction_isozymes=parameter_isozymes,
    gene_product_molar_masses,
    capacity,
)

pruned_solution = D.optimized_values(
    pkm,
    parameter_values;
    objective=pkm.objective.value,
    optimizer=T.Optimizer,
)
(tree = ConstraintTrees.Tree{Float64}(#= 12 elements =#), primal_values = [41.69304582306297, 3.3738864917780025, 0.7991675278074312, 0.7991675278074535, 3.373886491778005, 41.69304582306298, 8.390000014263562, 0.7407243746482269, 53.669001513849246, 0.7991675278074185  …  0.0029929638145053954, 0.019613928519562524, 0.070652071827256, 0.04032874991388847, 0.001959493158657936, 0.15248718390411423, 0.001389701890214975, 0.0011621347499833816, 0.001689819318361655, 0.0203858436174642], equality_dual_values = [0.02492930687014489, 0.014760868846430996, 0.014596725288290495, 0.014965230475163616, 0.015197498948580045, -0.00031185145643069606, 0.0, 0.004922650431641902, 0.00602434591003517, 0.03343676205069087  …  0.0011008155494533034, 0.0027816413336696046, 0.00014303200199665392, 6.0870518424461934e-5, 0.0014429753769459965, 0.0009355543548025853, 0.0015010372952537607, 0.0010786131569188657, 0.0007180292162103088, 0.00017951934251539064], inequality_dual_values = [-1.3851828240284269e-12, -5.011795376759092e-11, -3.709882083649073e-11, -3.874176899575392e-11, -5.072428333116834e-11, -1.4179922937351746e-12, -0.012547259306716914, -4.26844003327565e-11, -6.870164106469038e-13, -3.324292443370856e-11  …  -3.7987563045117077e-14, -3.790555906101654e-14, -9.777251415316644e-14, 0.0, -3.7867994935268234e-14, -3.785540430565886e-14, -3.781840397270636e-14, -4.6375268684640907e-14, -0.016731799175476156, -0.01735886145395185])

All reactions with zero flux have been removed in the pruned model

sort(collect(pruned_solution.tree.fluxes), by=ComposedFunction(abs, last))
52-element Vector{Pair{Symbol, Union{Float64, ConstraintTrees.Tree{Float64}}}}:
                     :GLNS => 0.18940322259736744
 :BIOMASS_Ecoli_core_w_GAM => 0.7407243746482269
                       :CS => 0.7991675278074185
                   :ACONTa => 0.7991675278074312
                   :ACONTb => 0.7991675278074535
                   :ICDHyr => 0.7991675278074873
                     :TKT2 => 1.716838610153485
                     :TALA => 1.9842401094015332
                     :TKT1 => 1.9842401094015338
                      :PPC => 2.1226197679914267
                           ⋮
                 :SUCCt2_2 => 45.10175150771673
                   :SUCCt3 => 45.101751507716735
                      :PDH => 48.642186653954965
                      :ENO => 53.247640453329765
                      :PGM => 53.247640453329765
                     :CO2t => 53.669001513849246
                 :EX_co2_e => 53.669001513849246
                      :PGK => 54.35576411780352
                     :GAPD => 54.35576411780354

Genes with zero concentration have also been removed.

sort(abs.(collect(values(pruned_solution.tree.gene_product_amounts))))
49-element Vector{Float64}:
 0.0005800203793505351
 0.0005856841395960682
 0.0007996712306205315
 0.0008318932491454554
 0.001689819318361655
 0.001959493158657936
 0.0025518366401983566
 0.0027916453170659383
 0.0029929638145053954
 0.0032366263256029797
 ⋮
 0.034702381966990506
 0.034702381966990506
 0.04032874991388847
 0.04032874991388847
 0.05349843841361214
 0.070652071827256
 0.1172633066639992
 0.15248718390411423
 0.26195044007731577

Optimal flux modes (OFMs)

We now wish to find the optimal flux modes (OFMs) of this optimal solution.

In our model, we have a fixed ATP maintenance flux

pruned_model.reactions["ATPM"].lower_bound
8.39

As a result of this fixed flux, standard EFM theory does not hold. Instead, we investigate the optimal flux modes (OFMs). These are the flux modes that are able to produce the optimal rate of the objective, whilst still being constrained to the fixed ATPM.

To find the OFMs, we must augment the stoichiometric matrix, and can then use the same find_efms function, since this augmented matrix behaves as required for the algorithm to work.

N = A.stoichiometry(pruned_model)
55×52 SparseArrays.SparseMatrixCSC{Float64, Int64} with 205 stored entries:
⎡⠀⠀⠀⠠⠀⠐⠀⠀⠀⠀⠀⠀⠁⠀⡀⠀⠀⠀⠨⡰⠀⠀⠀⠀⠀⠀⎤
⎢⡐⠀⢆⠀⠀⠀⠠⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⎥
⎢⢡⠒⠀⣈⠈⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⢁⢀⠀⠀⢅⠀⠀⠀⠀⎥
⎢⠐⠄⠀⠚⡠⠀⠀⠀⠀⠀⠀⠀⠀⠊⡀⡁⠀⡐⠐⠀⢀⠐⠀⠀⠀⠀⎥
⎢⠂⠀⠀⢐⠑⠀⠀⠁⠀⠀⠀⠄⠀⠀⠀⠀⠀⠂⠀⠀⠀⠂⠀⠀⡀⡠⎥
⎢⠀⠀⠈⠠⠀⠀⠃⠐⠀⠀⠀⡀⠀⠀⠀⠀⠀⢠⠄⠀⠀⠀⠀⠀⠄⠄⎥
⎢⠀⠀⠀⢘⠀⠀⠀⠀⠄⠀⠀⠑⠱⡀⠀⠀⠀⠀⠂⠀⠀⠀⠀⠀⠉⠉⎥
⎢⡀⠒⣀⣚⢐⡒⡀⠀⠠⠀⠀⢀⡀⣙⠰⢀⠀⢀⠀⡂⣐⢀⠀⣀⠀⠀⎥
⎢⡄⠐⢡⢠⠀⠁⠁⠀⠀⠁⠀⠀⡄⠀⠀⢪⠀⡄⠀⠀⠁⠀⠀⠉⠀⠀⎥
⎢⠀⠀⠀⠘⠀⠀⠀⠀⠀⢀⠀⠘⠀⠼⠃⠃⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⢠⠠⢁⠀⠀⠀⠀⠂⠀⢀⠀⠀⠀⠘⠀⠀⠀⢠⢀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠩⠀⡀⠀⠀⠀⠀⠐⠀⠡⠁⠀⢀⠀⠄⠀⠀⠋⠡⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠐⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠄⠈⠀⠀⠀⠀⠀⠀⠴⠀⣐⠀⎥
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠄⠛⠠⠄⎦

Take the fixed fluxes (here only ATPM) and the objective flux

atpm_idx = findfirst(x -> x == "ATPM", A.reactions(pruned_model))
biomass_idx = findfirst(x -> x == "BIOMASS_Ecoli_core_w_GAM", A.reactions(pruned_model))
fixed_fluxes = [atpm_idx, biomass_idx]
flux_values = [
    pruned_solution.tree.fluxes["ATPM"],
    pruned_solution.tree.fluxes["BIOMASS_Ecoli_core_w_GAM"],
]
2-element Vector{Float64}:
 8.390000014263562
 0.7407243746482269

Calculate the OFMs, using the get_ofms function

OFMs = get_ofms(Matrix(N), fixed_fluxes, flux_values)
OFM_dicts = [
    Dict(A.reactions(pruned_model) .=> OFMs[:, 1]),
    Dict(A.reactions(pruned_model) .=> OFMs[:, 2]),
]
2-element Vector{Dict{String, Float64}}:
 Dict("NH4t" => 4.039021870081851, "ACALD" => 48.44081880661824, "PTAr" => 0.0, "PGL" => 6.350267100077981, "ALCD2x" => 48.44081880661824, "PGK" => 57.72965060958195, "PDH" => 52.01607314573287, "CO2t" => 57.04288800562686, "PYK" => 23.206660196868704, "EX_nh4_e" => 4.039021870081851…)
 Dict("NH4t" => 4.039021870081861, "ACALD" => 0.0, "PTAr" => 24.22040940330914, "PGL" => 6.3502671000779864, "ALCD2x" => 0.0, "PGK" => 33.50924120627286, "PDH" => 27.795663742423752, "CO2t" => 32.82247860231776, "PYK" => 11.096455495214123, "EX_nh4_e" => 4.039021870081861…)

Scale the OFMs to have one unit flux through biomass

OFM_dicts = [
    Dict(x => y / OFM_dicts[1]["BIOMASS_Ecoli_core_w_GAM"] for (x, y) in OFM_dicts[1]),
    Dict(x => y / OFM_dicts[2]["BIOMASS_Ecoli_core_w_GAM"] for (x, y) in OFM_dicts[2]),
]
2-element Vector{Dict{String, Float64}}:
 Dict("ACALD" => 65.39655027502367, "PTAr" => 0.0, "ALCD2x" => 65.39655027502367, "PDH" => 70.22325027502372, "CO2t" => 77.0096002750237, "PYK" => 31.3296834708452, "EX_nh4_e" => 5.452799999999999, "CS" => 1.0789000000000004, "PGM" => 76.44075027502372, "TKT1" => 2.678783333333333…)
 Dict("ACALD" => 0.0, "PTAr" => 32.698275137511864, "ALCD2x" => 0.0, "PDH" => 37.52497513751188, "CO2t" => 44.3113251375119, "PYK" => 14.980545902089256, "EX_nh4_e" => 5.452800000000012, "CS" => 1.0788999999999962, "PGM" => 43.74247513751189, "TKT1" => 2.678783333333335…)

We see that one EFM is releasing ethanol, and the other acetate

OFM_dicts[1]["EX_etoh_e"]
OFM_dicts[1]["EX_ac_e"]


OFM_dicts[2]["EX_etoh_e"]
OFM_dicts[2]["EX_ac_e"]
32.698275137511864

Differentiate OFM usage

The weighted sum of these two OFMs is equal to the whole optimal flux solution, and can be easily calculated using two non-shared reactions

M = [
    OFM_dicts[1]["EX_etoh_e"] OFM_dicts[2]["EX_etoh_e"]
    OFM_dicts[1]["EX_ac_e"] OFM_dicts[2]["EX_ac_e"]
]

v = [
    pruned_solution.tree.fluxes["EX_etoh_e"]
    pruned_solution.tree.fluxes["EX_ac_e"]
]

λ = M \ v
2-element Vector{Float64}:
 0.6375419750388028
 0.10318239960943508

λ tells us the weighting of the two OFMs, so here we have 0.6 units of flux through OFM₁ and 0.1 units of flux through OFM₂ to produce our optimal biomass.

It is of interest to see how changes in the kinetic parameters affect this optimal weighting.

parameters = Ex.(collect(keys(parameter_values)))
rid_gcounts = Dict(
    rid => [v.gene_product_stoichiometry for (k, v) in d][1] for
    (rid, d) in pruned_reaction_isozymes
)
rid_pid =
    Dict(rid => [iso.kcat_forward for (k, iso) in v][1] for (rid, v) in parameter_isozymes)
sens = differentiate_ofm(
    OFM_dicts,
    parameters,
    rid_pid,
    parameter_values,
    rid_gcounts,
    capacity,
    gene_product_molar_masses,
    T.Optimizer,
)
2×33 Matrix{Float64}:
  2.34957e-6  -3.24672e-6   3.25167e-5   8.97662e-7   2.81279e-6   1.09438e-6  …   5.32262e-6   1.64894e-6   8.33172e-6  -4.56087e-5   0.000106976
 -9.72454e-7   6.36629e-6  -1.34582e-5  -3.71529e-7  -1.16417e-6  -4.52948e-7     -2.20296e-6  -6.82471e-7  -3.44838e-6   8.94313e-5  -4.42758e-5

To get control coefficients instead of sensititivities, we scale these derivatives.

scaled_sens = Matrix(undef, size(sens, 1), size(sens, 2))
for (i, col) in enumerate(eachcol(sens))
    scaled_sens[:, i] = collect(values(parameter_values))[i] .* col ./ λ
end

We plot the control coefficients to get a visual overview of the system.

sens_perm = sortperm(scaled_sens[1, :])
scaled_sens[:, sens_perm]
parameters[sens_perm]


f, a, hm = heatmap(
    scaled_sens[:, sens_perm]';
    colormap=CairoMakie.Reverse(:RdBu),
    axis=(
        yticks=(1:2, ["Ethanol producing", "Acetate producing"]),
        yticklabelrotation=pi / 2,
        xticklabelrotation=pi / 2,
        xlabel="Parameters",
        ylabel="Control coefficient, param/λ * ∂λ/∂param",
        xticks=(1:length(parameters), string.(parameters[sens_perm])),
        title="Control coefficients of OFM weightings in optimal solution",
        ylabelsize=23,
        xlabelsize=23,
        titlesize=25,
    ),
);
Colorbar(f[:, end+1], hm)
f
Example block output

We see that most kinetic parameters have little effect on the optimal OFM weightings, see the reactions from RPE to PGI. Those parameters that do affect optimal weightings always increase the use of one OFM and decrease the use of the other.

Increasing the turnover number of PGK is predicted to decrease the optimal flux through the acetate producing OFM, and increase the optimal flux through the ethanol producing OFM.


This page was generated using Literate.jl.