PyCoMo Loopless FVA

This tutorial show-cases the use of loopless FVA.

The expected runtime for this notebook is less than 10 minutes.

[1]:
from pathlib import Path
import sys
import os
import cobra
import matplotlib.pyplot as plt
from cobra import Reaction, Metabolite, Model
from cobra.flux_analysis.loopless import add_loopless, loopless_solution
import pandas as pd
import numpy as np
import math
import time
import warnings
[2]:
path_root = "../src"  # Change path according to your PyCoMo location
sys.path.append(str(path_root))
import pycomo
pycomo.configure_logger(level="info")
2025-02-11 16:57:04,459 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:04,460 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:04,461 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:04,462 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:04,465 - pycomo - INFO - Log level set to info

Community toy model

This simple community toy model consists of two identical community members. Each member has a two step conversion of the starting metabolite A. The products of these conversions (B and C) can be secreted and taken up from the medium, thus allowing exchange and forming a cycle. The members also have a biomass reaction with substrates C and D (an additional substrate).

[3]:
model_single = Model()
model_single.add_metabolites([Metabolite(i) for i in "ABCD"])

for met in model_single.metabolites:
    met.compartment = "c"

model_single.add_metabolites([Metabolite(i+"_e") for i in "ABCD"])

for i in "ABCD":
    model_single.metabolites.get_by_id(i+"_e").compartment = "e"

model_single.add_reactions([Reaction(i) for i in ["EX_A", "EX_B", "EX_C", "EX_D", "TP_A", "TP_B", "TP_C", "TP_D", "bio", "v1", "v2"]])

model_single.reactions.EX_A.add_metabolites({"A_e": 1})
model_single.reactions.EX_B.add_metabolites({"B_e": 1})
model_single.reactions.EX_C.add_metabolites({"C_e": 1})
model_single.reactions.EX_D.add_metabolites({"D_e": 1})
model_single.reactions.TP_A.add_metabolites({"A_e": -1, "A": 1})
model_single.reactions.TP_B.add_metabolites({"B_e": -1, "B": 1})
model_single.reactions.TP_C.add_metabolites({"C_e": -1, "C": 1})
model_single.reactions.TP_D.add_metabolites({"D_e": -1, "D": 1})
model_single.reactions.bio.add_metabolites({"C": -1, "D": -1})

model_single.reactions.v1.add_metabolites({"A": -1, "B": 1})
model_single.reactions.v2.add_metabolites({"B": -1, "C": 1})

model_single.reactions.TP_B.lower_bound = -500
model_single.reactions.TP_C.lower_bound = -500
model_single.reactions.v2.lower_bound = -1000

model_single.objective = 'bio'

Constructing the community model

[4]:
single_org_models_toy = []
for name in ["model_a", "model_b"]:
    print(name)
    single_org_model = pycomo.SingleOrganismModel(model_single, name)
    single_org_models_toy.append(single_org_model)
model_a
model_b
[5]:
community_name = "toy_com"
com_model_obj_toy = pycomo.CommunityModel(single_org_models_toy, community_name)
com_model_obj_toy.convert_to_fixed_abundance()
2025-02-11 16:57:04,528 - pycomo - INFO - No community model generated yet. Generating now:
2025-02-11 16:57:04,534 - pycomo - INFO - Identified biomass reaction from objective: bio
2025-02-11 16:57:04,535 - pycomo - INFO - Note: no products in the objective function, adding biomass to it.
2025-02-11 16:57:04,560 - pycomo - INFO - Identified biomass reaction from objective: bio
2025-02-11 16:57:04,560 - pycomo - INFO - Note: no products in the objective function, adding biomass to it.
2025-02-11 16:57:04,592 - pycomo - WARNING - No annotation overlap found for matching several metabolites (4). Please make sure that the matched metabolites are indeed representing the same substance in all models! The list of metaboliteswithout annotation overlap can be accessed via 'model.no_annotation_overlap'
2025-02-11 16:57:04,603 - pycomo - INFO - Generated community model.
[ ]:
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
Name toy_com
Memory address 7f27b1192e10
Number of metabolites 64
Number of reactions 71
Number of genes 0
Number of groups 0
Objective expression 1.0*community_biomass - 1.0*community_biomass_reverse_44dc1
Compartments model_a_c, model_a_e, model_a_medium, medium, fraction_reaction, model_b_e, model_b_c, model_b_medium
[7]:
com_model_obj_toy.summary()
[7]:

