The field catalogue¶
The field catalogue is the knowledge base behind everything that reasons about GMAT semantics: the
linter, and the language server's hover docs and completion. It records, for
every GMAT resource type, the fields that type defines — each field's type, allowed enumeration,
reference target, default, and unit — so a tool can tell that Spacecraft.SMA wants a number, that
ImpulsiveBurn.Axes is one of four values, or that Spacecraft.Tanks points at a FuelTank,
without running GMAT.
It is shipped as data, not derived at runtime. The wheel carries a precompiled JSON catalogue;
loading it imports no GMAT and touches no gmatpy.
Where it comes from — and the GMAT-free guarantee¶
The catalogue is reflected from a real GMAT install at build time, by a single generator, and then frozen into the package. Two halves, deliberately kept apart:
- Generation (build/CI time, GMAT required).
gmat_script.tools.gen_catalogis the only GMAT-touching code in the project. It importsgmatpy, walks GMAT's object factory and each object's parameter metadata, normalises the types, and writesgmat_script/data/fields-<version>.json. - Loading (runtime, no GMAT).
gmat_script.catalogreads that JSON and answers queries. It never importsgmatpy— so a plainpip install gmat-scriptcarries the full catalogue with no GMAT install anywhere.
This split is the GMAT-free guarantee in practice: GMAT's knowledge is captured once, offline, and travels with the package as a data file. (See the design decisions.)
The shipped catalogue is reflected from GMAT R2026a and records its 102 resource types and 2614 fields.
Reading the catalogue¶
load_catalog() returns a cached, queryable Catalog. It and its records are part of the public
API:
from gmat_script import load_catalog
cat = load_catalog()
cat.gmat_version # 'R2026a' — the release this was reflected from
cat.generated # '2026-06-08' — the ISO date it was generated
cat.has_type("Spacecraft") # True
cat.field_type("Spacecraft", "SMA") # 'real'
cat.enum_values("ImpulsiveBurn", "Axes")
# ('VNB', 'LVLH', 'MJ2000Eq', 'SpacecraftBody')
cat.ref_target("Spacecraft", "Tanks") # 'FuelTank'
A type name that GMAT spells differently in scripts resolves through an alias (Propagator ->
PropSetup, ODEModel -> ForceModel), so cat.has_type("Propagator") is True. Any query for an
unknown type or field returns None (or []) rather than raising — the linter and editor leans on
this to degrade to "no finding" wherever the catalogue is silent.
What a field carries¶
cat.field(type, name) returns a FieldSpec:
| Attribute | Meaning |
|---|---|
type |
The normalised catalogue type: real, integer, string, bool, enum, object, object_array, string_array, real_array, matrix, filename, on_off, color, gmat_time, … |
gmat_type |
GMAT's own raw type label, kept verbatim (e.g. Real, Rmatrix). |
read_only |
Whether GMAT exposes the field as output-only (not settable from a script). |
allowed |
The enum's allowed values, where GMAT exposes them — else None. |
ref_target |
The target GMAT type for an object-reference field — else None. |
default |
The default for a settable scalar field — else None. |
unit |
The field's unit, where GMAT reports one — else None. |
cat.type_spec(type) returns a TypeSpec carrying the type's GMAT object-type category and its
fields mapping. The lower-level Catalog.load(target_version=...) builds an uncached catalogue;
load_catalog is the convenience entry point most consumers want.
Versioning¶
The semantics a script must satisfy — valid field names, enums, defaults — vary by GMAT release, so the catalogue is version-pinned and provenance-stamped: every catalogue carries the GMAT version it was reflected from and the date it was generated. The grammar, by contrast, never enumerates types or keywords and is effectively version-independent; the catalogue is the one version-coupled artifact.
load_catalog() defaults to the newest shipped catalogue. Pass target_version to pin a specific
release:
load_catalog("R2026a") # an explicit release
load_catalog() # the newest available (currently R2026a)
The linter exposes the same selector — lint(source, target_version="R2026a") — and the language
server uses the default. Requesting a version that is not shipped raises ValueError listing what is
available.
Supporting another GMAT release is additive: a new fields-<version>.json is dropped into
gmat_script/data/, and load_catalog()'s "newest" default and the target_version selector pick
it up with no code change. There is no per-version code fork.
Regenerating the catalogue (the version-bump process)¶
Regenerating is only needed when targeting a new GMAT release or picking up a point-release metadata
change. It needs a real GMAT install — gmatpy is imported from it directly and is never
pip-installed.
# Regenerate and overwrite the shipped file, against a GMAT install:
$ python -m gmat_script.tools.gen_catalog
# Regenerate in memory and fail on any drift from the committed catalogue (the CI check):
$ python -m gmat_script.tools.gen_catalog --check
The generator locates GMAT from --gmat-root, else the GMAT_ROOT environment variable, else a set
of platform-standard install paths. A dedicated CI job runs the --check form on a schedule (and on
demand) against a freshly provisioned GMAT install, so a catalogue that drifts from the current GMAT
metadata is surfaced rather than silently rotting. The generation date is ignored in the drift
comparison, so re-running on a different day is not spurious drift.
To bump to a new release: regenerate against that GMAT version (so the file is written as
fields-<new-version>.json), commit the new data file alongside the existing one, and the loader's
"newest" default begins serving it. Keeping the prior file lets callers pin the older release through
target_version.