大阪市立科学館学芸員 江越航のホームページ  大阪市立科学館 学芸員

 江越 航のホームページ

Python で日食計算 (Skyfield版)

(→ PyEphem を使った計算方法はこちら

Skyfield を用いると、各地域で日食が起こる時刻を計算することができます。
(なお、日食が起こる概略の日時は既知のものとします。)

参考にしたページ:
Eclipse Calculations using Python
GitHub - Various scripts to calculate eclipses

必要モジュールをインポートします。

from skyfield.api import Topos, load
from pytz import timezone
import numpy as np

Timescale オブジェクトを読み込み、計算する日時を設定します。日時は、世界時で指定します。
時刻は配列で指定することが可能です。以下、時刻を指定した計算値は、配列で出力されます。
日食の継続時間は概ね3時間程度のため、ここでは11000秒分、計算します。
また、タイムゾーンを日本に設定します。

ts = load.timescale()
t = ts.utc(2020, 6, 21, 7, 0, range(0, 11000))
tz = timezone('Asia/Tokyo')

天体暦のファイルを読み込み、太陽、月、地球のオブジェクト(ここではsun, moon, earth)を作成します。

eph = load('de421.bsp')
sun, moon, earth = eph['sun'], eph['moon'], eph['earth']

観測者の位置を指定する Topos オブジェクトを設定します。

osaka = earth + Topos('34.6914 N', '135.4917 E')

大阪から見た太陽と月の位置を計算します。
時刻に対応して、結果は配列で出力されます。

sun_app = osaka.at(t).observe(sun).apparent()
moon_app = osaka.at(t).observe(moon).apparent()

太陽・月の半径、距離(ここではkm単位)から、見かけの大きさを計算します。
結果は配列で出力されます。

r_sun = 696000
sun_dist = sun_app.distance().km
sun_rad = np.arctan2(r_sun, sun_dist)

r_moon = 1737
moon_dist = moon_app.distance().km
moon_rad = np.arctan2(r_moon, moon_dist)

次に太陽と月の角距離を計算します。角距離は、太陽・月それぞれの見かけの位置をもとに、separation_from メソッドを使うことで、簡単に計算できます。
結果は配列で出力されます。

app_sep = sun_app.separation_from(moon_app).radians

食分を計算します。結果は配列で出力されます。
食分がプラス((r_sun + r_moon) > s ・・・太陽と月の半径の和よりも、太陽と月の角距離が小さい)なら、太陽が欠けています。

日食での太陽の食分の計算方法
percent_eclipse = (sun_rad + moon_rad - app_sep) / (sun_rad * 2)

食分が最大となる、配列のインデックスを探します。

max_i = np.argmax(percent_eclipse)

食分が最大となる時刻と食分値を出力します。

print('食の最大:', t[max_i].astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), 'JST')
print('最大食分: {0:.2f}'.format(percent_eclipse[max_i]))

欠け始めと食の終わりの時刻を探します。
最初に食分が0以上になった時刻が欠け始め(第一接触)、再び食分が0になった時刻が食の終わり(第四接触)です。

eclipse = False

for ti, pi in zip(t, percent_eclipse):
    if pi > 0 :
        if eclipse == False:
            print('欠け始め:', ti.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), 'JST')
            eclipse = True
    else :
        if eclipse == True:
            print('食の終わり:', ti.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), 'JST')
            eclipse = False

計算結果

食の最大: 2020-06-21 17:10:19 JST
最大食分: 0.54
欠け始め: 2020-06-21 16:06:24 JST
食の終わり: 2020-06-21 18:07:39 JST

全体は次のようになります。

from skyfield.api import Topos, load
from pytz import timezone
import numpy as np

# 初期時刻設定
ts = load.timescale()
t = ts.utc(2020, 6, 21, 7, 0, range(0, 11000))
tz = timezone('Asia/Tokyo')

# 太陽・月・地球
eph = load('de421.bsp')
sun, moon, earth = eph['sun'], eph['moon'], eph['earth']

# 観測地設定
osaka = earth + Topos('34.6914 N', '135.4917 E')

# 太陽・月の位置計算
sun_app = osaka.at(t).observe(sun).apparent()
moon_app = osaka.at(t).observe(moon).apparent()

# 太陽・月の見かけの大きさ計算
r_sun = 696000
sun_dist = sun_app.distance().km
sun_rad = np.arctan2(r_sun, sun_dist)

r_moon = 1737
moon_dist = moon_app.distance().km
moon_rad = np.arctan2(r_moon, moon_dist)

# 太陽・月の角距離の計算
app_sep = sun_app.separation_from(moon_app).radians

# 食分の計算
percent_eclipse = (sun_rad + moon_rad - app_sep) / (sun_rad * 2)
    
# 食の最大の検索	
max_i = np.argmax(percent_eclipse)
    
print('食の最大:', t[max_i].astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), 'JST')
print('最大食分: {0:.2f}'.format(percent_eclipse[max_i]))

# 欠け始めと食の終わりの検索
eclipse = False

for ti, pi in zip(t, percent_eclipse):
    if pi > 0 :
        if eclipse == False:
            print('欠け始め:', ti.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), 'JST')
            eclipse = True
    else :
        if eclipse == True:
            print('食の終わり:', ti.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S'), 'JST')
            eclipse = False