Objective

1.0 community_biomass = 1000.0000000000001

Uptake

Metabolite Reaction Flux C-Number C-Flux
A_medium EX_A_medium 1000 0 0.00%
D_medium EX_D_medium 1000 0 0.00%

Secretion

Metabolite Reaction Flux C-Number C-Flux
cpd11416_medium community_biomass -1000 0 0.00%

Loops in the toy model

[8]:
com_model_obj_toy.get_loops()
2025-02-11 16:57:06,931 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:06,934 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:06,935 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:06,943 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:07,062 - pycomo - INFO - Processed 33.33% of find loops steps
2025-02-11 16:57:07,095 - pycomo - INFO - Processed 66.67% of find loops steps
2025-02-11 16:57:07,120 - pycomo - INFO - Processed 100.0% of find loops steps
[8]:
reaction min_flux max_flux
0 model_a_TF_B_model_a_e -500.0 500.0
1 model_a_TF_C_model_a_e -500.0 500.0
2 model_a_TP_B -500.0 500.0
3 model_a_TP_C -500.0 500.0
4 model_a_v2_model_a_c -500.0 500.0
5 model_b_TF_B_model_b_e -500.0 500.0
6 model_b_TF_C_model_b_e -500.0 500.0
7 model_b_TP_B -500.0 500.0
8 model_b_TP_C -500.0 500.0
9 model_b_v2_model_b_c -500.0 500.0

FVA examples

FVA with loops

Normal fva will include the loops including metabolites B and C, reaction v2 and the transporters of B and C in the solutions.

The following scenarios and correct outcomes will be tested on a :

  • No medium: no reaction can carry flux

  • 100% objective value: reaction v2 carries maximum flux (500; = 1000 * 0.5 as normalized to equal abundance) and transport reactions of B and C are inactive

  • 0% objective value: reactions v2 and transporters of B and C carry maximum flux (250, reverse also for transporters)

  • 80% objective value: reaction v2 carries flux between 300 and 500. Transporter of B can be active between -200 and 200, transporter of C between -100 and 100. This solution arises by one member giving 200 of B to the other (keeping 300), which converts B to C with maximum flux of 500, then gives back 100 of C while keeping the required 400. This results in a biomass reaction of 400 for both (80% of 500).

COBRApy fva

The loopless version of COBRApy cannot be used on PyCoMo community models, due to their bound-free reaction structure. The resulting solutions include the loop:

[9]:
%%time
with com_model_obj_toy.model:
    com_model_obj_toy.medium = {}
    com_model_obj_toy.apply_medium()
    fva = cobra.flux_analysis.flux_variability_analysis(com_model_obj_toy.model,
                                                        com_model_obj_toy.model.reactions,
                                                        fraction_of_optimum=0.,
                                                        loopless=True)
fva
CPU times: user 38 ms, sys: 91.3 ms, total: 129 ms
Wall time: 347 ms
[9]:
minimum maximum
model_a_TF_A_model_a_e 0.000000e+00 0.000000e+00
model_a_TF_B_model_a_e -2.500000e+02 2.500000e+02
model_a_TF_C_model_a_e -2.500000e+02 2.500000e+02
model_a_TF_D_model_a_e 0.000000e+00 0.000000e+00
model_a_TP_A 0.000000e+00 -1.934676e-14
... ... ...
SK_model_b_v2_model_b_c_ub 2.500000e+00 7.500000e+00
SK_model_b_to_community_biomass_ub 5.000000e+00 5.000000e+00
f_final 1.000000e+00 1.000000e+00
abundance_reaction -1.298745e-13 0.000000e+00
community_biomass 0.000000e+00 0.000000e+00

