HN 日本語サマリー

← 一覧へ戻る
科学・技術

ジェット燃料の節約

Saving Jet Fuel (tech.marksblogg.com)

119 pointsby marklit59 コメント

要約

この記事は、オープンソースのフレームワークであるScikit-decideと航空機性能モデルであるOpenAPを活用し、風の条件や航空機の性能データに基づいて最適な飛行経路を計算することで、ジェット燃料の消費を削減する方法を解説しています。必要なソフトウェアのインストール手順や、OpenAPが提供する様々な航空機モデルのデータ構造についても詳細に説明しています。

全文翻訳

ジェット燃料の節約

ニューアーク・リバティー国際空港(EWR)からレオナルド・ダ・ヴィンチ=フィウミチーノ空港(FCO)までのノンストップフライトで、ボーイング787-9ドリームライナーは8.5時間の飛行で68,000ドルのジェット燃料を必要とする可能性があります。風の条件に合わせて飛行経路を調整することで、燃料消費を削減し、数千ドルを節約できる可能性があります。

Jeppesenのような企業もこの分野でサービスを提供していますが、Scikit-decideは、デルフト工科大学の教授が構築したナローボディおよびワイドボディの燃料消費モデルとNOAAの風データと組み合わせることで、オープンソースのソリューションを提供します。

Scikit-decideは6年間開発が進められています。これは、強化学習、自動計画、スケジューリングのためのフレームワークです。このプロジェクトは、飛行経路の最適化、航空会社の労働力スケジュールの再編成、ドローンの群れの経路計算を行うことができます。

OpenAPは、Junzi Sun博士によって開発された航空機性能モデルおよびツールキットです。Sun博士は航空交通管理の博士号を持ち、オランダのTU Delftで准教授としてこの分野のコースを教えています。

Scikit-decideの最適な飛行経路ソルバーは、異なる燃料消費モデルを使用するように設定できます。この記事では、Airbus A320とOpenAPの燃料消費モデルを使用して飛行した2つの飛行経路を比較します。

私のワークステーション

私は5.7 GHzのAMD Ryzen 9 9950X CPUを使用しています。16コア32スレッド、1.2 MBのL1、16 MBのL2、64 MBのL3キャッシュを備えています。液体クーラーが取り付けられており、広々としたフルサイズのCooler Master HAF 700コンピューターケースに収められています。

システムは4,800 MT/sでクロックされた96 GBのDDR5 RAMと、最大12,400 MB/sの読み取り速度を持つ第5世代Crucial T700 4 TB NVMe M.2 SSDを搭載しています。SSDにはヒートシンクがあり、温度を下げるのに役立ちます。これが私のシステムのCドライブです。

システムは1,200ワットのフルモジュラーCorsair電源によって電力を供給されており、ASRock X870E Nova 90マザーボード上に配置されています。

Windows 11 Pro上のUbuntu for Windowsを介してUbuntu 24 LTSを実行しています。なぜ私のプライマリワーク環境としてLinuxベースのデスクトップを実行しないのか疑問に思っているかもしれませんが、私はまだNvidia GTX 1080 GPUを使用しており、Windowsの方がドライバーのサポートが優れており、ArcGIS ProはネイティブでWindowsのみをサポートしているためです。

前提条件のインストール

この記事では、Python 3.12とjqを使用します。

$ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt update
$ sudo apt install \
    jq \
    python3-pip \
    python3.12-venv

Python仮想環境をセットアップし、scikit-decide、OpenAPオープン航空機パフォーマンスモデル、およびSun博士が開発したフライト軌道ツールキットであるOpenTopをインストールします。

$ python3 -m venv ~/.flight_planning
$ source ~/.flight_planning/bin/activate
$ pip install \
    'scikit-decide[all]' \
    'openap[all]' \
    opentop

上記は少なくとも8 GBのストレージ容量が必要です。インストールされたパッケージは以下の通りです。

$ pip install pipdeptree
$ pipdeptree -d0
lz4==4.4.5
openevolve==0.3.2
opentop==2.6.0
pip==24.0
pipdeptree==4.2.5
plado==0.1.6
pygeodesy==26.9.9
pygrib==2.1.8
pyRDDLGym-gurobi==0.2
pyRDDLGym-jax==3.1
pyRDDLGym-rl==0.2
pytz==2026.3.post1
ray==2.37.0
rddlrepository==2.2
sb3_contrib==2.3.0
scikit-decide==1.1.1
scikit-image==0.26.0
tensorboardX==2.6.5
torch-geometric==2.8.0.post1
typer==0.27.2
unified-planning==1.2.0
up-enhsp==0.0.27
up_fast_downward==0.5.2
up-pyperplan==1.1.0
z3-solver==5.1.0.0

この記事では、DuckDBとそのH3、JSON、Lindel、Parquet、Spatial拡張機能を使用します。

$ cd ~
$ wget -c https://github.com/duckdb/duckdb/releases/download/v1.5.4/duckdb_cli-linux-amd64.zip
$ unzip -j duckdb_cli-linux-amd64.zip
$ chmod +x duckdb
$ ~/duckdb
INSTALL h3 FROM community;
INSTALL lindel FROM community;
INSTALL json;
INSTALL parquet;
INSTALL spatial;

DuckDBを起動するたびにすべてのインストール済み拡張機能をロードするように設定します。

$ vi ~/.dudkbrc
.timer on
.width 180
LOAD h3;
LOAD lindel;
LOAD json;
LOAD parquet;
LOAD spatial;

この記事の地図はQGISバージョン4.2.1でレンダリングされました。QGISはWindows、macOS、Linuxで動作するデスクトップアプリケーションです。このアプリケーションは近年人気が高まっており、世界中のユーザーから毎月約2200万回のアプリケーション起動があります。

境界線と地名はNatural Earthから、海洋境界線はMarine Regionsから取得しました。

OpenAPの航空機タイプ

まずOpenAPリポジトリをクローンします。

$ git clone https://github.com/junzis/openap

単体テストとユーティリティスクリプトを除くと、このパッケージには3,369行のPythonコードが含まれています。

OpenAPのモデルは、コードベースにバンドルされた多数のデータセットに依存しています。これらは多種多様な航空機をカバーしています。以下は航空機メーカーのカウントです。

