Results¶
Results is the return value of
Mission.run. It exposes four keyed views over GMAT's
output files — reports, ephemerides, contacts, and solver_runs — each
typed as Mapping[str, pandas.DataFrame] and keyed by the resource name as
declared in the .script.
Parsing is lazy: a ReportFile listed in Results.reports is read from disk
and converted to a DataFrame only on first access, then cached for the life of
the instance.
When Mission.run was called without a working_dir,
the artefacts live under a tempfile.TemporaryDirectory that is cleaned up
when the Results is garbage-collected. Call
Results.persist to copy the artefacts to a
permanent location before the temp dir disappears.
Results.write_oem and
Results.write_oem_all re-emit
ephemerides as CCSDS-OEM files via ccsds-ndm (gated behind the
[ccsds-ndm] extra). Coordinate-system names are rewritten through a
small alias table on output:
| Source name | Written as | Notes |
|---|---|---|
EME2000, GCRF, ICRF, ITRF2000, TEME, TOD, … |
(passthrough) | CCSDS 502.0-B-2 §A.5 canonical frames |
EarthMJ2000Eq |
EME2000 |
GMAT internal |
EarthICRF |
ICRF |
GMAT internal |
J2000 |
EME2000 |
STK-TimePosVel convention |
Anything else raises ValueError rather than emitting a header
downstream tools cannot interpret. Time systems are restricted to
A1, TAI, UTC, TT, and TDB — the five GMAT speaks end-to-end.
Solver runs¶
Results.solver_runs keys a DataFrame by each Solver resource a Target or
Optimize block exercised — result.solver_runs["DC"], result.solver_runs["Yukon1"].
A mission with no solver yields an empty mapping. Each DataFrame has one row
per iteration's nominal pass:
| Column | Meaning |
|---|---|
iteration |
Iteration number as GMAT reports it. |
<variable> |
One column per Vary variable, named verbatim from the script (MOI.Element1). |
status |
"running" on every row except the last, which carries "converged", "max_iter", or "failed". |
A DifferentialCorrector adds, per Achieve goal, the quartet <goal> (the
achieved value), <goal>_desired, <goal>_residual (achieved - desired, so a
positive residual means the achieved value overshot the target), and
<goal>_tolerance. A Yukon optimizer instead adds cost (the cost-function
value) and <constraint>_residual per nonlinear constraint — its iteration log
records no achieved value, desired bound, or tolerance for a constraint.
df.attrs carries solver_type, solver_mode, n_iterations, n_variables,
n_goals, converged, and source_path.
runs = result.solver_runs["DC"]
runs.plot(x="iteration", y="geoSat.Earth.SMA_residual") # watch it home in
final = runs.iloc[-1]
Results.converged is a {solver: bool} shortcut over the same mapping, for
the common branch:
Convergence detection. A Yukon run is converged when its log stamps the
optimizer-converged marker. A DifferentialCorrector log carries no such
marker — the same "Targeting Completed" line ends a converged and a
non-converged run alike — so convergence is derived: the last iteration is
converged when every goal's abs(residual) <= tolerance. A non-converged run
that ran out of iterations (n_iterations reached the solver's
MaximumIterations) is reported as "max_iter"; any other non-converged
outcome is "failed".
A run-time GMAT error inside a solver block still raises
GmatRunError — solver_runs reports solver
outcomes, not underlying run failures.
Results
¶
Results(
*,
output_dir: Path,
log: str,
report_paths: Mapping[str, Path] | None = None,
ephemeris_paths: Mapping[str, Path] | None = None,
contact_paths: Mapping[str, Path] | None = None,
solver_paths: Mapping[str, Path] | None = None,
solver_max_iterations: Mapping[str, int] | None = None,
)
Aggregate of every output file GMAT wrote during a single run.
Construct one per call to :meth:Mission.run. Each path mapping is keyed
by the resource name declared in the .script ("ReportFile1",
"EphemerisFile1", "ContactLocator1", "DC", …). Path mappings are
defensively copied and re-exposed as read-only views, so callers cannot
mutate the run record after the fact.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
output_dir
|
Path
|
The working directory GMAT used for this run. Surfaced so callers can locate any output file gmat-run did not aggregate itself. |
required |
log
|
str
|
GMAT's stdout and stderr captured during the run, joined into a single string. |
required |
report_paths
|
Mapping[str, Path] | None
|
|
None
|
ephemeris_paths
|
Mapping[str, Path] | None
|
|
None
|
contact_paths
|
Mapping[str, Path] | None
|
|
None
|
solver_paths
|
Mapping[str, Path] | None
|
|
None
|
solver_max_iterations
|
Mapping[str, int] | None
|
|
None
|
converged
property
¶
{solver name: bool} — did each solver run reach its goal?
A convenience view over :attr:solver_runs for the common branching
case (if not result.converged["DC"]: ...). {} when the mission
declared no solvers.
Keys are the solvers whose .data log parsed successfully. A solver
whose log cannot be parsed — an unsupported solver type, a malformed
file — is omitted with a :class:UserWarning rather than failing the
whole property, so one bad log does not hide every other solver's
status. Use name in result.converged to tell an omitted solver
apart from a converged/diverged one.
Reading this materialises every solver run (it inspects each
DataFrame's attrs["converged"]), so the lazy-parse cost is paid on
first access — the same trade-off as iterating :attr:solver_runs
values directly.
persist
¶
persist(path: str | PathLike[str]) -> Results
Copy every output artefact under :attr:output_dir into path.
Mutates the :class:Results in place so future
report/ephemeris/contact/solver-run access reads from the persisted
location instead of the (potentially soon-to-be-cleaned) workspace.
The :class:tempfile.TemporaryDirectory backing a default-workspace
run is released as part of the call. Run with an explicit
working_dir: that directory is left intact — persist is a copy,
never a move.
Path mappings are rewritten so any path that lived under the old
output_dir now points at the matching file under path.
Absolute filenames the user pinned outside the workspace via
ReportFile.Filename = "/abs/elsewhere.txt" are kept as-is — the
user chose that destination and we honour it. Already-parsed
DataFrames stay cached; they are independent of the on-disk files.
Calling persist again later copies the artefacts to the new
destination. A no-op fast path applies when the destination already
equals the current output_dir.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str]
|
Directory to copy artefacts into. Created if missing.
|
required |
Returns:
| Type | Description |
|---|---|
Results
|
|
write_oem
¶
write_oem(
name: str,
path: str | PathLike[str],
*,
originator: str = "gmat-run",
object_name: str | None = None,
) -> Path
Write the ephemeris keyed by name to path as a CCSDS-OEM file.
The DataFrame is materialised through :attr:ephemerides (so the
same lazy-parse and cache contract applies) and emitted via
:func:gmat_run.writers.oem.write_oem. Requires the
[ccsds-ndm] extra; raises :class:ImportError with an install
hint if it is missing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Resource name under :attr: |
required |
path
|
str | PathLike[str]
|
Destination |
required |
originator
|
str
|
|
'gmat-run'
|
object_name
|
str | None
|
Override for the |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
The destination |
Raises:
| Type | Description |
|---|---|
KeyError
|
|
ImportError
|
|
ValueError
|
the ephemeris DataFrame is missing required
metadata for OEM emission. See
:func: |
write_oem_all
¶
Write every ephemeris in :attr:ephemerides to dirpath as OEM.
Each file is named <name>.oem after its resource key. dirpath
is created if missing. A run with no ephemerides is a no-op (the
directory is still created).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dirpath
|
str | PathLike[str]
|
Destination directory. |
required |
originator
|
str
|
|
'gmat-run'
|
Returns:
| Type | Description |
|---|---|
Path
|
|