71 rows × 2 columns

PyCoMo fva with loops

The PyCoMo wrapper for fva will also include loops, when loopless mode is not activated

[10]:
%%time
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
with com_model_obj_toy.model:
    com_model_obj_toy.medium = {}
    com_model_obj_toy.apply_medium()
    fva = com_model_obj_toy.run_fva(reactions=com_model_obj_toy.model.reactions, fraction_of_optimum=1., loopless=False)
fva
2025-02-11 16:57:09,823 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:09,827 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:09,827 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:09,831 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:09,870 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:09,872 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:09,873 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:09,876 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:09,878 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:09,913 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:09,914 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:09,914 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:09,917 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:09,921 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:09,928 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:09,929 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:09,929 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:09,932 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:09,946 - pycomo - INFO - Processed 66.67% of fva steps
2025-02-11 16:57:09,950 - pycomo - INFO - Processed 100.0% of fva steps
CPU times: user 71 ms, sys: 7.07 ms, total: 78.1 ms
Wall time: 2.46 s
[10]:
reaction_id min_flux max_flux
model_a_TF_A_model_a_e model_a_TF_A_model_a_e 0.0 0.000000e+00
model_a_TF_B_model_a_e model_a_TF_B_model_a_e -250.0 2.500000e+02
model_a_TF_C_model_a_e model_a_TF_C_model_a_e -250.0 2.500000e+02
model_a_TF_D_model_a_e model_a_TF_D_model_a_e 0.0 0.000000e+00
EX_A_medium EX_A_medium 0.0 0.000000e+00
EX_B_medium EX_B_medium 0.0 -2.892122e-14
EX_C_medium EX_C_medium 0.0 2.892122e-14
EX_D_medium EX_D_medium 0.0 0.000000e+00
model_a_to_community_biomass model_a_to_community_biomass 0.0 0.000000e+00
model_b_TF_A_model_b_e model_b_TF_A_model_b_e 0.0 0.000000e+00
model_b_TF_B_model_b_e model_b_TF_B_model_b_e -250.0 2.500000e+02
model_b_TF_C_model_b_e model_b_TF_C_model_b_e -250.0 2.500000e+02
model_b_TF_D_model_b_e model_b_TF_D_model_b_e 0.0 0.000000e+00
model_b_to_community_biomass model_b_to_community_biomass 0.0 0.000000e+00
community_biomass community_biomass 0.0 0.000000e+00

PyCoMo loopless fva

The following examples show that the loopless fva implemented in PyCoMo leads to the correct solutions in the 4 test cases (no medium, 100% objective value, 0% objective value, 80% objective value)

Note: For larger (genome-scale, >4 members) it will be beneficial to use parallel processing for loopless FVA. The number of processes can be specified either using the cobrapy configuration object, or directly in the functions of PyCoMo, with the processes argument.

[11]:
%%time
with com_model_obj_toy.model:
    com_model_obj_toy.medium = {}
    com_model_obj_toy.apply_medium()
    fva = com_model_obj_toy.run_fva(reactions=com_model_obj_toy.model.reactions, fraction_of_optimum=0., loopless=True)
