#!/usr/bin/env python # Wenchang Yang (wenchang@princeton.edu) # Thu Apr 16 10:27:14 AM EDT 2026 if __name__ == '__main__': import sys,os try: from misc.timer import Timer tt = Timer(f'[{os.getcwd()}] start ' + ' '.join(sys.argv)) except: pass import sys, os.path, os, glob, datetime import xarray as xr, numpy as np, pandas as pd, matplotlib.pyplot as plt #more imports wython = '/tigress/wenchang/wython' if wython not in sys.path: sys.path.append(wython); print('added to python path:', wython) from misc import get_kws_from_argv #from xtc.distance import gcdistance import xtc # if __name__ == '__main__': try: tt.check('end import') except: pass # #start from here """ #the first get_circule_points func is from Google Gemini, replaced by the np version import math def get_circle_points(lat, lon, radius_km, num_points=100): R = 6371.0 # Earth's radius in km lat1, lon1 = math.radians(lat), math.radians(lon) d_div_R = radius_km / R circle_lats, circle_lons = [], [] for theta in np.linspace(0, 2 * np.pi, num_points): lat2 = math.asin(math.sin(lat1) * math.cos(d_div_R) + math.cos(lat1) * math.sin(d_div_R) * math.cos(theta)) lon2 = lon1 + math.atan2(math.sin(theta) * math.sin(d_div_R) * math.cos(lat1), math.cos(d_div_R) - math.sin(lat1) * math.sin(lat2)) circle_lats.append(math.degrees(lat2)) circle_lons.append(math.degrees(lon2)) return circle_lats, circle_lons """ #wy: updated from the math version to numpy version def get_circle_points(lat, lon, radius_km, num_points=100): R = 6371.0 # Earth's radius in km lat1, lon1 = np.radians(lat), np.radians(lon) d_div_R = radius_km / R theta = np.linspace(0, 2 * np.pi, num_points) circle_lats = np.arcsin(np.sin(lat1) * np.cos(d_div_R) + np.cos(lat1) * np.sin(d_div_R) * np.cos(theta)) circle_lons = lon1 + np.arctan2(np.sin(theta) * np.sin(d_div_R) * np.cos(lat1), np.cos(d_div_R) - np.sin(lat1) * np.sin(circle_lats)) return np.degrees(circle_lats), np.degrees(circle_lons) radius_km = get_kws_from_argv('radius_km', 150) #km, threshold of distance to TC center radius_km = int(radius_km) wind = get_kws_from_argv('wind', 33) if wind is not None: wind = int(wind) locations = [ ('MulletPond', 29.925333, 275.662083), ('EasternLake', 30.311627, 273.906765), ] ifile = '/projects/w/wenchang/analysis/TC/HIRAM/amipLMR2019SST0850ic_tigercpu_intelmpi_18_540PE/netcdf/tc_tracks.TS.ens01.0850-1999.nc' ds0 = xr.open_dataset(ifile) if ds0.en.size == 1: ds0 = ds0.squeeze('en', drop=True) dss = [] ntcs = [] for location,lat0,lon0 in locations: print(location) #tracks ofile_tracks = os.path.basename(ifile).replace('.nc', f'.{location}.R{radius_km}km.nc') if wind is not None: ofile_tracks = ofile_tracks.replace('.nc', f'.TC{wind}.nc') if os.path.exists(ofile_tracks) and 'od' not in sys.argv: ds = xr.open_dataset(ofile_tracks) print('[loaded]:', ofile_tracks) else: d = xtc.tc_distance(ds0, lat0, lon0, units='km') if wind is not None: L = ((d<=radius_km)&(ds0.windmax>wind)).any('stage') else: L = (d<=radius_km).any('stage') ds = ds0.where(L).tc.deflate() encoding = {dname:{'zlib': True, 'complevel':1} for dname in list(ds.data_vars)} ds.to_netcdf(ofile_tracks, encoding=encoding) print('[saved]:', ofile_tracks) dss.append(ds) #count TC ofile_counts = ofile_tracks.replace('tc_tracks', 'tc_counts') if os.path.exists(ofile_counts) and 'od' not in sys.argv: ntc = xr.open_dataarray(ofile_counts) print('[loaded]:', ofile_counts) else: ntc = ds.tc.count().groupby('time.year').sum('time') ntc = ntc.assign_attrs(long_name=f'NTC', units='#') ntc.to_dataset(name='ntc').to_netcdf(ofile_counts) print('[saved]:', ofile_counts) ntcs.append(ntc) if __name__ == '__main__': from wyconfig import * #my plot settings from geoplots import mapplot fig,ax = plt.subplots(figsize=(8,4)) for (location,lat0,lon0),ds,ntc in zip(locations, dss, ntcs): print(location) color = 'C0' if location == 'MulletPond' else 'C1' NTC = ntc.sum('year').item() lon = ds.lon ds['lon'] = lon.where(~((lon<0).any('stage')), other=lon+360) ds.tc.trackplot(ezplot=True, lw=0.5, alpha=0.5, color=color, ax=ax) ax.plot(lon0, lat0, marker='x', color=color) lats, lons = get_circle_points(lat0, lon0, radius_km=radius_km) ax.plot(lons, lats, color=color, label=f'{location}, {NTC} TC{wind}') mapplot() ax.set_title('HiRAM last millennium simulation TC tracks passing two locations') ax.legend(loc='upper left', frameon=True) #savefig if 'savefig' in sys.argv or 's' in sys.argv: figname = __file__.replace('.py', f'__{location}_R{radius_km}km.png') if wind is not None: figname = figname.replace('.png', f'_TC{wind}.png') figname = figname.replace('.png', f'_tracks.png') if 'overwritefig' in sys.argv or 'o' in sys.argv: wysavefig(figname, overwritefig=True) else: wysavefig(figname) fig,ax = plt.subplots(figsize=(8,4)) for (location,lat0,lon0),ds,ntc in zip(locations, dss, ntcs): print(location) color = 'C0' if location == 'MulletPond' else 'C1' ntc.load().plot(label=location) ax.set_title('HiRAM last millennium simulation TC counts passing two locations') ax.legend(loc='upper left', frameon=True) #savefig if 'savefig' in sys.argv or 's' in sys.argv: figname = __file__.replace('.py', f'__{location}_R{radius_km}km.png') if wind is not None: figname = figname.replace('.png', f'_TC{wind}.png') figname = figname.replace('.png', f'_counts.png') if 'overwritefig' in sys.argv or 'o' in sys.argv: wysavefig(figname, overwritefig=True) else: wysavefig(figname) try: tt.check(f'**Done**') except: pass print() if 'notshowfig' in sys.argv or 'n' in sys.argv: pass else: if 'plt' in globals(): plt.show()