$ grep -ho 'aircraft: .*[a-z] '
    openap/data/aircraft/*.yml \
    | cut -d' ' -f2 \
    | sort \
    | uniq -c \
    | sort -rn
17 Boeing
13 Airbus
 5 Embraer
 1 Gulfstream
 1 Cessna

これらはAirbus A380-800のプロパティです。

$ cat openap/data/aircraft/a388.yml
aircraft: Airbus A380-800

mtow: 560000
mlw: 386000
oew: 277000
mfc: 320000
vmo: 340
mmo: 0.89
ceiling: 13100

pax:
  max: 853
  low: 410
  high: 620

fuselage:
  length: 72.72
  height: 8.41
  width: 7.14

wing:
  area: 845
  span: 79.75
  mac: null
  sweep: 33.5
  t/c: 0.08

flaps:
  type: single-slotted
  area: null
  bf/b: null
  lambda_f: 0.900
  cf/c: 0.150
  Sf/S: 0.150

cruise:
  height: 12800
  mach: 0.85
  range: 14800

engine:
  type: turbofan
  mount: wing
  number: 4
  default: GP7270
  options:
    A380-841: Trent 970-84
    A380-842: Trent 972-84
    A380-861: GP7270

drag:
  cd0: 0.016
  k: 0.050
  e: 0.855
  gears: 0.012

これらはその抗力係数です。

$ cat openap/data/dragpolar/a388.yml
aircraft: Airbus A380-800

clean:
  cd0:         0.016
  k:           0.050
  e:           0.855

gears:         0.012

flaps:
  lambda_f:    0.900
  cf/c:        0.150
  Sf/S:        0.150

これらは追加のプロパティです。

$ echo "import pandas as pd; print(
            pd.read_fwf('openap/data/wrap/a388.txt')
              .to_csv(index=False))" \
    | python3 \
    | ~/duckdb \
        -c '.maxwidth 150'
        -c "SELECT * EXCLUDE(parameters),
                   parameters: SPLIT(parameters, '|')
            FROM   READ_CSV('/dev/stdin')"
┌──────────────────────┬────────────────┬───────────────────────────────────────┬────────┬────────┬─────────┬─────────┬──────────────────────────────┐
│       variable       │  flight phase  │                 name                  │  opt   │  min   │   max   │  model  │          parameters          │
│       varchar        │    varchar     │                varchar                │ double │ double │ double  │ varchar │          varchar[]           │
├──────────────────────┼────────────────┼───────────────────────────────────────┼────────┼────────┼─────────┼─────────┼──────────────────────────────┤
│ to_v_lof             │ takeoff        │ Liftoff speed                         │   89.9 │   75.4 │   104.4 │ norm    │ [89.93, 10.07]               │
│ to_d_tof             │ takeoff        │ Takeoff distance                      │   2.56 │   1.35 │    3.78 │ norm    │ [2.56, 0.74]                 │
│ to_acc_tof           │ takeoff        │ Mean takeoff accelaration             │   1.35 │   1.04 │    1.66 │ norm    │ [1.35, 0.19]                 │
│ ic_va_avg            │ initial_climb  │ Mean airspeed                         │   88.0 │   80.0 │    96.0 │ norm    │ [88.15, 5.64]                │
│ ic_vs_avg            │ initial_climb  │ Mean vertical rate                    │   5.65 │    4.4 │    8.94 │ gamma   │ [4.76, 3.22, 0.65]           │
│ cl_d_range           │ climb          │ Climb range                           │  296.0 │  200.0 │   446.0 │ beta    │ [3.23, 5.18, 179.46, 335.24] │
│ cl_v_cas_const       │ climb          │ Constant CAS                          │  163.0 │  155.0 │   170.0 │ norm    │ [163.39, 4.51]               │
│ cl_v_mach_const      │ climb          │ Constant Mach                         │   0.84 │    0.8 │    0.86 │ beta    │ [12.23, 5.32, 0.72, 0.17]    │
│ cl_h_cas_const       │ climb          │ Constant CAS crossover altitude       │    3.3 │    1.3 │     5.3 │ norm    │ [3.29, 1.24]                 │
│ cl_h_mach_const      │ climb          │ Constant Mach crossover altitude      │    8.9 │    8.2 │     9.7 │ norm    │ [8.94, 0.47]                 │
│ cl_vs_avg_pre_cas    │ climb          │ Mean climb rate, pre-constant-CAS     │   7.85 │   5.95 │    9.75 │ norm    │ [7.85, 1.16]                 │
│ cl_vs_avg_cas_const  │ climb          │ Mean climb rate, constant-CAS         │   7.51 │    5.2 │    9.82 │ norm    │ [7.51, 1.40]                 │
│ cl_vs_avg_mach_const │ climb          │ Mean climb rate, constant-Mach        │   5.56 │   3.23 │    7.91 │ norm    │ [5.57, 1.42]                 │
│ cr_d_range           │ cruise         │ Cruise range                          │ 4348.0 │  892.0 │ 20565.0 │ gamma   │ [2.81, 246.73, 2274.81]      │
│ cr_v_cas_mean        │ cruise         │ Mean cruise CAS                       │  136.0 │  130.0 │   145.0 │ beta    │ [3.32, 5.27, 126.00, 29.75]  │
│ cr_v_cas_max         │ cruise         │ Maximum cruise CAS                    │  145.0 │  134.0 │   164.0 │ beta    │ [2.02, 3.21, 130.38, 46.65]  │
│ cr_v_mach_mean       │ cruise         │ Mean cruise Mach                      │   0.84 │   0.82 │    0.86 │ norm    │ [0.84, 0.01]                 │
│ cr_v_mach_max        │ cruise         │ Maximum cruise Mach                   │   0.87 │   0.85 │     0.9 │ gamma   │ [16.14, 0.80, 0.00]          │
│ cr_h_init            │ cruise         │ Initial cruise altitude               │  11.55 │    9.3 │   12.23 │ beta    │ [3.82, 1.66, 7.49, 5.01]     │
│ cr_h_mean            │ cruise         │ Mean cruise altitude                  │  11.73 │  10.87 │   12.28 │ beta    │ [7.22, 3.92, 9.59, 3.14]     │
│ cr_h_max             │ cruise         │ Maximum cruise altitude               │  12.06 │  11.52 │    12.6 │ norm    │ [12.06, 0.33]                │
│ de_d_range           │ descent        │ Descent range                         │  310.0 │  238.0 │   528.0 │ gamma   │ [4.73, 213.47, 25.87]        │
│ de_v_mach_const      │ descent        │ Constant Mach                         │   0.83 │    0.8 │    0.87 │ norm    │ [0.83, 0.02]                 │
│ de_v_cas_const       │ descent        │ Constant CAS                          │  154.0 │  142.0 │   167.0 │ norm    │ [154.84, 7.74]               │
│ de_h_mach_const      │ descent        │ Constant Mach crossover altitude      │   10.1 │    8.6 │    11.5 │ norm    │ [10.06, 0.88]                │
│ de_h_cas_const       │ descent        │ Constant CAS crossover altitude       │    6.6 │    3.9 │     9.4 │ norm    │ [6.64, 1.69]                 │
│ de_vs_avg_mach_const │ descent        │ Mean descent rate, constant-Mach      │  -6.06 │  -11.9 │   -2.97 │ beta    │ [3.43, 2.08, -15.98, 14.36]  │
│ de_vs_avg_cas_const  │ descent        │ Mean descent rate, constant-CAS       │  -8.36 │ -11.74 │   -4.97 │ norm    │ [-8.36, 2.06]                │
│ de_vs_avg_after_cas  │ descent        │ Mean descent rate, after-constant-CAS │  -5.48 │  -6.93 │   -4.02 │ norm    │ [-5.48, 0.88]                │
│ fa_va_avg            │ final_approach │ Mean airspeed                         │   73.0 │   68.0 │    77.0 │ norm    │ [73.28, 3.02]                │
│ fa_vs_avg            │ final_approach │ Mean vertical rate                    │  -3.71 │  -4.13 │   -2.92 │ gamma   │ [9.49, -4.74, 0.12]          │
│ fa_agl               │ final_approach │ Approach angle                        │    2.9 │   2.42 │    3.38 │ norm    │ [2.90, 0.29]                 │
│ ld_v_app             │ landing        │ Touchdown speed                       │   70.0 │   62.1 │    78.0 │ norm    │ [70.00, 5.52]                │
│ ld_d_brk             │ landing        │ Braking distance                      │   2.26 │   0.73 │     3.8 │ norm    │ [2.26, 0.93]                 │
│ ld_acc_brk           │ landing        │ Mean braking acceleration             │  -1.01 │  -1.51 │   -0.52 │ norm    │ [-1.01, 0.30]                │
└──────────────────────┴────────────────┴───────────────────────────────────────┴────────┴────────┴─────────┴─────────┴──────────────────────────────┘

これらは航空機タイプ名の別名リストです。

$ ~/duckdb -c "FROM READ_CSV('/dev/stdin')"
    < openap/data/aircraft/_synonym.csv
┌─────────┬─────────┐
│  orig   │   new   │
│ varchar │ varchar │
├─────────┼─────────┤
│ a124    │ b744    │
│ a306    │ a332    │
│ a310    │ a318    │
│ at72    │ e145    │
│ at75    │ e145    │
│ at76    │ e145    │
│ b733    │ b734    │
│ b735    │ b734    │
│ b762    │ b763    │
│ b77l    │ b77w    │
│ c25a    │ c550    │
│ c525    │ c550    │
│ c56x    │ c550    │
│ crj2    │ e145    │
│ crj9    │ e75l    │
│ e290    │ e190    │
│ glf5    │ glf6    │
│ gl5t    │ glf6    │
│ lj45    │ glf6    │
│ md11    │ b773    │
│ pc24    │ c550    │
│ su95    │ e170    │
└─────────┴─────────┘

航空機エンジン

航空機はしばしば少なくとも2つの異なるエンジンから選択するオプションを持っています。このパッケージのデータセットには427基のエンジンがリストされています。

$ wc -l openap/data/engine/engines.csv # 427

これらはTrent 970-84の詳細です。

$ echo "FROM  'openap/data/engine/engines.csv'
        WHERE name = 'Trent 970-84'
        LIMIT 1" \
    | ~/duckdb -json \
    | jq -S .
[
  {
    "bpr": 8.45,
    "cruise_alt": null,
    "cruise_mach": null,
    "cruise_sfc": null,
    "cruise_thrust": null,
    "ei_co_app": 1.16,
    "ei_co_co": 0.31,
    "ei_co_idl": 13.38,
    "ei_co_to": 0.32,
    "ei_hc_app": 0.08,
    "ei_hc_co": 0.12,
    "ei_hc_idl": 0.04,
    "ei_hc_to": 0.02,
    "ei_nox_app": 12.09,
    "ei_nox_co": 29.42,
    "ei_nox_idl": 5.44,
    "ei_nox_to": 38.29,
    "ff_app": 0.72,
    "ff_co": 2.157,
    "ff_idl": 0.255,
    "ff_to": 2.605,
    "fuel_lto": 965.0,
    "manufacturer": "Rolls-Royce plc",
    "max_thrust": 338700.0,
    "name": "Trent 970-84",
    "pr": 38.0,
    "type": "TF",
    "uid": "18RR081"
  }
]

これらはエンジンメーカーのカウントです。

$ ~/duckdb
CREATE OR REPLACE TABLE a AS
    FROM 'openap/data/engine/engines.csv';

SELECT   COUNT(*),
         manufacturer
FROM     a
GROUP BY 2
ORDER BY 1 DESC;
┌──────────────┬────────────────────────────┐
│ count_star() │        manufacturer        │
│    int64     │          varchar           │
├──────────────┼────────────────────────────┤
│          108 │ GE Aircraft Engines        │
│           94 │ CFM International          │
│           85 │ Pratt & Whitney            │
│           62 │ Rolls-Royce plc            │
│           13 │ International Aero Engines │
│           12 │ Pratt & Whitney Canada     │
│           11 │ Rolls-Royce Corporation    │
│            8 │ Rolls-Royce Deutschland    │
│            8 │ Honeywell                  │
│            7 │ Aviadvigatel               │
│            5 │ Textron Lycoming           │
│            4 │ KKBM                       │
│            3 │ IVCHENKO PROGRESS ZMBK     │
│            2 │ PowerJet S.A.              │
│            2 │ Allied Signal              │
│            1 │ Engine Alliance            │
│            1 │ Garret AiResearch          │
└──────────────┴────────────────────────────┘

これらは、このデータセットにおけるターボファン(TF)、混合流ターボファン(MTF)、ターボプロップ(TP)、ピストン(PS)エンジンのタイプ別カウントです。

SELECT   COUNT(*),
         type
FROM     a
GROUP BY 2
ORDER BY 1 DESC;
┌──────────────┬─────────┐
│ count_star() │  type   │
│    int64     │ varchar │
├──────────────┼─────────┤
│          322 │ TF      │
│           98 │ MTF     │
│            5 │ TP      │
│            1 │ PS      │
└──────────────┴─────────┘

これは、最大推力でランク付けされたエンジンのリストです。

SELECT   manufacturer,
         name,
         type,
         max_thrust
FROM     a
ORDER BY 4 DESC
LIMIT    25;
┌─────────────────────┬───────────────┬─────────┬────────────┐
│    manufacturer     │     name      │  type   │ max_thrust │
│       varchar       │    varchar    │ varchar │   double   │
├─────────────────────┼───────────────┼─────────┼────────────┤
│ GE Aircraft Engines │ GE90-115B     │ TF      │   513900.0 │
│ GE Aircraft Engines │ GE90-113B     │ TF      │   504900.0 │
│ GE Aircraft Engines │ GE90-110B1    │ TF      │   492600.0 │
│ Rolls-Royce plc     │ Trent XWB-97  │ TF      │   436748.0 │
│ GE Aircraft Engines │ GE90-94B      │ TF      │   430920.0 │
│ GE Aircraft Engines │ GE90-92B      │ TF      │   426720.0 │
│ GE Aircraft Engines │ GE90-90B      │ TF      │   419250.0 │
│ Rolls-Royce plc     │ Trent 895     │ TF      │   413050.0 │
│ Rolls-Royce plc     │ Trent 892     │ TF      │   411480.0 │
│ Pratt & Whitney     │ PW4090        │ TF      │   408300.0 │
│ GE Aircraft Engines │ GE90-85B      │ TF      │   397210.0 │
│ Rolls-Royce plc     │ Trent 884     │ TF      │   390100.0 │
│ Pratt & Whitney     │ PW4084D       │ TF      │   385900.0 │
│ Rolls-Royce plc     │ Trent XWB-84  │ TF      │   379000.0 │
│ Pratt & Whitney     │ PW4084        │ TF      │   369600.0 │
│ GE Aircraft Engines │ GE90-77B      │ TF      │   366750.0 │
│ Rolls-Royce plc     │ Trent 1000-R3 │ TF      │   363900.0 │
│ GE Aircraft Engines │ GE90-76B      │ TF      │   363220.0 │
│ Rolls-Royce plc     │ Trent 877     │ TF      │   361640.0 │
│ Rolls-Royce plc     │ Trent 1000-M3 │ TF      │   358100.0 │
│ Rolls-Royce plc     │ Trent 1000-N3 │ TF      │   358100.0 │
│ Pratt & Whitney     │ PW4077D       │ TF      │   355700.0 │
│ Rolls-Royce plc     │ Trent XWB-79B │ TF      │   355200.0 │
│ Rolls-Royce plc     │ Trent XWB-79  │ TF      │   355200.0 │
│ Rolls-Royce plc     │ Trent 970B-84 │ TF      │   352900.0 │
└─────────────────────┴───────────────┴─────────┴────────────┘

これらは燃料モデルのデフォルト値とオーバーライドです。

$ ~/duckdb -c "FROM READ_CSV('/dev/stdin')"
    < openap/data/fuel/fuel_models.csv
┌──────────┬─────────────┬────────────────────┬────────────────────┬────────────────────┐
│ typecode │ engine_type │         c1         │         c2         │         c3         │
│ varchar  │   varchar   │       double       │       double       │       double       │
├──────────┼─────────────┼────────────────────┼────────────────────┼────────────────────┤
│ A318     │ CFM56-5B9/3 │ 0.7769784596099123 │  1.765377288174942 │ 2.5349134936316693 │
│ A319     │ V2524-A5    │ 0.8694169413032631 │ 1.9542690629047836 │ 2.5028187026860103 │
│ A320     │ CFM56-5B4/P │ 1.0453208160586924 │ 2.3633720747416573 │ 1.2378127479131922 │
│ A321     │ V2533-A5    │ 1.3979999999999444 │  2.054028451829268 │ 1.0008941993511127 │
│ A332     │ Trent 772   │  2.886430057340283 │ 1.0960397632560752 │ 2.3772585567580293 │
│ A333     │ Trent 772   │ 3.1199999999999997 │ 1.0365152289922772 │  1.950599421257047 │
│ B737     │ CFM56-7B26  │ 1.0237419750954273 │ 1.4670109921175798 │ 3.2566140275646456 │
│ B738     │ CFM56-7B26E │  1.075484518912494 │ 1.8777303165419037 │ 1.8895522140156369 │
│ B739     │ CFM56-7B27E │ 1.3079999999999998 │ 1.5986016771932572 │ 1.2789091908108752 │
│ CRJ9     │ CF34-8C5    │ 0.6437136288905128 │ 1.9690234662778772 │ 1.4375859706162741 │
│ E170     │ CF34-8E5    │ 0.6341784688704629 │  2.778729428440142 │ 1.0149695061665696 │
│ E190     │ CF34-10E5   │ 0.8339999999998783 │ 2.3343013671118475 │ 0.4847704716061958 │
│ E195     │ CF34-10E5A1 │  0.911999999999993 │  1.929664699695295 │ 0.8452746256489131 │
│ E75L     │ CF34-8E5    │ 0.6340709359225759 │  2.614653287356019 │ 0.8714282723568036 │
│ default  │ default     │  0.937564901246902 │ 1.9767611682280135 │ 1.3954794843472482 │
└──────────┴─────────────┴────────────────────┴────────────────────┴────────────────────┘

## 空港とナビゲーション

このパッケージには約14,000の空港の場所とコードが含まれています。

$ wc -l openap/data/nav/airports.csv # 13796

$ ~/duckdb -c "FROM READ_CSV('/dev/stdin')
WHERE country = 'CA'
ORDER BY lat
LIMIT 20"
< openap/data/nav/airports.csv

┌─────────┬──────────┬───────────┬───────┬─────────┬───────────────────────────────┬────────────────┐
│ icao │ lat │ lon │ alt │ country │ name │ location │
│ varchar │ double │ double │ int64 │ varchar │ varchar │ varchar │
├─────────┼──────────┼───────────┼───────┼─────────┼───────────────────────────────┼────────────────┤
│ CYQG │ 42.27334 │ -82.97056 │ 622 │ CA │ Windsor │ Windsor │
│ CYQS │ 42.77202 │ -81.11923 │ 778 │ CA │ St Thomas Muni │ St. Thomas │
│ CYZR │ 43.00444 │ -82.31528 │ 594 │ CA │ Sarnia - Chris Hadfield │ Sarnia │
│ CYXU │ 43.04211 │ -81.1598 │ 912 │ CA │ London │ London │
│ CYFD │ 43.12389 │ -80.34667 │ 815 │ CA │ Brantford │ Brant │
│ CYHM │ 43.18056 │ -79.95306 │ 780 │ CA │ John C Munro Hamilton Intl │ Ancaster │
│ CYSN │ 43.18792 │ -79.1786 │ 321 │ CA │ Niagara District │ St. Catharines │
│ CYCE │ 43.28306 │ -81.51806 │ 824 │ CA │ Huron Airpark │ South Huron │
│ CYSA │ 43.41087 │ -80.93994 │ 1215 │ CA │ Stratford Municipal │ Stratford │
│ CZBA │ 43.445 │ -79.85472 │ 602 │ CA │ Burlington Airpark │ Burlington │
│ CYKF │ 43.45694 │ -80.39056 │ 1054 │ CA │ Waterloo │ Cambridge │
│ CYTZ │ 43.62747 │ -79.40336 │ 251 │ CA │ Toronto City Centre │ Toronto │
│ CYYZ │ 43.66073 │ -79.62394 │ 568 │ CA │ Toronto Lester B Pearson Intl │ Etobicoke │
│ CYZD │ 43.74972 │ -79.47417 │ 652 │ CA │ Downsview │ Concord │
│ CYGD │ 43.77111 │ -81.71639 │ 712 │ CA │ Goderich │ Goderich │
│ CYQI │ 43.8175 │ -66.0975 │ 141 │ CA │ Yarmouth │ Yarmouth │
│ CYKZ │ 43.86444 │ -79.37334 │ 650 │ CA │ Buttonville Muni │ Richmond Hill │
│ CYOO │ 43.92444 │ -78.90389 │ 459 │ CA │ Oshawa │ Oshawa │
│ CYTR │ 44.10889 │ -77.54222 │ 283 │ CA │ Trenton │ Quinte West │
│ CYGK │ 44.21833 │ -76.60083 │ 305 │ CA │ Kingston │ Kingston │
└─────────┴──────────┴───────────┴───────┴─────────┴───────────────────────────────┴────────────────┘


これらの空港は236の異なる国にまたがっています。

$ ~/duckdb

SELECT COUNT(DISTINCT country)
FROM READ_CSV('openap/data/nav/airports.csv');

236


これらは空港データセットで最も多く代表されている国です。

SELECT COUNT(*),
country
FROM READ_CSV('openap/data/nav/airports.csv')
GROUP BY 2
ORDER BY 1 DESC
LIMIT 20;

┌──────────────┬─────────┐
│ count_star() │ country │
│ int64 │ varchar │
├──────────────┼─────────┤
│ 2849 │ BR │
│ 2459 │ US │
│ 2062 │ AU │
│ 441 │ FR │
│ 345 │ CA │
│ 325 │ DE │
│ 258 │ GB │
│ 234 │ ID │
│ 176 │ NA │
│ 168 │ VE │
│ 156 │ RU │
│ 148 │ IN │
│ 143 │ AR │
│ 139 │ SE │
│ 126 │ JP │
│ 118 │ IT │
│ 106 │ NZ │
│ 99 │ CZ │
│ 98 │ BO │
│ 97 │ ZA │
└──────────────┴─────────┘


これらはナビゲーションウェイポイントの一部です。

$ echo "import pandas as pd; print(
pd.read_fwf('openap/data/nav/fix.dat',
skiprows=3,
header=None,
encoding='unicode_escape')
.to_csv(index=False))"
| python3
| ~/duckdb
-c '.maxwidth 150'
-c "FROM READ_CSV('/dev/stdin')
WHERE column0 BETWEEN 57 AND 59
AND column1 BETWEEN 21 AND 27
LIMIT 20"

┌──────────┬───────────┬─────────┐
│ column0 │ column1 │ column2 │
│ double │ double │ varchar │
├──────────┼───────────┼─────────┤
│ 57.133196 │ 23.888414 │ ALISA │
│ 57.105833 │ 25.254167 │ AMOLI │
│ 58.416389 │ 24.478333 │ ANAMA │
│ 58.412778 │ 22.521667 │ EIKLA │
│ 58.506944 │ 25.715278 │ EKLON │
│ 58.626667 │ 21.766111 │ EVERI │
│ 57.278611 │ 25.050556 │ GEKLI │
│ 58.9425 │ 25.576944 │ GONOS │
│ 58.053333 │ 26.762778 │ KANEP │
│ 58.331944 │ 22.221111 │ KARLA │
│ 58.7225 │ 24.586944 │ KEMET │
│ 58.725753 │ 26.736943 │ KOLEV │
│ 58.708056 │ 22.845833 │ KUKET │
│ 58.931111 │ 24.661944 │ KUNUX │
│ 58.176667 │ 26.93 │ KUUST │
│ 58.441667 │ 26.451667 │ LAEVA │
│ 58.553333 │ 25.934444 │ LALSI │
│ 57.336944 │ 22.636944 │ LAPSA │
│ 57.774167 │ 22.104444 │ LATEG │
│ 58.180278 │ 25.779167 │ LATKA │
└───────────┴───────────┴─────────┘


これらはナビゲーションエイドの一部です。

$ wc -l openap/data/nav/nav.dat # 26775

$ echo "import pandas as pd; print(
pd.read_fwf('openap/data/nav/nav.dat',
skiprows=3,
header=None,
encoding='unicode_escape')
.to_csv(index=False))"
| python3
| ~/duckdb
-c '.maxwidth 150'
-c "SELECT * EXCLUDE(column9)
FROM READ_CSV('/dev/stdin')
WHERE column1 BETWEEN 57 AND 59
AND column2 BETWEEN 21 AND 27
LIMIT 20"

┌─────────┬───────────┬───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬────────────────────┐
│ column0 │ column1 │ column2 │ column3 │ column4 │ column5 │ column6 │ column7 │ column8 │
│ int64 │ double │ double │ varchar │ double │ double │ double │ varchar │ varchar │
├─────────┼───────────┼───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────────────────┤
│ 2 │ 58.957117 │ 22.872158 │ 0 │ 317.0 │ 80.0 │ 0.0 │ OZ │ KARDLA NDB │
│ 2 │ 58.270722 │ 22.508778 │ 0 │ 350.0 │ 80.0 │ 0.0 │ WA │ KURESSAARE NDB │
│ 2 │ 58.490806 │ 24.571556 │ 0 │ 425.0 │ 80.0 │ 0.0 │ RC │ PARNU NDB │
│ 2 │ 58.435833 │ 24.495861 │ 0 │ 376.0 │ 25.0 │ 0.0 │ R │ PARNU NDB │
│ 2 │ 58.308583 │ 26.768417 │ 0 │ 397.0 │ 80.0 │ 0.0 │ UM │ TARTU NDB │
│ 3 │ 58.228333 │ 22.515361 │ 39 │ 240.0 │ 50.0 │ 3.0 │ KRS │ KURESSAARE VOR-DME │
│ 3 │ 58.416583 │ 24.465972 │ 58 │ 590.0 │ 25.0 │ 6.0 │ PRN │ PARNU VOR-DME │
│ 3 │ 57.366944 │ 21.556222 │ 0 │ 360.0 │ 130.0 │ 5.3 │ VNT │ VENTSPILS VOR-DME │
│ 3 │ 58.655889 │ 25.574778 │ 227 │ 490.0 │ 80.0 │ 5.0 │ VI │ VOHMA VOR-DME │
│ 1 │ 58.228333 │ 22.515361 │ 3 │ 124.0 │ 5.0 │ 0.0 │ KR │ KURESSAARE VOR-DME │
│ 1 │ 58.416583 │ 24.465972 │ 5 │ 159.0 │ 2.0 │ 0.0 │ PR │ PARNU VOR-DME │
│ 1 │ 57.366944 │ 21.556222 │ NULL │ 136.0 │ 13.0 │ 0.0 │ VN │ VENTSPILS VOR-DME │
│ 1 │ 58.655889 │ 25.574778 │ 22 │ 149.0 │ 8.0 │ 0.0 │ VI │ VOHMA VOR-DME │
│ 1 │ 58.992083 │ 22.830972 │ 3 │ 176.0 │ 2.0 │ 0.0 │ KR │ KARDLA DME │
└─────────┴───────────┴───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴────────────────────┘

トゥールーズからベルリンへ

以下で、トゥールーズ・ブラニャック空港(LFBO / TLS)からベルリン・ブランデンブルク空港(EDDB / BER)までの最適な飛行経路を見つけます。

$ python3
import numpy as np
from   openap.aero import cas2mach, ft, kts
from   openap.extra.nav import airport
from   pygeodesy.ellipsoidalVincenty import LatLon

from skdecide.hub.domain\
        .flight_planning\
        .aircraft_performance\
        .bean.aircraft_state \
    import AircraftState

from skdecide.hub.domain\
        .flight_planning\
        .aircraft_performance\
        .performance.performance_model_enum \
    import PerformanceModelEnum

from skdecide.hub.domain\
        .flight_planning\
        .aircraft_performance\
        .performance.phase_enum \
    import PhaseEnum

from skdecide.hub.domain\
        .flight_planning\
        .aircraft_performance\
        .performance.rating_enum \
    import RatingEnum

from skdecide.hub.domain\
        .flight_planning\
        .domain \
    import FlightPlanningDomain, \
           WeatherDate

from skdecide.hub.domain\
        .flight_planning\
        .flightplanning_utils \
    import plot_network_adapted

from skdecide.hub.solver.astar import Astar

ヒューリスティックパラメータは、「time」、「distance」、「lazy_fuel」、「lazy_time」、またはNoneのいずれかです。何も渡されない場合、A*はDijkstraライクな探索アルゴリズムを使用します。

origin        = "LFPG"
destination   = "LFBO"
aircraft      = "A320"
weather_date  = WeatherDate(day=1, month=5, year=2026)
heuristic     = "lazy_fuel"
cost_function = "fuel"

acState = AircraftState(
    model_type="A320",
    performance_model_type=PerformanceModelEnum.OPENAP,
    gw_kg=80_000,
    zp_ft=10_000,
    mach=cas2mach(250 * kts, h=10_000 * ft),
    phase=PhaseEnum.CLIMB,
    rating_level=RatingEnum.MCL,
    cg=0.3)

domain_factory = lambda: FlightPlanningDomain(
    aircraft_state=acState,
    mach_cruise=0.78,
    mach_climb=0.7,
    mach_descent=0.65,
    nb_forward_points=20,
    nb_lateral_points=10,
    nb_climb_descent_steps=5,
    flight_levels_ft=list(np.arange(30_000, 38_000 + 2_000, 2_000)),
    graph_width="medium",
    origin=LatLon(43.629444, 1.363056),
    destination="EDDB",
    objective=cost_function,
    heuristic_name=heuristic,
    weather_date=weather_date)

domain = domain_factory()

上記が実行されると、天気データがNOAAから取得されておらず、フライトの日付が過去6ヶ月以内である場合、GRB2ファイルがダウンロードされます。

$ du -hs ~/skdecide_data/weather/grib/nowcast/*/*.grb2
144M    /home/mark/skdecide_data/weather/grib/nowcast/20260501/gfs_4_20260501_0000_000.grb2
144M    /home/mark/skdecide_data/weather/grib/nowcast/20260501/gfs_4_20260501_0600_000.grb2
143M    /home/mark/skdecide_data/weather/grib/nowcast/20260501/gfs_4_20260501_1200_000.grb2
143M    /home/mark/skdecide_data/weather/grib/nowcast/20260501/gfs_4_20260501_1800_000.grb2

