10. Waves emulator#

Obtain synthetic waves timeseries

inputs required:

  • Historical DWTs

  • Historical AWT

  • Synthetic timeseries of AWT

  • Historical spectrum and partitions

  • Historical wave parameters (seas & swells)

  • Synthetic wave parameters (seas & swells)

in this notebook:

  • Obtain the historical number of swells for each DWT and direction

  • Generate synthetic time series of Sea conditions (daily)

  • Generate synthetic time series of Swell parameters (daily)

  • Reconstruct hourly time series of seas and swells

  • Reconstruct wave spectrum

  • Obtain bulk parameters

  • Validate the synthetic waves

Workflow:

TITLE

The synthetic generation of wave conditions on a regular climate (DWT1-36) is accomplished by selecting one of the copula simulated parameters for each DWT/AWT in the case of seas, and by randomly selecting the number of probable swells for each direction and DWT and the associated parameters from the k-nearest simulation

In the case on waves associated to a TC event (DWT36-42), the associated simulated waves from SHyTCWaves are incorporated in following notebooks.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# common
import os
import os.path as op

# pip
import numpy as np
import pandas as pd
import xarray as xr
from datetime import datetime
import matplotlib.pyplot as plt
import itertools

# DEV: override installed teslakit
import sys
sys.path.insert(0, op.join(os.path.abspath(''), '..', '..', '..'))

# teslakit
from bluemath.teslakit2.io.aux_nc import StoreBugXdset
from bluemath.teslakit2.util.time_operations import generate_hourly_time
from bluemath.teslakit2.waves.snakes import group_by_day_dir, reconstruct_snakes
from bluemath.teslakit2.waves_emulator import seas_emulator, swells_emulator, read_hourly_sim
from bluemath.teslakit2.waves.superpoint_partitions import spec_reconstr_snakes

from bluemath.teslakit2.plotting.snakes import plot_his_sim_NumSnakes, plot_sim_snakes
from bluemath.teslakit2.plotting.emulator import plot_his_sim_waves_hist
from bluemath.teslakit2.plotting.superpoint_partitions import Plot_superpoint_spectrum, axplot_spectrum
from bluemath.teslakit2.plotting.wts import axplot_WT_Probs
Warning: ecCodes 2.21.0 or higher is recommended. You are running version 2.20.0

10.1. Files and paths#

# project path
p_data =  r'/media/administrador/HD2/SamoaTonga/data'
site = 'Samoa'
p_site = op.join(p_data, site)

# deliverable folder
p_deliv = op.join(p_site, 'd09_TESLA')

# output path 
p_out = op.join(p_deliv,'SIMULATION')
p_out_emul = op.join(p_deliv,'SIMULATION', 'climate_emulator')
p_out_waves = os.path.join(p_out_emul,'WAVES_noTCs')
p_out_spec = os.path.join(p_out_waves,'spec')

if not os.path.isdir(p_out): os.makedirs(p_out)
if not os.path.isdir(p_out_emul): os.makedirs(p_out_emul)
if not op.isdir(p_out_waves): os.makedirs(p_out_waves)
if not op.isdir(p_out_spec): os.makedirs(p_out_spec)
    
    
# input data
seas_hist_file = op.join(p_deliv,'WAVES','Sea_partition_dwt.nc')
snakes_hist_file = op.join(p_deliv,'WAVES','Snakes_Parameters_dwt.nc')
awt_his_file = op.join(p_deliv, 'SST', 'SST_KMA.nc') # for plotting

awt_sim_file = op.join(p_deliv,'SST','SST_AWT_sim.nc')  
dwt_sim_file = op.join(p_deliv,'ESTELA','DWT_sim.nc')  
snakes_sim_file = op.join(p_deliv,'WAVES' ,'Snakes_Parameters_copula_sim.nc') # copula sim
seas_sim_file = op.join(p_deliv,'WAVES', 'Sea_Parameters_copula_sim.nc') # copula sim

part_spec_file = op.join(p_deliv,'WAVES', 'partitions_spectra_chunk_1_wcut_1e-11.nc') # for spectrum reconstruction
part_stats_file = op.join(p_deliv,'WAVES', 'partitions_stats_wcut_1e-11.nc') # for spectrum reconstruction
superpoint_file = op.join(p_deliv,'WAVES', 'Super_point_superposition_15.nc') # for plotting


