Skip to contents
library(cobrar)
#> Loading required package: Matrix
#> cobrar uses...
#>  - libSBML (v. 5.19.0)
#>  - glpk (v. 5.0)

Merge models of organisms to construct community metabolic models

Background

Microbial cells in multi-species communities frequently engage in metabolite exchange interactions1.

Workflow

First, we load the wild type E. coli core metabolic model.

fpath <- system.file("extdata", "e_coli_core.xml", package="cobrar")
mod <- readSBMLmod(fpath)

Next, two single-gene knockout genotypes are generated. For the example here, we chose the two genes b0720 (citrate synthase, cs) and b0767 (6-phosphogluconolactonase, pgl).

mod_dCS  <- rmGene(mod, "b0720")
mod_dPGL <- rmGene(mod, "b0767")

Now we are ready to generate a community metabolic model by joining the two models mod_dCS and mod_dPGL.

cmod <- joinModels(list(mod_dCS, mod_dPGL))
cmod
#> model ID:                   cobrar-model 
#> model name:                 cobrar-model 
#> number of compartments:     5 
#>                             M1_e  ( M1 extracellular space )
#>                             M1_c  ( M1 cytosol )
#>                             M2_e  ( M2 extracellular space )
#>                             M2_c  ( M2 cytosol )
#>                             e  ( shared extracellular space )
#> number of reactions:        208 
#> number of metabolites:      164 
#> number of unique genes:     272 
#> number of user constraints: 0 
#> number of subsystems:       0 
#> 
#> objective function:         +1 M1_BIOMASS_Ecoli_core_w_GAM +1 M2_BIOMASS_Ecoli_core_w_GAM 
#> objective direction:        maximize

The merged model contains five compartments: an extracellular space and a cytosol for each organism model M1 (“Δcs”) and M2 (“Δpgl”)), plus a new shared extracellular space e via which both genotypes can in principle exchange metabolites (see figure).

Figure: Commumnity model
Figure: Commumnity model

Before we predict reaction and exchange fluxes for the community, we add some additional constraints to the community model, namely flux coupling constraints. These constraints have the effect, that the absolute reaction flux bounds of organism x are proportional to the biomass formation of organism x. For more information on flux coupling, please see the documentation of the function fluxBMCoupling() and the paper by Heinken et al. (2013)2.

cmod <- fluxBMCoupling(cmod, cpl_c = 200, cpl_u = 0.01)

All set. We can now predict the flux distribution within the community metabolic model, e.g. via the heuristic parsimonious FBA and store the results in the variable sol.

sol <- pfbaHeuristic(cmod)

To inspect the predicted growth rate per genotype, we can run:

BMrxn <- guessBMReaction(cmod) # identify the biomass reactions within the community
growth <- sol@fluxes[react_pos(cmod,BMrxn)]
names(growth) <- BMrxn
growth
#> M1_BIOMASS_Ecoli_core_w_GAM M2_BIOMASS_Ecoli_core_w_GAM 
#>                   0.4533128                   0.3778828

Please note, that the organisms within the community are numbered consecutively in the same order as the models are supplied to the joinModels(...) function.

Analysis of metabolic interchange

Finally, we are interested in the metabolic interactions between both genotypes. To extract cross-feeding information from the flux prediction, we can inspect the flux rates of the organism exchange reactions (see figure above). Metabolites, which have non-zero flux rates for both respective organism exchange reactions and where the signs of predicted fluxes are different, are metabolites that are exchanged between both community members.

First, a data frame is constructed that contains all organism exchange reaction IDs, names, predicted fluxes, and the corresponding organism Index (M1 or M2).

dfCF <- data.frame(exrxn = cmod@react_id[grepl("^M[1|2]_EX_", cmod@react_id)])
dfCF$model <- substr(dfCF$exrxn,1,2)
dfCF$rxnname <- cmod@react_name[react_pos(cmod, dfCF$exrxn)]
dfCF$flux <- sol@fluxes[react_pos(cmod, dfCF$exrxn)]

Next we split the data frame by organism:

dfCF_M1 <- dfCF[dfCF$model == "M1", c("rxnname","flux")]
dfCF_M2 <- dfCF[dfCF$model == "M2", c("rxnname","flux")]

Finally, both data frames are merged in a way, that each row represents a metabolite and with two flux columns, one for the exchange reaction rates for each genotype. The re-organised data frame makes it easy to spot cross-fed metabolites.

dfCF <- merge(dfCF_M1, dfCF_M2, by = "rxnname", suffixes = c(".dCS",".dPGL"))

dfCF
#>                    rxnname   flux.dCS  flux.dPGL
#> 1  2-Oxoglutarate exchange  -1.299013   1.299013
#> 2    Acetaldehyde exchange   0.000000   0.000000
#> 3         Acetate exchange   5.224706  -5.224706
#> 4         Ammonia exchange  -2.471824  -2.060519
#> 5             CO2 exchange  11.864378  12.763680
#> 6      D-Fructose exchange   0.000000   0.000000
#> 7       D-Glucose exchange  -5.851613  -4.148387
#> 8       D-lactate exchange   0.000000   0.000000
#> 9         Ethanol exchange   0.000000   0.000000
#> 10        Formate exchange   0.000000   0.000000
#> 11       Fumarate exchange   0.000000   0.000000
#> 12             H+ exchange  11.720134   4.953649
#> 13            H2O exchange  12.568463  18.114355
#> 14    L-Glutamate exchange   0.000000   0.000000
#> 15    L-Glutamine exchange   0.000000   0.000000
#> 16       L-Malate exchange   0.000000   0.000000
#> 17             O2 exchange -10.041290 -13.625823
#> 18      Phosphate exchange  -1.667602  -1.390117
#> 19       Pyruvate exchange   0.000000   0.000000
#> 20      Succinate exchange   0.000000   0.000000

The output shows, that the “Δcs” genotype produces acetate and consumes 2-oxoglutarate, while the “Δpgl” consumes acetate and produced 2-oxoglutarate.