各ファイルは地球全体をカバーするデータを含んでいます。これらは、QGISでグローブ上にレンダリングされたgfs_4_20260501_1800_000.grb2の内容です。

[画像: Flight Planning]

これはソルバーの高度と地理的な探索空間です。

plot_network_adapted(
    graph=domain.network,
    p0=LatLon(43.629444, 1.363056),
    p1=LatLon(
        airport("EDDB")["lat"],
        airport("EDDB")["lon"],
        airport("EDDB")["alt"] * ft))

[画像: Flight Planning]

これはソルバーによる最適な飛行経路です。

solver = Astar(
            domain_factory=domain_factory,
            heuristic=lambda d, s: d.heuristic(s),
            parallel=False)

solver.solve()
A* finished to solve from state ... in 0.28 seconds
domain.custom_rollout(solver=solver, make_img=True)

[画像: Flight Planning]

Goal reached after 19 steps!
({'time': 7666.281474928903, 'fuel': 5855.093906205222}, None)

飛行計画の各ステップを、より読みやすいようにフォーマットします。

domain.observation.trajectory.to_csv('TLS-BER.csv', index=None)
$ ~/duckdb
SELECT   phase: UPPER(phase), 
         time_: ts::INT,
         alt:   alt::INT,
         mass:  mass::INT,
         mach:  ROUND(mach, 2),
         cas:   cas::INT,
         fuel:  fuel::INT,
         geom:  ST_POINT(lon, lat)