fva
2025-02-11 16:57:12,150 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:12,152 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:12,152 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:12,156 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:12,330 - pycomo - INFO - Processed 33.33% of find loops steps
2025-02-11 16:57:12,402 - pycomo - INFO - Processed 66.67% of find loops steps
2025-02-11 16:57:12,453 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:12,454 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:12,455 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:12,455 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:12,462 - pycomo - INFO - Processed 100.0% of find loops steps
2025-02-11 16:57:12,455 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:12,455 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:12,460 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:12,460 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:14,622 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:14,624 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:14,624 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:14,627 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:14,653 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:14,655 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:14,655 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:14,659 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:14,674 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:14,678 - pycomo - INFO - Loop correction will be applied on model_a_TF_B_model_a_e
2025-02-11 16:57:14,713 - pycomo - INFO - Loop correction will be applied on model_a_TF_C_model_a_e
2025-02-11 16:57:14,733 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:14,767 - pycomo - INFO - Loop correction will be applied on model_b_TF_B_model_b_e
2025-02-11 16:57:14,767 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:14,768 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:14,768 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:14,770 - pycomo - INFO - Loop correction will be applied on model_b_TF_C_model_b_e
2025-02-11 16:57:14,771 - pycomo - INFO - Processed 66.67% of fva steps
2025-02-11 16:57:14,773 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:14,803 - pycomo - INFO - Processed 100.0% of fva steps
CPU times: user 219 ms, sys: 34.8 ms, total: 254 ms
Wall time: 4.83 s
[11]:
reaction_id min_flux max_flux
model_a_TF_A_model_a_e model_a_TF_A_model_a_e 0.0 0.0
model_a_TF_B_model_a_e model_a_TF_B_model_a_e 0.0 0.0
model_a_TF_C_model_a_e model_a_TF_C_model_a_e 0.0 0.0
model_a_TF_D_model_a_e model_a_TF_D_model_a_e 0.0 0.0
EX_A_medium EX_A_medium 0.0 0.0
EX_B_medium EX_B_medium 0.0 0.0
EX_C_medium EX_C_medium 0.0 0.0
EX_D_medium EX_D_medium 0.0 0.0
model_a_to_community_biomass model_a_to_community_biomass 0.0 0.0
model_b_TF_A_model_b_e model_b_TF_A_model_b_e 0.0 0.0
model_b_TF_B_model_b_e model_b_TF_B_model_b_e 0.0 0.0
model_b_TF_C_model_b_e model_b_TF_C_model_b_e 0.0 0.0
model_b_TF_D_model_b_e model_b_TF_D_model_b_e 0.0 0.0
model_b_to_community_biomass model_b_to_community_biomass 0.0 0.0
community_biomass community_biomass 0.0 0.0

With loopless FVA, PyCoMo correctly calculates 0 flux for all reactions, when no medium is present.

[12]:
%%time
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
with com_model_obj_toy.model:
    fva = com_model_obj_toy.run_fva(reactions=com_model_obj_toy.model.reactions, fraction_of_optimum=1., loopless=True)