# output data
n_swells_file = op.join(p_out,'n_swells_fit.nc')  # number of swells/day for each direction
n_swells_file_sim = op.join(p_out,'n_swells_sim.nc')  # number of swells/day for each direction

mean_spec_file_sim = op.join(p_out_waves,'spec_mean_sim.nc')

10.2. Parameters#

# --------------------------------------
# load data and set parameters
DWTs_sim = xr.open_dataset(dwt_sim_file)
AWTs_sim = xr.open_dataset(awt_sim_file)

seas = xr.open_dataset(seas_hist_file)
snakes = xr.open_dataset(snakes_hist_file)

seas_copula_sim = xr.open_dataset(seas_sim_file)
snakes_copula_sim = xr.open_dataset(snakes_sim_file)

part_spec = xr.open_dataset(part_spec_file)
part_stats = xr.open_dataset(part_stats_file)

# number of DWTs
n_clusters = 36

10.3. Number of swells for each direction and for each DWT#

# Group snakes by day and by direction
n_swells = group_by_day_dir(snakes, n_clusters=n_clusters, amp_dir=30)

# save
n_swells.to_netcdf(n_swells_file)

#n_swells = xr.open_dataset(n_swells_file)

10.4. Climate Emulator - WAVES Simulation#

# historical. max number of snakes for one day
max_num_snakes = np.sum(n_swells.n_swells.values,axis=1)
max_num_snakes = int(np.max(max_num_snakes))
# Add AWT (needed for simulating seas)

# AWT to daily scale
AWTs_sim = AWTs_sim.resample(time='1D').pad()
AWTs_sim = AWTs_sim.sel(time=slice('2020-01-01','2100-01-01')) 

DWTs_sim['evbmus_sims_awt'] = (('time', 'n_sim'),AWTs_sim.evbmus_sims.values)
import time

# --------------------------------------
#  Climate Emulator. WAVES simulation NO TCs


# each DWT series will generate a different set of waves

for n in DWTs_sim.n_sim:
    print('- Sim: {0} -'.format(int(n)+1))

    start = time.time()
    
    # Select simulation of DWTs
    DWTs_sim_n = DWTs_sim.isel(n_sim=n)
    
   
    #---------------------------
    # emulate seas
    #---------------------------
    print('Simulating Seas')
    sea_params_sim = seas_emulator(DWTs_sim_n, seas_copula_sim)
    
    # save
    sea_params_sim.to_netcdf(op.join(p_out_waves,'sea_params_{0:08d}.nc'.format(int(n))))
    
    # to hourly
    sea_params_sim = sea_params_sim.resample(time='1H').pad() 
    
    
    #---------------------------
    # emulate swell parameters
    #---------------------------
    print('Simulating Swells')
    snake_params_sim = swells_emulator(n, DWTs_sim_n, n_swells, snakes_copula_sim, max_num_snakes, p_out_waves)
    
    # save
    StoreBugXdset(snake_params_sim, op.join(p_out_waves, 'snakes_params_{0:08d}.nc'.format(int(n)) ))
    
    
    #---------------------------
    # reconstruct swells 
    years = snake_params_sim.time.dt.year.values
    for yy in np.arange(years[0],years[-1]):

        # data for one year
        y_ini = str(yy) + '-01-01'
        y_end = str(yy+1) + '-01-01'        
        snake_params_sim_y = snake_params_sim.sel(time=slice(y_ini,y_end))
       
        # reconstruct snakes (snakes parameters to hourly snakes)
        time_h, hs_swell, tp_swell, dir_swell = reconstruct_snakes(max_num_snakes, snake_params_sim_y, 'sim')
        
        
        #---------------------------
        # reconstruct spectrum
        spectro, bulk = spec_reconstr_snakes(part_spec, part_stats, time_h, hs_swell, tp_swell, dir_swell, sea_params_sim, seaswell='both')
        
        
        # Join years of agregated snakes and spectrum
        if yy == years[0]:
            bulk_params = bulk.copy(deep=True)
            
        else:
            bulk_params = xr.concat([bulk_params, bulk], dim='time')
        
        # save spectrum for each year
        StoreBugXdset(spectro, op.join(p_out_spec, 'spectrum_hourly_{0:08d}_{1}.nc'.format(int(n),yy)))
        
    # save each sim
    print('sim completed')
    StoreBugXdset(bulk_params, op.join(p_out_waves, 'bulk_params_hourly_{0:08d}.nc'.format(int(n))))

        
    end = time.time()
    print((end - start)/3600)
    print()
    