FROM     'TLS-BER.csv'
ORDER BY ts;
┌─────────┬───────┬───────┬───────┬────────┬───────┬───────┬────────────────────────────────────────────────┐
│  phase  │ time_ │  alt  │ mass  │  mach  │  cas  │ fuel  │                      geom                      │
│ varchar │ int32 │ int32 │ int32 │ double │ int32 │ int32 │                    geometry                    │
├─────────┼───────┼───────┼───────┼────────┼───────┼───────┼────────────────────────────────────────────────┤
│ CLIMB   │ 28800 │     0 │ 80000 │   0.45 │   154 │     0 │ POINT (1.363056 43.629444)                     │
│ CLIMB   │ 29183 │ 12000 │ 79402 │    0.7 │   194 │   598 │ POINT (1.3614644301412264 44.431571122861556)  │
│ CLIMB   │ 29767 │ 18000 │ 78717 │    0.7 │   173 │   685 │ POINT (0.8028803270337778 45.54329961845038)   │
│ CLIMB   │ 30367 │ 24000 │ 78104 │    0.7 │   154 │     2 │ POINT (0.22308186850312028 46.64860136322122)  │
│ CLIMB   │ 30369 │ 24000 │ 78102 │    0.7 │   154 │     2 │ POINT (0.2213600223765711 46.65181397470445)   │
│ CLIMB   │ 30691 │ 30000 │ 77808 │    0.7 │   135 │   294 │ POINT (0.781910701674247 47.146798229437145)   │
│ CRUISE  │ 31222 │ 30000 │ 77331 │   0.78 │   152 │   477 │ POINT (0.18057649919536045 48.254448220756515) │
│ CRUISE  │ 31515 │ 30000 │ 77070 │   0.78 │   152 │   262 │ POINT (0.7571242215277763 48.74964861278412)   │
│ CRUISE  │ 31805 │ 30000 │ 76812 │   0.78 │   152 │   258 │ POINT (1.3446668642885302 49.24219441171162)   │
│ CRUISE  │ 32093 │ 30000 │ 76555 │   0.78 │   152 │   256 │ POINT (1.943586016571517 49.732000774167815)   │
│ CRUISE  │ 32382 │ 30000 │ 76298 │   0.78 │   152 │   257 │ POINT (2.554285524033502 50.218985517421046)   │
│ CRUISE  │ 32672 │ 30000 │ 76041 │   0.78 │   152 │   257 │ POINT (3.1771982015706532 50.70307279893911)   │
│ CRUISE  │ 32961 │ 30000 │ 75786 │   0.78 │   152 │   256 │ POINT (3.812797488711077 51.18419969012497)    │
│ CRUISE  │ 33252 │ 30000 │ 75529 │   0.78 │   152 │   257 │ POINT (4.461619033936127 51.66232853895605)    │
│ CRUISE  │ 33547 │ 32000 │ 75270 │   0.78 │   146 │   259 │ POINT (5.1243038676232 52.13747186609129)      │
│ CRUISE  │ 34103 │ 30000 │ 74794 │   0.78 │   152 │   476 │ POINT (7.006628090474678 51.93831668841654)    │
│ DESCENT │ 34458 │ 24031 │ 74503 │   0.65 │   142 │   290 │ POINT (7.7011825929472675 52.40044729715865)   │
│ DESCENT │ 35058 │ 18063 │ 73995 │   0.65 │   160 │    51 │ POINT (9.423199233939213 52.1821111144618)     │
│ DESCENT │ 35114 │ 18063 │ 73944 │   0.65 │   160 │    51 │ POINT (9.580613666598857 52.160789987133924)   │
│ DESCENT │ 35714 │ 12094 │ 73388 │   0.65 │   179 │    13 │ POINT (11.398384342445423 51.895274115132494)  │
│ DESCENT │ 35727 │ 12094 │ 73375 │   0.65 │   179 │    13 │ POINT (11.435080901553494 51.88959204464927)   │
│ DESCENT │ 36063 │  6126 │ 73023 │   0.65 │   199 │   352 │ POINT (12.17769156482302 52.32776873134525)    │
│ DESCENT │ 36466 │    48 │ 72534 │   0.65 │   221 │   488 │ POINT (13.48503 52.36769)                      │
└─────────┴───────┴───────┴───────┴────────┴───────┴───────┴────────────────────────────────────────────────┘