fva
2025-02-11 16:57:17,164 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:17,165 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:17,165 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:17,166 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:17,167 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:17,167 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:17,168 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:17,170 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:17,221 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:17,223 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:17,224 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:17,232 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:17,278 - pycomo - INFO - Processed 33.33% of find loops steps
2025-02-11 16:57:17,302 - pycomo - INFO - Processed 66.67% of find loops steps
2025-02-11 16:57:17,314 - pycomo - INFO - Processed 100.0% of find loops steps
2025-02-11 16:57:19,520 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:19,526 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:19,527 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:19,531 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:19,585 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:19,591 - pycomo - INFO - Loop correction will be applied on model_a_TF_B_model_a_e
2025-02-11 16:57:19,608 - pycomo - INFO - Loop correction will be applied on model_a_TF_C_model_a_e
2025-02-11 16:57:19,612 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:19,613 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:19,613 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:19,616 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:19,628 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:19,634 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:19,635 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:19,636 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:19,639 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:19,650 - pycomo - INFO - Processed 66.67% of fva steps
2025-02-11 16:57:19,650 - pycomo - INFO - Loop correction will be applied on model_b_TF_B_model_b_e
2025-02-11 16:57:19,659 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:19,660 - pycomo - INFO - Loop correction will be applied on model_b_TF_C_model_b_e
2025-02-11 16:57:19,670 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:19,672 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:19,672 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:19,672 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:19,673 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:19,674 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:19,676 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:19,677 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:19,679 - pycomo - INFO - Processed 100.0% of fva steps
CPU times: user 126 ms, sys: 13.1 ms, total: 139 ms
Wall time: 4.86 s
[12]:
reaction_id min_flux max_flux
model_a_TF_A_model_a_e model_a_TF_A_model_a_e 5.000000e+02 5.000000e+02
model_a_TF_B_model_a_e model_a_TF_B_model_a_e 7.340937e-14 -7.340937e-14
model_a_TF_C_model_a_e model_a_TF_C_model_a_e 0.000000e+00 6.493724e-14
model_a_TF_D_model_a_e model_a_TF_D_model_a_e 5.000000e+02 5.000000e+02
EX_A_medium EX_A_medium -1.000000e+03 -1.000000e+03
EX_B_medium EX_B_medium 0.000000e+00 -1.312518e-13
EX_C_medium EX_C_medium 0.000000e+00 -1.890943e-13
EX_D_medium EX_D_medium -1.000000e+03 -1.000000e+03
model_a_to_community_biomass model_a_to_community_biomass 5.000000e+02 5.000000e+02
model_b_TF_A_model_b_e model_b_TF_A_model_b_e 5.000000e+02 5.000000e+02
model_b_TF_B_model_b_e model_b_TF_B_model_b_e 7.340937e-14 -7.340937e-14
model_b_TF_C_model_b_e model_b_TF_C_model_b_e 0.000000e+00 0.000000e+00
model_b_TF_D_model_b_e model_b_TF_D_model_b_e 5.000000e+02 5.000000e+02
model_b_to_community_biomass model_b_to_community_biomass 5.000000e+02 5.000000e+02
community_biomass community_biomass 1.000000e+03 1.000000e+03

At maximum growht-rate, the reactions that are part of loops are not used, as this would lead to a decrease in growth-rate.

[13]:
%%time
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
with com_model_obj_toy.model:
    fva = com_model_obj_toy.run_fva(reactions=com_model_obj_toy.model.reactions, fraction_of_optimum=0., loopless=True)
fva
2025-02-11 16:57:21,902 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:21,904 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:21,904 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:21,909 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:21,986 - pycomo - INFO - Processed 33.33% of find loops steps
2025-02-11 16:57:22,080 - pycomo - INFO - Processed 66.67% of find loops steps
2025-02-11 16:57:22,116 - pycomo - INFO - Processed 100.0% of find loops steps
2025-02-11 16:57:24,218 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:24,220 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:24,221 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:24,225 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:24,225 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:24,226 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:24,226 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:24,229 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:24,297 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:24,304 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:24,306 - pycomo - INFO - Loop correction will be applied on model_a_TF_B_model_a_e
2025-02-11 16:57:24,319 - pycomo - INFO - Loop correction will be applied on model_a_TF_C_model_a_e
2025-02-11 16:57:24,367 - pycomo - INFO - Loop correction will be applied on model_b_TF_B_model_b_e
2025-02-11 16:57:24,369 - pycomo - INFO - Processed 66.67% of fva steps
2025-02-11 16:57:24,369 - pycomo - INFO - Loop correction will be applied on model_b_TF_C_model_b_e
2025-02-11 16:57:24,370 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:24,371 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:24,372 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:24,376 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:24,384 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:24,385 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:24,385 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:24,389 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:24,399 - pycomo - INFO - Processed 100.0% of fva steps
CPU times: user 193 ms, sys: 30.3 ms, total: 224 ms
Wall time: 4.71 s
[13]:
reaction_id min_flux max_flux
model_a_TF_A_model_a_e model_a_TF_A_model_a_e -6.493724e-14 500.0
model_a_TF_B_model_a_e model_a_TF_B_model_a_e -2.500000e+02 250.0
model_a_TF_C_model_a_e model_a_TF_C_model_a_e -2.500000e+02 250.0
model_a_TF_D_model_a_e model_a_TF_D_model_a_e 0.000000e+00 500.0
EX_A_medium EX_A_medium -1.000000e+03 0.0
EX_B_medium EX_B_medium 0.000000e+00 500.0
EX_C_medium EX_C_medium 0.000000e+00 500.0
EX_D_medium EX_D_medium -1.000000e+03 0.0
model_a_to_community_biomass model_a_to_community_biomass 0.000000e+00 500.0
model_b_TF_A_model_b_e model_b_TF_A_model_b_e -2.892122e-14 500.0
model_b_TF_B_model_b_e model_b_TF_B_model_b_e -2.500000e+02 250.0
model_b_TF_C_model_b_e model_b_TF_C_model_b_e -2.500000e+02 250.0
model_b_TF_D_model_b_e model_b_TF_D_model_b_e 0.000000e+00 500.0
model_b_to_community_biomass model_b_to_community_biomass 0.000000e+00 500.0
community_biomass community_biomass 0.000000e+00 1000.0