10.5. Climate Emulator Simulation Validation#

10.5.1. Validate emulator: Seas (daily values)#

# --------------------------------------
# Select Simulation to plot

n_sim = 39
sea_params_sim = xr.open_dataset(op.join(p_out_waves, 'sea_params_{0:08d}.nc'.format(int(n_sim))))        
    
# Compare historical and simulated seas (all DWTs)
seas_his = seas.copy(deep=True)
seas_his = seas_his.rename({'hs':'Hs_sea', 'tp':'Tp_sea', 'dpm':'Dir_sea'})

sea_sim = sea_params_sim.copy(deep=True)
sea_sim = sea_sim.drop({'evbmus_sims', 'evbmus_sims_awt'})


plot_his_sim_waves_hist(seas_his, sea_sim, title='Seas from all DWTs')
_images/10_Waves_Emulator_16_0.png
# Compare historical and simulated seas for each DWTs

seas_his = seas.copy(deep=True)
seas_his = seas_his.rename({'hs':'Hs_sea', 'tp':'Tp_sea', 'dpm':'Dir_sea'})

seas_sim = sea_params_sim.copy(deep=True)

for wt in range(n_clusters):
    
    seas_his_wt = seas_his.where(seas_his.bmus==wt, drop=True)
    seas_sim_wt = seas_sim.where(seas_sim.evbmus_sims==wt+1, drop=True)
    
    seas_sim_wt = seas_sim_wt.drop({'evbmus_sims','evbmus_sims_awt'})

    plot_his_sim_waves_hist(seas_his_wt, seas_sim_wt, title='Seas from DWT ' +  str(wt+1) + '. ' + str(len(seas_his_wt.time)) + ' data')
_images/10_Waves_Emulator_17_0.png _images/10_Waves_Emulator_17_1.png _images/10_Waves_Emulator_17_2.png _images/10_Waves_Emulator_17_3.png _images/10_Waves_Emulator_17_4.png _images/10_Waves_Emulator_17_5.png _images/10_Waves_Emulator_17_6.png _images/10_Waves_Emulator_17_7.png _images/10_Waves_Emulator_17_8.png _images/10_Waves_Emulator_17_9.png _images/10_Waves_Emulator_17_10.png _images/10_Waves_Emulator_17_11.png _images/10_Waves_Emulator_17_12.png _images/10_Waves_Emulator_17_13.png _images/10_Waves_Emulator_17_14.png _images/10_Waves_Emulator_17_15.png _images/10_Waves_Emulator_17_16.png _images/10_Waves_Emulator_17_17.png _images/10_Waves_Emulator_17_18.png _images/10_Waves_Emulator_17_19.png _images/10_Waves_Emulator_17_20.png _images/10_Waves_Emulator_17_21.png _images/10_Waves_Emulator_17_22.png _images/10_Waves_Emulator_17_23.png _images/10_Waves_Emulator_17_24.png _images/10_Waves_Emulator_17_25.png _images/10_Waves_Emulator_17_26.png _images/10_Waves_Emulator_17_27.png _images/10_Waves_Emulator_17_28.png _images/10_Waves_Emulator_17_29.png _images/10_Waves_Emulator_17_30.png _images/10_Waves_Emulator_17_31.png _images/10_Waves_Emulator_17_32.png _images/10_Waves_Emulator_17_33.png _images/10_Waves_Emulator_17_34.png _images/10_Waves_Emulator_17_35.png

10.5.2. Validate emulator: Seas by AWT#

# historical
awt_his = xr.open_dataset(awt_his_file)

