pyqihou 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyqihou-0.1.0/PKG-INFO +45 -0
- pyqihou-0.1.0/README.md +28 -0
- pyqihou-0.1.0/pyproject.toml +28 -0
- pyqihou-0.1.0/pyqihou/__init__.py +0 -0
- pyqihou-0.1.0/pyqihou/energy.py +497 -0
- pyqihou-0.1.0/pyqihou/statistic.py +0 -0
- pyqihou-0.1.0/pyqihou/water.py +260 -0
- pyqihou-0.1.0/pyqihou.egg-info/PKG-INFO +45 -0
- pyqihou-0.1.0/pyqihou.egg-info/SOURCES.txt +11 -0
- pyqihou-0.1.0/pyqihou.egg-info/dependency_links.txt +1 -0
- pyqihou-0.1.0/pyqihou.egg-info/requires.txt +4 -0
- pyqihou-0.1.0/pyqihou.egg-info/top_level.txt +1 -0
- pyqihou-0.1.0/setup.cfg +4 -0
pyqihou-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyqihou
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python Package for Climate Analysis
|
|
5
|
+
Author-email: VVVictorZhou <ahme02147483647a@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/VVVICTORZHOU/
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: numpy>=1.19.5
|
|
14
|
+
Requires-Dist: scipy>=1.5.4
|
|
15
|
+
Requires-Dist: windspharm>=1.7.0
|
|
16
|
+
Requires-Dist: xarray>=0.16.2
|
|
17
|
+
|
|
18
|
+
## How to install the `pyqihou` package:
|
|
19
|
+
|
|
20
|
+
- Method 1: `npm install pyqihou`
|
|
21
|
+
|
|
22
|
+
- Method 2: `pip install pyqihou`
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Functions in the `pyqihou` package:
|
|
26
|
+
### `water.py`
|
|
27
|
+
|
|
28
|
+
- `MassIntegrationAlongPressure`: 沿著垂直氣壓層執行大氣變數的質量積分。
|
|
29
|
+
- `HorizontalMoistureAdvection`: 計算水平水氣平流的熱力學與動力學分解項,並進行氣壓層垂直積分。
|
|
30
|
+
- `VerticalMoistureAdvection`: 計算垂直水氣平流的熱力學與動力學分解項,並進行氣壓層垂直積分。
|
|
31
|
+
- `RelativeHumidity`: 依據溫度、比濕與氣壓計算大氣相對濕度百分比。
|
|
32
|
+
- `VaporPressureDeficit`: 依據溫度、比濕與氣壓計算水氣壓差 (VPD)。
|
|
33
|
+
|
|
34
|
+
### `energy.py`
|
|
35
|
+
|
|
36
|
+
- `MixedLayerHeatBudget`: 計算混合層熱平衡的各項輻射、熱通量與物理分解項。
|
|
37
|
+
- `NetEnergyInput`: 計算大氣與地表的淨能量輸入 (NEI)。
|
|
38
|
+
- `EnergyFluxPotential`: 計算能量通量位勢 (EFP) 並自動處理標準網格內插與還原。
|
|
39
|
+
- `MeridionalMassStreamFunction`: 計算經向質量經流函數 (MSF)。
|
|
40
|
+
- `ZonalMassStreamFunction`: 計算緯向質量經流函數 (MSF)。
|
|
41
|
+
- `HorizontalTN01WaveActivityFlux`: 計算幾何校正版的 Takaya-Nakamura (2001) 水平波活動通量與其散度。
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
pyqihou-0.1.0/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## How to install the `pyqihou` package:
|
|
2
|
+
|
|
3
|
+
- Method 1: `npm install pyqihou`
|
|
4
|
+
|
|
5
|
+
- Method 2: `pip install pyqihou`
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Functions in the `pyqihou` package:
|
|
9
|
+
### `water.py`
|
|
10
|
+
|
|
11
|
+
- `MassIntegrationAlongPressure`: 沿著垂直氣壓層執行大氣變數的質量積分。
|
|
12
|
+
- `HorizontalMoistureAdvection`: 計算水平水氣平流的熱力學與動力學分解項,並進行氣壓層垂直積分。
|
|
13
|
+
- `VerticalMoistureAdvection`: 計算垂直水氣平流的熱力學與動力學分解項,並進行氣壓層垂直積分。
|
|
14
|
+
- `RelativeHumidity`: 依據溫度、比濕與氣壓計算大氣相對濕度百分比。
|
|
15
|
+
- `VaporPressureDeficit`: 依據溫度、比濕與氣壓計算水氣壓差 (VPD)。
|
|
16
|
+
|
|
17
|
+
### `energy.py`
|
|
18
|
+
|
|
19
|
+
- `MixedLayerHeatBudget`: 計算混合層熱平衡的各項輻射、熱通量與物理分解項。
|
|
20
|
+
- `NetEnergyInput`: 計算大氣與地表的淨能量輸入 (NEI)。
|
|
21
|
+
- `EnergyFluxPotential`: 計算能量通量位勢 (EFP) 並自動處理標準網格內插與還原。
|
|
22
|
+
- `MeridionalMassStreamFunction`: 計算經向質量經流函數 (MSF)。
|
|
23
|
+
- `ZonalMassStreamFunction`: 計算緯向質量經流函數 (MSF)。
|
|
24
|
+
- `HorizontalTN01WaveActivityFlux`: 計算幾何校正版的 Takaya-Nakamura (2001) 水平波活動通量與其散度。
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyqihou"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python Package for Climate Analysis"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "VVVictorZhou", email = "ahme02147483647a@gmail.com" }
|
|
12
|
+
]
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
requires-python = ">=3.6"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"numpy>=1.19.5",
|
|
22
|
+
"scipy>=1.5.4",
|
|
23
|
+
"windspharm>=1.7.0",
|
|
24
|
+
"xarray>=0.16.2"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/VVVICTORZHOU/"
|
|
File without changes
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import xarray as xr
|
|
3
|
+
from windspharm.tools import prep_data, recover_data
|
|
4
|
+
from windspharm.xarray import VectorWind
|
|
5
|
+
import spharm.spharm
|
|
6
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
7
|
+
|
|
8
|
+
def MixedLayerHeatBudget(EXP_TS, EXP_FSNS, EXP_FSNSC, EXP_FLNS, EXP_SHFLX, EXP_LHFLX, EXP_U10, EXP_RHREFHT, EXP_TREFHT,
|
|
9
|
+
CTL_TS, CTL_LHFLX, CTL_U10, CTL_RHREFHT, CTL_TREFHT):
|
|
10
|
+
"""
|
|
11
|
+
計算混合層熱平衡(Mixed Layer Heat Budget)各項通量與分解項。
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
EXP_TS : np.ndarray 或 xr.DataArray
|
|
16
|
+
實驗組地表溫度 (Surface Temperature),單位:K,維度形狀:(time, lat, lon) 或通用網格。
|
|
17
|
+
EXP_FSNS : np.ndarray 或 xr.DataArray
|
|
18
|
+
實驗組表面淨短波輻射 (Surface Net Shortwave Radiation),單位:W/m^2,維度形狀同上。
|
|
19
|
+
EXP_FSNSC : np.ndarray 或 xr.DataArray
|
|
20
|
+
實驗組表面無雲淨短波輻射 (Clear-sky Surface Net Shortwave Radiation),單位:W/m^2,維度形狀同上。
|
|
21
|
+
EXP_FLNS : np.ndarray 或 xr.DataArray
|
|
22
|
+
實驗組表面淨長波輻射 (Surface Net Longwave Radiation),單位:W/m^2,維度形狀同上。
|
|
23
|
+
EXP_SHFLX : np.ndarray 或 xr.DataArray
|
|
24
|
+
實驗組表面感熱通量 (Surface Sensible Heat Flux),單位:W/m^2,維度形狀同上。
|
|
25
|
+
EXP_LHFLX : np.ndarray 或 xr.DataArray
|
|
26
|
+
實驗組表面潛熱通量 (Surface Latent Heat Flux),單位:W/m^2,維度形狀同上。
|
|
27
|
+
EXP_U10 : np.ndarray 或 xr.DataArray
|
|
28
|
+
實驗組 10 公尺風速 (10-meter Wind Speed),單位:m/s,維度形狀同上。
|
|
29
|
+
EXP_RHREFHT : np.ndarray 或 xr.DataArray
|
|
30
|
+
實驗組參考高度相對濕度 (Relative Humidity at Reference Height),無因次 (0-1),維度形狀同上。
|
|
31
|
+
EXP_TREFHT : np.ndarray 或 xr.DataArray
|
|
32
|
+
實驗組參考高度溫度 (Temperature at Reference Height),單位:K,維度形狀同上。
|
|
33
|
+
CTL_TS : np.ndarray 或 xr.DataArray
|
|
34
|
+
對照組地表溫度,單位:K,維度形狀同上。
|
|
35
|
+
CTL_LHFLX : np.ndarray 或 xr.DataArray
|
|
36
|
+
對照組表面潛熱通量,單位:W/m^2,維度形狀同上。
|
|
37
|
+
CTL_U10 : np.ndarray 或 xr.DataArray
|
|
38
|
+
對照組 10 公尺風速,單位:m/s,維度形狀同上。
|
|
39
|
+
CTL_RHREFHT : np.ndarray 或 xr.DataArray
|
|
40
|
+
對照組參考高度相對濕度,無因次 (0-1),維度形狀同上。
|
|
41
|
+
CTL_TREFHT : np.ndarray 或 xr.DataArray
|
|
42
|
+
對照組參考高度溫度,單位:K,維度形狀同上。
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
tuple of np.ndarray
|
|
47
|
+
包含以下各項的計算結果陣列(維度形狀與輸入相同):
|
|
48
|
+
- TS_TERM : 地表溫度項
|
|
49
|
+
- FSNS_TERM : 淨短波輻射項
|
|
50
|
+
- FSNS_CLR_TERM : 無雲淨短波輻射項
|
|
51
|
+
- FSNS_CLD_TERM : 雲對短波輻射效應項
|
|
52
|
+
- FLNS_TERM : 淨長波輻射項
|
|
53
|
+
- SHFLX_TERM : 感熱通量項
|
|
54
|
+
- LHFLX_TERM : 潛熱通量項
|
|
55
|
+
- LHFLX_WIND_TERM : 潛熱通量風速分解項(若缺少參數則為 None)
|
|
56
|
+
- LHFLX_RH_TERM : 潛熱通量相對濕度分解項(若缺少參數則為 None)
|
|
57
|
+
- LHFLX_TGRAD_TERM : 潛熱通量溫度梯度分解項(若缺少參數則為 None)
|
|
58
|
+
- Residual_TERM : 殘差項
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
Alpha = 0.06
|
|
62
|
+
|
|
63
|
+
# Left Hand Side
|
|
64
|
+
TS_TERM = EXP_TS
|
|
65
|
+
|
|
66
|
+
EXP_DeltaT = EXP_TREFHT - EXP_TS
|
|
67
|
+
CTL_DeltaT = CTL_TREFHT - CTL_TS
|
|
68
|
+
|
|
69
|
+
# Right Hand Side
|
|
70
|
+
FSNS_TERM = EXP_FSNS / (Alpha * CTL_LHFLX)
|
|
71
|
+
FSNS_CLR_TERM = EXP_FSNSC / (Alpha * CTL_LHFLX)
|
|
72
|
+
FSNS_CLD_TERM = FSNS_TERM - FSNS_CLR_TERM
|
|
73
|
+
FLNS_TERM = - EXP_FLNS / (Alpha * CTL_LHFLX)
|
|
74
|
+
SHFLX_TERM = - EXP_SHFLX / (Alpha * CTL_LHFLX)
|
|
75
|
+
LHFLX_TERM = - EXP_LHFLX / (Alpha * CTL_LHFLX)
|
|
76
|
+
|
|
77
|
+
# Residual Term
|
|
78
|
+
Residual_TERM = TS_TERM - (FSNS_TERM + FLNS_TERM + SHFLX_TERM + LHFLX_TERM)
|
|
79
|
+
|
|
80
|
+
# Latent Heat Term Decomposition
|
|
81
|
+
if EXP_RHREFHT is not None and EXP_U10 is not None and EXP_DeltaT is not None:
|
|
82
|
+
LHFLX_WIND_TERM = - (CTL_LHFLX * EXP_U10) / (CTL_U10 * Alpha * CTL_LHFLX)
|
|
83
|
+
LHFLX_RH_TERM = (CTL_LHFLX * EXP_RHREFHT) / (Alpha * CTL_LHFLX * (np.exp(- Alpha * CTL_DeltaT) - CTL_RHREFHT))
|
|
84
|
+
LHFLX_TGRAD_TERM = (Alpha * CTL_LHFLX * CTL_RHREFHT * EXP_DeltaT) / (Alpha * CTL_LHFLX * (np.exp(- Alpha * CTL_DeltaT) - CTL_RHREFHT))
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
LHFLX_WIND_TERM = None
|
|
88
|
+
LHFLX_RH_TERM = None
|
|
89
|
+
LHFLX_TGRAD_TERM = None
|
|
90
|
+
|
|
91
|
+
return TS_TERM, FSNS_TERM, FSNS_CLR_TERM, FSNS_CLD_TERM, FLNS_TERM, SHFLX_TERM, LHFLX_TERM, LHFLX_WIND_TERM, LHFLX_RH_TERM, LHFLX_TGRAD_TERM, Residual_TERM
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def NetEnergyInput(FSNT, FLNT, FSNS, FLNS, LHFLX, SHFLX):
|
|
95
|
+
"""
|
|
96
|
+
計算大氣與地表的淨能量輸入 (Net Energy Input, NEI)。
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
FSNT : np.ndarray 或 xr.DataArray
|
|
101
|
+
大氣頂向下淨短波輻射 (TOA Net Shortwave Radiation),單位:W/m^2,維度形狀:(time, lat, lon) 或 (lat, lon)。
|
|
102
|
+
FLNT : np.ndarray 或 xr.DataArray
|
|
103
|
+
大氣頂向上長波輻射 (TOA Net Longwave Radiation),單位:W/m^2,維度形狀同上。
|
|
104
|
+
FSNS : np.ndarray 或 xr.DataArray
|
|
105
|
+
表面向下淨短波輻射 (Surface Net Shortwave Radiation),單位:W/m^2,維度形狀同上。
|
|
106
|
+
FLNS : np.ndarray 或 xr.DataArray
|
|
107
|
+
表面向下淨長波輻射 (Surface Net Longwave Radiation),單位:W/m^2,維度形狀同上。
|
|
108
|
+
LHFLX : np.ndarray 或 xr.DataArray
|
|
109
|
+
表面潛熱通量 (Surface Latent Heat Flux),單位:W/m^2,維度形狀同上。
|
|
110
|
+
SHFLX : np.ndarray 或 xr.DataArray
|
|
111
|
+
表面感熱通量 (Surface Sensible Heat Flux),單位:W/m^2,維度形狀同上。
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
NEI : np.ndarray 或 xr.DataArray
|
|
116
|
+
淨能量輸入,單位:W/m^2,維度形狀與輸入相同。
|
|
117
|
+
"""
|
|
118
|
+
# Calculate the net energy input
|
|
119
|
+
NEI = (FSNT - FLNT) - (FSNS - FLNS - LHFLX - SHFLX)
|
|
120
|
+
return NEI
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def EnergyFluxPotential(NEI, lat=None, lon=None, rsphere=6.3712e6):
|
|
124
|
+
"""
|
|
125
|
+
計算能量通量位勢 (Energy Flux Potential, EFP),並自動內插回與輸入 NEI 相同的網格與座標。
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
NEI : np.ndarray
|
|
130
|
+
淨能量輸入,單位:W/m^2,維度形狀:(time, lat, lon)。
|
|
131
|
+
lat : np.ndarray, optional
|
|
132
|
+
輸入的 1D 緯度座標,單位:度 (°),維度形狀:(nlat,)。若為 None 則自動產生。
|
|
133
|
+
lon : np.ndarray, optional
|
|
134
|
+
輸入的 1D 經度座標,單位:度 (°),維度形狀:(nlon,)。若為 None 則自動產生。
|
|
135
|
+
rsphere : float, optional
|
|
136
|
+
地球半徑,單位:m,預設值為 6.3712e6。
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
efp_out : np.ndarray
|
|
141
|
+
能量通量位勢 (EFP),維度形狀與輸入 NEI 相同:(time, lat, lon)。
|
|
142
|
+
"""
|
|
143
|
+
nt, nlat, nlon = NEI.shape
|
|
144
|
+
|
|
145
|
+
# 1. 自動建立或接收輸入的經緯度座標
|
|
146
|
+
if lat is None:
|
|
147
|
+
lat = np.linspace(-90, 90, nlat)
|
|
148
|
+
if lon is None:
|
|
149
|
+
lon = np.linspace(0, 360, nlon, endpoint=False)
|
|
150
|
+
|
|
151
|
+
# 2. 建立輸入資料的 xarray.DataArray
|
|
152
|
+
year_coords = [1] if nt == 1 else list(range(1, nt + 1))
|
|
153
|
+
nei_da = xr.DataArray(
|
|
154
|
+
NEI,
|
|
155
|
+
coords=[('year', year_coords), ('lat', lat), ('lon', lon)],
|
|
156
|
+
name='NEI'
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# 3. 核心計算所需的標準網格(spharm 通常需要特定或標準解析度,此處維持 193 點緯度)
|
|
160
|
+
nlat_calc = 193
|
|
161
|
+
lat_calc = np.linspace(-90, 90, nlat_calc)
|
|
162
|
+
lon_calc = lon # 經度可以保持與輸入一致
|
|
163
|
+
|
|
164
|
+
# 先將輸入內插至計算所需的網格
|
|
165
|
+
nei_calc = nei_da.interp(lat=lat_calc, lon=lon_calc, method='linear')
|
|
166
|
+
|
|
167
|
+
# 若只有 1 個時間點,仿原程式複製成 3 個 year 以符合計算模組需求
|
|
168
|
+
is_single_time = (nt == 1)
|
|
169
|
+
if is_single_time:
|
|
170
|
+
nei_calc = xr.concat([nei_calc, nei_calc, nei_calc], dim='year')
|
|
171
|
+
nei_calc['year'] = [1, 2, 3]
|
|
172
|
+
|
|
173
|
+
# 4. 執行頻譜計算 (Spherical harmonic inverse Laplacian)
|
|
174
|
+
scalar_field, shape_info = prep_data(nei_calc.data, "tyx")
|
|
175
|
+
vw = VectorWind(nei_calc, nei_calc)
|
|
176
|
+
S = vw._api.s # Spharmt instance
|
|
177
|
+
|
|
178
|
+
scalar_field_spec = S.grdtospec(scalar_field)
|
|
179
|
+
ilap_spec = spharm.spharm._spherepack.invlap(scalar_field_spec, S.rsphere)
|
|
180
|
+
ilap_grid = S.spectogrd(ilap_spec)
|
|
181
|
+
efp_calc = recover_data(ilap_grid, shape_info)
|
|
182
|
+
|
|
183
|
+
# 包成計算網格的 DataArray
|
|
184
|
+
efp_calc_da = xr.DataArray(efp_calc, coords=nei_calc.coords, dims=nei_calc.dims, name='EFP')
|
|
185
|
+
|
|
186
|
+
# 5. 【關鍵簡化】:直接將計算結果內插回使用者「原本輸入的網格」(lat, lon)
|
|
187
|
+
efp_final_da = efp_calc_da.interp(lat=lat, lon=lon, method='linear')
|
|
188
|
+
|
|
189
|
+
# 6. 恢復原本的時間維度形狀
|
|
190
|
+
if is_single_time:
|
|
191
|
+
# 如果原本時間維度只有 1,取第一筆並擴展軸回 (1, lat, lon)
|
|
192
|
+
efp_out = np.expand_dims(efp_final_da.isel(year=0).values, axis=0)
|
|
193
|
+
else:
|
|
194
|
+
efp_out = efp_final_da.isel(year=slice(0, nt)).values
|
|
195
|
+
|
|
196
|
+
return efp_out
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def MeridionalMassStreamFunction(V, lev, lat):
|
|
200
|
+
"""
|
|
201
|
+
計算經向質量經流函數 (Meridional Mass Streamfunction, MSF)。
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
V : np.ndarray
|
|
206
|
+
經向風速 (Meridional Wind Speed),單位:m/s,維度形狀:(lev, lat)。
|
|
207
|
+
lev : np.ndarray
|
|
208
|
+
氣壓高度層 (Pressure Levels),單位:hPa(程式碼內部會依此計算層厚度 dP),維度形狀:(lev,)。
|
|
209
|
+
lat : np.ndarray
|
|
210
|
+
緯度座標,單位:度 (°),維度形狀:(lat,)。
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
final_msf : np.ndarray
|
|
215
|
+
經向質量經流函數,單位:kg/s,維度形狀:(lev, lat)。
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
# 1. 處理 NaN:將地形以下的 NaN 設為 0
|
|
219
|
+
V_safe = np.nan_to_num(V, nan=0.0)
|
|
220
|
+
|
|
221
|
+
# 2. 計算 dP (單位已轉換: hPa -> Pa)
|
|
222
|
+
# 假設 lev 是從地面到高空 [1000, 950, ..., 50]
|
|
223
|
+
# 計算各層厚度,這裡建議使用更精確的層界(bounds)
|
|
224
|
+
#! only foe evenly spaced levels, for unevenly spaced levels, we should calculate the layer thickness based on the actual level values
|
|
225
|
+
dP = np.abs(np.gradient(lev)) # 為 Pascal
|
|
226
|
+
|
|
227
|
+
# 3. 質量修正 (V_adj) - 確保垂直積分和為 0
|
|
228
|
+
total_Vdp = np.sum(V_safe * dP[:, None], axis=0)
|
|
229
|
+
total_dP = np.sum((~np.isnan(V)) * dP[:, None], axis=0)
|
|
230
|
+
V_adj = total_Vdp / total_dP
|
|
231
|
+
|
|
232
|
+
# 4. 積分 (從大氣頂部向下累加)
|
|
233
|
+
# 使用 np.cumsum 會比寫 for loop 快且安全
|
|
234
|
+
V_corrected = (V_safe - V_adj)
|
|
235
|
+
# Multiply V and dP first, then flip and cumsum
|
|
236
|
+
msf = np.cumsum((V_corrected * dP[:, None])[::-1, :], axis=0)[::-1, :]
|
|
237
|
+
|
|
238
|
+
# 5. 物理常數
|
|
239
|
+
re = 6.371e6 # 地球半徑
|
|
240
|
+
g = 9.81
|
|
241
|
+
cos_lat = np.cos(np.deg2rad(lat))
|
|
242
|
+
|
|
243
|
+
final_msf = (2 * np.pi * re * cos_lat / g) * msf
|
|
244
|
+
return final_msf
|
|
245
|
+
|
|
246
|
+
def ZonalMassStreamFunction(U_lonmean, lev, lat_bounds=(-10.0, 10.0), re=6.371e6, g=9.81):
|
|
247
|
+
"""
|
|
248
|
+
計算緯向質量經流函數 (Zonal Mass Streamfunction, MSF) 或稱為 Walker 環流。
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
U_lonmean : np.ndarray
|
|
253
|
+
經向平均的緯向風速 (Zonal Wind Speed),單位:m/s,維度形狀:(nlev, nlon)。
|
|
254
|
+
NaN 值表示地形以下的缺測值。
|
|
255
|
+
lev : np.ndarray
|
|
256
|
+
氣壓高度層 (Pressure Levels),單位:hPa,維度形狀:(nlev,),遞減 (地面到高空)。
|
|
257
|
+
lat_bounds : tuple, optional
|
|
258
|
+
緯度平均的邊界 (南緯, 北緯),單位:度 (°),預設為 (-10.0, 10.0)。
|
|
259
|
+
re : float, optional
|
|
260
|
+
地球半徑,單位:m,預設值為 6.371e6。
|
|
261
|
+
g : float, optional
|
|
262
|
+
重力加速度,單位:m/s^2,預設值為 9.81。
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
final_msf : np.ndarray
|
|
267
|
+
緯向質量經流函數 (Zonal Mass Streamfunction),單位:kg/s,維度形狀:(nlev, nlon)。
|
|
268
|
+
返回的結果保留原始氣壓層順序,地形以下的層仍為 NaN。
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
U = np.asarray(U_lonmean, dtype=float)
|
|
273
|
+
p = np.asarray(lev, dtype=float) * 100.0 # hPa -> Pa
|
|
274
|
+
|
|
275
|
+
# --------------------------------------------------
|
|
276
|
+
# 1. Input validation
|
|
277
|
+
# --------------------------------------------------
|
|
278
|
+
if U.ndim != 2 or U.shape[0] != len(p):
|
|
279
|
+
raise ValueError("U_lonmean must have shape (nlev, nlon).")
|
|
280
|
+
|
|
281
|
+
if not np.all(np.isfinite(p)):
|
|
282
|
+
raise ValueError("Pressure levels must be finite.")
|
|
283
|
+
|
|
284
|
+
if not np.all(np.diff(p) > 0) and not np.all(np.diff(p) < 0):
|
|
285
|
+
raise ValueError("Pressure levels must be strictly monotonic.")
|
|
286
|
+
|
|
287
|
+
# Sort pressure from top to bottom (increasing pressure)
|
|
288
|
+
if p[0] > p[-1]:
|
|
289
|
+
order = np.arange(len(p) - 1, -1, -1)
|
|
290
|
+
else:
|
|
291
|
+
order = np.arange(len(p))
|
|
292
|
+
|
|
293
|
+
p_sorted = p[order]
|
|
294
|
+
U_sorted = U[order, :]
|
|
295
|
+
|
|
296
|
+
nlev, nlon = U.shape
|
|
297
|
+
|
|
298
|
+
# --------------------------------------------------
|
|
299
|
+
# 2. Meridional width
|
|
300
|
+
# --------------------------------------------------
|
|
301
|
+
phi_s, phi_n = np.deg2rad(lat_bounds)
|
|
302
|
+
delta_phi = phi_n - phi_s
|
|
303
|
+
|
|
304
|
+
if delta_phi <= 0:
|
|
305
|
+
raise ValueError("Invalid latitude boundaries.")
|
|
306
|
+
|
|
307
|
+
factor = re * delta_phi / g
|
|
308
|
+
|
|
309
|
+
# --------------------------------------------------
|
|
310
|
+
# 3. Allocate output
|
|
311
|
+
# --------------------------------------------------
|
|
312
|
+
psi_sorted = np.full((nlev, nlon), np.nan)
|
|
313
|
+
|
|
314
|
+
# --------------------------------------------------
|
|
315
|
+
# 4. Process individual longitude profiles
|
|
316
|
+
# --------------------------------------------------
|
|
317
|
+
for j in range(nlon):
|
|
318
|
+
|
|
319
|
+
u_col = U_sorted[:, j]
|
|
320
|
+
|
|
321
|
+
# Valid levels
|
|
322
|
+
valid = np.isfinite(u_col)
|
|
323
|
+
|
|
324
|
+
# Find the first missing level (usually terrain)
|
|
325
|
+
invalid_idx = np.where(~valid)[0]
|
|
326
|
+
|
|
327
|
+
if len(invalid_idx) > 0:
|
|
328
|
+
n_valid = invalid_idx[0]
|
|
329
|
+
|
|
330
|
+
# Do not interpolate across internal missing levels
|
|
331
|
+
if np.any(valid[n_valid:]):
|
|
332
|
+
raise ValueError(
|
|
333
|
+
"Non-contiguous NaNs at longitude index {}."
|
|
334
|
+
.format(j)
|
|
335
|
+
)
|
|
336
|
+
else:
|
|
337
|
+
n_valid = nlev
|
|
338
|
+
|
|
339
|
+
# At least two valid pressure levels are necessary
|
|
340
|
+
if n_valid < 2:
|
|
341
|
+
continue
|
|
342
|
+
|
|
343
|
+
# Extract the contiguous atmospheric column
|
|
344
|
+
p_col = p_sorted[:n_valid]
|
|
345
|
+
u_col = u_col[:n_valid]
|
|
346
|
+
|
|
347
|
+
# Actual pressure thickness between adjacent levels
|
|
348
|
+
dp = np.diff(p_col)
|
|
349
|
+
|
|
350
|
+
# --------------------------------------------------
|
|
351
|
+
# 5. Pressure-weighted vertical mean wind
|
|
352
|
+
# --------------------------------------------------
|
|
353
|
+
total_Udp = np.sum(
|
|
354
|
+
0.5 * (u_col[:-1] + u_col[1:]) * dp
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
total_dP = p_col[-1] - p_col[0]
|
|
358
|
+
|
|
359
|
+
U_adj = total_Udp / total_dP
|
|
360
|
+
|
|
361
|
+
# Remove vertically averaged zonal wind
|
|
362
|
+
U_corrected = u_col - U_adj
|
|
363
|
+
|
|
364
|
+
# --------------------------------------------------
|
|
365
|
+
# 6. Cumulative vertical integration
|
|
366
|
+
# --------------------------------------------------
|
|
367
|
+
increments = (
|
|
368
|
+
0.5 * (U_corrected[:-1] + U_corrected[1:])
|
|
369
|
+
* dp
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
msf = np.zeros(n_valid)
|
|
373
|
+
|
|
374
|
+
msf[1:] = np.cumsum(increments)
|
|
375
|
+
|
|
376
|
+
# Enforce exact zero at the lower boundary
|
|
377
|
+
msf[-1] = 0.0
|
|
378
|
+
|
|
379
|
+
# --------------------------------------------------
|
|
380
|
+
# 7. Convert to mass streamfunction (kg/s)
|
|
381
|
+
# --------------------------------------------------
|
|
382
|
+
psi_sorted[:n_valid, j] = factor * msf
|
|
383
|
+
|
|
384
|
+
# --------------------------------------------------
|
|
385
|
+
# 8. Restore original pressure ordering
|
|
386
|
+
# --------------------------------------------------
|
|
387
|
+
final_msf = np.empty_like(psi_sorted)
|
|
388
|
+
final_msf[order, :] = psi_sorted
|
|
389
|
+
|
|
390
|
+
return final_msf
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def HorizontalTN01WaveActivityFlux(CTL_U, CTL_V, EXP_Z, lon=np.linspace(0, 358.75, 288), lat=np.linspace(-90, 90, 192), p_lev=500.0):
|
|
394
|
+
"""
|
|
395
|
+
幾何校正版:計算 Takaya and Nakamura (JAS, 2001) 水平波活動通量 (HOR-TN01-WAF)。
|
|
396
|
+
直接在公尺 (meter) 尺度下進行偏微分,解決低緯度/高緯度幾何變形產生的假向量。
|
|
397
|
+
|
|
398
|
+
Parameters
|
|
399
|
+
----------
|
|
400
|
+
CTL_U : np.ndarray
|
|
401
|
+
對照組緯向風速 (Zonal Wind Speed),單位:m/s,維度形狀:(lat, lon)。
|
|
402
|
+
CTL_V : np.ndarray
|
|
403
|
+
對照組經向風速 (Meridional Wind Speed),單位:m/s,維度形狀:(lat, lon)。
|
|
404
|
+
EXP_Z : np.ndarray
|
|
405
|
+
實驗組位勢高度或擾動位勢高度 (Geopotential Height),單位:m,維度形狀:(lat, lon)。
|
|
406
|
+
lon : np.ndarray, optional
|
|
407
|
+
經度座標,單位:度 (°),預設為 288 個點由 0 到 358.75,維度形狀:(lon,)。
|
|
408
|
+
lat : np.ndarray, optional
|
|
409
|
+
緯度座標,單位:度 (°),預設為 192 個點由 -90 到 90,維度形狀:(lat,)。
|
|
410
|
+
p_lev : float, optional
|
|
411
|
+
計算通量時所使用的氣壓高度層,單位:hPa,預設值為 500.0。
|
|
412
|
+
|
|
413
|
+
Returns
|
|
414
|
+
-------
|
|
415
|
+
px : np.ndarray
|
|
416
|
+
水平波活動通量的經向/x 分量,單位:m^2/s^2,維度形狀:(lat, lon)。
|
|
417
|
+
py : np.ndarray
|
|
418
|
+
水平波活動通量的緯向/y 分量,單位:m^2/s^2,維度形狀:(lat, lon)。
|
|
419
|
+
psi_p : np.ndarray
|
|
420
|
+
擾動流函數 (Perturbation Streamfunction),單位:m^2/s,維度形狀:(lat, lon)。
|
|
421
|
+
waf_div : np.ndarray
|
|
422
|
+
波活動通量散度 (WAF Divergence),單位:s^-1 相關,維度形狀:(lat, lon)。
|
|
423
|
+
"""
|
|
424
|
+
# 常數設定
|
|
425
|
+
a = 6.37e6 # 地球半徑 (m)
|
|
426
|
+
omega = 7.292e-5 # 地球自轉角速度 (rad/s)
|
|
427
|
+
|
|
428
|
+
# 角度轉弧度
|
|
429
|
+
lon_rad = np.deg2rad(lon)
|
|
430
|
+
lat_rad = np.deg2rad(lat)
|
|
431
|
+
|
|
432
|
+
# 計算基本的弧度網格距
|
|
433
|
+
dlon_rad = np.gradient(lon_rad)
|
|
434
|
+
dlat_rad = np.gradient(lat_rad)
|
|
435
|
+
|
|
436
|
+
# 幾何參數 (2D 廣播化)
|
|
437
|
+
cos_lat = np.cos(lat_rad)[:, np.newaxis]
|
|
438
|
+
sin_lat = np.sin(lat_rad)[:, np.newaxis]
|
|
439
|
+
|
|
440
|
+
# 科氏參數 f (2D),避開赤道無窮大
|
|
441
|
+
f = 2 * omega * sin_lat
|
|
442
|
+
f[np.abs(f) < 1e-5] = np.nan
|
|
443
|
+
|
|
444
|
+
# 1. 計算擾動流函數 psi_p (單位: m^2/s)
|
|
445
|
+
psi_p = EXP_Z / f
|
|
446
|
+
|
|
447
|
+
# =====================================================================
|
|
448
|
+
# 核心幾何修正:計算真正對應「公尺」單位的網格距 dx, dy
|
|
449
|
+
# dy = a * dlat (緯度方向的實際距離,每個地方都一樣)
|
|
450
|
+
# dx = a * cos(lat) * dlon (經度方向的實際距離,隨緯度增高而縮小)
|
|
451
|
+
# =====================================================================
|
|
452
|
+
dy_2d = a * dlat_rad[:, np.newaxis]
|
|
453
|
+
dx_2d = a * cos_lat * dlon_rad[np.newaxis, :]
|
|
454
|
+
|
|
455
|
+
# 2. 計算流函數對實際距離 (m) 的一階偏微分
|
|
456
|
+
dpsi_dx = np.gradient(psi_p, axis=1) / dx_2d
|
|
457
|
+
dpsi_dy = np.gradient(psi_p, axis=0) / dy_2d
|
|
458
|
+
|
|
459
|
+
# 3. 計算實際距離 (m) 的二階偏微分
|
|
460
|
+
d2psi_dx2 = np.gradient(dpsi_dx, axis=1) / dx_2d
|
|
461
|
+
d2psi_dy2 = np.gradient(dpsi_dy, axis=0) / dy_2d
|
|
462
|
+
d2psi_dxdy = np.gradient(dpsi_dy, axis=1) / dx_2d # 混合偏微分
|
|
463
|
+
|
|
464
|
+
# 4. 計算波活動度矩陣項 (完全回歸標準 Cartesian 物理形式)
|
|
465
|
+
termxu = dpsi_dx * dpsi_dx - psi_p * d2psi_dx2
|
|
466
|
+
termxv = dpsi_dx * dpsi_dy - psi_p * d2psi_dxdy
|
|
467
|
+
termyv = dpsi_dy * dpsi_dy - psi_p * d2psi_dy2
|
|
468
|
+
|
|
469
|
+
# 5. 計算風速基本場
|
|
470
|
+
magU = np.sqrt(CTL_U**2 + CTL_V**2)
|
|
471
|
+
magU[magU == 0] = np.nan
|
|
472
|
+
|
|
473
|
+
# T-N01 公式核心係數: p_normalized / (2 * |U|)
|
|
474
|
+
p_normalized = p_lev / 1000.0
|
|
475
|
+
coeff = p_normalized / (2 * magU)
|
|
476
|
+
|
|
477
|
+
# 6. 計算 WAF 分量 (單位: m^2/s^2)
|
|
478
|
+
# 此時公式變得極度乾淨,再也不需要除以額外的 a^2 或 cos_lat
|
|
479
|
+
px = coeff * (CTL_U * termxu + CTL_V * termxv)
|
|
480
|
+
py = coeff * (CTL_U * termxv + CTL_V * termyv)
|
|
481
|
+
|
|
482
|
+
# 7. 計算散度 (Divergence)
|
|
483
|
+
# 既然 px, py 已經是標準米制分量,散度直接用標準的 d(px)/dx + d(py)/dy 即可!
|
|
484
|
+
dp_dx = np.gradient(px, axis=1) / dx_2d
|
|
485
|
+
dp_dy = np.gradient(py, axis=0) / dy_2d
|
|
486
|
+
waf_div = dp_dx + dp_dy
|
|
487
|
+
|
|
488
|
+
# 8. 設定科學合理的邊界遮罩 (Mask)
|
|
489
|
+
# 準地轉理論在南北緯 10 度以內、以及 80 度以上本身就不適用
|
|
490
|
+
lat_mask = (np.abs(lat) > 80) | (np.abs(lat) < 15)
|
|
491
|
+
|
|
492
|
+
px[lat_mask, :] = np.nan
|
|
493
|
+
py[lat_mask, :] = np.nan
|
|
494
|
+
waf_div[lat_mask, :] = np.nan
|
|
495
|
+
|
|
496
|
+
return px, py, psi_p, waf_div
|
|
497
|
+
|
|
File without changes
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def MassIntegrationAlongPressure(pressure, variable, pmin=None, pmax=None):
|
|
5
|
+
"""
|
|
6
|
+
沿著垂直氣壓層進行質量積分(Mass Integration Along Pressure)。
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
pressure : np.ndarray
|
|
11
|
+
氣壓高度層,單位:hPa,維度形狀:(lev,)。
|
|
12
|
+
variable : np.ndarray
|
|
13
|
+
欲積分的大氣變數,單位:W/kg 或對應物理量,維度形狀:(lev, lat, lon)。
|
|
14
|
+
pmin : float, optional
|
|
15
|
+
積分的氣壓下限(高空/低壓側),單位:hPa,預設為 None(從最頂層開始)。
|
|
16
|
+
pmax : float, optional
|
|
17
|
+
積分的氣壓上限(地表/高壓側),單位:hPa,預設為 None(到最底層結束)。
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
integrated_variable : np.ndarray
|
|
22
|
+
經垂直質量積分後的 2D 變數,維度形狀:(lat, lon)。
|
|
23
|
+
"""
|
|
24
|
+
# 1. 參數驗證
|
|
25
|
+
if pmin is not None and not isinstance(pmin, (int, float)):
|
|
26
|
+
raise ValueError("pmin must be a numeric value.")
|
|
27
|
+
if pmax is not None and not isinstance(pmax, (int, float)):
|
|
28
|
+
raise ValueError("pmax must be a numeric value.")
|
|
29
|
+
if pmin is not None and pmax is not None and pmin >= pmax:
|
|
30
|
+
raise ValueError("pmin must be less than pmax.")
|
|
31
|
+
|
|
32
|
+
# 2. 轉換壓力單位為 Pa
|
|
33
|
+
p_pa = np.asarray(pressure, dtype=float) * 100.0
|
|
34
|
+
Pa_to_kg_per_m2 = 0.1 # 1/g
|
|
35
|
+
|
|
36
|
+
# 3. 計算不等間距的 dp 與對應的 dm
|
|
37
|
+
dp_array = np.abs(np.gradient(p_pa)) # [lev,]
|
|
38
|
+
dm = dp_array * Pa_to_kg_per_m2 # [lev,]
|
|
39
|
+
|
|
40
|
+
# 4. 建立垂直層條件遮罩 (Mask)
|
|
41
|
+
mask = np.ones(p_pa.shape[0], dtype=bool)
|
|
42
|
+
pmin_pa = pmin * 100.0 if pmin is not None else None
|
|
43
|
+
pmax_pa = pmax * 100.0 if pmax is not None else None
|
|
44
|
+
|
|
45
|
+
for i, p in enumerate(p_pa):
|
|
46
|
+
if pmin_pa is not None and p < pmin_pa:
|
|
47
|
+
mask[i] = False
|
|
48
|
+
if pmax_pa is not None and p > pmax_pa:
|
|
49
|
+
mask[i] = False
|
|
50
|
+
|
|
51
|
+
# 套用遮罩:不符合範圍的層其 dm 設為 0
|
|
52
|
+
masked_dm = np.where(mask, dm, 0.0)
|
|
53
|
+
|
|
54
|
+
# 5. 處理變數中的 NaN 並進行廣播矩陣乘法
|
|
55
|
+
var_clean = np.nan_to_num(variable, nan=0.0)
|
|
56
|
+
|
|
57
|
+
# 利用 broadcasting 將 dm [lev] 擴展為 [lev, 1, 1] 與 variable 相乘後在 lev 軸(axis=0)加總
|
|
58
|
+
integrated_variable = np.sum(var_clean * masked_dm[:, None, None], axis=0)
|
|
59
|
+
|
|
60
|
+
return integrated_variable.astype(np.float32)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def HorizontalMoistureAdvection(pressure, CTL_u, CTL_v, CTL_q, EXP_u, EXP_v, EXP_q, pmin=None, pmax=None):
|
|
64
|
+
"""
|
|
65
|
+
計算水平水氣平流項的熱力學分解與動力學分解,並沿氣壓層垂直積分。
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
pressure : np.ndarray
|
|
70
|
+
氣壓高度層,單位:hPa,維度形狀:(lev,)。
|
|
71
|
+
CTL_u : np.ndarray
|
|
72
|
+
對照組緯向風速 (Zonal Wind),單位:m/s,維度形狀:(lev, lat, lon)。
|
|
73
|
+
CTL_v : np.ndarray
|
|
74
|
+
對照組經向風速 (Meridional Wind),單位:m/s,維度形狀:(lev, lat, lon)。
|
|
75
|
+
CTL_q : np.ndarray
|
|
76
|
+
對照組比濕 (Specific Humidity),單位:kg/kg,維度形狀:(lev, lat, lon)。
|
|
77
|
+
EXP_u : np.ndarray
|
|
78
|
+
實驗組緯向風速,單位:m/s,維度形狀:(lev, lat, lon)。
|
|
79
|
+
EXP_v : np.ndarray
|
|
80
|
+
實驗組經向風速,單位:m/s,維度形狀:(lev, lat, lon)。
|
|
81
|
+
EXP_q : np.ndarray
|
|
82
|
+
實驗組比濕,單位:kg/kg,維度形狀:(lev, lat, lon)。
|
|
83
|
+
pmin : float, optional
|
|
84
|
+
垂直積分的氣壓下限,單位:hPa,預設為 None。
|
|
85
|
+
pmax : float, optional
|
|
86
|
+
垂直積分的氣壓上限,單位:hPa,預設為 None。
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
integrated_thermodynamic_term : np.ndarray
|
|
91
|
+
積分後的水平水氣平流熱力學項,單位:W/m^2 (經潛熱轉換),維度形狀:(lat, lon)。
|
|
92
|
+
integrated_dynamic_term : np.ndarray
|
|
93
|
+
積分後的水平水氣平流動力學項,單位:W/m^2 (經潛熱轉換),維度形狀:(lat, lon)。
|
|
94
|
+
"""
|
|
95
|
+
assert CTL_u.shape == CTL_v.shape == CTL_q.shape == EXP_u.shape == EXP_v.shape == EXP_q.shape
|
|
96
|
+
|
|
97
|
+
# set up the grid
|
|
98
|
+
dx, dy = 1.25*110*1000, 180/192*110*1000 # m
|
|
99
|
+
thermodynamic_term = np.zeros_like(CTL_u)
|
|
100
|
+
dynamic_term = np.zeros_like(CTL_u)
|
|
101
|
+
|
|
102
|
+
# unit conversion for q
|
|
103
|
+
latent_heat = 2.5e6 # [J/kg]
|
|
104
|
+
CTL_q_energy = CTL_q * latent_heat
|
|
105
|
+
EXP_q_energy = EXP_q * latent_heat
|
|
106
|
+
|
|
107
|
+
# calculate the gradient of q
|
|
108
|
+
CTL_dqdx = np.gradient(CTL_q_energy, dx, axis=2)
|
|
109
|
+
CTL_dqdy = np.gradient(CTL_q_energy, dy, axis=1)
|
|
110
|
+
EXP_dqdx = np.gradient(EXP_q_energy, dx, axis=2)
|
|
111
|
+
EXP_dqdy = np.gradient(EXP_q_energy, dy, axis=1)
|
|
112
|
+
|
|
113
|
+
# 計算平流項
|
|
114
|
+
for lev in range(0, CTL_u.shape[0]):
|
|
115
|
+
thermodynamic_term[lev, :, :] = CTL_u[lev, :, :] * EXP_dqdx[lev, :, :] + CTL_v[lev, :, :] * EXP_dqdy[lev, :, :]
|
|
116
|
+
dynamic_term[lev, :, :] = CTL_dqdx[lev, :, :] * EXP_u[lev, :, :] + CTL_dqdy[lev, :, :] * EXP_v[lev, :, :]
|
|
117
|
+
|
|
118
|
+
# 呼叫積分函數
|
|
119
|
+
integrated_thermodynamic_term = -MassIntegrationAlongPressure(pressure, thermodynamic_term, pmin=pmin, pmax=pmax)
|
|
120
|
+
integrated_dynamic_term = -MassIntegrationAlongPressure(pressure, dynamic_term, pmin=pmin, pmax=pmax)
|
|
121
|
+
|
|
122
|
+
return integrated_thermodynamic_term, integrated_dynamic_term
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def VerticalMoistureAdvection(pressure, CTL_omega, CTL_q, EXP_omega, EXP_q, pmin=None, pmax=None):
|
|
126
|
+
"""
|
|
127
|
+
計算垂直水氣平流項的熱力學分解與動力學分解,並沿氣壓層垂直積分。
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
pressure : np.ndarray
|
|
132
|
+
氣壓高度層,單位:hPa,維度形狀:(lev,)。
|
|
133
|
+
CTL_omega : np.ndarray
|
|
134
|
+
對照組垂直p速度 (Pressure Velocity),單位:Pa/s,維度形狀:(lev, lat, lon)。
|
|
135
|
+
CTL_q : np.ndarray
|
|
136
|
+
對照組比濕,單位:kg/kg,維度形狀:(lev, lat, lon)。
|
|
137
|
+
EXP_omega : np.ndarray
|
|
138
|
+
實驗組垂直p速度,單位:Pa/s,維度形狀:(lev, lat, lon)。
|
|
139
|
+
EXP_q : np.ndarray
|
|
140
|
+
實驗組比濕,單位:kg/kg,維度形狀:(lev, lat, lon)。
|
|
141
|
+
pmin : float, optional
|
|
142
|
+
垂直積分的氣壓下限,單位:hPa,預設為 None。
|
|
143
|
+
pmax : float, optional
|
|
144
|
+
垂直積分的氣壓上限,單位:hPa,預設為 None。
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
integrated_thermodynamic_term : np.ndarray
|
|
149
|
+
積分後的垂直水氣平流熱力學項,單位:W/m^2 (經潛熱轉換),維度形狀:(lat, lon)。
|
|
150
|
+
integrated_dynamic_term : np.ndarray
|
|
151
|
+
積分後的垂直水氣平流動力學項,單位:W/m^2 (經潛熱轉換),維度形狀:(lat, lon)。
|
|
152
|
+
"""
|
|
153
|
+
assert CTL_omega.shape == CTL_q.shape == EXP_omega.shape == EXP_q.shape
|
|
154
|
+
|
|
155
|
+
# --- 將 pressure 轉為 Pa 陣列,直接作為 np.gradient 的坐標軸 ---
|
|
156
|
+
p_pa = pressure * 100.0
|
|
157
|
+
|
|
158
|
+
# unit conversion for q
|
|
159
|
+
latent_heat = 2.5e6 # [J/kg]
|
|
160
|
+
CTL_q_energy = CTL_q * latent_heat
|
|
161
|
+
EXP_q_energy = EXP_q * latent_heat
|
|
162
|
+
|
|
163
|
+
# --- 使用非等間距的 p_pa 陣列進行垂直梯度計算 ---
|
|
164
|
+
# 當 np.gradient 的第二個參數傳入 1D array 時,它會自動依據該坐標計算非等間距差分
|
|
165
|
+
CTL_dqdp = np.gradient(CTL_q_energy, p_pa, axis=0)
|
|
166
|
+
EXP_dqdp = np.gradient(EXP_q_energy, p_pa, axis=0)
|
|
167
|
+
|
|
168
|
+
thermodynamic_term = np.zeros_like(CTL_omega)
|
|
169
|
+
dynamic_term = np.zeros_like(CTL_omega)
|
|
170
|
+
|
|
171
|
+
for lev in range(0, CTL_omega.shape[0]):
|
|
172
|
+
thermodynamic_term[lev, :, :] = CTL_omega[lev, :, :] * EXP_dqdp[lev, :, :]
|
|
173
|
+
dynamic_term[lev, :, :] = CTL_dqdp[lev, :, :] * EXP_omega[lev, :, :]
|
|
174
|
+
|
|
175
|
+
integrated_thermodynamic_term = MassIntegrationAlongPressure(pressure, thermodynamic_term, pmin=pmin, pmax=pmax)
|
|
176
|
+
integrated_dynamic_term = MassIntegrationAlongPressure(pressure, dynamic_term, pmin=pmin, pmax=pmax)
|
|
177
|
+
|
|
178
|
+
return integrated_thermodynamic_term, integrated_dynamic_term
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def RelativeHumidity(T, Q, P):
|
|
182
|
+
"""
|
|
183
|
+
利用溫度、比濕與氣壓計算相對濕度 (Relative Humidity, RH)。
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
T : float 或 np.ndarray
|
|
188
|
+
溫度,單位:Kelvin (K),維度形狀:與輸入相符(純量或任意陣列)。
|
|
189
|
+
Q : float 或 np.ndarray
|
|
190
|
+
比濕 (Specific Humidity),單位:kg/kg,維度形狀:與輸入相符。
|
|
191
|
+
P : float 或 np.ndarray
|
|
192
|
+
氣壓 (Pressure),單位:Pa,維度形狀:與輸入相符。
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
rh : np.ndarray
|
|
197
|
+
相對濕度,單位:百分比 (%),已被限制在 0-100 之間,維度形狀與輸入相同。
|
|
198
|
+
"""
|
|
199
|
+
# 1. 轉為攝氏溫度
|
|
200
|
+
T_c = T - 273.15
|
|
201
|
+
|
|
202
|
+
# 2. 計算飽和蒸氣壓 es (單位: Pa)
|
|
203
|
+
# 使用 Bolton (1980) 公式,精度高且適合大氣運算
|
|
204
|
+
es = 611.2 * np.exp((17.67 * T_c) / (T_c + 243.5))
|
|
205
|
+
|
|
206
|
+
# 3. 計算混合比 w (mixing ratio)
|
|
207
|
+
w = Q / (1.0 - Q)
|
|
208
|
+
|
|
209
|
+
# 4. 計算實際蒸氣壓 e (單位: Pa)
|
|
210
|
+
# epsilon (水氣與乾空氣分子量比) ≅ 0.622
|
|
211
|
+
epsilon = 0.622
|
|
212
|
+
e = (w * P) / (epsilon + w)
|
|
213
|
+
|
|
214
|
+
# 5. 計算 RH
|
|
215
|
+
rh = (e / es) * 100.0
|
|
216
|
+
|
|
217
|
+
# 限制 RH 在 0-100% 之間
|
|
218
|
+
rh = np.clip(rh, 0, 100)
|
|
219
|
+
|
|
220
|
+
return rh.astype(np.float32)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def VaporPressureDeficit(T, Q, P):
|
|
225
|
+
"""
|
|
226
|
+
利用溫度、比濕與氣壓計算水氣壓差 (Vapor Pressure Deficit, VPD)。
|
|
227
|
+
|
|
228
|
+
Parameters
|
|
229
|
+
----------
|
|
230
|
+
T : float 或 np.ndarray
|
|
231
|
+
溫度,單位:Kelvin (K),維度形狀:與輸入相符。
|
|
232
|
+
Q : float 或 np.ndarray
|
|
233
|
+
比濕 (Specific Humidity),單位:kg/kg,維度形狀:與輸入相符。
|
|
234
|
+
P : float 或 np.ndarray
|
|
235
|
+
氣壓 (Pressure),單位:Pa,維度形狀:與輸入相符。
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
vpd : np.ndarray
|
|
240
|
+
水氣壓差,單位:Pa,已確保不會出現負值,維度形狀與輸入相同。
|
|
241
|
+
"""
|
|
242
|
+
# 1. 轉為攝氏溫度
|
|
243
|
+
T_c = T - 273.15
|
|
244
|
+
|
|
245
|
+
# 2. 計算飽和蒸氣壓 es (單位: Pa)
|
|
246
|
+
# 使用 Bolton (1980) 公式,精度高且適合大氣運算
|
|
247
|
+
es = 611.2 * np.exp((17.67 * T_c) / (T_c + 243.5))
|
|
248
|
+
|
|
249
|
+
# 3. 計算混合比 w (mixing ratio)
|
|
250
|
+
w = Q / (1.0 - Q)
|
|
251
|
+
|
|
252
|
+
# 4. 計算實際蒸氣壓 e (單位: Pa)
|
|
253
|
+
# epsilon (水氣與乾空氣分子量比) ≅ 0.622
|
|
254
|
+
epsilon = 0.622
|
|
255
|
+
e = (w * P) / (epsilon + w)
|
|
256
|
+
|
|
257
|
+
# 5. 計算 VPD
|
|
258
|
+
vpd = np.maximum(0, es - e) # 確保不會出現負值
|
|
259
|
+
|
|
260
|
+
return vpd.astype(np.float32)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyqihou
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python Package for Climate Analysis
|
|
5
|
+
Author-email: VVVictorZhou <ahme02147483647a@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/VVVICTORZHOU/
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.6
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: numpy>=1.19.5
|
|
14
|
+
Requires-Dist: scipy>=1.5.4
|
|
15
|
+
Requires-Dist: windspharm>=1.7.0
|
|
16
|
+
Requires-Dist: xarray>=0.16.2
|
|
17
|
+
|
|
18
|
+
## How to install the `pyqihou` package:
|
|
19
|
+
|
|
20
|
+
- Method 1: `npm install pyqihou`
|
|
21
|
+
|
|
22
|
+
- Method 2: `pip install pyqihou`
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Functions in the `pyqihou` package:
|
|
26
|
+
### `water.py`
|
|
27
|
+
|
|
28
|
+
- `MassIntegrationAlongPressure`: 沿著垂直氣壓層執行大氣變數的質量積分。
|
|
29
|
+
- `HorizontalMoistureAdvection`: 計算水平水氣平流的熱力學與動力學分解項,並進行氣壓層垂直積分。
|
|
30
|
+
- `VerticalMoistureAdvection`: 計算垂直水氣平流的熱力學與動力學分解項,並進行氣壓層垂直積分。
|
|
31
|
+
- `RelativeHumidity`: 依據溫度、比濕與氣壓計算大氣相對濕度百分比。
|
|
32
|
+
- `VaporPressureDeficit`: 依據溫度、比濕與氣壓計算水氣壓差 (VPD)。
|
|
33
|
+
|
|
34
|
+
### `energy.py`
|
|
35
|
+
|
|
36
|
+
- `MixedLayerHeatBudget`: 計算混合層熱平衡的各項輻射、熱通量與物理分解項。
|
|
37
|
+
- `NetEnergyInput`: 計算大氣與地表的淨能量輸入 (NEI)。
|
|
38
|
+
- `EnergyFluxPotential`: 計算能量通量位勢 (EFP) 並自動處理標準網格內插與還原。
|
|
39
|
+
- `MeridionalMassStreamFunction`: 計算經向質量經流函數 (MSF)。
|
|
40
|
+
- `ZonalMassStreamFunction`: 計算緯向質量經流函數 (MSF)。
|
|
41
|
+
- `HorizontalTN01WaveActivityFlux`: 計算幾何校正版的 Takaya-Nakamura (2001) 水平波活動通量與其散度。
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
pyqihou/__init__.py
|
|
4
|
+
pyqihou/energy.py
|
|
5
|
+
pyqihou/statistic.py
|
|
6
|
+
pyqihou/water.py
|
|
7
|
+
pyqihou.egg-info/PKG-INFO
|
|
8
|
+
pyqihou.egg-info/SOURCES.txt
|
|
9
|
+
pyqihou.egg-info/dependency_links.txt
|
|
10
|
+
pyqihou.egg-info/requires.txt
|
|
11
|
+
pyqihou.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyqihou
|
pyqihou-0.1.0/setup.cfg
ADDED