飛行計画をParquetにエクスポートし、地上レベルの風データの上にQGISでレンダリングします。

COPY (
    SELECT   * EXCLUDE(lon, lat), 
             geometry: ST_POINT(lon, lat)
    FROM     'TLS-BER.csv'
    ORDER BY ts
) TO 'TLS-BER.parquet' (
      FORMAT 'PARQUET',
      CODEC  'ZSTD',
      COMPRESSION_LEVEL 22,
      ROW_GROUP_SIZE 15000);

[画像: Flight Planning]

トゥールーズからワルシャワへ

以下で、トゥールーズ・ブラニャック空港(LFBO / TLS)からワルシャワ・ショパン空港(EPWA / WAW)までの最適な飛行経路を見つけます。

初期目標高度は、前の例よりもはるかに高くなります。その結果、はるかに直接的なルートを取ることができるフライトになります。

acState = AircraftState(
    model_type="A320",
    performance_model_type=PerformanceModelEnum.OPENAP,
    gw_kg=80_000,
    zp_ft=18000.0,
    mach=cas2mach(250 * kts, h=10_000 * ft),
    phase=PhaseEnum.CLIMB,
    rating_level=RatingEnum.MCL,
    cg=0.3,
    x_graph=5,
    y_graph=5,
    z_graph=10)

