def find_lat_index_bins(basin, lat): """ find index of latitude bin in coefficients list Parameters ---------- basin : basin. lat : latitude position. Returns ------- latindex : index of bin. """ s, monthdummy, lat0, lat1, lon0, lon1 = Basins_WMO(basin) base = 5 latindex = np.floor(float(lat - lat0) / base) return latindex
def Check_if_landfall(lat, lon, basin, land_mask): """ Parameters ---------- lat : latitude position of TC lon : longitude position of TC lat1 : upper left corner latitude coordinate of basin lon0 : upper left corner longitude coordinate of basin land_mask : land-sea mask Returns ------- l : 0=no landfall, 1=landfall """ s, monthdummy, lat0_WMO, lat1_WMO, lon0_WMO, lon1_WMO = Basins_WMO(basin) x = int(10 * (lon - lon0_WMO)) y = int(10 * (lat1_WMO - lat)) l = land_mask[y, x] return l
#============================================================================== # Step 1: Define basin and number of years to run #============================================================================== #please set basin (EP,NA,NI,SI,SP,WP) basin = 'EP' loop = 0 #ranges between 0 and 9 to simulate slices of 1000 years total_years = 1000 #set the total number of years you'd like to simulate TC_data = [ ] #This list is composed of: [year,storm number,lat,lon,pressure,wind,rmax,category,Holland B parameter,precipitation,landfall flag] #============================================================================== # Step 2: load grid with weighted genesis counts #============================================================================== for year in range(0, total_years): storms_per_year, genesis_month, lat0, lat1, lon0, lon1 = Basins_WMO(basin) if storms_per_year > 0: #============================================================================== # Step 3: Generate (list of) genesis locations #============================================================================== lon_genesis_list, lat_genesis_list = Startingpoint( storms_per_year, genesis_month, basin) #============================================================================== # Step 4: Generate initial conditions #============================================================================== latlist, lonlist, landfalllist = TC_movement(lon_genesis_list, lat_genesis_list, basin) TC_data = TC_pressure(basin, latlist, lonlist, landfalllist, year,
def TC_movement(lon_genesis_list, lat_genesis_list, basin): """ Parameters ---------- lon_genesis_list : list of longitudinal positions of genesis in a year lat_genesis_list : list of latitudinal positions of genesis in a year basin : basin Returns --------- latall : all latitude positions of the eye of TC for every TC in a year lonall : all longitude positions of the eye of TC for every TC in a year landfallall : landfall (0=no 1=yes) along the track for every TC in a year """ basins = ['EP', 'NA', 'NI', 'SI', 'SP', 'WP'] basin_name = dict(zip(basins, [0, 1, 2, 3, 4, 5])) idx = basin_name[basin] constants_all = np.load(os.path.join(__location__, 'TRACK_COEFFICIENTS.npy'), allow_pickle=True, encoding='latin1').item() land_mask = np.loadtxt( os.path.join(__location__, 'Land_ocean_mask_' + str(basin) + '.txt')) constants = constants_all[idx] s, monthdummy, lat0, lat1, lon0, lon1 = Basins_WMO(basin) latall = [] lonall = [] landfallall = [] for lat_genesis, lon_genesis in zip(lat_genesis_list, lon_genesis_list): #load data for longitude/latitude latlijst = [] lonlijst = [] landfalllijst = [] lat = lat_genesis lon = lon_genesis latlijst.append(lat) lonlijst.append(lon) landfall = Check_if_landfall(lat, lon, lat1, lon0, land_mask) #1=landfall 0=no landfall landfalllijst.append(landfall) var = 0 #var is the 'stop-parameter'. The track generation ends when the storm moves over land or out of the basin while var == 0: ind = int(find_lat_index_bins(basin, lat)) #constants values for latitude/longitude [ a0, a1, b0, b1, b2, Elatmu, Elatstd, Elonmu, Elonstd, Dlat0mu, Dlat0std, Dlon0mu, Dlon0std ] = constants[ind] if len( latlijst ) == 1: #if this is the first time step after genesis, we need to sample the first change in lon/lat/pressure dlat0 = np.random.normal(Dlat0mu, Dlat0std, 1) dlon0 = np.random.normal(Dlon0mu, Dlon0std, 1) dlat1 = LAT_JAMES_MASON(dlat0, lat, b0, b1, b2) if basin == 'SP' or basin == 'SI': if lat > -10.: dlat1 = float(dlat1 - np.abs(np.random.normal(Elatmu, Elatstd))) else: dlat1 = float(dlat1 + np.random.normal(Elatmu, Elatstd)) else: if lat < 10.: dlat1 = float(dlat1 + np.abs(np.random.normal(Elatmu, Elatstd))) else: dlat1 = float(dlat1 + np.random.normal(Elatmu, Elatstd)) dlon1 = LON_JAMES_MASON(dlon0, a0, a1) epsilon = np.random.normal(Elonmu, Elonstd) dlon1 = float(dlon1 + epsilon) if np.abs(lat) >= 45: if dlon1 < 0.: dlon1 = 0 lat = round(dlat1 + lat, 1) lon = round(dlon1 + lon, 1) dlat0 = dlat1 dlon0 = dlon1 if lat <= lat1 - 0.1 and lat > lat0 and lon <= lon1 - 0.1 and lon > lon0: #if storm is still inside the domain. The 0.1's are added to make sure we don't end up at the upper/rightmost edge latlijst.append(lat) lonlijst.append(lon) landfall = Check_if_landfall( lat, lon, lat1, lon0, land_mask) #1=landfall 0=no landfall landfalllijst.append(landfall) else: #the storm has moved out of the domain. Stop the algorithm var = 1 latall.append( latlijst ) #all locations of all storms (seperate entries) are contained in these two lists lonall.append(lonlijst) landfallall.append(landfalllijst) return (latall, lonall, landfallall)
def confidence_interval(data, confidence=0.95): a = 1.0 * np.array(data) n = len(a) m, se = np.mean(a), scipy.stats.sem(a) h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1) return m+h, m-h #============================================================================== # Make a list of different return periods #============================================================================== returnperiods=[] returnperiods.extend(np.linspace(10,100,10)) returnperiods.extend(np.linspace(200,1000,9)) returnperiods.extend(np.linspace(2000,10000,9)) lat0,lat1,lon0,lon1=Basins_WMO(basin)[2:] # ============================================================================= # Make a list of all lon/lat points in the basin # ============================================================================= res=0.1 if lat0>0: latspace=np.arange(lat0+res/2.,lat1+res/2.,res) else: latspace=np.arange(lat0-res/2.,lat1-res/2.,res) lonspace=np.arange(lon0+res/2.,lon1+res/2.,res) points=[(i,j) for i in latspace for j in lonspace] all_data={i:[] for i in range(len(points))}
def TC_pressure(basin, latlist, lonlist, landfalllist, year, storms, monthlist, TC_data): """ Calculate TC pressure Parameters ---------- basin : basin. latlist : array of TC track latitude positions. lonlist : array of TC track longitude positions. landfalllist : array of TC landfall (0=no 1=yes). year : year storms : number of storms. monthlist : months of TC occurrence. TC_data : array of TC data. Returns ------- TC_data : array of TC data + new TCs """ basin_name = dict( zip(['EP', 'NA', 'NI', 'SI', 'SP', 'WP'], [0, 1, 2, 3, 4, 5])) idx = basin_name[basin] latidx_penv = np.linspace(90, -90, 721) lonidx_penv = np.linspace(0, 359.75, 1440) JM_pressure = np.load( os.path.join(__location__, 'COEFFICIENTS_JM_PRESSURE.npy')).item() Genpres = np.load(os.path.join(__location__, 'DP0_PRES_GENESIS.npy')).item() WPR_coefficients = np.load( os.path.join(__location__, 'COEFFICIENTS_WPR_PER_MONTH.npy')).item() Genwind = np.load(os.path.join(__location__, 'GENESIS_WIND.npy')).item() intlist = [5, 3, 2, 5, 5, 5] int_thres = intlist[idx] s, monthdummy, lat0, lat1, lon0, lon1 = Basins_WMO(basin) wind_threshold = 18. #if vmax<18, the storm is a tropical depression and we stop tracking it. for storm_number, month, latfull, lonfull, landfallfull in zip( range(0, int(storms)), monthlist, latlist, lonlist, landfalllist): i = 0 vmax = 0 count = 0 p = np.nan #This is the full MSLP field, with lat0=90 deg, lat1=-90 deg, lon0=0 deg, lon1=359.75 deg. len(lat)=721, len(lon)=1440 Penv_field = np.loadtxt( os.path.join(dir_path, 'Monthly_mean_MSLP_' + str(month) + '.txt')) constants_pressure = JM_pressure[idx][month] constants_pressure = np.array(constants_pressure) coef = WPR_coefficients[idx][month] coef = np.array(coef) p_threshold = min(constants_pressure[:, 6]) - 10. EP = Genpres[idx][month] while i < len(latfull): lat, lon, landfall = latfull[i], lonfull[i], landfallfull[i] lat_dummy = np.abs(latidx_penv - lat).argmin() lon_dummy = np.abs(lonidx_penv - lon).argmin() Penv = Penv_field[lat_dummy, lon_dummy] if lat0 <= lat <= lat1 and lon0 <= lon <= lon1: #make sure we're inside the basin if ((p < p_threshold) | math.isnan(p)): #something went wrong. start again i = 0 vmax = 0 if i == 0: vmax = random.choice(Genwind[idx][month]) p = Calculate_Pressure(vmax, Penv, coef) pressure_list = [] wind_list = [] #at genesis, we need to sample the genesis pressure and dp1. This is done basin-wide: [Pmu, Pstd, DP0mu, DP0std, dpmin, dpmax] = EP dp0 = np.random.normal(DP0mu, DP0std) dp1 = -1. * np.abs(dp0) pressure_list.append(p) wind_list.append(vmax) i = i + 1 #next: check if the storm makes landfall. In that case, we move to the dissipation-formula if landfall == 1: #landfall --> use Kaplan and DeMaria Formula for dissipation if ((p < p_threshold) | math.isnan(p)): print('Landfall', p, p_threshold) i = 0 vmax = 0 elif vmax < wind_threshold or p > Penv: #The storm makes landfall as a tropical depression TC_data = add_parameters_to_TC_data( pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 1000000000000000 else: #calculate the landfall pressure ind = int( find_index_pressure( basin, lat, lon, lat0, lon0, lon1)) #find index for pressure [c0, c1, c2, c3, EPmu, EPstd, mpi] = constants_pressure[ind] y = PRESSURE_JAMES_MASON(dp1, p, c0, c1, c2, c3, mpi) epsilon = np.random.normal(EPmu, EPstd) dp0 = float(y + epsilon) while dp0 < dpmin: #if more intensification than seen in the underlying dataset if y - dpmin > EPmu - 2. * EPstd: #epsilon should be resampled epsilon = np.random.normal(EPmu, EPstd) dp0 = y + epsilon else: #y is already smaller than dpmin. dp0 = np.random.uniform(dpmin, 0) while dp0 > dpmax: #if more weakening than seen in the underlying dataset if y - dpmax < EPmu + 2. * EPstd: epsilon = np.random.normal(EPmu, EPstd) dp0 = y + epsilon else: dp0 = np.random.uniform(0, dpmax) if p < mpi: #if pressure has dropped below mpi if dp0 < 0: #if intensification if count < 2: #if intensification has been going on for less than 2 time steps count = count + 1 else: dp0 = abs(dp0) else: #the storm is above mpi count = 0 p = round(dp0 + p, 1) dp1 = dp0 if vmax < wind_threshold or p > Penv: #The storm is no longer a tropical storm #print('Dissipated',len(pressure_list),len(landfallfull)) TC_data = add_parameters_to_TC_data( pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 10000000000000000000000000000000 else: pressure_list.append(p) vmax = Calculate_Vmax(Penv, p, coef) vmax = round(vmax, 1) wind_list.append(vmax) if any( c < 1 for c in landfallfull[i:] ): #check whether the storm moves back over the ocean check_move_ocean = i + np.where( np.array(landfallfull[i:]) == 0.)[0][0] #storm moves back over open ocean: apply decay function for i till check_move_ocean if check_move_ocean > i + 3: #if this is not the case, we're crossing a very small island and no decay function should be used then decay_pressure, decay_wind = decay_after_landfall( lat, lon, latfull[i:i + check_move_ocean], lonfull[i:i + check_move_ocean], p, coef, Penv) for d in range(len(decay_pressure)): pressure_list.append(decay_pressure[d]) wind_list.append(decay_wind[d]) #if the storm has decayed before moving back over the ocean: if wind_list[-1] < wind_threshold: TC_data = add_parameters_to_TC_data( pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 10000000000000000000000000. #if the storm has not decayed: else: dp1 = pressure_list[-1] - pressure_list[-2] p = pressure_list[-1] i = check_move_ocean else: #the storm does not move back over open ocean, so use the decay function until the storm has dissipated decay_pressure, decay_wind = decay_after_landfall( lat, lon, latfull[i:], lonfull[i:], p, coef, Penv) for d in range(len(decay_pressure)): pressure_list.append(decay_pressure[d]) wind_list.append(decay_wind[d]) #print('Decayed over land',len(pressure_list),len(landfallfull)) TC_data = add_parameters_to_TC_data( pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 1000000000000 else: #no landfall if ((p < p_threshold) | math.isnan(p)): print('No landfall', p, p_threshold) i = 0 vmax = 0 elif vmax < wind_threshold or p > Penv and i > 3: #The storm is no longer a tropical storm #print('Dissipated',len(pressure_list),len(landfallfull)) TC_data = add_parameters_to_TC_data( pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 1000000000000000 else: #apply James-Mason formula to find next change in pressure ind = int( find_index_pressure( basin, lat, lon, lat0, lon0, lon1)) #find index for pressure [c0, c1, c2, c3, EPmu, EPstd, mpi] = constants_pressure[ind] y = PRESSURE_JAMES_MASON(dp1, p, c0, c1, c2, c3, mpi) epsilon = np.random.normal(EPmu, EPstd) dp0 = float(y + epsilon) while dp0 < dpmin: #if more intensification than seen in the underlying dataset if y - dpmin > EPmu - 2. * EPstd: #epsilon should be resampled epsilon = np.random.normal(EPmu, EPstd) dp0 = y + epsilon else: #y is already smaller than dpmin. dp0 = np.random.uniform(dpmin, 0) while dp0 > dpmax: #if more weakening than seen in the underlying dataset if y - dpmax < EPmu + 2. * EPstd: epsilon = np.random.normal(EPmu, EPstd) dp0 = y + epsilon else: dp0 = np.random.uniform(0, dpmax) if p < mpi: #if pressure has dropped below mpi if dp0 < 0: #if intensification if count < 2: #if intensification has been going on for less than 2 time steps count = count + 1 else: dp0 = abs(dp0) else: #the storm is above mpi count = 0 if i < int_thres: dp0 = -1. * np.abs(dp0) p = round(dp0 + p, 1) dp1 = dp0 if vmax < wind_threshold or p > Penv: #The storm is no longer a tropical storm #print('Dissipated',len(pressure_list),len(landfallfull)) TC_data = add_parameters_to_TC_data( pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 10000000000000000000000000000000 else: pressure_list.append(p) vmax = Calculate_Vmax(Penv, p, coef) vmax = round(vmax, 1) wind_list.append(vmax) i = i + 1 else: #we are outside the basin. Move on to the next storm TC_data = add_parameters_to_TC_data(pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) i = 100000000000000000. if i == len(latfull): TC_data = add_parameters_to_TC_data(pressure_list, wind_list, latfull, lonfull, year, storm_number, month, basin, landfallfull, pressure_list, TC_data, idx) return (TC_data)
def Startingpoint(no_storms, monthlist, basin): """ This function samples the genesis locations of every TC in a given year Parameters ---------- no_storms : number of TCs in given year monthlist : months in which TCs were formed. basin : basin. Returns ------- lon_coordinates : list of longitude coordinates of genesis locations lat_coordinates : list of latitude coordinates of genesis locations """ basins = ['EP', 'NA', 'NI', 'SI', 'SP', 'WP'] basin_name = dict(zip(basins, [0, 1, 2, 3, 4, 5])) idx = basin_name[basin] lon_coordinates = [] lat_coordinates = [] weighted_list_index = [] weighted_list = [] s, monthdummy, lat0, lat1, lon0, lon1 = Basins_WMO(basin) land_mask = np.loadtxt( os.path.join(dir_path, 'Land_ocean_mask_' + str(basin) + '.txt')) for month in monthlist: grid_copy = np.loadtxt( os.path.join( dir_path, 'GRID_GENESIS_MATRIX_' + str(idx) + '_' + str(month) + '.txt')) grid_copy = np.array(grid_copy) grid_copy = np.round(grid_copy, 1) #============================================================================== # Make a list with weighted averages. The corresponding grid-index is calculated as len(col)*row_index+col_index #============================================================================== for i in range(0, len(grid_copy[:, 0])): for j in range(0, len(grid_copy[0, :])): if grid_copy[i, j] > -1: value = int(10 * grid_copy[i, j]) else: value = 0 if value > 0.: for k in range(0, value): weighted_list.append(value) weighted_list_index.append(i * (len(grid_copy[0, :]) - 1) + j) #============================================================================== # The starting longitude and latitude coordinates #============================================================================== var = 0 while var == 0: idx0 = random.choice(weighted_list_index) row = int(np.floor(idx0 / (len(grid_copy[0, :]) - 1))) col = int(idx0 % (len(grid_copy[0, :]) - 1)) lat_pert = random.uniform( 0, 0.94 ) #take 0.94 to make sure the randomly selected point is still inside the grid box after rounding off. lon_pert = random.uniform(0, 0.94) lon = lon0 + round(col + lon_pert, 1) lat = lat1 - round(row + lat_pert, 1) if lon < lon1 and lat < lat1: check = Check_if_landfall( lat, lon, basin, land_mask) #make sure the coordinate isn't on land if basin == 'EP': check = Check_NA_formation(lat, lon) if basin == 'NA': check = Check_EP_formation(lat, lon) if check == 0: lon_coordinates.append(lon) lat_coordinates.append(lat) var = 1 else: var = 0 if len(lon_coordinates) == no_storms: return lon_coordinates, lat_coordinates