Tutorial 4: Fix an erroneous model#
GEMs are powerful because of their size, but that size also makes errors
almost inevitable, whether you build a model yourself or use someone else's. This
exercise is a systematic round of quality control on a deliberately broken
version of the small yeast model, smallYeastBad.yml.
Model validation is iterative: some errors only surface once others are fixed. The RAVEN philosophy is that it is more valuable to try to make a model do something it should not (e.g. create mass from nothing) than to test only for what it should do.
tutorial4.m leaves several gaps for you to fill; the completed version is in
tutorial4_solutions.m. Many of these checks are also wrapped by the
gapReport function, but doing them step by step is far more instructive.
Note
Many of these changes are easier to do in the Excel sheet. They are done here in code just to avoid having several model files. It is assumed that you have already completed Tutorials 2–3 and are familiar with linear programming.
Step by step#
1. Can the model make something from nothing?#
Import the broken model, close all uptake, and maximise the sum of the producing exchange reactions. Any non-zero solution is a problem.
model = readYAMLmodel('smallYeastBad.yml');
model = setParam(model, 'eq', {'glcIN', 'o2IN'}, [0 0]);
model = setParam(model, 'obj', {'acOUT' 'biomassOUT' 'co2OUT' 'ethOUT' 'glyOUT'}, [1 1 1 1 1]);
sol = solveLP(model);
printFluxes(model, sol.x, true); %Nothing produced, good
3. Allow every metabolite to be excreted#
Add a second column to model.b so RAVEN treats it as lower/upper bounds on the
equality constraints, letting anything be excreted. This exposes errors that
need a partner metabolite to be dumped.
model.b = [model.b inf(numel(model.b), 1)];
sol = solveLP(model, 1);
printFluxes(model, sol.x, false, 10^-5, [], '%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
??? success "Answer to Question 3: two unbalanced reactions" By looking at the reactions which were unbalanced and that were in the flux list, one can see that two reactions should each result in only one unit of product:
- `FBP` should give one unit of F6P: change it to
`beta-D-fructofuranose 1,6-bisphosphate[c] => beta-D-fructofuranose 6-phosphate[c] + phosphate[c]`.
- `PFK` should give one unit of F16P: change it to
`ATP[c] + beta-D-fructofuranose 6-phosphate[c] => ADP[c] + beta-D-fructofuranose 1,6-bisphosphate[c]`.
Apply each with `changeRxns(model, …, 3)`.
4. canExchange#
Set all uptakes and production to 0, drop the excretion column again, and check which metabolites can be consumed without any production:
model = setParam(model, 'eq', getExchangeRxns(model), 0);
model.b = model.b(:, 1);
I = canExchange(model, 'consume');
disp(model.mets(I)); %These 12 metabolites can be consumed without any production
canExchange with 'consume' reports the metabolites the model can consume
even when no production is allowed; 'produce' asks the opposite. Allow all
uptake again, then force the uptake of one of them (here CO2), and study the
fluxes (a negative output means input):
model.b = [ones(numel(model.b), 1) * -1000 model.b];
model = setParam(model, 'eq', {'co2OUT'}, -1);
sol = solveLP(model);
printFluxes(model, sol.x, false, 10^-5, [], '%rxnID (%rxnName):\n\t%eqn\n\t%flux\n'); %Now it works
??? success "Answer to Question 4: PDC is missing a product"
PDC converts pyruvate (3 carbons) to acetaldehyde (2 carbons) without any
other products: CO2 is missing. The change can be made in the Excel file or
with changeRxns; as an exercise, add the cytosolic CO2 coefficient
directly to the stoichiometric matrix:
```matlab
Irxn = ismember(model.rxns, 'PDC');
Imet = ismember(model.mets, 'CO2_c');
model.S(Imet, Irxn) = 1; %The coefficient is 1.0
constructEquations(model, Irxn)
```
The solution is now infeasible, meaning it is no longer possible to force
uptake of CO2 without any output.
5. Gap-filling against a reference model#
tutorial4.m also imports a reference model to draw reactions from, allows all
excretion, and reports gaps before filling them:
model = readYAMLmodel('smallYeastBad.yml');
refModel = readYAMLmodel('smallYeast.yml');
model.b = [model.b inf(numel(model.b), 1)];
sol = solveLP(model, 1);
printFluxes(model, sol.x, false, 10^-5, [], '%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
I = canExchange(model, 'consume');
disp(model.mets(I));
gapReport(model, {refModel});
[newConnected, cannotConnect, addedRxns, newModel] = fillGaps(model, {refModel}, false);
disp(addedRxns);
disp(newConnected);
disp(cannotConnect);
gapReport automates this whole quality-control workflow, while fillGaps
adds reactions from the reference model (here the Tutorial 3 small yeast model)
to connect the remaining gaps.
Full script#
% tutorial4
% This script contains the list of functions necessary for running
% Tutorial 4, see Tutorial 4 on the RAVEN wiki for more details:
% https://raven-docs.readthedocs.io/en/latest/tutorials/
% Several key stages may be missing, try to fill these gaps before
% checking the solutions in tutorial4_solutions. It is assumed that the
% user is somewhat familiar with linear programming.
%
% It is assumed that the user has already completed Tutorials 2-3
%Import the model to gap-fill and the reference model to draw reactions from
model=readYAMLmodel('smallYeastBad.yml');
refModel=readYAMLmodel('smallYeast.yml');
model.b=[model.b inf(numel(model.b),1)];
sol=solveLP(model,1);
printFluxes(model,sol.x,false,10^-5,[],'%rxnID (%rxnName):\n\t%eqn\n\t%flux\n');
I=canExchange(model,'consume');
disp(model.mets(I));
gapReport(model,{refModel});
[newConnected, cannotConnect, addedRxns, newModel]=fillGaps(model,{refModel},false);
disp(addedRxns);
disp(newConnected);
disp(cannotConnect);