Skip to content

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:

if not result.converged["DC"]:
    raise RuntimeError("targeter did not reach its goal")

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 GmatRunErrorsolver_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

{name: path} for every ReportFile resource. Defaults to empty.

None
ephemeris_paths Mapping[str, Path] | None

{name: path} for every EphemerisFile resource. Defaults to empty.

None
contact_paths Mapping[str, Path] | None

{name: path} for every ContactLocator resource. Defaults to empty.

None
solver_paths Mapping[str, Path] | None

{name: path} for every Solver resource whose .data iteration log was found on disk after the run. Defaults to empty.

None
solver_max_iterations Mapping[str, int] | None

{name: MaximumIterations} for those same solvers. Passed through to the solver-log parser so a max-iteration stop can be told apart from a generic failure. Defaults to empty.

None

converged property

converged: dict[str, bool]

{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. ~ is expanded and relative paths are resolved against the caller's CWD at submit time, so :attr:output_dir stays absolute after the call.

required

Returns:

Type Description
Results

self, so the call composes with Mission.run().persist(...).

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:ephemerides.

required
path str | PathLike[str]

Destination .oem file. Parent directories are created.

required
originator str

ORIGINATOR header value. Defaults to "gmat-run".

'gmat-run'
object_name str | None

Override for the OBJECT_NAME meta field. When None, falls back to df.attrs["object_name"].

None

Returns:

Type Description
Path

The destination Path.

Raises:

Type Description
KeyError

name is not a known ephemeris resource.

ImportError

ccsds-ndm is not installed.

ValueError

the ephemeris DataFrame is missing required metadata for OEM emission. See :func:gmat_run.writers.oem.write_oem for the full list.

write_oem_all

write_oem_all(
    dirpath: str | PathLike[str],
    *,
    originator: str = "gmat-run",
) -> Path

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

ORIGINATOR header for every emitted file.

'gmat-run'

Returns:

Type Description
Path

dirpath as a resolved :class:pathlib.Path.