domain_factory = lambda: FlightPlanningDomain(
    aircraft_state=acState,
    mach_cruise=0.78,
    mach_climb=0.7,
    mach_descent=0.65,
    nb_forward_points=20,
    nb_lateral_points=10,
    nb_climb_descent_steps=5,
    flight_levels_ft=list(np.arange(30_000, 38_000 + 2_000, 2_000)),
    graph_width="medium",
    origin=LatLon(43.629444, 1.363056),
    destination="EPWA",
    objective=cost_function,
    heuristic_name=heuristic,
    weather_date=weather_date)

domain = domain_factory()

solver = Astar(
            domain_factory=domain_factory,
            heuristic=lambda d, s: d.heuristic(s),
            parallel=False)

solver.solve()
A* finished to solve from state ... in 29.45 seconds.
domain.custom_rollout(solver=solver, make_img=True)

[画像: Flight Planning]

Goal reached after 14 steps!
({'time': 6153.660431613251, 'fuel': 5600.171145693044}, None)

ワルシャワはベルリンよりもトゥールーズから500km遠いですが、与えられた風条件下での巡航高度へのより速い上昇により、航空機はより直接的なルートを取ることができました。ワルシャワには約45分早く到着し、ベルリン便が必要とした燃料の76%しか必要としませんでした。