AWT_his = xr.Dataset({'bmus':(('time'), awt_his.bmus.values)}, 
    coords = {'time':(('time'), awt_his.time.values)})

AWT_his = AWT_his.resample(time='1D').pad()


seas_his = seas.copy(deep=True)
seas_his = seas_his.rename({'hs':'Hs_sea', 'tp':'Tp_sea', 'dpm':'Dir_sea'})


AWT_his = AWT_his.sel(time=slice(seas_his.time.values[0], seas_his.time.values[-1]))
seas_his = seas_his.sel(time=slice(AWT_his.time.values[0], AWT_his.time.values[-1]))
seas_his['wt'] = (('time'), AWT_his.bmus.values)
# simulation

AWTs_sim_n = AWTs_sim.isel(n_sim=n_sim)

seas_sim = sea_params_sim.copy(deep=True)

seas_sim['wt'] = (('time'), AWTs_sim_n.evbmus_sims.values)
# plot seas by AWT

for wt in range(6):

    seas_his_wt = seas_his.where(seas_his.wt==wt, drop=True)
    seas_sim_wt = seas_sim.where(seas_sim.wt==wt+1, drop=True)
    
    seas_sim_wt = seas_sim_wt.drop({'evbmus_sims','wt','evbmus_sims_awt'})
    
    plot_his_sim_waves_hist(seas_his_wt, seas_sim_wt, title='Seas from AWT ' +  str(wt+1) + '. ' + str(len(seas_his_wt.time)) + ' data')
_images/10_Waves_Emulator_21_0.png _images/10_Waves_Emulator_21_1.png _images/10_Waves_Emulator_21_2.png _images/10_Waves_Emulator_21_3.png _images/10_Waves_Emulator_21_4.png _images/10_Waves_Emulator_21_5.png

10.5.3. Validate emulator: Snakes Parameteres (daily values)#

# --------------------------------------
# Select Simulation to plot

n_sim = 39
snake_params_sim = xr.open_dataset(op.join(p_out_waves, 'snakes_params_{0:08d}.nc'.format(int(n_sim))))
# Compare historical and simulated swells (all DWTs)

snake_sim = snake_params_sim.copy(deep=True)
snake_sim = snake_sim.drop({'evbmus_sims', 'evbmus_sims_awt'})

plot_his_sim_waves_hist(snakes, snake_sim, title='Snake parameters from all DWTs')
_images/10_Waves_Emulator_24_0.png
# Choose one DWT to compare all parameters
wt = 3

snake_his = snakes.copy(deep=True)
snake_his = snake_his.where(snake_his.bmus==wt)

snake_sim = snake_params_sim.copy(deep=True)
snake_sim = snake_sim.where(snake_sim.evbmus_sims==wt+1)
snake_sim = snake_sim.drop({'evbmus_sims','evbmus_sims_awt'})


plot_his_sim_waves_hist(snake_his, snake_sim, title='Snake parameters from DWT ' + str(wt+1))
_images/10_Waves_Emulator_25_0.png

10.5.4. Plot snakes reconstruction#

#--------------------------
# Plot simulated snakes 
n=39
snake_params_sim = xr.open_dataset(op.join(p_out_waves,'snakes_params_{0:08d}.nc'.format(int(n))))

# reconstruct snakes
snake_params_sim_y = snake_params_sim.sel(time=slice('2050-01-01', '2050-02-01'))
time_h, hs_swell, tp_swell, dir_swell = reconstruct_snakes(max_num_snakes, snake_params_sim_y, 'sim')

# plot
sea_params_sim_y = sea_params_sim.sel(time=slice(time_h[0], time_h[-1]))

plot_sim_snakes(time_h, hs_swell, tp_swell, dir_swell, sea_params_sim_y)
_images/10_Waves_Emulator_27_0.png

10.5.5. Plot number of swells for each DWT and each directional sector#

# define output
n_swells_sim =  xr.Dataset(
    {
        'n_swells':(('time','n_dirs'), np.zeros((len(snake_params_sim.time.values), len( n_swells.n_dirs.values)))*np.nan),
        'bmus':(('time'), np.zeros((len(snake_params_sim.time.values)))*np.nan), 
    },
    coords = {
        'time':(('time'), snake_params_sim.time.values),
        'n_dirs':(('n_dirs'), n_swells.n_dirs.values),
    },
)

