自动下载并读取 GEOS-FP数据中的10米风速

Mar 12, 2025 · 3 min read

GEOS-FP数据门户提供了包括tavg1_2d_slv_Nx 在内的多种产品。

命名规则

  • tavg1:表示 1 小时平均(Time-averaged over 1 hour)。
  • 2d:表示 二维变量(2D variables),即地表或接近地表的大气变量。
  • slv:表示 地表变量(Surface and near-surface variables),包括温度、风速、降水、湿度等。
  • Nx:表示 非插值网格(Non-interpolated grid),即数据在模型网格上直接输出,没有额外的插值处理。

主要变量

tavg1_2d_slv_Nx 产品包含多个气象变量,以下是一些常见变量:

变量名称描述
T2M2米气温(Surface Air Temperature, K)
U10M10米东西向风速(10-meter U-component Wind, m/s)
V10M10米南北向风速(10-meter V-component Wind, m/s)
PS地表气压(Surface Pressure, Pa)
RH2M2米相对湿度(Relative Humidity, %)
PRECTOT总降水率(Precipitation Rate, mm/s)
QV2M2米比湿(Specific Humidity, kg/kg)

代码

#!/usr/bin/env python
# coding: utf-8
# Date Created: 2024-11-20
# Author: Zhipeng Pei (zhipeng.pei@whu.edu.cn)
# Description:测试自动下载并读取 GEOS-FP数据中的10米风速,分辨率为 0.25 (纬度)×0.3125 (经度)

import os
import numpy as np
import xarray as xr
import requests
import matplotlib.pyplot as plt
from datetime import datetime, timedelta


def download_nc_file_with_progress(url, local_path):
    """
    下载文件并实时显示进度。
    如果文件已存在,则直接使用。

    :param url: 文件URL
    :param local_path: 本地保存路径
    """
    os.makedirs(os.path.dirname(local_path), exist_ok=True)

    if os.path.exists(local_path):
        print(f"文件已存在,直接使用: {local_path}")
        return

    response = requests.get(url, stream=True)
    if response.status_code != 200:
        raise ValueError(f"下载失败: {url},状态码: {response.status_code}")

    total_size = int(response.headers.get('Content-Length', 0))
    print(f"正在下载文件: {url}")
    print(f"文件大小: {total_size / (1024 * 1024):.2f} MB\n")

    with open(local_path, "wb") as f:
        downloaded = 0
        for chunk in response.iter_content(chunk_size=1024 * 1024):  # 1MB per chunk
            if chunk:
                f.write(chunk)
                downloaded += len(chunk)
                percent = (downloaded / total_size) * 100
                print(f"\r下载进度: {percent:.2f}%", end='')

    print("\n下载完成。\n")

def get_nearest_wind_speed_tavg(lon, lat, dtime):
    """
    获取全球U10M、V10M风场,以及指定位置的最近10m平均风速(tavg1_2d_slv_Nx产品)

    :param lon: 经度 (float)
    :param lat: 纬度 (float)
    :param dtime: datetime (datetime)
    :return:
        global_u10m: 全球U10M风速场 (2D array)
        global_v10m: 全球V10M风速场 (2D array)
        lons: 经度数组
        lats: 纬度数组
        u10: 最近点的u10风速
        v10: 最近点的v10风速
    """
    data_folder = "geos_fp_data"
    if not os.path.exists(data_folder):
        os.makedirs(data_folder)

    # 1. 计算最近的30分钟时刻
    if dtime.minute <= 30:
        nearest_time = dtime.replace(minute=30)
    else:
        nearest_time = dtime.replace(minute=30) + timedelta(hours=1)

    nearest_year = nearest_time.year
    nearest_month = nearest_time.month
    nearest_day = nearest_time.day
    nearest_hour = nearest_time.hour
    nearest_minute = nearest_time.minute

    # 2. 构建tavg1_2d_slv_Nx文件名和URL
    date_str = f"{nearest_year}{nearest_month:02d}{nearest_day:02d}"
    time_str = f"{nearest_hour:02d}{nearest_minute:02d}"
    product_str = "tavg1_2d_slv_Nx"

    file_name = f"GEOS.fp.asm.{product_str}.{date_str}_{time_str}.V01.nc4"
    file_url = (
        f"https://portal.nccs.nasa.gov/datashare/gmao/geos-fp/das/"
        f"Y{nearest_year}/M{nearest_month:02d}/D{nearest_day:02d}/{file_name}"
    )
    local_path = os.path.join(data_folder, file_name)

    # 3. 下载文件(已有则跳过)
    download_nc_file_with_progress(file_url, local_path)

    # 4. 读取数据
    ds = xr.open_dataset(local_path)

    # 5. 读取全球U10M和V10M
    global_u10m = ds['U10M'].squeeze().values  # 自动去掉单一维度 (1, 721, 1152) -> (721, 1152)
    global_v10m = ds['V10M'].squeeze().values  # 自动去掉单一维度 (1, 721, 1152) -> (721, 1152)
    lons = ds['lon'].values
    lats = ds['lat'].values

    # 6. 找到最近的经纬度网格点
    lon_idx = np.abs(lons - lon).argmin()
    lat_idx = np.abs(lats - lat).argmin()

    # 7. 读取指定位置的风速,并转换为标量
    u10 = ds['U10M'].isel(lon=lon_idx, lat=lat_idx).squeeze().values
    v10 = ds['V10M'].isel(lon=lon_idx, lat=lat_idx).squeeze().values
    U10 = np.sqrt(u10**2 + v10**2)
    nearest_lon = ds['lon'].isel(lon=lon_idx).squeeze().values
    nearest_lat = ds['lat'].isel(lat=lat_idx).squeeze().values

    print(f'Nearest time is : {nearest_time}')
    print(f'Nearest Lon is : {nearest_lon}')
    print(f'Nearest Lat is : {nearest_lat}')
    print(f'Nearest Point U10 is : {U10:.2f}')
    ds.close()
    return global_u10m, global_v10m, lons, lats, u10, v10

# 示例调用
if __name__ == "__main__":
    lon = -179
    lat = 90
    year = 2025
    month = 3
    day = 1
    hour = 0
    minute = 56
    dtime = datetime.datetime(year, \
                              month, \
                              day, \
                              hour, minute, \
                              tzinfo=datetime.timezone.utc)
    global_u10m, global_v10m, lons, lats, u10, v10 = get_nearest_wind_speed_tavg(lon, lat, dtime)
    U10 = np.sqrt(u10**2 + v10**2)
    print(f"最近的10米平均风速: {U10:.2f} m/s")

    # 可视化U10M全球风场
    plt.figure(figsize=(8, 4))
    plt.pcolormesh(lons, lats, global_u10m, shading='auto', cmap='jet',vmin=-5, vmax=5)
    plt.colorbar(label='U10 (m/s)',pad=0.02)
    plt.xlabel('Longitude')
    plt.ylabel('Latitude')
    plt.title(f'Global 10-meter eastward wind at {dtime}')
    plt.grid(True)
    plt.show()