これらは上記の飛行計画のステップです。

domain.observation.trajectory.to_csv('TLS-WAW.csv', index=None)
$ ~/duckdb
SELECT   phase: UPPER(phase),
         time_: ts::INT,
         alt:   alt::INT,
         mass:  mass::INT,
         mach:  ROUND(mach, 2),
         cas:   cas::INT,
         fuel:  fuel::INT,
         geom:  ST_POINT(lon, lat)
FROM     'TLS-WAW.csv'
ORDER BY ts;
┌─────────┬───────┬───────┬───────┬────────┬───────┬───────┬───────────────────────────────────────────────┐
│  phase  │ time_ │  alt  │ mass  │  mach  │  cas  │ fuel  │                     geom                      │
│ varchar │ int32 │ int32 │ int32 │ double │ int32 │ int32 │                   geometry                    │
├─────────┼───────┼───────┼───────┼────────┼───────┼───────┼───────────────────────────────────────────────┤
│ CLIMB   │ 28800 │ 30000 │ 80000 │   0.45 │    85 │     0 │ POINT (6.321780309765473 45.78957852956533)   │
│ CRUISE  │ 29196 │ 32000 │ 79639 │   0.78 │   146 │   361 │ POINT (7.274006698354081 46.275553205520794)  │
│ CRUISE  │ 29604 │ 34000 │ 79276 │   0.78 │   139 │   363 │ POINT (8.243146568075773 46.75332543362598)   │
│ CRUISE  │ 30018 │ 36000 │ 78915 │   0.78 │   133 │   361 │ POINT (9.229481862337197 47.22261290149568)   │
│ CRUISE  │ 30436 │ 38000 │ 78555 │   0.78 │   127 │   360 │ POINT (10.23327610333648 47.68312559281413)   │
│ CRUISE  │ 30861 │ 38000 │ 78191 │   0.78 │   127 │   365 │ POINT (11.25477083484032 48.13456566890824)   │
│ CRUISE  │ 31297 │ 36000 │ 77819 │   0.78 │   133 │   371 │ POINT (12.29418112454121 48.57662718213212)   │
│ CRUISE  │ 31732 │ 34000 │ 77449 │   0.78 │   139 │   370 │ POINT (13.351689351409924 49.00899540355703)  │
│ CRUISE  │ 32161 │ 32000 │ 77082 │   0.78 │   146 │   367 │ POINT (14.427435465032865 49.431345252175966) │
│ CRUISE  │ 32589 │ 30000 │ 76710 │   0.78 │   152 │   372 │ POINT (15.521498988740307 49.843337488922174) │
│ DESCENT │ 33101 │ 24066 │ 76280 │   0.65 │   142 │   429 │ POINT (16.633858546975723 50.2446086742223)   │
│ DESCENT │ 33589 │ 18131 │ 75861 │   0.65 │   160 │   420 │ POINT (17.764276668345886 50.63474030548783)  │
│ DESCENT │ 34046 │ 12197 │ 75433 │   0.65 │   179 │   428 │ POINT (18.91184798374883 51.01313503252613)   │
│ DESCENT │ 34472 │  6262 │ 74984 │   0.65 │   199 │   449 │ POINT (20.071843186874247 51.378167839584854) │
│ DESCENT │ 34954 │   100 │ 74400 │   0.65 │   221 │   584 │ POINT (20.94663 52.17147)                     │
└─────────┴───────┴───────┴───────┴────────┴───────┴───────┴───────────────────────────────────────────────┘