# bins dir
dir_ini = n_swells_sim.n_dirs.values
dir_end =  np.append(n_swells_sim.n_dirs.values[1:], 360.01)
 
# snakes for each day    
for ind_t, t in enumerate(snake_params_sim.time.values):

    snake_params_sim_d = snake_params_sim.sel(time=t)
    
    n_swells_sim['bmus'][ind_t] = snake_params_sim_d.evbmus_sims.values.astype('int') 
    
    if len(np.where(~np.isnan(snake_params_sim_d.Hs))[0])>0:
        snake_params_sim_d = snake_params_sim_d.where(~np.isnan(snake_params_sim_d.Hs), drop=True)
    else:
        continue   # there are no snakes that day
    
    
    # snakes/day for each dir    
    n_swells_dir = np.zeros((len(n_swells.n_dirs), max_num_snakes))

    cont=0
    for s in snake_params_sim_d.n_snake.values:
        snake_params_sim_d_s = snake_params_sim_d.isel(n_snake = s)

        for ind_dir in range(len(n_swells_sim.n_dirs)):

            if snake_params_sim_d_s.Dir.values>=dir_ini[ind_dir] and snake_params_sim_d_s.Dir.values<dir_end[ind_dir]:
                n_swells_dir[ind_dir,cont] = 1
                cont = cont+1
    
    n_swells_sim['n_swells'][ind_t,:] = np.sum(n_swells_dir, axis=1)

    
# save
n_swells_sim.to_netcdf(n_swells_file_sim)  
n_swells_sim = xr.open_dataset(n_swells_file_sim)  
            
plot_his_sim_NumSnakes(n_swells, n_swells_sim);
_images/10_Waves_Emulator_31_0.png

10.5.6. Plot probability of swells for DWT and direction#

# historical
out_his = np.full((n_clusters,12,12), 0)

for wt in range(n_clusters):
    n_swells_wt = n_swells.where(n_swells.bmus==wt, drop=True)
 
    for t in n_swells_wt.time.values:
        n_swells_wt_t = n_swells_wt.sel(time=t)
        ind = np.where(n_swells_wt_t.n_swells!=0)
        
        for pair in itertools.permutations(ind[0],2):
            out_his[wt, pair[0], pair[1]] = out_his[wt, pair[0], pair[1]] + 1
    

# simulation
out_sim = np.full((n_clusters,12,12), 0)

for wt in range(n_clusters):
    n_swells_wt = n_swells_sim.where(n_swells_sim.bmus==wt+1, drop=True)
 
    for t in n_swells_wt.time.values:
        n_swells_wt_t = n_swells_wt.sel(time=t)
        ind = np.where(n_swells_wt_t.n_swells!=0)
        
        for pair in itertools.permutations(ind[0],2):
            out_sim[wt, pair[0], pair[1]] = out_sim[wt, pair[0], pair[1]] + 1
            
for wt in range(n_clusters):
    out_his_wt = out_his[wt,:,:]
    out_sim_wt = out_sim[wt,:,:]
    
    max_his = np.max(out_his_wt)
    max_sim = np.max(out_sim_wt)
    
    out_his_wt = out_his_wt/max_his
    out_sim_wt = out_sim_wt/max_sim
    
    
    fig, ax = plt.subplots(1,2, figsize=(10,5))
    
    axplot_WT_Probs(ax[0], np.flipud(out_his_wt),
                     ttl = 'DWT ' + str(wt+1) + '. historical', vmin = 0, vmax = 0.5,
                     cmap = 'Blues', caxis='black')
    ax[0].set_xticks([0,3,6,9,12])
    ax[0].set_xticklabels(['N','E','S','W','N'])
    ax[0].set_yticks([0,3,6,9,12])
    ax[0].set_yticklabels(['N','E','S','W','N'])
    
    axplot_WT_Probs(ax[1], np.flipud(out_sim_wt),
                     ttl = 'DWT ' + str(wt+1) + '. simulation', vmin = 0, vmax = 0.5,
                     cmap = 'Blues', caxis='black')
    ax[1].set_xticks([0,3,6,9,12])
    ax[1].set_xticklabels(['N','E','S','W','N'])
    ax[1].set_yticks([0,3,6,9,12])
    ax[1].set_yticklabels(['N','E','S','W','N'])
