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]:
import pycomo
pycomo.configure_logger(level="info")
2025-11-24 15:59:11,155 - PyCoMo - INFO - Logger initialized.
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-11-24 15:59:11,255 - PyCoMo - INFO - No community model generated yet. Generating now:
2025-11-24 15:59:11,263 - PyCoMo - INFO - Identified biomass reaction from objective: bio
2025-11-24 15:59:11,265 - PyCoMo - INFO - Note: no products in the objective function, adding biomass to it.
2025-11-24 15:59:11,296 - PyCoMo - INFO - Identified biomass reaction from objective: bio
2025-11-24 15:59:11,296 - PyCoMo - INFO - Note: no products in the objective function, adding biomass to it.
2025-11-24 15:59:11,329 - 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-11-24 15:59:11,338 - PyCoMo - WARNING - There are 20 metabolites without elements in the model. Mass balance checks may be unreliable.
2025-11-24 15:59:11,339 - PyCoMo - INFO - Generated community model.
[6]:
com_model_obj_toy.medium = {'EX_A_medium': 1000.0, 'EX_D_medium': 1000.0}
com_model_obj_toy.apply_medium()
[6]:
| Name | toy_com |
| Memory address | 1d4ba00ead0 |
| 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-11-24 15:59:11,447 - PyCoMo - INFO - Removing 3 fraction reactions
2025-11-24 15:59:11,448 - PyCoMo - INFO - Removing 38 metabolites
2025-11-24 15:59:18,579 - PyCoMo - INFO - Processed 100.0% of find loops steps
2025-11-24 15:59:18,580 - PyCoMo - INFO - While pending closed!
2025-11-24 15:59:18,659 - PyCoMo - INFO - Loop detection finished
[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: total: 156 ms
Wall time: 10.1 s
[9]:
| minimum | maximum | |
|---|---|---|
| model_a_TF_A_model_a_e | 0.0 | 0.0 |
| model_a_TF_B_model_a_e | -250.0 | 250.0 |
| model_a_TF_C_model_a_e | -250.0 | 250.0 |
| model_a_TF_D_model_a_e | 0.0 | 0.0 |
| model_a_TP_A | 0.0 | 0.0 |
| ... | ... | ... |
| SK_model_b_v2_model_b_c_ub | 2.5 | 7.5 |
| SK_model_b_to_community_biomass_ub | 5.0 | 5.0 |
| f_final | 1.0 | 1.0 |
| abundance_reaction | 0.0 | 0.0 |
| community_biomass | 0.0 | 0.0 |
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(fraction_of_optimum=1., loopless=False)
fva
[<Reaction model_a_TF_A_model_a_e at 0x1d4ba03a950>, <Reaction model_a_TF_B_model_a_e at 0x1d4ba03ab10>, <Reaction model_a_TF_C_model_a_e at 0x1d4ba03ac90>, <Reaction model_a_TF_D_model_a_e at 0x1d4ba03ae10>, <Reaction EX_A_medium at 0x1d4b9dbb550>, <Reaction EX_B_medium at 0x1d4b9ee12d0>, <Reaction EX_C_medium at 0x1d4ba04a410>, <Reaction EX_D_medium at 0x1d4b9f703d0>, <Reaction model_b_TF_A_model_b_e at 0x1d4ba07f210>, <Reaction model_b_TF_B_model_b_e at 0x1d4ba0e8790>, <Reaction model_b_TF_C_model_b_e at 0x1d4ba0e8850>, <Reaction model_b_TF_D_model_b_e at 0x1d4ba0e8950>]
2025-11-24 15:59:32,314 - PyCoMo - INFO - Processed 100.0% of fva steps
CPU times: total: 62.5 ms
Wall time: 3.56 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.0 |
| model_a_TF_B_model_a_e | model_a_TF_B_model_a_e | -250.0 | 250.0 |
| model_a_TF_C_model_a_e | model_a_TF_C_model_a_e | -250.0 | 250.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_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 | -250.0 | 250.0 |
| model_b_TF_C_model_b_e | model_b_TF_C_model_b_e | -250.0 | 250.0 |
| model_b_TF_D_model_b_e | model_b_TF_D_model_b_e | 0.0 | 0.0 |
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(fraction_of_optimum=0., loopless=True)
fva
[<Reaction model_a_TF_A_model_a_e at 0x1d4ba03a950>, <Reaction model_a_TF_B_model_a_e at 0x1d4ba03ab10>, <Reaction model_a_TF_C_model_a_e at 0x1d4ba03ac90>, <Reaction model_a_TF_D_model_a_e at 0x1d4ba03ae10>, <Reaction EX_A_medium at 0x1d4b9dbb550>, <Reaction EX_B_medium at 0x1d4b9ee12d0>, <Reaction EX_C_medium at 0x1d4ba04a410>, <Reaction EX_D_medium at 0x1d4b9f703d0>, <Reaction model_b_TF_A_model_b_e at 0x1d4ba07f210>, <Reaction model_b_TF_B_model_b_e at 0x1d4ba0e8790>, <Reaction model_b_TF_C_model_b_e at 0x1d4ba0e8850>, <Reaction model_b_TF_D_model_b_e at 0x1d4ba0e8950>]
2025-11-24 15:59:35,936 - PyCoMo - INFO - worker 22840: Loop correction will be applied on model_a_TF_A_model_a_e
2025-11-24 15:59:35,938 - PyCoMo - INFO - worker 25520: Loop correction will be applied on model_a_TF_B_model_a_e
2025-11-24 15:59:35,942 - PyCoMo - INFO - worker 22840: Loop correction will be applied on model_a_TF_C_model_a_e
2025-11-24 15:59:35,945 - PyCoMo - INFO - worker 29560: Loop correction will be applied on model_a_TF_D_model_a_e
2025-11-24 15:59:35,956 - PyCoMo - INFO - worker 25520: Loop correction will be applied on model_b_TF_A_model_b_e
2025-11-24 15:59:35,958 - PyCoMo - INFO - worker 24196: Loop correction will be applied on model_b_TF_B_model_b_e
2025-11-24 15:59:35,959 - PyCoMo - INFO - worker 10380: Loop correction will be applied on model_b_TF_C_model_b_e
2025-11-24 15:59:35,961 - PyCoMo - INFO - worker 32444: Loop correction will be applied on model_b_TF_D_model_b_e
2025-11-24 15:59:35,976 - PyCoMo - INFO - Processed 100.0% of fva steps
CPU times: total: 125 ms
Wall time: 3.64 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_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 |
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(fraction_of_optimum=1., loopless=True)
fva
[<Reaction model_a_TF_A_model_a_e at 0x1d4ba03a950>, <Reaction model_a_TF_B_model_a_e at 0x1d4ba03ab10>, <Reaction model_a_TF_C_model_a_e at 0x1d4ba03ac90>, <Reaction model_a_TF_D_model_a_e at 0x1d4ba03ae10>, <Reaction EX_A_medium at 0x1d4b9dbb550>, <Reaction EX_B_medium at 0x1d4b9ee12d0>, <Reaction EX_C_medium at 0x1d4ba04a410>, <Reaction EX_D_medium at 0x1d4b9f703d0>, <Reaction model_b_TF_A_model_b_e at 0x1d4ba07f210>, <Reaction model_b_TF_B_model_b_e at 0x1d4ba0e8790>, <Reaction model_b_TF_C_model_b_e at 0x1d4ba0e8850>, <Reaction model_b_TF_D_model_b_e at 0x1d4ba0e8950>]
2025-11-24 15:59:39,497 - PyCoMo - INFO - worker 25412: Loop correction will be applied on model_a_TF_A_model_a_e
2025-11-24 15:59:39,500 - PyCoMo - INFO - worker 15196: Loop correction will be applied on model_a_TF_B_model_a_e
2025-11-24 15:59:39,502 - PyCoMo - INFO - worker 25412: Loop correction will be applied on model_a_TF_C_model_a_e
2025-11-24 15:59:39,526 - PyCoMo - INFO - worker 5156: Loop correction will be applied on model_a_TF_D_model_a_e
2025-11-24 15:59:39,532 - PyCoMo - INFO - worker 25412: Loop correction will be applied on model_b_TF_A_model_b_e
2025-11-24 15:59:39,534 - PyCoMo - INFO - worker 27260: Loop correction will be applied on model_b_TF_B_model_b_e
2025-11-24 15:59:39,535 - PyCoMo - INFO - worker 34084: Loop correction will be applied on model_b_TF_C_model_b_e
2025-11-24 15:59:39,537 - PyCoMo - INFO - worker 15196: Loop correction will be applied on model_b_TF_D_model_b_e
2025-11-24 15:59:39,549 - PyCoMo - INFO - Processed 100.0% of fva steps
CPU times: total: 93.8 ms
Wall time: 3.56 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 | 0.000000e+00 |
| 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 | -2.313698e-13 |
| EX_D_medium | EX_D_medium | -1.000000e+03 | -1.000000e+03 |
| 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 |
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(fraction_of_optimum=0., loopless=True)
fva
[<Reaction model_a_TF_A_model_a_e at 0x1d4ba03a950>, <Reaction model_a_TF_B_model_a_e at 0x1d4ba03ab10>, <Reaction model_a_TF_C_model_a_e at 0x1d4ba03ac90>, <Reaction model_a_TF_D_model_a_e at 0x1d4ba03ae10>, <Reaction EX_A_medium at 0x1d4b9dbb550>, <Reaction EX_B_medium at 0x1d4b9ee12d0>, <Reaction EX_C_medium at 0x1d4ba04a410>, <Reaction EX_D_medium at 0x1d4b9f703d0>, <Reaction model_b_TF_A_model_b_e at 0x1d4ba07f210>, <Reaction model_b_TF_B_model_b_e at 0x1d4ba0e8790>, <Reaction model_b_TF_C_model_b_e at 0x1d4ba0e8850>, <Reaction model_b_TF_D_model_b_e at 0x1d4ba0e8950>]
2025-11-24 15:59:43,088 - PyCoMo - INFO - worker 32888: Loop correction will be applied on model_a_TF_A_model_a_e
2025-11-24 15:59:43,089 - PyCoMo - INFO - worker 29008: Loop correction will be applied on model_a_TF_B_model_a_e
2025-11-24 15:59:43,092 - PyCoMo - INFO - worker 21896: Loop correction will be applied on model_a_TF_C_model_a_e
2025-11-24 15:59:43,097 - PyCoMo - INFO - worker 32888: Loop correction will be applied on model_a_TF_D_model_a_e
2025-11-24 15:59:43,101 - PyCoMo - INFO - worker 29008: Loop correction will be applied on model_b_TF_A_model_b_e
2025-11-24 15:59:43,104 - PyCoMo - INFO - worker 21896: Loop correction will be applied on model_b_TF_B_model_b_e
2025-11-24 15:59:43,107 - PyCoMo - INFO - worker 32888: Loop correction will be applied on model_b_TF_C_model_b_e
2025-11-24 15:59:43,124 - PyCoMo - INFO - worker 29008: Loop correction will be applied on model_b_TF_D_model_b_e
2025-11-24 15:59:43,136 - PyCoMo - INFO - Processed 100.0% of fva steps
CPU times: total: 46.9 ms
Wall time: 3.57 s
[13]:
| reaction_id | min_flux | max_flux | |
|---|---|---|---|
| model_a_TF_A_model_a_e | model_a_TF_A_model_a_e | 0.0 | 5.000000e+02 |
| 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 | 5.000000e+02 |
| EX_A_medium | EX_A_medium | -1000.0 | 1.136868e-13 |
| EX_B_medium | EX_B_medium | 0.0 | 5.000000e+02 |
| EX_C_medium | EX_C_medium | 0.0 | 5.000000e+02 |
| EX_D_medium | EX_D_medium | -1000.0 | 0.000000e+00 |
| model_b_TF_A_model_b_e | model_b_TF_A_model_b_e | 0.0 | 5.000000e+02 |
| 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 | 5.000000e+02 |
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(fraction_of_optimum=0.8, loopless=True)
fva
[<Reaction model_a_TF_A_model_a_e at 0x1d4ba03a950>, <Reaction model_a_TF_B_model_a_e at 0x1d4ba03ab10>, <Reaction model_a_TF_C_model_a_e at 0x1d4ba03ac90>, <Reaction model_a_TF_D_model_a_e at 0x1d4ba03ae10>, <Reaction EX_A_medium at 0x1d4b9dbb550>, <Reaction EX_B_medium at 0x1d4b9ee12d0>, <Reaction EX_C_medium at 0x1d4ba04a410>, <Reaction EX_D_medium at 0x1d4b9f703d0>, <Reaction model_b_TF_A_model_b_e at 0x1d4ba07f210>, <Reaction model_b_TF_B_model_b_e at 0x1d4ba0e8790>, <Reaction model_b_TF_C_model_b_e at 0x1d4ba0e8850>, <Reaction model_b_TF_D_model_b_e at 0x1d4ba0e8950>]
2025-11-24 15:59:46,690 - PyCoMo - INFO - worker 2676: Loop correction will be applied on model_a_TF_A_model_a_e
2025-11-24 15:59:46,693 - PyCoMo - INFO - worker 25740: Loop correction will be applied on model_a_TF_B_model_a_e
2025-11-24 15:59:46,694 - PyCoMo - INFO - worker 8020: Loop correction will be applied on model_a_TF_C_model_a_e
2025-11-24 15:59:46,696 - PyCoMo - INFO - worker 17608: Loop correction will be applied on model_a_TF_D_model_a_e
2025-11-24 15:59:46,704 - PyCoMo - INFO - worker 17608: Loop correction will be applied on model_b_TF_A_model_b_e
2025-11-24 15:59:46,704 - PyCoMo - INFO - worker 30180: Loop correction will be applied on model_b_TF_B_model_b_e
2025-11-24 15:59:46,705 - PyCoMo - INFO - worker 24420: Loop correction will be applied on model_b_TF_C_model_b_e
2025-11-24 15:59:46,707 - PyCoMo - INFO - worker 8052: Loop correction will be applied on model_b_TF_D_model_b_e
2025-11-24 15:59:46,722 - PyCoMo - INFO - Processed 100.0% of fva steps
CPU times: total: 62.5 ms
Wall time: 3.57 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_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 |
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(composition_agnostic=True, fraction_of_optimum=0., loopless=True, processes=1)
fva
2025-11-24 15:59:46,896 - PyCoMo - INFO - Loop correction will be applied on model_a_TF_A_model_a_e
2025-11-24 15:59:46,906 - PyCoMo - INFO - Loop correction will be applied on model_a_TF_B_model_a_e
2025-11-24 15:59:46,915 - PyCoMo - INFO - Loop correction will be applied on model_a_TF_C_model_a_e
2025-11-24 15:59:46,926 - PyCoMo - INFO - Loop correction will be applied on model_a_TF_D_model_a_e
2025-11-24 15:59:46,942 - PyCoMo - INFO - Loop correction will be applied on model_b_TF_A_model_b_e
[<Reaction model_a_TF_A_model_a_e at 0x1d4ba03a950>, <Reaction model_a_TF_B_model_a_e at 0x1d4ba03ab10>, <Reaction model_a_TF_C_model_a_e at 0x1d4ba03ac90>, <Reaction model_a_TF_D_model_a_e at 0x1d4ba03ae10>, <Reaction EX_A_medium at 0x1d4b9dbb550>, <Reaction EX_B_medium at 0x1d4b9ee12d0>, <Reaction EX_C_medium at 0x1d4ba04a410>, <Reaction EX_D_medium at 0x1d4b9f703d0>, <Reaction model_b_TF_A_model_b_e at 0x1d4ba07f210>, <Reaction model_b_TF_B_model_b_e at 0x1d4ba0e8790>, <Reaction model_b_TF_C_model_b_e at 0x1d4ba0e8850>, <Reaction model_b_TF_D_model_b_e at 0x1d4ba0e8950>]
2025-11-24 15:59:46,951 - PyCoMo - INFO - Loop correction will be applied on model_b_TF_B_model_b_e
2025-11-24 15:59:46,960 - PyCoMo - INFO - Loop correction will be applied on model_b_TF_C_model_b_e
2025-11-24 15:59:46,972 - PyCoMo - INFO - Loop correction will be applied on model_b_TF_D_model_b_e
2025-11-24 15:59:46,982 - PyCoMo - INFO - Processed 100.0% of fva steps
CPU times: total: 125 ms
Wall time: 220 ms
[15]:
| reaction_id | min_flux | max_flux | |
|---|---|---|---|
| model_a_TF_A_model_a_e | model_a_TF_A_model_a_e | 0.0 | 1000.000000 |
| model_a_TF_B_model_a_e | model_a_TF_B_model_a_e | -500.0 | 166.666667 |
| model_a_TF_C_model_a_e | model_a_TF_C_model_a_e | -500.0 | 166.666667 |
| model_a_TF_D_model_a_e | model_a_TF_D_model_a_e | 0.0 | 1000.000000 |
| EX_A_medium | EX_A_medium | -1000.0 | 0.000000 |
| EX_B_medium | EX_B_medium | 0.0 | 500.000000 |
| EX_C_medium | EX_C_medium | 0.0 | 500.000000 |
| EX_D_medium | EX_D_medium | -1000.0 | 0.000000 |
| model_b_TF_A_model_b_e | model_b_TF_A_model_b_e | 0.0 | 1000.000000 |
| model_b_TF_B_model_b_e | model_b_TF_B_model_b_e | -500.0 | 333.333333 |
| model_b_TF_C_model_b_e | model_b_TF_C_model_b_e | -500.0 | 333.333333 |
| model_b_TF_D_model_b_e | model_b_TF_D_model_b_e | 0.0 | 1000.000000 |
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.