However, there are valid flux configurations where the “loopy” reactions can carry flux, with both directions being viable without the use of loops.

[14]:
%%time
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
with com_model_obj_toy.model:
    fva = com_model_obj_toy.run_fva(reactions=com_model_obj_toy.model.reactions, fraction_of_optimum=0.8, loopless=True)
fva
2025-02-11 16:57:26,371 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:26,373 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:26,373 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:26,377 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:26,494 - pycomo - INFO - Processed 33.33% of find loops steps
2025-02-11 16:57:26,521 - pycomo - INFO - Processed 66.67% of find loops steps
2025-02-11 16:57:26,558 - pycomo - INFO - Processed 100.0% of find loops steps
2025-02-11 16:57:28,464 - pycomo - INFO - Logger initialized.
2025-02-11 16:57:28,465 - pycomo - INFO - Process Pool Logger initialized.
2025-02-11 16:57:28,465 - pycomo - INFO - Utils Logger initialized.
2025-02-11 16:57:28,467 - pycomo - INFO - Multiprocess Logger initialized.
2025-02-11 16:57:28,505 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:28,508 - pycomo - INFO - Loop correction will be applied on model_a_TF_B_model_a_e
2025-02-11 16:57:28,524 - pycomo - INFO - Loop correction will be applied on model_a_TF_C_model_a_e
2025-02-11 16:57:28,614 - pycomo - INFO - Processed 66.67% of fva steps
2025-02-11 16:57:28,614 - pycomo - INFO - Loop correction will be applied on model_b_TF_B_model_b_e
2025-02-11 16:57:28,627 - pycomo - INFO - Loop correction will be applied on model_b_TF_C_model_b_e
2025-02-11 16:57:28,674 - pycomo - INFO - Processed 100.0% of fva steps
CPU times: user 177 ms, sys: 12.9 ms, total: 189 ms
Wall time: 4.26 s
[14]:
reaction_id min_flux max_flux
model_a_TF_A_model_a_e model_a_TF_A_model_a_e 300.0 500.0
model_a_TF_B_model_a_e model_a_TF_B_model_a_e -200.0 200.0
model_a_TF_C_model_a_e model_a_TF_C_model_a_e -100.0 100.0
model_a_TF_D_model_a_e model_a_TF_D_model_a_e 400.0 500.0
EX_A_medium EX_A_medium -1000.0 -800.0
EX_B_medium EX_B_medium 0.0 200.0
EX_C_medium EX_C_medium 0.0 200.0
EX_D_medium EX_D_medium -1000.0 -800.0
model_a_to_community_biomass model_a_to_community_biomass 400.0 500.0
model_b_TF_A_model_b_e model_b_TF_A_model_b_e 300.0 500.0
model_b_TF_B_model_b_e model_b_TF_B_model_b_e -200.0 200.0
model_b_TF_C_model_b_e model_b_TF_C_model_b_e -100.0 100.0
model_b_TF_D_model_b_e model_b_TF_D_model_b_e 400.0 500.0
model_b_to_community_biomass model_b_to_community_biomass 400.0 500.0
community_biomass community_biomass 800.0 1000.0

