1. Getting started#
Load a model, see how large it is, and inspect a single reaction, metabolite and gene. Every later page assumes these operations.
Functions on this page#
MATLAB |
Python |
|
|---|---|---|
read a RAVEN YAML model |
||
read an SBML model |
||
model attributes cobrapy |
how big the model is |
|
look something up by identifier |
||
a reaction as a readable string |
||
is a reaction balanced |
Setup#
The examples use smallYeast.yml, a model of central carbon metabolism in yeast
that ships with RAVEN. Download it from
docs/data/smallYeast.yml and run everything from the
directory you put it in.
Install one of the toolboxes first (Installation).
Every page lists the files it needs under Setup; they are all in
docs/data/, or clone the repository and work in
raven-docs/docs/data. The blocks on a page build on each other: run them in
order in one MATLAB or Python session.
1.1 Load the model#
model = readYAMLmodel('smallYeast.yml');
disp(model.id);
smallYeast
A RAVEN model is a MATLAB struct of parallel arrays. model.rxns,
model.mets and model.genes hold the identifiers, and every other field
lines up with one of them by position: model.lb(4) is the lower bound of
the reaction named in model.rxns{4}. The stoichiometric matrix model.S
has one row per metabolite and one column per reaction, in those same orders.
Working with the model means working with indices into these arrays, which is
why looking an identifier up comes first.
from raven_toolbox.io import read_yaml_model
model = read_yaml_model("smallYeast.yml")
print(model.id)
smallYeast
In Python a RAVEN model is a cobra.Model. raven-toolbox defines no
model class of its own, so every cobrapy tool, and anything else in the
Python COBRA ecosystem, operates on it directly. Reactions, metabolites and
genes are objects held in collections rather than parallel arrays, and each
knows what it is connected to, so there are no indices to keep aligned.
For SBML use importModel in MATLAB and cobrapy's read_sbml_model in Python;
3. Reading and writing models covers every supported format.
1.2 How big is it?#
printModelStats(model);
Network statistics for smallYeast: Central carbon metabolism for yeast
Genes* 61
cytosol 52
mitochondria 17
Reactions* 53
cytosol 45
mitochondria 19
Unique reactions** 53
Metabolites 52
cytosol 35
mitochondria 17
Unique metabolites 45
* Genes and reactions are counted for each compartment if any of the corresponding metabolites are in that compartment. The sum may therefore not add up to the total number.
** Unique reactions are defined as being biochemically unique (no compartmentalization)
Two things in that output are easy to misread. The per-compartment counts do not sum to the total, because a reaction or gene is counted in every compartment its metabolites touch; a transport reaction appears under both. And unique means biochemically unique, ignoring compartments: 52 metabolites collapse to 45 because seven of them exist in both the cytosol and the mitochondrion. The same 45 is what 20. Combining and simplifying arrives at when it flattens the compartments away.
print(len(model.reactions), len(model.metabolites), len(model.genes))
53 52 61
There is no equivalent of printModelStats. The collections are attributes,
so their lengths are the totals, and per-compartment counts come from
filtering them. After a solve, model.summary() prints the exchange fluxes
and the objective.
1.3 Look at a reaction#
Glucose-6-phosphate isomerase, PGI, exercises most of what a reaction carries:
it is reversible, it has a gene association, and it should be mass balanced.
idx = getIndexes(model, 'PGI', 'rxns');
disp(model.rxnNames{idx});
eqn = constructEquations(model, model.rxns(idx));
fprintf('%s\n', eqn{1});
fprintf('bounds: [%g %g]\n', model.lb(idx), model.ub(idx));
disp(model.grRules{idx});
Glucose-6-phosphate isomerase
alpha-D-glucose 6-phosphate[c] <=> beta-D-fructofuranose 6-phosphate[c]
bounds: [-1000 1000]
YBR196C
getIndexes turns an identifier into the position used by every other
field. constructEquations builds the readable equation from the
stoichiometric column, substituting metabolite names rather than
identifiers, which is why the equation reads in words.
pgi = model.reactions.get_by_id("PGI")
print(pgi.name)
print(pgi.reaction)
print("bounds:", pgi.bounds, "reversible:", pgi.reversibility)
print(pgi.gene_reaction_rule)
Glucose-6-phosphate isomerase
G6P_c <=> F6P_c
bounds: (-1000.0, 1000.0) reversible: True
YBR196C
Reaction.reaction substitutes metabolite identifiers, so the same
reaction reads differently from the MATLAB equation above. Both describe the
same stoichiometry.
The bounds carry the direction. A lower bound of −1000 means the reaction may
run backwards; a lower bound of zero means it may not. cobrapy derives
reversibility from the bounds each time it is asked, so the two can never
disagree. RAVEN stores an explicit model.rev field alongside the bounds, which
means it is possible to set one without the other;
2. Model structure and identifiers covers what to do about
that.
1.4 Look at a metabolite#
idx = getIndexes(model, 'G6P_c', 'mets');
fprintf('%s (%s) in compartment %s\n', model.metNames{idx}, ...
model.metFormulas{idx}, model.comps{model.metComps(idx)});
fprintf('takes part in %d reactions\n', nnz(model.S(idx, :)));
alpha-D-glucose 6-phosphate (C6H13O9P) in compartment c
takes part in 4 reactions
The reaction count comes from counting non-zero entries in the metabolite's
row of model.S. model.metComps holds an index into model.comps rather
than the compartment letter itself, which is why the compartment needs two
lookups.
g6p = model.metabolites.get_by_id("G6P_c")
print(f"{g6p.name} ({g6p.formula}) in compartment {g6p.compartment}")
print(f"takes part in {len(g6p.reactions)} reactions")
alpha-D-glucose 6-phosphate (C6H13O9P) in compartment c
takes part in 4 reactions
Metabolite.reactions is maintained by the model as reactions are added and
removed, so it needs no matrix lookup.
The formula is what makes a mass-balance check possible at all. A metabolite without one leaves every reaction it appears in undecidable, which is the distinction 1.6 turns on.
1.5 Look at a gene#
gi = find(strcmp(model.genes, 'YBR196C'));
rxnIds = model.rxns(model.rxnGeneMat(:, gi) ~= 0);
fprintf('%s -> %s\n', model.geneShortNames{gi}, strjoin(rxnIds, ', '));
PGI1 -> PGI
model.rxnGeneMat is a reactions-by-genes incidence matrix; a non-zero entry
means that gene appears in that reaction's rule. It records which genes are
involved, not how they combine; the and/or structure lives only in
model.grRules as text.
gene = model.genes.get_by_id("YBR196C")
print(gene.name, "->", [rxn.id for rxn in gene.reactions])
PGI1 -> ['PGI']
Both toolboxes distinguish the identifier from the name. YBR196C is the
systematic identifier and PGI1 the standard gene name; the model is keyed on
the former.
1.6 Is the reaction balanced?#
Draft models routinely contain reactions that do not balance. Checking one is the same operation in both toolboxes; doing it for a whole model is 9. Quality control.
balance = getElementalBalance(model, 'rxns', {'PGI'});
fprintf('elemental %d, charge %d\n', balance.balanceStatus, balance.chargeStatus);
elemental 1, charge -1
The two statuses are reported separately, and they use three values, not two:
1 balanced, 0 unbalanced, -1 undecidable. PGI balances elementally
and its charge is undecidable: the metabolites in this model carry formulas
but no charges, so there is nothing to sum. That is a different statement
from "the charges do not balance": one says the check could not run, the
other says it failed.
print(pgi.check_mass_balance())
{}
An empty dictionary means the reaction balances. Anything listed is an
element, or charge, mapped to the amount by which the two sides differ, so
the result says both whether and by how much. cobrapy folds charge into the
same dictionary rather than reporting it separately, and omits what it cannot
decide, so an empty result covers both "balanced" and "nothing to check".
Warning
What can go wrong
A lookup fails. Identifiers are case-sensitive and carry the compartment suffix (
G6P_c, notG6P). Neither toolbox returns a sentinel for a name it cannot find:getIndexeserrors withCould not find object 'X' in the modelandget_by_idraisesKeyError, so a typo stops the script rather than indexing something else without warning.The model loads but nothing grows. In
smallYeast.ymlevery uptake reaction is closed (glcINhas bounds[0 0]). Opening a medium is 5. Growth media and conditions.Gene identifiers differ between model and FASTA. Systematic names (
YBR196C) and standard names (PGI1) are not interchangeable; RAVEN stores the systematic name as the identifier and the standard name as the gene name.
See also#
Guide overview, the other pages, and what is still planned.
MATLAB vs Python, what each toolbox has, and where cobrapy takes over.
API reference, every function in both toolboxes.