_images/10_Waves_Emulator_34_0.png _images/10_Waves_Emulator_34_1.png _images/10_Waves_Emulator_34_2.png _images/10_Waves_Emulator_34_3.png _images/10_Waves_Emulator_34_4.png _images/10_Waves_Emulator_34_5.png _images/10_Waves_Emulator_34_6.png _images/10_Waves_Emulator_34_7.png _images/10_Waves_Emulator_34_8.png _images/10_Waves_Emulator_34_9.png _images/10_Waves_Emulator_34_10.png _images/10_Waves_Emulator_34_11.png _images/10_Waves_Emulator_34_12.png _images/10_Waves_Emulator_34_13.png _images/10_Waves_Emulator_34_14.png _images/10_Waves_Emulator_34_15.png _images/10_Waves_Emulator_34_16.png _images/10_Waves_Emulator_34_17.png _images/10_Waves_Emulator_34_18.png _images/10_Waves_Emulator_34_19.png _images/10_Waves_Emulator_34_20.png _images/10_Waves_Emulator_34_21.png _images/10_Waves_Emulator_34_22.png _images/10_Waves_Emulator_34_23.png _images/10_Waves_Emulator_34_24.png _images/10_Waves_Emulator_34_25.png _images/10_Waves_Emulator_34_26.png _images/10_Waves_Emulator_34_27.png _images/10_Waves_Emulator_34_28.png _images/10_Waves_Emulator_34_29.png _images/10_Waves_Emulator_34_30.png _images/10_Waves_Emulator_34_31.png _images/10_Waves_Emulator_34_32.png _images/10_Waves_Emulator_34_33.png _images/10_Waves_Emulator_34_34.png _images/10_Waves_Emulator_34_35.png

10.5.7. Validates Spectrum reconstruction#

# Average spectrum

for n in DWTs_sim.n_sim.values:
    print(n)
    for yy in range(2020,2100):
        spectro_yy = xr.open_dataset(op.join(p_out_spec,'spectrum_hourly_{0:08d}_{1}.nc'.format(int(n),yy)))
        spectro_yy_mean = spectro_yy.mean(dim='time')

        if yy==2020:
            spectro_mean = spectro_yy_mean.copy(deep=True)
        else:
            spectro_mean = xr.concat([spectro_mean, spectro_yy_mean], dim='year')
    
    if n==0:
        spectro_sim = spectro_mean.mean(dim='year')
    else:
        spectro_sim = xr.concat([spectro_sim, spectro_mean.mean(dim='year')],'n_sim')

        
# mean of all sims
spectro_sim = spectro_sim.mean(dim='n_sim')        
spectro_sim.to_netcdf(mean_spec_file_sim)
spectro_sim = xr.open_dataset(mean_spec_file_sim)
# Plot mean spectra

# historical
spectro_his = xr.open_dataset(superpoint_file)
Plot_superpoint_spectrum(spectro_his, average=True);


# simulated
spectro_sim = spectro_sim.expand_dims(dim='time')
Plot_superpoint_spectrum(spectro_sim.transpose('time', "freq", "dir"), average=False); # already averaged
_images/10_Waves_Emulator_38_0.png _images/10_Waves_Emulator_38_1.png
# Plot mean differences
mean_dif = np.sqrt(spectro_sim.efth.isel(time=0).transpose("freq", "dir")) - np.sqrt(spectro_his.efth.mean(dim='time'))

# reconstructed from partitions
fig = plt.figure(figsize = (20,10))
ax = fig.add_subplot(1,3,1, projection = 'polar')
axplot_spectrum(ax, np.deg2rad(spectro_his.dir.values), spectro_his.freq.values, mean_dif.values, vmin = -.02,  vmax=0.02, cmap='seismic')
ax.set_title('mean simulation - mean historical (superpoint)', fontsize=14);
_images/10_Waves_Emulator_39_0.png