## Airbus A320 vs Boeing 737

OpenTopはOpenAPと組み合わせて、空港間の飛行軌道を計算するために使用できます。

そのオプティマイザーはグリッドコストファイルを必要とします。まず、プロジェクトが提供する例の142 MBのNetCDFファイルをダウンロードします。

$ wget https://opendap.4tu.nl/thredds/fileServer/data2/djht/bea8a3fe-e34c-4598-9f94-c5a5c63348e5/1/contrail_original.nc


コストファイルはCasadiまたはParquet形式のいずれかです。私はそのドキュメントの[例](https://openap.dev/optimize/contrails.html)から作業し、246 KBのCasadiファイルが生成されました。

import openap
import pandas as pd
from scipy.ndimage import gaussian_filter
from opentop.tools import cached_interpolant_from_dataframe
import xarray as xr

ds = xr.open_dataset('contrail_original.nc')
.sel(time='2015-12-18')

level_pressure = [
0.0000,
10.0000,
30.0000,
50.0000,
70.0000,
90.0787,
110.6606,
132.3968,
155.7909,
181.1544,
208.6494,
238.3258,
270.1530,
304.0465,
339.8891,
377.5467,
416.8789,
457.7442,
500.0000,
543.4970,
588.0685,
633.5144,
679.5799,
725.9285,
772.1102,
817.5241,
861.3757,
902.6287,
939.9520,
971.6610,
995.6532,
1009.3396]

df = (
ds.to_dataframe()
.reset_index()
.assign(lev=lambda x: x.lev.astype(int))
.merge(
pd.DataFrame(level_pressure, columns=["hPa"]).reset_index(names="lev"),
on="lev",
)
.assign(height=lambda x: openap.aero.h_isa(x.hPa * 100).round(-2))
.assign(longitude=lambda x: ((x.lon + 180) % 360 - 180))
.query("height<15000"))

df_cost_world = df.rename(
columns={
"lat": "latitude",
"atr20_contrail": "cost",
}
)[["time",
"latitude",
"longitude",
"hPa",
"height",
"cost"]]

df_cost = df_cost_world.query(
"-20<longitude<40 and 30<latitude<70 and time.dt.hour==12"
).sort_values(["height", "latitude", "longitude"])

cost = df_cost.cost.values.reshape(
df_cost.height.nunique(),
df_cost.latitude.nunique(),
df_cost.longitude.nunique())

cost_ = gaussian_filter(cost, sigma=1, mode="nearest")
df_cost = df_cost.assign(cost=cost_.flatten())

interpolant = cached_interpolant_from_dataframe(
df_cost,
"contrail.casadi",
shape="bspline")


これらはその内容の最初と最後の数バイトです。

$ hexdump -C contrail.casadi | head

00000000 6a 68 70 6e 6e 61 67 69 69 65 61 68 61 61 61 61 |jhpnnagiieahaaaa|
00000010 64 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 |daaaaaaaaaaaaaaa|
00000020 61 61 66 61 65 67 61 61 6c 61 61 61 61 61 61 61 |aafaegaalaaaaaaa|
00000030 6a 65 6f 67 65 68 66 67 63 68 61 68 70 67 6d 67 |jeogehfgchahpgmg|
00000040 62 67 6f 67 65 68 68 61 61 61 61 61 61 61 63 67 |bgogehhaaaaaaacg|
00000050 64 68 61 68 6d 67 6a 67 6f 67 66 67 63 61 61 61 |dhahmgjgogfgcaaa|
00000060 61 61 61 61 6a 61 61 61 61 61 61 61 68 67 63 68 |aaaajaaaaaaahgch|
00000070 6a 67 65 67 70 66 64 67 70 67 64 68 65 68 61 61 |jgegpfdgpgdhehaa|
00000080 61 61 61 61 61 61 62 61 69 61 61 61 61 61 61 61 |aaaaaabaiaaaaaaa|
00000090 62 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 |baaaaaaaaaaaaaaa|

$ hexdump -C contrail.casadi | tail

0003d620 61 61 61 61 61 61 64 62 61 61 61 61 61 61 61 61 |aaaaaadbaaaaaaaa|
0003d630 61 61 61 61 61 61 67 62 61 61 61 61 61 61 61 61 |aaaaaagbaaaaaaaa|
0003d640 61 61 61 61 61 61 68 62 61 61 61 61 61 61 61 61 |aaaaaahbaaaaaaaa|
0003d650 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 |aaaaaaaaaaaaaaaa|
0003d660 61 61 61 61 61 61 62 61 61 61 61 61 61 61 61 61 |aaaaaabaaaaaaaaa|
0003d670 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 |aaaaaaaaaaaaaaaa|
0003d680 61 61 61 61 61 61 62 61 61 61 62 61 61 61 61 61 |aaaaaabaaabaaaaa|
0003d690 61 61 61 61 61 61 61 61 61 61 63 68 67 61 61 61 |aaaaaaaaaachgaaa|
0003d6a0 61 61 61 61 61 61 61 61 61 61 61 61 |aaaaaaaaaaaa|
0003d6ac


内容は繰り返しが多く、よく圧縮されることに気づきました。

$ gzip -9 < contrail.casadi | wc -c

90949


アムステルダム・スキポール空港(EHAM / AMS)とフランクフルト(EDDF / FRA)間のエアバスA320での最適なフライトの指標を取得します。

$ opentop optimize
EHAM EDDF
-a A320
--phase all
--obj "0.3fuel+0.7grid"
--grid contrail.casadi

aircraft: A320
route: EHAM → EDDF
phase: all
objective: 0.3fuel+0.7grid
m0: 0.85
max_iter: 1500
grid file: contrail.casadi

success: True
return_status: Solve_Succeeded
iter_count: 179
wall time: 12.2 s
objective: 4.8768e+02
fuel burn: 1625.6 kg
max altitude: 19891 ft
flight time: 35.8 min


次に、同様のことをボーイング737で行います。

$ opentop optimize
EHAM EDDF
-a B737
--phase all
--obj "0.3fuel+0.7grid"
--grid contrail.casadi

aircraft: B737
route: EHAM → EDDF
phase: all
objective: 0.3fuel+0.7grid
m0: 0.85
max_iter: 1500
grid file: contrail.casadi

success: True
return_status: Solve_Succeeded
iter_count: 141
wall time: 9.9 s
objective: 4.8662e+02
fuel burn: 1622.1 kg
max altitude: 21968 ft
flight time: 39.2 min


この記事をお読みいただきありがとうございます。北米とヨーロッパのクライアント向けにコンサルティングおよび実務開発サービスを提供しています。私のサービスがビジネスにどのように役立つかについてご相談したい場合は、[LinkedIn](https://uk.linkedin.com/in/marklitwintschik/)からご連絡ください。