contrailopt.optimize¶
Trajectory optimization with Poll-Schumann Aircraft Performance and climate term.
Functions
|
Determine the candidate cruise flight levels for a given origin-destination pair. |
|
Estimate upper-bound flight duration in hours. |
|
Solve shortest-path DP on the topo-sorted DAG, tracking mass exactly. |
|
Solve the vertical profile along a fixed track, choosing the flight level and Mach number. |
Classes
|
The output of Optimizer.solve(). |
|
Working arrays for the wavefront dynamic program. |
|
Trajectory optimizer for a single origin-destination pair based on the PS model. |
- class contrailopt.optimize.DAGState(*, best_cost: NDArray[float32], best_mass: NDArray[float32], best_time: NDArray[float32], best_mach: NDArray[float32], best_climb_dist: NDArray[float32], best_climb_time: NDArray[float32], best_prev_h: NDArray[int64], best_prev_fi: NDArray[int64])[source]¶
Bases:
objectWorking arrays for the wavefront dynamic program.
- class contrailopt.optimize.DAGResult(*, state: DAGState, amass_init: float, trip_fuel: float, payload: float, reserve_fuel: float, landing_mass: float)[source]¶
Bases:
objectThe output of Optimizer.solve().
- contrailopt.optimize.solve_dag(dag: HorizontalDAG, amass_init: float, fl_choices: NDArray[float32], mach_choices: NDArray[float32], atyp: PSAircraftEngineParams, cost_index: float, eef_cost_factor: float, origin_elev_ft: float, dest_elev_ft: float, takeoff_time: Timestamp, met_lookup: EdgeMetLookup | None, allow_cooling_credit: bool, step_penalty_kg: float, on_wavefront: Callable[[NDArray[int64], DAGState], None] | None = None) DAGState[source]¶
Solve shortest-path DP on the topo-sorted DAG, tracking mass exactly.
- contrailopt.optimize.solve_track(dag: HorizontalDAG | Track, profile: Dataset, amass_init: float, fl_choices: NDArray[float32], mach_choices: NDArray[float32], atyp: PSAircraftEngineParams, cost_index: float, eef_cost_factor: float, origin_elev_ft: float, dest_elev_ft: float, allow_cooling_credit: bool, step_penalty_kg: float) DAGState[source]¶
Solve the vertical profile along a fixed track, choosing the flight level and Mach number.
Unlike
solve_dag(), there is no precomputed edge set. From a given state, each action determines how far the aircraft advances: a level cruise (target FL equal to or below the current one) moves one waypoint; a climb moves as far as the climb model’s own distance requires, then cruises the remainder of that segment to the next waypoint.
- contrailopt.optimize.estimate_flight_hours(origin: AirportCoords, dest: AirportCoords, mach_number: float = 0.75, max_headwind: float = 40.0) int[source]¶
Estimate upper-bound flight duration in hours.
Computes the worst-case flight time assuming the aircraft flies at
mach_numberat FL400 with a sustained headwind ofmax_headwind.- Parameters:
- Returns:
Ceiling of estimated flight time in hours.
- Return type:
- contrailopt.optimize.cruise_flight_levels(origin_icao: str | AirportCoords, dest_icao: str | AirportCoords) NDArray[float32][source]¶
Determine the candidate cruise flight levels for a given origin-destination pair.
This function applies the common eastbound/westbound FL rules of even FLs for westbound flights and odd FLs for eastbound flights. There is not per-aircraft-type ceiling applied (this could be added if needed).
- Parameters:
origin_icao (
str | AirportCoords) – ICAO code for the origin airport (e.g."KLAX") or pre-fetched coordinates.dest_icao (
str | AirportCoords) – ICAO code for the destination airport or pre-fetched coordinates.
- Returns:
Array of candidate cruise flight levels in feet (e.g.
[29000., 31000., ..., 41000.]).- Return type:
- class contrailopt.optimize.Optimizer(origin_icao: str | AirportCoords, dest_icao: str | AirportCoords, aircraft_type: str, takeoff_time: Timestamp, *, met: MetDataset | Dataset | None = None, eef: DataArray | MetDataArray | None = None, dag: HorizontalDAG | Track | None = None, cost_index: float = 60.0, dollar_tonne_co2e: float = 0.0, dollar_kg_fuel: float = 1.0, step_penalty_kg: float = 0.0, met_spacing_m: float = 25000.0, flight_hours: int | None = None, allow_cooling_credit: bool = False, avoidance_regions: list[list[tuple[float, float]]] | None = None, fl_choices: NDArray[float32] | None = None, **kwargs: Any)[source]¶
Bases:
objectTrajectory optimizer for a single origin-destination pair based on the PS model.
The optimizer builds a horizontal directed acyclic graph between two airports, optionally interpolates met data onto edge sample points, and solves for the minimum-cost path across valid flight levels and Mach numbers.
- Parameters:
origin_icao (
str | AirportCoords) – ICAO code for the origin airport (e.g."KLAX"), or anAirportCoordsinstance.dest_icao (
str | AirportCoords) – ICAO code for the destination airport, or anAirportCoordsinstance.aircraft_type (
str) – Aircraft type key in the PS model parameter table (e.g."A320").takeoff_time (
pandas.Timestamp) – Departure time, used for met interpolation.met (
MetDataset | xr.Dataset | None, defaultNone) – Gridded met data withair_temperature,eastward_wind, andnorthward_wind. If None, cruise performance uses ISA temperatures and zero wind.eef (
xr.DataArray | MetDataArray | None, defaultNone) – Optionaleef_per_mDataArray on its own lon/lat grid. If provided, EEF is interpolated onto sample points independently from the weather grid, avoiding the need to pre-merge onto a common grid. Takes precedence overeef_per_minmetif both are present. Assumed to adhere to pycontrailsMetDataArrayconventions.dag (
HorizontalDAGorNone, defaultNone) – Pre-built DAG. If None, a DAG is generated via Poisson-disk sampling along the great circle. The DAG origin and destination must agree with the airport coordinates.cost_index (
float, default60.0) – Fuel-vs-time tradeoff in kg per minute. Higher values penalize time more, favoring faster (and more fuel-intensive) routes.dollar_tonne_co2e (
float, default0.0) – Carbon price in US dollars per tonne (1000kg) of CO2-equivalent. A value of 0.0 disables the carbon cost term. If positive, eithermetmust contain aeef_per_mvariable or theeefparameter must be provided.dollar_kg_fuel (
float, default1.0) – Fuel price in US dollars per kg. Only used to convert the carbon cost into the fuel-equivalent units of the objective function. Ignored ifdollar_tonne_co2eis 0.0.step_penalty_kg (
float, default0.0) – Cost in kg of fuel charged for changing flight level, on top of the maneuver’s own fuel and time, to discourage marginally beneficial steps. The initial climb and final descent are exempt. The penalty enters the objective only, not the aircraft mass, so reported fuel burn stays physical.met_spacing_m (
float, default25_000.0) – Spacing in meters between met sample points along each edge.flight_hours (
intorNone, defaultNone) – Upper-bound flight duration in hours for met time window. If None, estimated from the aircraft type. Providing an explicit value decouples the met lookup from the aircraft, allowing the user to call thesolve()method with a different aircraft type without re-initializing the optimizer.allow_cooling_credit (
bool, defaultFalse) – If True, negative EEF (cooling contrails) reduces cost whendollar_tonne_co2eis set. If False, negative EEF is clipped to zero in the cost function but still reported in the output flight. Only used ifdollar_tonne_co2eis set.avoidance_regions (
listofpolygon coordinate lists, orNone) – Polygons to exclude from the search, defined as lists of(lon, lat)vertices. Edges intersecting any polygon are removed and the DAG is re-pruned.**kwargs – Additional parameters for DAG generation if
dagis None. Passed intoHorizontalDAG.from_poisson.
- classmethod from_flight(flight: Flight, *, met: MetDataset | Dataset | None = None, fl_profile: Dataset | None = None, aircraft_type: str | None = None, origin_icao: str | None = None, dest_icao: str | None = None, eef: DataArray | MetDataArray | None = None, altitude_ft: NDArray[floating] | None = None, cost_index: float = 60.0, dollar_tonne_co2e: float = 0.0, dollar_kg_fuel: float = 1.0, allow_cooling_credit: bool = False, use_flown_climb_descent: bool = False) Self[source]¶
Build a vertical-profile optimizer from a
pycontrails.Flighttrajectory.The optimized flight follows the flight’s lateral path exactly, choosing flight level and Mach number along it via
solve_track(). Weather comes from one of two sources:met: raw gridded 4D met, used to interpolate the flight’s waypoints at each candidate flight level.fl_profile: an already-interpolated(waypoint, altitude_ft)dataset carryingair_temperature,u_wind,v_wind, and optionallyeef_per_m, aligned waypoint-for-waypoint withflight.
- Parameters:
flight (
pycontrails.Flight) – Trajectory supplying the lateral path, schedule, and (foruse_flown_climb_descent) the flown altitude profile.met (
pycontrails.MetDatasetorxarray.DatasetorNone) – Gridded met to interpolate. Mutually exclusive withfl_profile.fl_profile (
xarray.DatasetorNone) – Pre-interpolated per-waypoint met columns. Mutually exclusive withmet.aircraft_type (
strorNone) – PS model key. If None, taken fromflight.attrs.origin_icao (
strorNone) – ICAO codes. If None, taken fromflight.attrs, else the nearest airport.dest_icao (
strorNone) – ICAO codes. If None, taken fromflight.attrs, else the nearest airport.eef (
xarray.DataArrayorpycontrails.MetDataArrayorNone) – Effective energy forcing per meter, if supplied separately frommet.altitude_ft (
numpy.ndarrayorNone) – Candidate flight levels in feet, used only withmet. If None, the eastbound/westbound defaults fromcruise_flight_levels()are used. Withfl_profilethe levels come from itsaltitude_ftcoordinate.cost_index (
float, default60.0) – Fuel-vs-time tradeoff in kg per minute.dollar_tonne_co2e (
float, default0.0) – Carbon price per tonne CO2-equivalent. If positive, the met source must carryeef_per_m.dollar_kg_fuel (
float, default1.0) – Fuel price per kg, converting carbon cost into fuel-equivalent units.allow_cooling_credit (
bool, defaultFalse) – If True, negative EEF reduces cost whendollar_tonne_co2eis set.use_flown_climb_descent (
bool, defaultFalse) – If True, the flown initial climb and final descent below the lowest candidate flight level are taken fromflightunchanged, and only the cruise phase above it is optimized.
- property eef_cost_factor: float¶
Compute the kg-fuel-equivalent cost per J of effective energy forcing.
- property kind: str¶
Return the optimization variant.
Returns
"track"for a fixed-path vertical-profile 2d optimizer (built viafrom_flight()), or"dag"for the full 4d lateral-plus-vertical DAG optimizer.
- solve(n_iter: int = 3, cost_index: float | None = None, dollar_tonne_co2e: float | None = None, aircraft_type: str | None = None, payload: float | None = None, allow_cooling_credit: bool | None = None, step_penalty_kg: float | None = None) DAGResult[source]¶
Solve the trajectory optimization via shortest-path dynamic programming on the DAG.
This method iteratively re-solves the DAG to converge on takeoff mass.
Estimate trip fuel from great-circle distance and set initial takeoff mass as
landing mass + estimated trip fuel, capped at MTOWSolve the dynamic program to find the optimal path and trip fuel
Update takeoff mass as
landing mass + trip fuel, capped at MTOWStop after the takeoff mass estimate converges or after
n_iteriterations
- Parameters:
n_iter (
int, default3) – Maximum number of mass-convergence iterations. Each iteration re-solves the full DP.cost_index (
floatorNone, defaultNone) – If provided, updatesself.cost_indexbefore solving. This parameter is safe to vary between calls without rebuilding intermediate artifacts.dollar_tonne_co2e (
floatorNone, defaultNone) – If provided, updatesself.dollar_tonne_co2ebefore solving. Safe to vary between calls without rebuilding intermediate artifacts.aircraft_type (
strorNone, defaultNone) – If provided, updatesself.aircraft_type,self.atyp, andself.mach_choicesbefore solving. Safe to vary between calls without rebuilding the DAG or met lookup provided the met lookup was built with a sufficiently longflight_hourswindow to accommodate the new aircraft’s speed.payload (
floatorNone, defaultNone) – Aircraft payload in kg if known. If None, this is estimated with pycontrails.allow_cooling_credit (
boolorNone, defaultNone) – If provided, updatesself.allow_cooling_creditbefore solving.
- Returns:
DP state, takeoff mass, trip fuel, payload, reserve fuel, and landing mass.
- Return type:
- reconstruct_path() tuple[NDArray[int64], NDArray[int64], NDArray[float32]][source]¶
Trace backpointers from destination to origin to recover the optimal path.
The returned arrays are ordered origin-first.
Geographic coordinates for each waypoint are available via
self.dag.lon[path_h]andself.dag.lat[path_h]. Flight levels areself.fl_choices[path_fl_idx]for interior waypoints; the origin uses a sentinel index (len(fl_choices)) representing ground level.- Returns:
path_h (
numpy.ndarray) – Horizontal node indices along the path. The first and last entries are origin and destination.path_fl_idx (
numpy.ndarray) – Flight level index at each node. The origin uses a sentinelground_fl_idx = len(fl_choices); the destination uses the actual cruise FL from which the final descent begins.path_mach (
numpy.ndarray) – Cruise Mach number on each incoming leg. The first entrypath_mach[0]is NaN (no incoming leg at the origin).
- to_flight() Flight[source]¶
Return the optimal trajectory as a pycontrails.Flight.
When met data is available, waypoints are emitted at each edge sample point (~20 km spacing) with proper climb/descent altitude profiles and per-sample
eef_per_m. Without met, falls back to one waypoint per DAG node.The
solve()method must be called first.
- plot_met(altitude_ft: float | None = None, time: Timestamp | None = None, ax: TypeAliasForwardRef('cartopy.mpl.geoaxes.GeoAxes') | None = None, show_wind_quiver: bool = True, show_eef: bool = True, **kwargs) cartopy.mpl.geoaxes.GeoAxes[source]¶
Plot met data on DAG nodes for a given flight level and time.
Draws a wind quiver overlay. When
eef_per_mis available in the met lookup, also draws a scatter plot colored by EEF.- Parameters:
altitude_ft (
floatorNone) – Flight level in feet (e.g.37000). Snaps to the nearest available level. If None, uses the first available level.time (
pandas.TimestamporNone) – Time to select. Snaps to the nearest available time step. If None, uses the first available time step.ax (
cartopy.mpl.geoaxes.GeoAxesorNone) – Cartopy GeoAxes to plot on. If None, callsself.dag.plot()to create one.**kwargs – Passed to
ax.quiver.
- Returns:
The axes with the met overlay.
- Return type:
- animate_solve(display_fl_idx: int | None = None, ax: TypeAliasForwardRef('cartopy.mpl.geoaxes.GeoAxes') | None = None) matplotlib.animation.FuncAnimation[source]¶
Re-run the DP with converged mass and return a wavefront animation.
solve()must be called first. This re-runs a singlesolve_dagpass with the convergedamass_init, capturing wavefront snapshots. Nodes are colored bybest_cost[:, display_fl_idx]for a single FL. Edges whose source node has been processed are shown in blue; remaining edges are shown muted. The optimal path is then revealed dest to origin, matching the backpointer reconstruction order.- Parameters:
display_fl_idx (
int | None) – FL index intofl_choicesto display costs for. If None, uses the FL the optimal path spends the most legs at.ax (
GeoAxes | None) – Cartopy GeoAxes to draw on. If None, a new figure is created.
- Returns:
Wavefront animation with cost coloring and optimal path reveal.
- Return type: