Skip to content

GECKO 4.0.0b2 - #27

Merged
edkerk merged 26 commits into
mainfrom
release/4.0.0b2
Nov 2, 2026
Merged

edkerk merged 26 commits into
mainfrom
release/4.0.0b2

Conversation

@edkerk

@edkerk edkerk commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Changes since 4.0.0b1.

Changes that alter results or raise errors

  • The default kcat preset is brenda_ec_tree, which estimates kcats from the EC tree (#10).
  • calculateFfactor averages each protein over the samples in which it is measured, and loadProtData raises an error when the sum of replPerCond differs from the number of replicate columns (#18).
  • getEnzymeBottlenecks ranks the enzymes by the re-solved change of the objective (#20).
  • getPfbaEnzymes with fractionOfOptimum below 1 works for a negative optimum (#25).
  • sigmaFitter, getStandardKcat and getSubsetEcModel handle a plateau, subsystem case, empty subsystems and light ecModels (#15).
  • GECKO requires RAVEN 3.0.0 and ignores the pre-release suffix of the installed RAVEN (#24).

Added

  • Gathered kcats are kept as kcat source files, one per source, and assigned as model_adapter.toml sets (#3, #9).
  • BRENDA data from toolbox-assets and upgrades of GECKO 3 projects (#11).
  • levelComplexConcs, getComplexFamilies and the option perComplex of flexibilizeEnzConcs (#19).
  • Saved ecModels record the ecModel format and the GECKO release (#12).

Fixes

  • getECstring splits EC numbers on semicolons as well as spaces (#14).
  • GECKO gives the same result as geckopy in the scenarios where they differed (#13, #21).

Repository

version.txt and the README version exist on main only (#26). Links point to CompMetEng (#8), references to the retired Bayesian kcat tuning are removed (#16), the HTML documentation is regenerated (#17) and the README has the new logo (#22).

edkerk added 26 commits October 4, 2026 20:23
…Chalmers/GECKO#484)

A BRENDA kcat is a turnover per molecule of one named substrate, so it
becomes a reaction turnover by dividing by that substrate's coefficient.
The division used the smallest coefficient in the reaction instead.

Where a reaction is not written per the measured substrate, that inflates
the kcat. yeast-GEM writes cytochrome c oxidase per one cytochrome c, so
oxygen carries 0.25, and BRENDA's S. cerevisiae value of 1500/s became
6000/s in the full_ecModel tutorial.

The matching loop already knows which substrate matched: it is subs{k}.

Original-commit: SysBioChalmers/GECKO@7b50750
kcat.tsv and sa.tsv each record a maximum and a median per (EC,
substrate, organism) triple, and loadBRENDAdata read the maximum with no
way to ask for the other. geckopy, which produces these files, lets a
project choose; this brings GECKO in line.

params.kcatAggregation on the model adapter sets it for a project, and
'kcatAggregation' as a name-value argument to fuzzyKcatMatching or
loadBRENDAdata sets it per call. The default is 'max', unchanged.

Both aggregation layers switch together, as they do in geckopy: the
column read from the file, and the collapse of the entries matched at
one search level. Which triple is matched, at which wildcard level and
origin, is untouched -- only the value taken from it changes.

The argument validators are new. Mixing positional and name-value
arguments puts an argument name into a positional slot, and
`while forceWClvl > 0` on a char array is all-true, so the run silently
wildcarded every EC number instead of failing. The validators reject
that, and the test covers it.

Original-commit: SysBioChalmers/GECKO@4e631c0
A project's parameters can now live in model_adapter.toml, so one file
configures GECKO and geckopy. ModelAdapterManager.getAdapter and setDefault
take the project folder or the TOML file and return a TomlAdapter; adapter
classes keep working as before.

- readAdapterToml and writeAdapterToml read and write the subset of TOML
  that adapters use, with an error naming the line for anything else.
- loadAdapterParams maps the snake_case keys to GECKO's field names and
  fills geckopy's defaults, both from adapterParameters.tsv, and rejects
  unknown keys. Evotune source groups become kcatSources and
  sigma0logSource.
- TomlAdapter takes spontaneous reactions from [spontaneous] and the
  anaerobic and protein-content functions from [matlab]; parameters only
  GECKO uses go under [matlab.params]. A class that needs more code can
  subclass TomlAdapter.
- convertAdapterToToml writes an existing adapter class to
  model_adapter.toml and lists the methods it cannot carry over.
- createGECKOproject writes model_adapter.toml by default
  ('adapterFormat','class' for the old template).
- The full_ecModel tutorial uses model_adapter.toml; HumanGEMAdapter in
  light_ecModel subclasses TomlAdapter. It now marks all 14 reactions that
  data/spontaneousReactions.tsv flags as spontaneous; the old class found
  one.

Original-commit: SysBioChalmers/GECKO@5b32cf6
CatRange predicts a class of kcat values, which OpenKineticsPredictor
returns as its range ('10 to 100 s^-1') in the column 'Predicted kcat
range: kcat (1/s)'. readOpenKineticsPredictorOutput read that text as
NaN and dropped every CatRange prediction. It now reads a range as its
geometric midpoint, or half the upper bound for the class starting at 0.
The list of available predictors is updated.

Original-commit: SysBioChalmers/GECKO@a8292f9
Read the model adapter from model_adapter.toml, shared with geckopy
Read CatRange predictions returned as class ranges
* Keep gathered kcats as kcat source files, one per source

Gathered kcats are written to data/kcats/<source>.tsv in the project,
keyed by conventional reaction and enzyme set rather than model.ec.rxns,
so a file stays valid when the ecModel is rebuilt and applies to full and
light models alike. geckopy reads and writes the same files.

- kcatsFromBrenda, kcatsFromDLKcat and kcatsFromOKP gather one source each,
  with that source's own options, and write its file. OKP keeps a job and
  a result per method, and only uses a stored result of the current job.
- saveKcatSource writes any kcat list; readKcatSource reads a file;
  loadKcatSource maps a file to a model and reports rows with an invalid
  kcat, rows that fit no reaction, and rows that match only part of an
  enzyme set.
- getEcRxnKeys gives the conventional reaction and enzyme set of every
  model.ec reaction.
- fetchOpenKineticsPredictor takes the file to store the result in.

Original-commit: SysBioChalmers/GECKO@f3a696c

* Read kcat source files in loadKcatSource; no private helper folder

loadKcatSource reads the file itself and returns the header lines and the
rows as read in report.header and report.records; readKcatSource is gone.
The helpers of src/gather_kcats/private are local functions of the
functions that use them. kcatsFrom<Source> no longer check the file name
before gathering; saveKcatSource does when it writes.
* Keep gathered kcats as kcat source files, one per source

Gathered kcats are written to data/kcats/<source>.tsv in the project,
keyed by conventional reaction and enzyme set rather than model.ec.rxns,
so a file stays valid when the ecModel is rebuilt and applies to full and
light models alike. geckopy reads and writes the same files.

- kcatsFromBrenda, kcatsFromDLKcat and kcatsFromOKP gather one source each,
  with that source's own options, and write its file. OKP keeps a job and
  a result per method, and only uses a stored result of the current job.
- saveKcatSource writes any kcat list; readKcatSource reads a file;
  loadKcatSource maps a file to a model and reports rows with an invalid
  kcat, rows that fit no reaction, and rows that match only part of an
  enzyme set.
- getEcRxnKeys gives the conventional reaction and enzyme set of every
  model.ec reaction.
- fetchOpenKineticsPredictor takes the file to store the result in.

Original-commit: SysBioChalmers/GECKO@f3a696c

* Assign kcats from the kcat source files, as model_adapter.toml sets

collectKcats decides one kcat per reaction from the files in data/kcats,
in the stages of [kcat_assignment] (a preset, or [[kcat_assignment.stage]]
tables), without changing the model, and writes
output/<model id>_kcatDecisions.tsv. assignKcats applies the decisions and
the kcat constraints. updateKcatSources gathers the source files the
configuration needs that are missing, or outdated against
[kcat_sources.<name>].

The default preset, gecko_median, orders custom kcats, the BRENDA median
of matches without EC wildcards at origins 1 to 4, DLKcat, then the other
BRENDA matches. gecko_classic keeps the GECKO 3 order with the BRENDA
maximum; the full_ecModel tutorial uses it and gives the same model as
before.

* Read kcat source files in loadKcatSource; no private helper folder

loadKcatSource reads the file itself and returns the header lines and the
rows as read in report.header and report.records; readKcatSource is gone.
The helpers of src/gather_kcats/private are local functions of the
functions that use them. kcatsFrom<Source> no longer check the file name
before gathering; saveKcatSource does when it writes.
…et (#10)

* Keep gathered kcats as kcat source files, one per source

Gathered kcats are written to data/kcats/<source>.tsv in the project,
keyed by conventional reaction and enzyme set rather than model.ec.rxns,
so a file stays valid when the ecModel is rebuilt and applies to full and
light models alike. geckopy reads and writes the same files.

- kcatsFromBrenda, kcatsFromDLKcat and kcatsFromOKP gather one source each,
  with that source's own options, and write its file. OKP keeps a job and
  a result per method, and only uses a stored result of the current job.
- saveKcatSource writes any kcat list; readKcatSource reads a file;
  loadKcatSource maps a file to a model and reports rows with an invalid
  kcat, rows that fit no reaction, and rows that match only part of an
  enzyme set.
- getEcRxnKeys gives the conventional reaction and enzyme set of every
  model.ec reaction.
- fetchOpenKineticsPredictor takes the file to store the result in.

Original-commit: SysBioChalmers/GECKO@f3a696c

* Assign kcats from the kcat source files, as model_adapter.toml sets

collectKcats decides one kcat per reaction from the files in data/kcats,
in the stages of [kcat_assignment] (a preset, or [[kcat_assignment.stage]]
tables), without changing the model, and writes
output/<model id>_kcatDecisions.tsv. assignKcats applies the decisions and
the kcat constraints. updateKcatSources gathers the source files the
configuration needs that are missing, or outdated against
[kcat_sources.<name>].

The default preset, gecko_median, orders custom kcats, the BRENDA median
of matches without EC wildcards at origins 1 to 4, DLKcat, then the other
BRENDA matches. gecko_classic keeps the GECKO 3 order with the BRENDA
maximum; the full_ecModel tutorial uses it and gives the same model as
before.

* Estimate kcats from the EC tree; make brenda_ec_tree the default preset

measurements.tsv, next to the BRENDA kcat, sa and mw files, holds the
wild-type BRENDA kcats with an EC number after the outlier screen, one row
per measurement. kcatsFromECTree gives each reaction the median kcat of
its EC number, shrunk toward its 3- and 2-digit EC classes with weight
n / (n + k), and keeps it as data/kcats/ec_tree.tsv (ecTreeEstimate,
loadBrendaMeasurements). saveKcatSource takes further detail pairs from a
detail field.

The brenda_ec_tree preset, now the default, orders custom kcats, the
BRENDA median of measurements of the organism on the reaction's substrate
(origin 1), the EC tree, then DLKcat for reactions without an EC number.
updateKcatSources gathers ec_tree.tsv and marks it outdated for another k
or BRENDA release.

* Read kcat source files in loadKcatSource; no private helper folder

loadKcatSource reads the file itself and returns the header lines and the
rows as read in report.header and report.records; readKcatSource is gone.
The helpers of src/gather_kcats/private are local functions of the
functions that use them. kcatsFrom<Source> no longer check the file name
before gathering; saveKcatSource does when it writes.
…des (#11)

* Download the BRENDA files from toolbox-assets instead of shipping them

fetchBrendaAsset downloads the files of the toolbox-assets release
brenda-2026.1-1 (kcat, sa, mw and measurements, and on request sequences,
substrates and combination parameters) with RAVEN's fetchToolboxAsset into
databases/brenda-2026.1-1 and unpacks them. ModelAdapter.getBrendaDBFolder
gives the project's data/brenda when it exists and these files otherwise.
databases/ no longer holds BRENDA files.

* Find keggPhylDist.mat under src/reconstruction/kegg of RAVEN 3

* Combine kcat sources; presets for OKP; OKP replaces DLKcat in the default

A combine stage gives each open reaction the precision-weighted mean of
its sources' values, each corrected for the source's bias and weighted by
1 / its error variance for the source's evidence (EC tree: measurements and
EC level; OKP method: similarity to its training sequences), with an
uncertainty for a new measurement. The parameters are kcatCombineParams.tsv
of the BRENDA files. model.ec.source reads 'combined (sd=..., n=...)' and
the decisions get sdLog10.

okp in a stage stands for the OKP methods of [kcat_sources.okp] methods.
OKP results keep the similarity to the method's training data, written as
similarity= in the detail column, and loadKcatSource passes the detail on.

Presets: brenda_ec_tree combines the OKP methods for the reactions without
an EC number instead of using DLKcat; ec_tree_okp combines the EC tree with
the OKP methods; okp uses the OKP methods only.

* Convert custom kcats; upgrade GECKO 3 projects and ecModels

convertCustomKcats writes data/customKcats.tsv as the curated kcat source
file data/kcats/custom.tsv: the kcats applyCustomKcats gives the ecModel,
one row per reaction keyed by conventional reaction and enzyme set.
upgradeEcModel turns the protein usage and pool reactions of a GECKO 3
ecModel forward. upgradeGECKOproject lists, and with 'dryRun', false
carries out, the steps from a GECKO 3 project: adapter class to
model_adapter.toml, custom kcats, stored predictions to source files,
saved ecModels, project_format = 4; it lists script lines that call
renamed or replaced functions. Originals go to legacy/<date>/.
model_adapter.toml takes project_format. The full_ecModel tutorial shows
how to choose a kcat strategy.

* Estimate kcats from measured homologs

kcatsFromHomologs searches each enzyme of a reaction with DIAMOND (RAVEN's)
against the sequences of the BRENDA measurements (at least 40% identity,
80% coverage of both), and takes the identity-weighted mean of the medians
of up to 10 homologs measured on the reaction's substrate, matched by
InChIKey through ChEBI or MetaNetX annotations, or else the name. It writes
data/kcats/homologs.tsv with the nearest identity and the number of
homologs in the detail column. extractEnzymeSubstratePairs, taken out of
writeDLKcatInput, gives the substrates. The ec_tree_homologs preset
combines the EC tree with homologs, and ec_tree_okp combines homologs too;
updateKcatSources gathers homologs.tsv.

* Treat homologs at 95% identity and above as the same protein

When a homolog at 95% identity or more was measured on the substrate,
the homolog estimate is the median of those homologs alone, and the
combination gives it the exact bin of kcatCombineParams.tsv: no bias and
the smallest error variance, an assumption, as the benchmark the other
bins were fitted on hides such homologs. The detail column writes the
identity with three decimals, as DIAMOND reports it, so that it falls in
the right bin.

* Use OKP's TurNup name; reaction-level OKP methods are not run per enzyme-substrate pair

OpenKineticsPredictor names the method TurNup. CatPred and TurNup take the
substrates (and products) of a whole reaction, so the default OKP methods
are the eight pair-based ones with combination parameters, and asking for
CatPred or TurNup per pair is an error.
)

saveEcModel writes gecko_format and generator to the YAML metaData.
loadEcModel upgrades a GECKO 3 ecModel in the loaded model with a warning
and refuses a format it does not know. Part of #5.
… differences (#13)

* saveEcModel: drop the description assignment, which neither writer reads

* ecFSEOF: select targets by the correlation with the enforced production, as RAVEN's FSEOF does

A reaction is a target when its absolute flux correlates with the enforced production flux (absolute Pearson correlation of at least corrThreshold, default 0.9), instead of requiring a strictly monotonic flux and a slope above the 75th percentile. The targets are ranked by slope times correlation.

* Proteomics filters, complex stoichiometry and kcat lists: loadProtData blanks the whole row above maxRSD and uses cutLowest, applyComplexData assigns the stoichiometry per protein ID, assignKcatValues drops the source of a zero kcat with its row, loadBRENDAdata finds lowercase EC codes

* Gene rules, partial complexes, reaction id suffixes and isozymes without enzymes

makeEcModel (light) and addNewRxnsToEC expand a gene rule to its isozymes in disjunctive normal form, so 'G1 and (G2 or G3)' is the isozymes {G1,G2} and {G1,G3}, and read gene ids as whole words, so an id such as orfX is not split at 'or'.

makeEcModel couples a reaction to the subunits of its complex that the databases know, also when another subunit is missing from them.

mapRxnsToConv and ecFVA strip the _REV and _EXP_n suffixes only at the end of a reaction id, so an id such as R_REVX is kept.

applyKcatConstraints (light) ignores an isozyme row without enzymes when it selects the cheapest isozyme of a reaction.

* Verification matrix: list the tests of the changed functions; give the ecFSEOF test its own label

* Match geckopy on the BRENDA outlier filter, the fuzzy kcat matching and the custom kcats

loadBRENDAdata drops a kcat more than four orders of magnitude below the median of its EC (filterOutlierKcats, default true). The reference of an EC is the median of the positive kcats of the max and median views of kcat.tsv and of the kcats derived from sa.tsv and mw.tsv, widened to the 3-, 2- and 1-level prefix while fewer than five values are available. On the BRENDA 2026.1 release it drops the same rows as geckopy's load_brenda_data.

fuzzyKcatMatching escalates an EC number without a match to '-.-.-.-' (wildcard level 4), reports the wildcard level and origin of the selected EC when several EC numbers share the highest kcat, keeps the order and repeats of ecRxns given as indices, and returns no reactions for an empty selection other than []. kcatsFromBrenda refuses such a selection.

applyCustomKcats skips an entry without a kcat, does not append an empty note, matches protein and reaction ids exactly and works on light ecModels.

* Gene ids that contain 'and', case-insensitive gene rule operators, usage reactions out of the ecFSEOF gene targets, forced wildcards from the right, and usable kcats in light applyKcatConstraints

addNewRxnsToEC couples a new reaction to the whole-word genes of its rule. makeEcModel (light) keeps one row for a reaction with an empty gene rule, and gene rule operators in capitals are accepted there and in addNewRxnsToEC. ecFSEOF leaves the usage reactions out before the gene slopes and actions are derived, reports an empty result for a corrThreshold above every correlation, and corrThreshold is tested. fuzzyKcatMatching forceWClvl replaces the rightmost level that is not a wildcard yet, for each EC number of a reaction. applyKcatConstraints (light) lets only isozymes with a positive kcat and a positive enzyme mass set the cost.

* Gene rule operators in any case in addNewRxnsToEC; EC codes without four levels in fuzzyKcatMatching

addNewRxnsToEC writes the operators AND and OR of every gene rule in lower case before the reversible reactions are split and the rules are passed to addRxns, so a rule with a single clause such as 'Sand1 AND andA' no longer reaches RAVEN with an operator it does not parse. Gene ids that contain the letters are left as they are.

fuzzyKcatMatching stops escalating an EC code that does not have four levels (for example 1.1.1) and returns the no-match values, as geckopy does, instead of indexing a fourth level that is not there. The help text of forceWClvl in fuzzyKcatMatching and kcatsFromBrenda says that it replaces extra levels from the right, on top of any wildcard already in the code.

Tests: upper-case operators on a reversible single-clause rule and an OR rule, an isozyme whose enzymes have no mass, trailing wildcards and a level above the remaining levels for forceWClvl, and a code without four levels.

* mergeKcats and mergeDLKcatAndFuzzyKcats default to topOriginLimit 4; findECInDB drops a wildcard code next to a specific code for a single gene

Origin 5 and 6 matches come from specific activities times molecular weights and do not always beat a prediction, so they rank after DLKcat by default; topOriginLimit 6 ranks them above, as the gecko_classic preset does. A gene whose database cell holds '1.1.1.4 1.1.1.- 1.1.1.303' now gives 1.1.1.4;1.1.1.303, as an enzyme complex already did.

* applyKcatConstraints clears the constraints of a full ecModel when every kcat is 0, sensitivityTuning stops when growth gains less than 1e-9, getKcatAcrossIsozymes averages the isozymes with a kcat above 0

* sensitivityTuning ends with a warning when no protein usage reaction carries flux

* sensitivityTuning ends with a warning when the LP has no solution; findECInDB leaves codes with fewer than four levels alone

sensitivityTuning stops with a warning when solveLP returns no objective value inside the tuning loop (full and light branches), as geckopy does for a solve that is not optimal.

For a single gene, findECInDB removes a wildcard code only next to a four-level code of the same prefix and removes empty codes, so '1.1.1 1.1.1.4' keeps both codes and a trailing space leaves no empty code. The help text states that the combined EC set of a complex is not cleaned of a wildcard next to a specific code.

* findECInDB cleans the EC set of an enzyme complex of a wildcard next to a specific code, as the set of a single gene, and leaves out genes without an EC number

The cross-gene union and intersection use the same four-level wildcard rule as the single-gene clean-up, replacing the trimmed-substring comparison, and the combined set is cleaned before the codes are joined.
A database cell that joins codes with ';' and no space, such as '1.1.1.1;2.2.2.2', gave the single string 'EC1.1.1.12.2.2.2'. The cell is now split on white space and semicolons, as geckopy does.
… case, empty subsystems, light ecModels (#15)

* sigmaFitter: return the start of the growth plateau on every solver

Growth that is flat from some sigma on is returned with rounding noise by some solvers. The smallest sigma whose growth is within 1e-9 (relative) of the best growth is now chosen instead of the exact minimum error.

* getStandardKcat, getSubsetEcModel: handle subSystem case and empty subSystems, models without genes to remove, light ecModels

getStandardKcat treats subSystem names that differ only in letter case as one subSystem, as its lookup already did, instead of raising when two list entries match. A reaction with an empty subSystem gets the model-wide standard kcat instead of raising an index error.

getSubsetEcModel returns the model when no gene has to be removed, instead of raising in removeGenes. In a light ecModel the reaction identifiers of the ec structure carry the prefix 001_ and were all compared to the model reactions with it, so every entry was removed. The prefix is now removed before the comparison.
* Remove the references to the retired Bayesian kcat tuning

* Regenerate the HTML documentation
…calculateFfactor averages over the measured samples (#18)
)

* Rank getEnzymeBottlenecks by the re-solved change of the objective

* getEnzymeBottlenecks warns when no enzyme is at its usage bound
* fillCarbonNum: warn and weight 1 for a model without metabolite formulas

A model without metFormulas gave an error from the field access; every
exchange now gets the carbon number 1 and a warning, as geckopy does.

* ecStatsFromBrenda gives the median and the maximum per EC and keeps the EC case

* loadBrendaMeasurements reads measurements.tsv.xz

* getECfromDatabase warns on multiplicities with action display and searches the selected reactions only

* Record the new tests in the verification matrix

* addNewRxnsToEC adds reactions whose genes already have enzymes

* applyComplexData counts a subunit without stoichiometry as one subunit

* calculateFfactor and relaxProteomicsGreedy read an empty cell array as no enzymes

* constrainEnzConcs and fillEnzConcs raise for a negative concentration

* constrainFluxData raises for an invalid maxMinGrowth, a missing reaction and an unknown condition

* loadFluxData raises for a missing file, a ragged row and no exchange columns, and reads nested parentheses in headers

* getEnzymeUsage leaves out enzymes without usage reaction; reportEnzymeUsage reads filtered usage and rejects a negative topAbsUsage

* truncateValues returns infinite values unchanged

* Add tests for the contract changes of round 5 group 3

* getECfromDatabase treats an empty logical ecRxns as no selection

* collectKcats counts each source file once, and mergeKcats matches OKP-tagged sources by their underlying source

* loadKcatSource skips a row of empty quoted fields and raises for a field with an opening quote and no closing one

* readDLKcatOutput drops rows without a finite kcat before checking reactions and leaves out unknown substrates; writeDLKcatInput writes an empty file for an empty selection

* kcatAssignmentConfig rejects invalid values; kcatsFromECTree takes an explicit empty ecRxns as no reaction; getStandardKcat matches pseudoreaction names in any case and runs twice on a light ecModel

* saveEcModel writes a default metaData description when there is none

A model without annotation.description gets 'Enzyme-constrained model of <id>'; an
existing description is kept, as save_ec_model does.

* applyKcatConstraints takes cobra and ec reaction ids on a light model and clears its pool coefficients when every kcat is 0

* assignKcatValues writes ec.source as the lower case source followed by the wildcard level and origin in brackets, as geckopy does

* resolveTunableContext puts a kcat in the group of its source name before the bracketed detail

* findMetSmiles uses the last row of a duplicated cache name and skips unnamed metabolites with a warning

* getECfromDatabase raises for actions other than display and ignore

* reviewKcatAssignment help text states the case rule for sources

* Record tc0407 to tc0409 in the verification matrix

* tc0009: the reloaded annotation carries the default description

* saveEcModel: a note counts as the description; tc0009 expects the note

* Test saveEcModel and loadEcModel with an ec reaction without enzymes (tc0700)

* ecFSEOF orders targets by the rounded score; getECfromGEM warns when the model has no eccodes
* README: use the new GECKO logo, with a white variant for dark mode

* README: describe GECKO 4 and geckopy, link the documentation and the parity ledger

* README: link the gecko-docs pages first, badges for documentation and support
…d RAVEN (#24)

The installer compared the installed RAVEN version with 2.9.1, which accepted RAVEN 2. The minimum is 3.0.0, and a pre-release such as 3.0.0b2 satisfies it.
version.txt and the version sentence of the README exist on main only. convertCustomKcats reads the version through a local function that returns 'unknown' when version.txt is absent, as saveEcModel does. A check rejects version.txt on develop, and CONTRIBUTING describes the release branch and the sync back into develop.
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

Unit test results

73 tests   73 ✅  12s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit 16f28df.

@edkerk
edkerk merged commit a4b20c3 into main Nov 2, 2026
2 checks passed
@edkerk
edkerk deleted the release/4.0.0b2 branch November 2, 2026 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant