수치모델:단중기모델:한국형수치예보모델_kim
차이
문서의 선택한 두 판 사이의 차이를 보여줍니다.
| 양쪽 이전 판이전 판다음 판 | 이전 판 | ||
| 수치모델:단중기모델:한국형수치예보모델_kim [2024/05/29 10:54] – [한국형수치예보모델(KIM)] admin | 수치모델:단중기모델:한국형수치예보모델_kim [2026/08/19 09:23] (현재) – [1. 개요] 118.235.66.82 | ||
|---|---|---|---|
| 줄 1: | 줄 1: | ||
| ====== | ====== | ||
| [[수치모델: | [[수치모델: | ||
| - | ===== 1. 개요 ===== | + | ===== 1. 개용 |
| ==== 1.1. 수치예보 설명 ==== | ==== 1.1. 수치예보 설명 ==== | ||
| 수치예보(Numerical Weather Prediction : NWP)란 대기현상의 역학 및 물리적 원리에 대한 지배방정식들을 컴퓨터를 활용하여 연속적으로 수치적분함으로써 현재의 대기상태를 분석하고 나아가 미래의 대기상태를 정략적으로 예측하는 일련의 과정입니다. | 수치예보(Numerical Weather Prediction : NWP)란 대기현상의 역학 및 물리적 원리에 대한 지배방정식들을 컴퓨터를 활용하여 연속적으로 수치적분함으로써 현재의 대기상태를 분석하고 나아가 미래의 대기상태를 정략적으로 예측하는 일련의 과정입니다. | ||
| 줄 102: | 줄 102: | ||
| - | ===== 2. 현업 수치예모보모델 ===== | + | ===== 2. 현업 수치예보모델 ===== |
| ==== 2.1. KIM-전구(KIM-Global) ==== | ==== 2.1. KIM-전구(KIM-Global) ==== | ||
| === 2.1.1. 기본정보 === | === 2.1.1. 기본정보 === | ||
| 줄 149: | 줄 149: | ||
| === 2.2.2 파일명 === | === 2.2.2 파일명 === | ||
| r030_v040_ne36_{pres|unis|etas}_h{000~072}.yyyymmddhh.gb2 | r030_v040_ne36_{pres|unis|etas}_h{000~072}.yyyymmddhh.gb2 | ||
| + | |||
| + | ==== 2.3. 한국형 수치모델 grib파일별 격자 및 변수 참고 정보 ==== | ||
| + | ^ 구분 ^ 종류 ^ 파일명 ^ 격자 및 변수 정보 ^ | ||
| + | |KIM 전구 | 등압면 | kim_g128_ne36_pres_h??? | ||
| + | |KIM 전구 | 단일면 | kim_g128_ne36_unis_h??? | ||
| + | |KIM 지역(12km) | 등압면 | kim_g120_ne36_pres_h??? | ||
| + | |KIM 지역(12km) | 단일면 | kim_g120_ne36_unis_h??? | ||
| + | |KIM 지역(3km) | 등압면 | r030_v040_ne36_pres_h??? | ||
| + | |KIM 지역(3km) | 단일면 | r030_v040_ne36_unis_h??? | ||
| + | |||
| ===== 3. 검색 및 획득 ===== | ===== 3. 검색 및 획득 ===== | ||
| 줄 161: | 줄 171: | ||
| * 호출 방법: {{기상관측: | * 호출 방법: {{기상관측: | ||
| + | ===== 4. 활용방안 ===== | ||
| + | |||
| + | 소스코드: | ||
| + | |||
| + | === 4.1. 라이브러리 설치 === | ||
| + | |||
| + | <code py> | ||
| + | # API 요청을 위한 requests 라이브러리와 | ||
| + | # 지도 표출을 위한 cartopy 라이브러리, | ||
| + | # 연직단면도 표출을 위한 metpy 라이브러리를 설치합니다. | ||
| + | %pip install requests cartopy metpy | ||
| + | </ | ||
| + | |||
| + | === 4.2. KIM 모델 자료 다운로드 === | ||
| + | |||
| + | 기상청 API허브에 회원가입 후 자신의 인증키를 사용하여 API 요청을 할 수 있습니다. | ||
| + | |||
| + | API 인증키를 코드상에 직접 남겨 사용하는 방식은 다른 사람에게 코드를 공유할때 인증키가 노출되는 문제가 생길 수 있습니다. | ||
| + | |||
| + | 따라서 인증키를 환경변수로 등록하여 코드상에 노출되지 않도록 사용하는 것이 바람직합니다. | ||
| + | |||
| + | <code py> | ||
| + | import os # 환경변수를 불러오는 표준 라이브러리 | ||
| + | |||
| + | # 바람직하지 않은 방법 | ||
| + | # my_api_key = ' | ||
| + | |||
| + | # 인증키를 ' | ||
| + | my_api_key = os.environ[' | ||
| + | </ | ||
| + | |||
| + | <code py> | ||
| + | import requests | ||
| + | import numpy as np # 배열을 다루는 라이브러리 | ||
| + | |||
| + | # KIM 자료를 txt파일로 조회하는 API url | ||
| + | api_url = ' | ||
| + | |||
| + | # 요청인자로 모델 구분, 모델 기반 종류, 자료 종류, 변수명, 고도, | ||
| + | # 사용영역 및 격자영역, | ||
| + | |||
| + | # 1. 모델 구분으로 KIMG를 입력합니다. | ||
| + | group = ' | ||
| + | |||
| + | # 2. 모델 기반 종류로 NE36을 입력합니다. | ||
| + | nwp = ' | ||
| + | |||
| + | # 3. 자료 종류로 등압면(P), | ||
| + | # 여기선 등압면을 사용합니다. | ||
| + | data = ' | ||
| + | |||
| + | # 4. 변수명을 입력합니다. | ||
| + | # 여기선 사용할 변수명으로 기온(Air Temperature, | ||
| + | name = ' | ||
| + | |||
| + | # 5. 고도를 입력합니다. | ||
| + | # 여기선 등압면 24개 레벨을 모두 사용합니다. | ||
| + | pres_levels = np.array([ | ||
| + | 1000, 975, 950, 925, 900, 875, 850, 800, 750, 700, 650, 600, 550, | ||
| + | 500, 450, 400, 350, 300, 250, 200, 150, 100, 70, 50 | ||
| + | ]) | ||
| + | |||
| + | # 6. 사용영역을 전체(F), 일부(S), LCC변환 일부(R)중 하나를 입력합니다. | ||
| + | # 여기선 동아시아 일부 영역을 사용하므로 S를 입력합니다. | ||
| + | # 동아시아 영역의 좌표를 입력하기 위해 전체 영역이 사용하는 좌표계를 이해해야 합니다. | ||
| + | # 전체 영역은 위도[-89.9375, | ||
| + | # 경도[0, 360)를 2880개로 나눈 격자 좌표(0.125간격)를 사용합니다. | ||
| + | # 동아시아 영역이 나타내는 범위를 위도 25.8125 ~ 50.3125, 경도 104 ~ 148로 가정합니다. | ||
| + | # 마지막으로 격자영역은 xmin, ymin, xmax, ymax 형태로 입력합니다. | ||
| + | map = ' | ||
| + | degree_interval = 0.125 | ||
| + | lat_1, lon_1, lat_2, lon_2 = 25.8125, 104, 50.3125, 148 | ||
| + | |||
| + | # 격자 좌표를 계산하기위해 전체영역의 시작 위도를 더해줍니다. | ||
| + | # 그리고 첫 시작점을 1부터 시작시키며 정수로 변환합니다. | ||
| + | sub = ( | ||
| + | np.array([lon_1, | ||
| + | ).astype(int) | ||
| + | |||
| + | # 7. 분석시간을 년월일시(UTC)로 입력합니다. | ||
| + | # 예시로 2023년 8월 9일 06시(UTC)를 입력합니다. | ||
| + | tmfc_utc = ' | ||
| + | |||
| + | # 8. 예측시각을 입력합니다. | ||
| + | # 예시로 24시간 후의 예측을 입력합니다. | ||
| + | hf = 24 | ||
| + | |||
| + | # 9. 마지막으로 제공형태를 ASCI(A), Binary(B) 중 하나를 입력합니다. | ||
| + | # 활용 편의를 위해 이진형태의 파일을 선택합니다. | ||
| + | disp = ' | ||
| + | |||
| + | # 파일 명명 규칙에 따라 24개 레벨의 파일 이름을 저장할 배열을 정의합니다. | ||
| + | # API 응답을 저장할 파일 형식은 bin 파일로 저장합니다. | ||
| + | file_name_list = np.array([ | ||
| + | f" | ||
| + | for level in pres_levels | ||
| + | ]) | ||
| + | |||
| + | # 파일 목록을 출력합니다. | ||
| + | file_name_list | ||
| + | </ | ||
| + | | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | |||
| + | <code py> | ||
| + | # 각 파일의 존재 여부를 확인합니다. | ||
| + | file_exists_list = np.vectorize(os.path.exists)(file_name_list) | ||
| + | |||
| + | # 저장된 파일이 없는 경우에만 API를 요청해 파일을 다운로드합니다. | ||
| + | if not file_exists_list.all(): | ||
| + | # API 요청인자들을 묶어 dictionary로 정의합니다. | ||
| + | api_parameters = { | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | } | ||
| + | # 파일 다운로드를 위한 세션을 만듭니다. | ||
| + | with requests.Session() as session: | ||
| + | # 존재하지 않는 파일에 대해서 반복합니다. | ||
| + | for idx in np.where(~file_exists_list)[0]: | ||
| + | # API 요청인자에 레벨을 입력합니다. | ||
| + | api_parameters[' | ||
| + | |||
| + | # API 요청인자와 함께 API 요청 | ||
| + | response = session.get(api_url, | ||
| + | |||
| + | # 잘못된 응답을 받거나 짧은 에러메세지를 응답으로 받은 경우 에러 메세지 출력 | ||
| + | if response.status_code != 200 or len(response.content) < 300: | ||
| + | raise requests.RequestException( | ||
| + | response.content.decode(' | ||
| + | ) | ||
| + | else: | ||
| + | # 그 외의 올바른 응답에 대해서만 파일로 저장합니다. | ||
| + | with open(file_name_list[idx], | ||
| + | f.write(response.content) | ||
| + | print(f' | ||
| + | </ | ||
| + | kimg_ne36_pres_T_p000_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p975_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p950_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p925_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p900_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p875_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p850_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p800_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p750_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p700_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p650_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p600_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p550_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p500_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p450_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p400_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p350_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p300_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p250_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p200_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p150_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p100_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p070_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | kimg_ne36_pres_T_p050_h024.2023080906.bin 파일 다운로드 완료 | ||
| + | |||
| + | === 4.3. 연직단면도 표출 === | ||
| + | |||
| + | 다운로드받은 24개 레벨의 파일을 읽어 하나의 xarray 데이터셋으로 합쳐서 다룹니다. | ||
| + | |||
| + | <code py> | ||
| + | import xarray as xr # 자료를 다루기 위한 라이브러리 | ||
| + | |||
| + | # 24개 레벨의 파일을 읽어 xarray 데이터셋을 만듭니다. | ||
| + | # xarray 데이터셋은 크게 variables, coordinates, | ||
| + | # attributes는 데이터에 사용된 각종 정보들을 나타냅니다. | ||
| + | xr_array = xr.Dataset( | ||
| + | # variable은 실제 데이터를 담고 있는 변수로 변수이름과 (차원, 데이터)를 dictionary 형태로 입력합니다. | ||
| + | # 데이터는 파일로부터 읽어들이며 이진 파일의 앞의 4byte, 4byte는 데이터의 가로, 세로 격자수를 나타내며 | ||
| + | # 나머지 데이터는 4byte 소수형으로 읽어들입니다. | ||
| + | data_vars={ | ||
| + | ' | ||
| + | [' | ||
| + | np.fromfile(f, | ||
| + | sub[3] - sub[1] + 1, sub[2] - sub[0] + 1 | ||
| + | ) | ||
| + | for f in file_name_list])) | ||
| + | ) | ||
| + | }, | ||
| + | # coordinates는 variable의 차원을 나타내는 변수입니다. | ||
| + | # 앞서 variable의 차원부분에 입력한 이름에 맞게 실제 차원 수를 dictionary 형태로 입력합니다. | ||
| + | coords={ | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | }, | ||
| + | # attributes는 데이터에 사용된 각종 정보들을 나타냅니다. | ||
| + | # API 요청시 사용한 인자들을 dictionary 형태로 입력합니다. | ||
| + | attrs={ | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | } | ||
| + | ) | ||
| + | |||
| + | # 생성한 xarray 데이터셋을 출력합니다. | ||
| + | xr_array | ||
| + | </ | ||
| + | < | ||
| + | Dimensions: | ||
| + | Coordinates: | ||
| + | * isobaric | ||
| + | * lat (lat) float64 2kB 25.81 25.94 26.06 26.19 ... 50.06 50.19 50.31 | ||
| + | * lon (lon) float64 3kB 104.0 104.1 104.2 104.4 ... 147.8 147.9 148.0 | ||
| + | Data variables: | ||
| + | temperature | ||
| + | Attributes: | ||
| + | group: | ||
| + | nwp: NE36 | ||
| + | data: P | ||
| + | name: T | ||
| + | map: S | ||
| + | sub: 833, | ||
| + | tmfc: | ||
| + | hf: 24 | ||
| + | |||
| + | 연직단면도를 표출하기 앞서, 기압 레벨에 따른 한반도 지역의 기온 분포도를 표출합니다. | ||
| + | |||
| + | <code py> | ||
| + | import matplotlib.pyplot as plt # 이미지 표출에 사용되는 라이브러리 | ||
| + | import cartopy.crs as ccrs # 지도 표출에 사용되는 라이브러리 | ||
| + | import cartopy.feature as cfeature | ||
| + | from cartopy.crs import PlateCarree # 지도 표출에 사용되는 라이브러리 | ||
| + | |||
| + | # 분포도에서 색상표 위치, 크기를 조절하는 함수 | ||
| + | from mpl_toolkits.axes_grid1 import make_axes_locatable | ||
| + | |||
| + | # 비교할 분포도는 1000hPa과 200hPa입니다. | ||
| + | # 분포도 내의 최대, 최소 값을 이용해 값의 범위를 구합니다. | ||
| + | isobaric_1 = 1000 | ||
| + | isobaric_2 = 200 | ||
| + | isobaric_1_arr = xr_array[' | ||
| + | isobaric_2_arr = xr_array[' | ||
| + | vmin1, vmax1 = isobaric_1_arr.min() - isobaric_1_arr.min() % 5, isobaric_1_arr.max() - isobaric_1_arr.max() % 5 + 5 | ||
| + | vmin2, vmax2 = isobaric_2_arr.min() - isobaric_2_arr.min() % 5, isobaric_2_arr.max() - isobaric_2_arr.max() % 5 + 5 | ||
| + | |||
| + | # 이미지를 그릴 크기를 지정합니다. | ||
| + | kw = {' | ||
| + | fig, axes = plt.subplots(1, | ||
| + | fig.tight_layout(pad=3) | ||
| + | |||
| + | # 이미지의 제목을 지정합니다. | ||
| + | fig.suptitle(f" | ||
| + | axes[0].set_title(f" | ||
| + | axes[1].set_title(f" | ||
| + | |||
| + | # 이미지를 각 영역에 그립니다. | ||
| + | im1 = axes[0].imshow(isobaric_1_arr, | ||
| + | im2 = axes[1].imshow(isobaric_2_arr, | ||
| + | |||
| + | # 이미지에 해안선과 경계를 표시합니다. | ||
| + | axes[0].coastlines() | ||
| + | axes[1].coastlines() | ||
| + | axes[0].add_feature(cfeature.STATES.with_scale(' | ||
| + | axes[1].add_feature(cfeature.STATES.with_scale(' | ||
| + | |||
| + | # 색상표의 크기와 위치를 조절합니다. | ||
| + | divider1 = make_axes_locatable(axes[0]) | ||
| + | cax1 = divider1.append_axes(' | ||
| + | divider2 = make_axes_locatable(axes[1]) | ||
| + | cax2 = divider2.append_axes(' | ||
| + | |||
| + | # 색상표에 표시될 글자 크기, 제목, 범위를 설정합니다. | ||
| + | cbar1 = fig.colorbar(im1, | ||
| + | cbar2 = fig.colorbar(im2, | ||
| + | cbar1.ax.tick_params(labelsize=8) | ||
| + | cbar1.ax.set_yticks(np.linspace(vmin1, | ||
| + | cbar1.ax.set_title(' | ||
| + | cbar2.ax.tick_params(labelsize=8) | ||
| + | cbar2.ax.set_yticks(np.linspace(vmin2, | ||
| + | cbar2.ax.set_title(' | ||
| + | |||
| + | # 그린 이미지를 표출합니다. | ||
| + | plt.show() | ||
| + | </ | ||
| + | |||
| + | {{: | ||
| + | |||
| + | 마지막으로 연직단면도를 표출합니다. | ||
| + | |||
| + | <code py> | ||
| + | from metpy.interpolate import cross_section # 연직단면도 계산을 위한 함수 | ||
| + | from pyproj import CRS # 좌표계 형식을 사용하기 위한 라이브러리 | ||
| + | |||
| + | |||
| + | # 연직단면도를 위한 두 점의 좌표를 위·경도 좌표로 입력합니다. | ||
| + | start = (36.9, 120.7) | ||
| + | end = (37.5, 133.4) | ||
| + | |||
| + | # metpy를 활용하기 위해 데이터가 좌표계(CRS)를 인식할 수 있도록 CRS를 등록(assign_crs)합니다. | ||
| + | xr_array = xr_array.metpy.assign_crs(CRS(' | ||
| + | |||
| + | # 데이터를 두 점을 연결하는 선으로 자른 연직단면도로 변환합니다. | ||
| + | cross = cross_section(xr_array, | ||
| + | |||
| + | # 변환된 연직단면도 데이터를 표출합니다. | ||
| + | # 기온 변수의 차원이 lat, lon좌표에서 index(100개)로 변환된 것을 확인할 수 있습니다. | ||
| + | cross | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | Dimensions: | ||
| + | Coordinates: | ||
| + | * isobaric | ||
| + | metpy_crs | ||
| + | lon (index) float64 800B 120.7 120.8 121.0 ... 133.1 133.3 133.4 | ||
| + | lat (index) float64 800B 36.9 36.91 36.93 36.94 ... 37.5 37.5 37.5 | ||
| + | * index (index) int64 800B 0 1 2 3 4 5 6 7 ... 92 93 94 95 96 97 98 99 | ||
| + | Data variables: | ||
| + | temperature | ||
| + | Attributes: | ||
| + | group: | ||
| + | nwp: NE36 | ||
| + | data: P | ||
| + | name: T | ||
| + | map: S | ||
| + | sub: 833, | ||
| + | tmfc: | ||
| + | hf: 24 | ||
| + | |||
| + | <code py> | ||
| + | # 연직단면도를 그릴 크기를 지정합니다. | ||
| + | fig, ax = plt.subplots(figsize=(16, | ||
| + | |||
| + | # 기온을 표출하기 위해 등치선을 채우는 방식(filled contour, contourf)을 사용합니다. | ||
| + | # 레벨은 최소값과 최대값을 이용해 구합니다. | ||
| + | temperature_contour = ax.contourf( | ||
| + | cross[' | ||
| + | levels=np.arange( | ||
| + | cross[' | ||
| + | cross[' | ||
| + | ) | ||
| + | ) | ||
| + | # 등치선의 색상표를 표출합니다. | ||
| + | temperature_colorbar = fig.colorbar(temperature_contour, | ||
| + | temperature_colorbar.ax.set_title(' | ||
| + | |||
| + | # Y축을 로그 스케일(log scale)로 변환합니다. 또, 표출할 범위를 지정합니다. | ||
| + | ax.set_yscale(' | ||
| + | ax.set_ylim(cross[' | ||
| + | ax.set_yticks(np.arange(1000, | ||
| + | ax.set_yticklabels(np.arange(1000, | ||
| + | |||
| + | # 데이터의 좌표계를 정의하고, | ||
| + | data_crs = cross[' | ||
| + | ax_inset = fig.add_axes([0.85, | ||
| + | |||
| + | # start, end 두 점의 위·경도 좌표를 지도가 사용하는 좌표계로 변환하여 지도에 표출합니다. | ||
| + | # 또, 앞서 그린 분포도를 표출합니다. | ||
| + | endpoints = data_crs.transform_points( | ||
| + | ccrs.Geodetic(), | ||
| + | ) | ||
| + | ax_inset.imshow(isobaric_2_arr, | ||
| + | ax_inset.scatter(endpoints[:, | ||
| + | ax_inset.plot(cross[' | ||
| + | |||
| + | # 지도에 해안선 및 경계를 표출합니다. | ||
| + | ax_inset.coastlines() | ||
| + | ax_inset.add_feature(cfeature.STATES.with_scale(' | ||
| + | |||
| + | # 단면도의 제목과 축의 라벨을 지정합니다. | ||
| + | ax_inset.set_title('' | ||
| + | ax.set_title(f' | ||
| + | ax.set_ylabel(' | ||
| + | ax.set_xlabel(' | ||
| + | temperature_colorbar.set_label(' | ||
| + | |||
| + | # 단면도를 표출합니다. | ||
| + | plt.show() | ||
| + | </ | ||
| + | {{: | ||