The example of 80% of maximum growth-rate shows, that the loopless solution scales down the flux of the “loopy” reactions, to the level where they can be active without decreasing the maximum growth-rate below the 80% threshold.

[15]:
%%time
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
with com_model_obj_toy.model:
    fva = com_model_obj_toy.run_fva(reactions=com_model_obj_toy.model.reactions, composition_agnostic=True, fraction_of_optimum=0., loopless=True, processes=1)
fva
2025-02-11 16:57:28,801 - pycomo - INFO - Processed 33.33% of find loops steps
2025-02-11 16:57:28,817 - pycomo - INFO - Processed 66.67% of find loops steps
2025-02-11 16:57:28,831 - pycomo - INFO - Processed 100.0% of find loops steps
2025-02-11 16:57:28,842 - pycomo - INFO - Log level set to 20
2025-02-11 16:57:28,845 - pycomo - INFO - Loop correction will be applied on model_a_TF_B_model_a_e
2025-02-11 16:57:28,853 - pycomo - INFO - Loop correction will be applied on model_a_TF_C_model_a_e
2025-02-11 16:57:28,877 - pycomo - INFO - Processed 66.67% of fva steps
2025-02-11 16:57:28,877 - pycomo - INFO - Loop correction will be applied on model_b_TF_B_model_b_e
2025-02-11 16:57:28,886 - pycomo - INFO - Loop correction will be applied on model_b_TF_C_model_b_e
2025-02-11 16:57:28,900 - pycomo - INFO - Processed 100.0% of fva steps
CPU times: user 196 ms, sys: 4.24 ms, total: 200 ms
Wall time: 198 ms
[15]:
reaction_id min_flux max_flux
model_a_TF_A_model_a_e model_a_TF_A_model_a_e 0.000000e+00 1.000000e+03
model_a_TF_B_model_a_e model_a_TF_B_model_a_e -5.000000e+02 3.333333e+02
model_a_TF_C_model_a_e model_a_TF_C_model_a_e -5.000000e+02 3.333333e+02
model_a_TF_D_model_a_e model_a_TF_D_model_a_e -1.298745e-13 1.000000e+03
EX_A_medium EX_A_medium -1.000000e+03 1.136868e-13
EX_B_medium EX_B_medium 0.000000e+00 5.000000e+02
EX_C_medium EX_C_medium 0.000000e+00 5.000000e+02
EX_D_medium EX_D_medium -1.000000e+03 0.000000e+00
model_a_to_community_biomass model_a_to_community_biomass 0.000000e+00 1.000000e+03
model_b_TF_A_model_b_e model_b_TF_A_model_b_e 0.000000e+00 1.000000e+03
model_b_TF_B_model_b_e model_b_TF_B_model_b_e -5.000000e+02 3.333333e+02
model_b_TF_C_model_b_e model_b_TF_C_model_b_e -5.000000e+02 3.333333e+02
model_b_TF_D_model_b_e model_b_TF_D_model_b_e 3.621417e-14 1.000000e+03
model_b_to_community_biomass model_b_to_community_biomass 0.000000e+00 1.000000e+03
community_biomass community_biomass 0.000000e+00 1.000000e+03

Using the composition agnostic approach to find all potential cross-feeding interactions shows the same pattern of feasible flux directions as with 0% fraction of optimum FVA, but with higher flux ranges due to the relaxed constraints.

This demonstrates that no other flux directions are feasible, even when considering all abundance profiles and not just 50/50 for model a and model b.