Gathering kcats#
An empty ecModel has the structure to carry enzyme constraints but no kcat
values yet. This page covers sourcing those values: fuzzy matching against
the BRENDA database, and predicting them with DLKcat or
OpenKineticsPredictor. Each source produces a kcatList
(pandas.DataFrame in Python) with the same shape, so results from
different sources merge into one list at the end of this page.
Not every enzymatic reaction has a reported measurement. GECKO matches a BRENDA kcat to a reaction when both the EC number and the organism's reaction substrate match a BRENDA annotation; if several kcats match, the highest is selected. DLKcat and OpenKineticsPredictor instead predict a kcat from enzyme sequence and substrate structure, independent of EC number, which is useful for less-studied organisms or as the sole kcat source. Custom values and standard fallback values, covered on Applying kcats, complete the picture for reactions none of these sources cover.
Functions on this page#
MATLAB |
Python |
|
|---|---|---|
populate EC numbers |
||
match kcats from BRENDA by EC number, substrate and organism |
||
annotate metabolites with SMILES for DLKcat/OKP |
||
run DLKcat locally |
||
|
run a kcat prediction job on OpenKineticsPredictor |
|
merge kcat lists from multiple sources |
Choose kcat sources#
At least one kcat source is required; any combination works, in any order:
Fuzzy matching with the BRENDA database.
Prediction via the hosted OpenKineticsPredictor service (GECKO 4).
Deep learning prediction with DLKcat, run locally via Docker.
A list of manually curated custom kcat values (see Applying kcats).
A standard kcat value assigned as a fallback (see Applying kcats).
Both DLKcat and OpenKineticsPredictor are current GECKO 4 options for predicted kcats; neither supersedes the other; the difference is that OpenKineticsPredictor runs as a hosted service with a choice of predictor methods, and DLKcat runs locally via Docker.
Fuzzy matching with BRENDA#
Matching reactions against BRENDA data by EC number and substrate name is called fuzzy matching, because it allows wildcards in the EC number, or kcat values from other substrates, when no exact match exists.
Assign EC numbers to reactions#
EC numbers can come from the starting GEM's EC number annotation, if it has
one (MATLAB: the model.eccodes field; Python: each reaction's
annotation["ec-code"]), or be gathered from UniProt and KEGG annotations.
If the starting GEM has manually curated EC numbers, populate
ecModel.ec.eccodes with model-derived EC numbers first, then fill the
rest from the database. getECfromDatabase overwrites entries in
ecModel.ec.eccodes, so mark which entries are still empty before calling
it:
ecModel = getECfromGEM(ecModel);
noEC = cellfun(@isempty, ecModel.ec.eccodes);
ecModel = getECfromDatabase(ecModel, noEC);
If the starting GEM is not annotated with a model.eccodes field, does not
contain standard EC numbers (four groups of digits separated by periods),
or its EC annotations are not trusted, run only getECfromDatabase:
ecModel = getECfromDatabase(ecModel);
applyECcodes transfers EC numbers derived from the database back into the
model.eccodes fields, if desired.
from geckopy import fill_eccodes_from_database, fill_eccodes_from_gem
fill_eccodes_from_gem(ec_model)
fill_eccodes_from_database(ec_model, uniprot_db)
fill_eccodes_from_database only fills entries that are still empty, so
calling fill_eccodes_from_gem first and fill_eccodes_from_database
second already has the same effect as the MATLAB two-call sequence above,
with no separate "which entries were not populated" step. If the starting
GEM's EC annotations are not trusted, skip fill_eccodes_from_gem and run
only fill_eccodes_from_database. copy_ec_to_gem is the equivalent of
applyECcodes, writing into each reaction's annotation["ec-code"]
(cobrapy's convention) rather than a top-level model.eccodes cell array.
Note
Difficulties finding EC numbers for the reactions Fuzzy matching requires EC numbers to be matched to reactions. EC numbers can be entered manually in the model's EC annotation field, based on any species-specific information available, and loaded with the functions above. Because DLKcat does not require EC numbers, using only DLKcat as the kcat source is an alternative strategy when EC numbers are hard to find.
Query BRENDA#
With ec.eccodes populated, gather kcat from BRENDA, queried by EC
number, substrate and organism:
kcatList_fuzzy = fuzzyKcatMatching(ecModel);
BRENDA data and the KEGG phylogenetic-distance file (used to find the phylogenetically closest organism when no exact match exists) are loaded explicitly, then passed in:
from geckopy import fuzzy_kcat_matching, load_brenda_data, load_phyl_dist
brenda = load_brenda_data(adapter.get_brenda_db_folder())
phyl_dist = load_phyl_dist(params.path / "data" / "PhylDist.mat")
kcat_list_fuzzy = fuzzy_kcat_matching(ec_model, brenda, phyl_dist)
The result documents which kcat was assigned to each reaction and how precise the match is: the number of EC wildcards used, and whether the match agreed on substrate and organism. It is common, even for model organisms such as S. cerevisiae, that no full match exists, in which case a partial organism or substrate match is returned. When no kcat exists for a particular EC number, wildcards are introduced: EC 2.4.2.3 (uridine phosphorylase), for example, becomes 2.4.2.- (pentosyltransferase).
If a resolution priority is not met, hierarchical matching criteria apply in order: if the organism does not match, the kcat of the phylogenetically closest organism is used; if the substrate does not match, the kcat for any alternative substrate is used; if the EC number is not found, a fuzzy EC number (with wildcards) is used; and if no kcat is available in BRENDA at all, specific activities (typically in micromol per min per mg protein) are converted into kcat values.
Note
Example output
In the full_ecModel tutorial, the fuzzy kcatList documents, per
reaction: the substrates and EC numbers used to match against BRENDA, the
proposed kcat, wildcardLvl (Python: wildcard_level; 0: w.x.y.z, 1: w.x.y.-, 2: w.x.-.-, 3:
w.-.-.-), and origin, the specificity level of the match reached (1:
correct organism and substrate; 2: closest related organism, correct
substrate; 3: correct organism, any substrate; 4: closest related organism,
any substrate; 5: correct organism, specific activity; 6: closest related
organism, specific activity).
Deep learning prediction with DLKcat#
DLKcat predicts a kcat from enzyme sequence information and substrate structural information in SMILES format.
Tip
GECKO 4: prefer OpenKineticsPredictor over a local DLKcat
The steps below run DLKcat locally via Docker, the GECKO 3 approach. In
GECKO 4, submitting the same prediction job to the hosted
OpenKineticsPredictor (OKP) service,
covered further down this page, is the recommended alternative: no Docker
install, and a choice of several predictor methods behind one API
(CataPro, CatPred, DLKcat itself, EITLEM, KinForm-H, KinForm-L and UniKP),
defaulting to CataPro. OKP shares the same SMILES-annotation step with
DLKcat and produces a kcatList in the same shape, so the two are
interchangeable from the merge step onward.
Gather SMILES annotations#
Starting GEMs rarely include SMILES annotation, so this queries PubChem,
creating data/smilesDB.tsv, or fills that file manually.
[ecModel, noSMILES] = findMetSmiles(ecModel);
Unmatched metabolites. findMetSmiles reports the percentage of
unique metabolites assigned a SMILES annotation. If it is below 100%, as
is almost always the case, inspect the noSMILES list of metabolite
names. In some models the metabolite name is suffixed with the metabolite
formula, which prevents matching with PubChem; curating ecModel.metNames
resolves such issues.
MATLAB prints the progress and result:
Check for local SMILES database... done.
SMILES could be found for 64% of the unique metabolite names.
from geckopy import find_met_smiles
find_met_smiles(ec_model, cache_path=params.path / "data" / "smilesDB.tsv")
find_met_smiles mutates ec_model in place and reads/writes through the
cache_path TSV; an existing cache, as shipped with the tutorial, means no
PubChem network access is needed. Unmatched metabolites are logged rather
than returned as a noSMILES list: the number of unique metabolite names
with a SMILES and the number without are logged at INFO level, and each
name without a SMILES at DEBUG level. As in MATLAB, curate metabolite names
(metabolite.name) if a formula suffix or similar naming issue prevents
matching.
Note
Example output
In the full_ecModel tutorial, a SMILES annotation is found for 64% of the
unique metabolite names, after checking the local SMILES database and
querying PubChem for the rest.
Write the DLKcat input file#
DLKcat runs as a separate tool (see below), so an input file is written
from the ecModel. Currency metabolites that occur in pairs
(for example ATP versus ADP, NADH versus NAD) and a selection of small
molecules (for example Fe2+) are excluded, unless a reaction has no other
reactants after removing them, as for ATP synthase. Nonexhaustive exclusion
lists ship with the toolbox as DLKcatCurrencyMets.tsv and
DLKcatIgnoreMets.tsv (MATLAB: in GECKO/databases; Python: inside the
geckopy.data package); an ecModel-specific override can be placed under
data/ in the adapter folder with the same filenames.
writeDLKcatInput(ecModel);
Full and light ecModels need separate input files. DLKcat.tsv is
specific to either the full or the light version of the ecModel, because
it carries reaction identifiers from ec.rxns needed when loading the
predicted values back into the ecModel. Build separate DLKcat.tsv files
for the two ecModel versions.
from geckopy import load_dlkcat_ignore_lists, write_dlkcat_input
ignore_lists = load_dlkcat_ignore_lists(params.path / "data")
write_dlkcat_input(
ec_model, params.path / "data" / "DLKcat.tsv", ignore_lists,
)
load_dlkcat_ignore_lists reads the currency-metabolite and small-molecule
exclusion lists (DLKcatCurrencyMets.tsv/DLKcatIgnoreMets.tsv, or their
project-specific overrides under data/) that MATLAB's writeDLKcatInput
reads implicitly. The same full-vs-light DLKcat.tsv caveat applies: the
file encodes ec_model.ec.rxns identifiers, so a full-model file cannot be
reused for a light model or the reverse.
Run DLKcat and load its output#
Running DLKcat downloads and starts a Docker image automatically. No other
input is given, because the function assumes the input file is at
data/DLKcat.tsv:
runDLKcat();
kcatList_DLKcat = readDLKcatOutput(ecModel);
from geckopy import read_dlkcat_output, run_dlkcat
run_dlkcat(params.path / "data" / "DLKcat.tsv")
kcat_list_dlkcat = read_dlkcat_output(
ec_model, params.path / "data" / "DLKcat.tsv",
)
Note
DLKcat fails to run
DLKcat's Docker requirement can be an obstacle if Docker Desktop cannot be
installed or its use is restricted. GECKO/src/dlkcat-gecko/ (in the
GECKO repository) contains the Python scripts and data needed to run
DLKcat directly with the DLKcat.tsv file written above, without Docker:
pipenv install -r requirements.txt
pipenv run python DLKcat.py DLKcat.tsv DLKcatOutput.tsv
Run both commands from the system terminal, not from MATLAB or Python.
The resulting kcatList is similar in shape to the BRENDA one, but has no
EC numbers, wildcard levels or origin values, and instead includes the
genes of each reaction. In MATLAB the fields eccodes, wildcardLvl and
origin are absent; in Python the columns eccode, wildcard_level and
origin are present but empty.
Kcat prediction with OpenKineticsPredictor (GECKO 4)#
OpenKineticsPredictor (OKP) shares its input with DLKcat: protein sequences and single-substrate SMILES, gathered in the section above. It needs no Docker.
Warning
The API key is a secret; never put it in the model adapter
OKP requires a personal Bearer API key (looks like ak_...). Get one free,
no registration required, at
predictor.openkinetics.org/api-docs
(the "API key generator" section; the key is shown once, and can be revoked
and regenerated from the same page). Keys are tied to the requesting IP and
carry a daily prediction quota that resets at midnight UTC.
Because the model adapter is typically shared and committed to version
control, the key does not belong there. Both languages resolve it the same
way, checked in this order: an explicit function argument; the
OKP_API_KEY environment variable; or a plain-text data/okpApiKey.txt
file in the adapter folder (a filename already git-ignored by GECKO).
Submit a prediction job for the whole ecModel:
jobId = submitOpenKineticsPredictor(ecModel);
from geckopy import submit_open_kinetics_predictor
job_id = submit_open_kinetics_predictor(ec_model)
Both write the request to data/OKP.csv and the returned job id to
data/OKP_job.txt, so a later call can find the job without passing
jobId/job_id explicitly. Pass method='DLKcat' (MATLAB) /
method="DLKcat" (Python) to request DLKcat specifically through OKP
instead of the CataPro default.
Poll until the job finishes and parse the result into a kcatList:
[done, kcatList_OKP] = fetchOpenKineticsPredictor(ecModel, 'wait', true);
from geckopy import fetch_open_kinetics_predictor
done, kcat_list_okp = fetch_open_kinetics_predictor(ec_model, wait=True)
wait=True polls every 30 seconds (poll_interval=) until the job is done
or timeout (default one hour) is reached. wait=False, the default,
checks once and returns immediately, useful for checking a long-running job
from a separate call without blocking.
The downloaded result is cached at data/OKP_output.csv; pass useStored
(MATLAB) / use_stored=True (Python) to re-parse it without contacting the
API again. kcatList_OKP / kcat_list_okp has the same shape as the
BRENDA and DLKcat kcat lists above, so the merge step below, and
assignKcatValues/apply_kcat_list on
Applying kcats, accept it
identically.
Merge DLKcat and BRENDA structures#
Merging kcat lists from multiple sources increases coverage, keeping the
highest-priority source for each reaction. The same call works with
kcatList_OKP / kcat_list_okp in place of the DLKcat list, since the
function merges by structure rather than by name, regardless of which
predictor produced the second list:
kcatList_merged = mergeKcats({kcatList_fuzzy, kcatList_DLKcat}, ...
{'database_top', 'dlkcat', 'database_bottom'});
from geckopy import merge_kcats
kcat_list_merged = merge_kcats(
kcat_list_fuzzy, kcat_list_dlkcat,
source_priority=["database_top", "dlkcat", "database_bottom"],
)
mergeKcats (MATLAB) / merge_kcats (Python) accepts any number of
kcatLists, for example BRENDA, DLKcat and OpenKineticsPredictor results
together, each assigned an explicit priority in sourcePriority /
source_priority. 'database_top'/'database_bottom' are reserved tier
tokens for strong/weak fuzzy BRENDA matches; any other token ('dlkcat',
'catapro', ...) matches a row's own source value. A third reserved
tier, 'database_exact', ranks above both: an exact experimental
measurement with no fuzzy wildcarding, which OpenKineticsPredictor can
return directly.
Note
mergeDLKcatAndFuzzyKcats/merge_dlkcat_and_fuzzy_kcats are deprecated
Both are thin two-source wrappers around mergeKcats/merge_kcats with the
priority order ['database_top', 'dlkcat', 'database_bottom'], kept only
for backward compatibility with the original two-source signature:
kcatList_merged = mergeDLKcatAndFuzzyKcats(kcatList_DLKcat, kcatList_fuzzy);
from geckopy import merge_dlkcat_and_fuzzy_kcats
kcat_list_merged = merge_dlkcat_and_fuzzy_kcats(
kcat_list_dlkcat, kcat_list_fuzzy,
)
Both raise a deprecation warning when called; use mergeKcats/merge_kcats
directly, as shown above.
Merging assigns a single kcat to each reaction, with priority given to BRENDA values from a full EC number match. Mismatches on organism and substrate are allowed by default, and modifiable through additional input parameters. If no exact EC match exists, the DLKcat (or OKP) value is used. If neither predicted a value for a reaction, for example because its substrate had no SMILES annotation, EC number wildcard matches from fuzzy matching are allowed if available: instead of assigning a kcat to pyruvate oxidase by querying EC 1.2.3.3, the merge queries EC 1.2.3.-, which includes any oxidoreductase acting on the aldehyde or oxo group of donors with oxygen as acceptor, and selects the highest kcat from that group.
Note
Example output
The merged kcatList carries the same fields as the fuzzy-matching result
above, plus a kcatSource field (Python: the source column, which every
list has) naming which source contributed each value individually.
See also#
Applying kcats, the next step: custom values, isozyme means, a standard fallback, and applying the resulting constraints to the ecModel.
Building an empty ecModel, where the empty ecModel these kcats attach to comes from.
API reference, every function in both toolboxes.