From 55fba36d97ff9c00922f5c49b78da003403910d1 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 14 Dec 2020 11:55:52 -0500 Subject: [PATCH 01/26] adding base 1DVar files to new development --- RunWorkflow_1DVar.py | 306 +++++++++++++++ bin/1DVar/assim_1dh.m | 412 +++++++++++++++++++++ bin/1DVar/old/assim_1dh.m | 409 ++++++++++++++++++++ bin/1DVar/old/waveModel/ad_symmetryCheck.m | 36 ++ bin/1DVar/old/waveModel/ad_waveModel.m | 361 ++++++++++++++++++ bin/1DVar/old/waveModel/tl_waveModel.m | 257 +++++++++++++ bin/1DVar/old/waveModel/waveModel.m | 184 +++++++++ bin/1DVar/old/waveModel/waveModelParams.m | 9 + bin/1DVar/waveModel/ad_symmetryCheck.m | 36 ++ bin/1DVar/waveModel/ad_waveModel.m | 362 ++++++++++++++++++ bin/1DVar/waveModel/tl_waveModel.m | 259 +++++++++++++ bin/1DVar/waveModel/waveModel.m | 172 +++++++++ bin/1DVar/waveModel/waveModelParams.m | 9 + 13 files changed, 2812 insertions(+) create mode 100644 RunWorkflow_1DVar.py create mode 100644 bin/1DVar/assim_1dh.m create mode 100644 bin/1DVar/old/assim_1dh.m create mode 100644 bin/1DVar/old/waveModel/ad_symmetryCheck.m create mode 100644 bin/1DVar/old/waveModel/ad_waveModel.m create mode 100644 bin/1DVar/old/waveModel/tl_waveModel.m create mode 100644 bin/1DVar/old/waveModel/waveModel.m create mode 100644 bin/1DVar/old/waveModel/waveModelParams.m create mode 100644 bin/1DVar/waveModel/ad_symmetryCheck.m create mode 100644 bin/1DVar/waveModel/ad_waveModel.m create mode 100644 bin/1DVar/waveModel/tl_waveModel.m create mode 100644 bin/1DVar/waveModel/waveModel.m create mode 100644 bin/1DVar/waveModel/waveModelParams.m diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py new file mode 100644 index 0000000..c239973 --- /dev/null +++ b/RunWorkflow_1DVar.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- +# import matplotlib +# matplotlib.use('Agg') +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec +from mpl_toolkits.mplot3d import Axes3D +import os, getopt, sys, shutil, glob, logging, yaml, re, pickle +import datetime as DT +from subprocess import check_output +import numpy as np +from getdatatestbed.getDataFRF import getObs, getDataTestBed +from testbedutils import fileHandling +from oct2py import octave +import matlab +import math +import netCDF4 as nc + +Q = 0 +def assim_currents(X, currents_object, obs, i): + #print("current object crosshore location ", currents_object['xFRF']) + awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) + obs['v']['d'] = np.append(obs['v']['d'], matlab.float64([currents_object['aveN'][i]])) + obs['v']['ind'] = np.append(obs['v']['ind'], matlab.float64([awac_indice])) + return obs + +def assim_waves(X, waves_object, obs, i): + #print("waves object crosshore location ", waves_object['xFRF']) + hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) + obs['H']['d'] = np.append(obs['H']['d'], (matlab.float64([waves_object['Hs'][i]]))) + obs['H']['ind'] = np.append(obs['H']['ind'], (matlab.float64([hs_indice]))) + return obs + +def calculate_Ch(prior, spec8m, X, delta_t, Q): + # calculate Q(x,t) (measured process error) according to holman et al 2013 + # Q = Cq * Hmo ^ (-[(x-x0)/sigma_x]^2) + # deltat is difference from survey to assimilation step; + # add q each time-step to increase uncertainty as the Ch decreases in posterior posterior.ch + new S *N *S with just the tiny delta_t + Cq = 0.067 # from sandy duck experiment + Hmo = np.mean(spec8m['Hs']) # significant wave height of highest 1/3 of waves + x0 = 50 # x0 and sigma_x reflect the typical location of breaking waves at this particular beach + sigma_x = 150 + delta_t = delta_t/(60*60*24) + print("Delta t:", delta_t) + xx = np.meshgrid(X) + Lx = 25 # decorrelation length scale, larger Lx leads to smoother results + N = np.exp((-(xx - np.transpose(xx)) ** 2) / (2 * Lx)) + Q = Q + (Cq * Hmo) ** (np.power(-((X - x0) / sigma_x), 2))*delta_t + S = np.diag(np.sqrt(Q)) + Ch = S * N * S + + prior['Ch'] = Ch # populate elevation covariance + return prior, Q + +def find_nearest(array, value): + array = np.asarray(array) + idx = (np.abs(array - value)).argmin() + return array[idx], idx + +def Master_1DVar_run(inputDict): + """This function will run CMS with any version prefix given start, end, and timestep. + + Args: + inputDict: a dictionary that is read from the input yaml + + Returns: + None + + """ + ## unpack input Dictionary + version_prefix = inputDict['modelSettings']['version_prefix'] + endTime = inputDict['endTime'] + startTime = inputDict['startTime'] + simulationDuration = inputDict['simulationDuration'] + workingDir = os.path.join(inputDict['workingDirectory'], 'waveModels') + generateFlag = inputDict['generateFlag'] + runFlag = inputDict['runFlag'] + analyzeFlag = inputDict['analyzeFlag'] + pFlag = inputDict['pFlag'] + model = inputDict.get('model', '1DVar') + modeldir = inputDict['modelExecutable'] + log = inputDict.get('logging', True) + + # __________________pre-processing checks________________________________ + fileHandling.checkVersionPrefix(model, inputDict) + # __________________input directories________________________________ + baseDir = os.getcwd() # location of working directory + # check executable + if inputDict['modelExecutable'].startswith(baseDir): # change to relative path + inputDict['modelExecutable'] = re.sub(baseDir, '', inputDict['modelExecutable']) + workingDirectory = os.path.join(workingDir, model.lower(), version_prefix) + inputDict['netCDFdir'] = os.path.join(inputDict['netCDFdir'], 'waveModels') + inputDict['path_prefix'] = workingDirectory + # ______________________ Logging ____________________________ + # auto generated Log file using start_end time? + # LOG_FILENAME = fileHandling.logFileLogic(workingDirectory, version_prefix, startTime, endTime, log=log) + # __________________get time list to loop over________________________________ + try: + projectEnd = DT.datetime.strptime(endTime, '%Y-%m-%dT%H:%M:%SZ') + projectStart = DT.datetime.strptime(startTime, '%Y-%m-%dT%H:%M:%SZ') + except TypeError: # if input date was parsed as datetime + projectEnd = endTime + projectStart = startTime + # This is the portion that creates a list of simulation end times + dt_DT = DT.timedelta(0, simulationDuration * 60 * 60) # timestep in datetime + # make List of Datestring items, for simulations + dateStartList = [projectStart] + dateStringList = [dateStartList[0].strftime("%Y-%m-%dT%H:%M:%SZ")] + for i in range(int(np.ceil((projectEnd - projectStart).total_seconds() / dt_DT.total_seconds())) - 1): + dateStartList.append(dateStartList[-1] + dt_DT) + dateStringList.append(dateStartList[-1].strftime("%Y-%m-%dT%H:%M:%SZ")) + # fileHandling.displayStartInfo(projectStart, projectEnd, version_prefix, LOG_FILENAME, model) + # ______________________________gather all data _____________________________ + if generateFlag == True: + go = getObs(projectStart, projectEnd, server='chl') # initialize get observation + bathygo = getDataTestBed(projectStart, projectEnd) + #bathyTransect = bathygo.getBathyTransectFromNC(method=1) # grab bathymetry transects + bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects + + adop_35m_currents = go.getCurrents(gaugenumber='adop-3.5m', roundto=1) + awac_45m_currents = go.getCurrents(gaugenumber='awac-4.5m', roundto=1) + awac_6m_currents = go.getCurrents(gaugenumber='awac-6m', roundto=1) + awac_8m_currents = go.getCurrents(gaugenumber='awac-8m', roundto=1) + #awac_11m_currents = go.getCurrents(gaugenumber='awac-11m', roundto=1) + + spec125m = go.getWaveSpec(gaugenumber='xp125m', specOnly=False) # grab 125m pressure array obs + spec150m = go.getWaveSpec(gaugenumber='xp150m', specOnly=False) # grab 150m pressure array obs + spec200m = go.getWaveSpec(gaugenumber='xp200m', specOnly=False) # grab 200m pressure array obs + spec8m = go.getWaveSpec(gaugenumber='8m-array', specOnly=False) # grab 8m array observations + + h = bathyTransect['elevation'][200,45:] + X = np.linspace(0, np.max(bathyTransect['xFRF']), len(h)) + delta_t = spec8m['time'][0] - bathyTransect['time'] + delta_t = delta_t.seconds + # load example mat file to populate prior and observations with data for model run + input = octave.load('./data/waveModels/1DVar/input_oct.mat') + prior = input['prior'] + obs = input['obs'] + X = np.reshape(X, (len(X), 1)) + h = np.reshape(h, (len(h), 1)) + Q=0 + prior, Q = calculate_Ch(prior, spec8m, X, delta_t, Q) + # populate prior with data + # Prior.ka_drag is constant + # Prior.Ctheta0 is constant + # Prior.CH0 is constant + # Prior.Cka is constant + prior['x'] = X # populate with bathy cross-shore distance data + prior['h'] = -h # populate with elevation data + prior['hErr'] = -h / 2 # populate with elevation error data (h/2 for now) + orig_obs_h = obs['H']['e'] + orig_obs_v = obs['v']['e'] + orig_obs_tauw = obs['tauw']['e'] + # ________________________________________________ RUN LOOP ________________________________________________ + i = 0 + print(dateStringList) + for time in dateStringList: + try: + print('-------------------------------Beginning Simulation {}-------------------------------'.format( + DT.datetime.now())) + + if runFlag == True: # run model + os.chdir(modeldir) # changing locations to where the model will be ran from for octave + print(os.getcwd()) + print('Running {} Simulation'.format(model.upper())) + dt = DT.datetime.now() + + prior['theta0'] = spec8m['waveDp'][i]*(3.1415/180) # populate with theta0 data + prior['H0'] = spec8m['Hs'][i] # populate with offshore wave height data + prior['sigma'] = 2 * np.pi * spec8m['peakf'][i] # calculate offshore sigma value""" + print("theta: ", prior['theta0']) + # select indices of bathymetry where we have Hs measurements + obs['H']['d'] = [] + obs['H']['ind'] = [] + if spec125m != None: + try: + obs = assim_waves(X, spec125m, obs, i) + except: + pass + if spec150m != None: + try: + obs = assim_waves(X, spec150m, obs, i) + except: + pass + if spec200m != None: + try: + obs = assim_waves(X, spec200m, obs, i) + except: + pass + if spec8m != None: + try: + obs = assim_waves(X, spec8m, obs, i) + except: + pass + + obs['H']['e'] = orig_obs_h + try: + obs['H']['e'] = matlab.float64(obs['H']['e'][0][:len(obs['H']['d'])]) + except: + obs['H']['e'] = matlab.float64(obs['H']['e'][:len(obs['H']['d'])]) + print("Wave Heights to Assimilate: ", obs['H']['d']) + print("Wave Indices to Assimilate: ", obs['H']['ind']) + print("Wave Errors to Assimilate: ", obs['H']['e']) + + # select indices of bathymetry where we have v measurements + obs['v']['d'] = [] + obs['v']['ind'] = [] + obs['v']['e'] = orig_obs_v + if adop_35m_currents != None: + try: + obs = assim_currents(X, adop_35m_currents, obs, i) + except: + pass + if awac_45m_currents != None: + try: + obs = assim_currents(X, awac_45m_currents, obs, i) + except: + pass + if awac_6m_currents != None: + try: + obs = assim_currents(X, awac_6m_currents, obs, i) + except: + pass + if awac_8m_currents != None: + try: + obs = assim_currents(X, awac_8m_currents, obs, i) + except: + pass + #if awac_11m_currents != None: + #obs = assim_currents(X, awac_11m_currents, obs, i) + + try: + obs['v']['e'] = matlab.float64(obs['v']['e'][0][:len(obs['v']['d'])]) + except: + obs['v']['e'] = matlab.float64(obs['v']['e'][:len(obs['v']['d'])]) + print("Currents to Assimilate: ", obs['v']['d']) + print("Current Indices to Assimilate: ", obs['v']['ind']) + print("Current Errors to Assimilate: ", obs['v']['e']) + + + posterior, diagnostics = octave.feval('C:/cmtb/bin/1DVar/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ + + + print('Simulation took %s ' % (DT.datetime.now() - dt)) + + prior = posterior + try: + delta_t = spec8m['time'][i+1] - spec8m['time'][i] + delta_t = delta_t.seconds + except: + pass + prior, Q = calculate_Ch(prior, spec8m, X, delta_t, Q) + + i += 1 + print('-------------------------------SUCCESS-----------------------------------------') + + except Exception as e: + print('<< ERROR >> HAPPENED IN THIS TIME STEP\n{}'.format(e)) + logging.exception('\nERROR FOUND @ {}\n'.format(time), exc_info=True) + os.chdir(modeldir) + + + print(posterior['herr']) + fig = plt.figure(figsize=(6, 9)) + grid = gridspec.GridSpec(1, 3, figure=fig) + ax0 = fig.add_subplot(grid[0, 0]) + ax0.set_title("Prior") + ax0.set_ylim(ymax=-1, ymin=-6) + ax0.set_xlabel("Crosshore distance") + ax0.set_ylabel("Depth") + ax0.set_xlim(xmax=80) + ax1 = fig.add_subplot(grid[0, 1]) + ax1.set_title("Posterior") + ax1.set_xlabel("Crosshore distance") + ax1.set_ylabel("Depth") + ax1.set_ylim(ymax=-1, ymin=-6) + ax1.set_xlim(xmax=80) + ax0.plot(h) + ax1.plot(-posterior['h']) + ax2 = fig.add_subplot(grid[0, 2]) + ax2.scatter(X, h+posterior['h']) + plt.show() + +if __name__ == "__main__": + model = '1DVar' + opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) + print('___________________________________\n___________________________________\n___________________' + '________________\n') + print('USACE FRF Coastal Model Test Bed : {}'.format(model)) + + args = ['./yaml_files/TestBedExampleInputs/1DVar_Input_example.yml'] + try: + # assume the user gave the path + yamlLoc = args[0] + if os.path.exists('.cmtbSettings'): + with open('.cmtbSettings', 'r') as fid: + a = yaml.safe_load(fid) + with open(os.path.join(yamlLoc), 'r') as f: + inputDict = yaml.safe_load(f) + inputDict.update(a) + + except: + raise IOError( + 'Input YAML file required. See yaml_files/TestBedExampleInputs/{}_Input_example for example yaml file.'.format( + model)) + + Master_1DVar_run(inputDict=inputDict) diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVar/assim_1dh.m new file mode 100644 index 0000000..b8f37f5 --- /dev/null +++ b/bin/1DVar/assim_1dh.m @@ -0,0 +1,412 @@ +function [posterior,diagnostics,representers]=assim_1dh(prior,obs,verb,bkgd) +% +% [posterior,diagnostics]=assim_1dh(prior,obs) +% +% Assimilate data into 1DH wave and current model. +% +% INPUTS: +% +% prior.x = model grid +% prior.h = bathymetry +% prior.theta0, prior.H0 = boundary conditions +% prior.ka_drag = bed roughness +% prior.tauw = wind stress in m2/s2 units (default = 0 if not included) +% +% prior.Ch, prior.Ctheta0, prior.CH0, prior.Cka = covariance matrices for h, +% theta0, H0, ka_drag +% +% obs.H.ind = model indeces for rms wave height observations +% obs.H.d = rms wave height data +% obs.H.e = error stdev for rms wave height +% obs.v.* = as in obs.H, but for longshore current +% +% NOTE, if any of obs.* are missing or empty, they will be ignored +% +% OUTPUTS: +% +% posterior = struct with same fields as prior but updated by assimilation +% +% diagnostics = struct with diagnostic fields for analyzing the update. See +% comments near end of this code for descriptions +% +addpath 'C:/cmtb/bin/1DVar/waveModel' +warning('off','all'); +if(~exist('verb')) + verb=1; +end + +hmin=.2; +nitermax=500; + +% unpack some variables +Ch=prior.Ch; +CH0=prior.CH0; +Ctheta0=prior.Ctheta0; +Cka=prior.Cka; +x=prior.x; +nx=length(x); +nt=length(prior); + +% if any obs types missing, set to empty +fld='Hv'; +noobs.ind=[]; +noobs.d=[]; +noobs.e=[]; +for i=1:length(fld) + if(~isfield(obs,fld(i))) + obs=setfield(obs,fld(i),noobs); + end +end + +% initialize prior model state using NL model +if(verb) + disp('running prior forward model...') +end +prior=waveModel(x,prior.H0,prior.theta0,prior); +if(verb) + disp('...done') +end + +% outer loop +eps=nan; +for n=1:nitermax + disp(['iteration ' num2str(n) ', itermax = ' num2str(nitermax) ', eps = ' num2str(eps)]) + % keyboard + + % define background and forecast states for this outer loop iteration + if(n==1) + if(~exist('bkgd')) + bkgd=prior; + else + disp('using provided bkgd state') + end + fcst=prior; + else + bkgd=posterior; + fcst=prior; + tl_h=fcst.h-bkgd.h; + tl_H0=fcst.H0-bkgd.H0; + tl_theta0=fcst.theta0-bkgd.theta0; + tl_ka_drag=fcst.ka_drag-bkgd.ka_drag; + [tl_H,tl_theta,tl_v]=tl_waveModel(x,tl_h,tl_H0,tl_theta0,tl_ka_drag,bkgd); + fcst.H=bkgd.H+tl_H; + fcst.theta=bkgd.theta+tl_theta; + fcst.v=bkgd.v+tl_v; + end + + % initialize representer sub-matrices + % + % LEGEND: + % + % R_XY = sensitivity of observation type Y, to delta-perturbations of + % observation X. These are sub-blocks of L*M*C*M'*L' (note, C is the + % prior covariance). + % + % r_X = sensitivity of model input vector phi = [h; H0; theta0; ka_drag]) + % to delta-perturbations of observation X. These are rows of C*M'*L'. + % + % r_XY = sensitivity of model variable Y to delta-perturbations of + % observation X. This is used for diagnostic purposes only, not for + % assimilation, and is output in struct 'rdiag'. These are just like r_X + % (above) but are for variables that aren't treated as model inputs + % (v,H,k,etc.). + % + % ad_X_Y = sensitivity of model input variable Y (one of the phi + % variables) to delta-perturbations of observation X. These are direct + % outputs of the adjoint model, i.e. M'*L', with no smoothing by the prior + % covariance. Stored for diagnostic purposes only. + % + clear R_* r_* rep_* + r_H=zeros(nx+3,length(obs.H.ind)); + r_v=zeros(nx+3,length(obs.v.ind)); + ad_H_h =zeros(length(obs.H.ind),nx); + ad_H_H0 =zeros(length(obs.H.ind),1); + ad_H_theta0 =zeros(length(obs.H.ind),1); + ad_H_ka_drag=zeros(length(obs.H.ind),1); + ad_v_h =zeros(length(obs.v.ind),nx); + ad_v_H0 =zeros(length(obs.v.ind),1); + ad_v_theta0 =zeros(length(obs.v.ind),1); + ad_v_ka_drag=zeros(length(obs.v.ind),1); + R_HH=zeros(length(obs.H.ind),length(obs.H.ind)); + R_Hv=zeros(length(obs.H.ind),length(obs.v.ind)); + R_vH=zeros(length(obs.v.ind),length(obs.H.ind)); + R_vv=zeros(length(obs.v.ind),length(obs.v.ind)); + r_HH=zeros(length(obs.H.ind),nx); + r_Hv=zeros(length(obs.H.ind),nx); + r_vH=zeros(length(obs.v.ind),nx); + r_vv=zeros(length(obs.v.ind),nx); + + % compute representers for wave height: apply delta-perturbations to + % observations of type X, to compute (a) sensitivity of model inputs psi + % (r_* matrices), and (b) sensitivity of observations of type Y (R_* + % matrices) + for i=1:length(obs.H.ind) + comb=zeros(nx,1); + comb(obs.H.ind(i))=1; % data functional (delta-fn, aka identity matrix) + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + r_H(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' + ad_H_h(i,:)=ad_h; + ad_H_H0(i,:)=ad_H0; + ad_H_theta0(i,:)=ad_theta0; + ad_H_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + r_HH(i,:)=tl_H; + r_Hv(i,:)=tl_v; + r_Hh(i,:)=Ch*ad_h; + R_HH(i,:)=tl_H(obs.H.ind); + R_Hv(i,:)=tl_v(obs.v.ind); + end + + % repeat to compute representers for longshore current + for i=1:length(obs.v.ind) + comb=zeros(nx,1); + comb(obs.v.ind(i))=1; % data functional (delta-fn, aka identity matrix) + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + r_v(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' + ad_v_h(i,:)=ad_h; + ad_v_H0(i,:)=ad_H0; + ad_v_theta0(i,:)=ad_theta0; + ad_v_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + r_vH(i,:)=tl_H; + r_vv(i,:)=tl_v; + r_vh(i,:)=Ch*ad_h; + R_vH(i,:)=tl_H(obs.H.ind); + R_vv(i,:)=tl_v(obs.v.ind); + end + + % assemble matrices for updating + Cd=diag([obs.H.e(:); + obs.v.e(:)].^2); + CMt=[r_H r_v]; + d=[obs.H.d(:); + obs.v.d(:)]; + Lu=[fcst.H(obs.H.ind); + fcst.v(obs.v.ind)]; + R=[R_HH R_Hv; + R_vH R_vv]; + obstype=[repmat('H',[length(obs.H.d) 1]); + repmat('v',[length(obs.v.d) 1])]; + + % compute the update + hedge=1; %tanh(n/5); % reduce magnitude of update for 1st few iterations + update=hedge*CMt*inv(R+Cd)*(d-Lu); + if(n>1) + update=.5*(u0+update); + end + u0=update; + posterior=prior; + posterior.H0=prior.H0+update(nx+1); + posterior.theta0=prior.theta0+update(nx+2); + posterior.ka_drag=max(1e-4,prior.ka_drag+update(nx+3)); + posterior.h = prior.h + update(1:nx); + posterior.h(posterior.h0) + % eps=eps+(posterior.H0-bkgd.H0)^2/CH0; + %end + %if(Ctheta0>0) + % eps=eps+(posterior.theta0-bkgd.theta0)^2/Ctheta0; + %end + %if(Cka>0) + % eps=eps+(posterior.ka_drag-bkgd.ka_drag)^2/Cka; + %end + if(eps<1e-4) + disp(eps) + break; + end + +end % outer loop iterations + +% OPTIONAL: for diagnostics, repeat the representer calculations for both +% variables, but this time do ALL the gridpoints. These can be used for +% prior and posterior covariances for v, H. This is not used for the outer +% loop updates, so only need to do it once at the end +if(nargout>2) + disp('Calculating full representer matrix as requested (nargout>2)') + + adj_H_h =zeros(nx,nx); + adj_H_H0 =zeros(nx,1); + adj_H_theta0 =zeros(nx,1); + adj_H_ka_drag=zeros(nx,1); + adj_v_h =zeros(nx,nx); + adj_v_H0 =zeros(nx,1); + adj_v_theta0 =zeros(nx,1); + adj_v_ka_drag=zeros(nx,1); + rep_HH=zeros(nx); + rep_Hv=zeros(nx); + rep_Hh=zeros(nx); + rep_vH=zeros(nx); + rep_vv=zeros(nx); + rep_vh=zeros(nx); + rep_HH_post=zeros(nx); + rep_Hv_post=zeros(nx); + rep_Hh_post=zeros(nx); + rep_vH_post=zeros(nx); + rep_vv_post=zeros(nx); + rep_vh_post=zeros(nx); + + % wave height representers + for i=1:nx + comb=zeros(nx,1); + comb(i)=1; + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + adj_H_h(i,:)=ad_h; + adj_H_H0(i,:)=ad_H0; + adj_H_theta0(i,:)=ad_theta0; + adj_H_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + rep_HH(i,:)=tl_H; + rep_Hv(i,:)=tl_v; + rep_Hh(i,:)=Ch*ad_h; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); + rep_HH_post(i,:)=tl_H; + rep_Hv_post(i,:)=tl_v; + rep_Hh_post(i,:)=posterior.Ch*ad_h; + end + + % longshore current representers + for i=1:nx + comb=zeros(nx,1); + comb(i)=1; % data functional (delta-fn, aka identity matrix) + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + adj_v_h(i,:)=ad_h; + adj_v_H0(i,:)=ad_H0; + adj_v_theta0(i,:)=ad_theta0; + adj_v_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + rep_vH(i,:)=tl_H; + rep_vv(i,:)=tl_v; + rep_vh(i,:)=Ch*ad_h; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); + rep_vH_post(i,:)=tl_H; + rep_vv_post(i,:)=tl_v; + rep_vh_post(i,:)=posterior.Ch*ad_h; + end + + representers.adj_H_h =adj_H_h ; + representers.adj_H_H0 =adj_H_H0 ; + representers.adj_H_theta0 =adj_H_theta0 ; + representers.adj_H_ka_drag=adj_H_ka_drag; + representers.adj_v_h =adj_v_h ; + representers.adj_v_H0 =adj_v_H0 ; + representers.adj_v_theta0 =adj_v_theta0 ; + representers.adj_v_ka_drag=adj_v_ka_drag; + representers.H_H=rep_HH; + representers.H_v=rep_Hv; + representers.H_h=rep_Hh; + representers.v_H=rep_vH; + representers.v_v=rep_vv; + representers.v_h=rep_vh; + representers.H_H_post=rep_HH_post; + representers.H_v_post=rep_Hv_post; + representers.H_h_post=rep_Hh_post; + representers.v_H_post=rep_vH_post; + representers.v_v_post=rep_vv_post; + representers.v_h_post=rep_vh_post; + +end + +%--------------------------------------- +% remaining code: reformat output variables in diagnostics struct, for +% convenience +%--------------------------------------- +clear diagnostics + +diagnostics.niter=n; +diagnostics.eps=eps; + +% terms in update equation +diagnostics.update.CMt=CMt; +diagnostics.update.R =R ; +diagnostics.update.Cd =Cd ; +diagnostics.update.d =d ; +diagnostics.update.Lu =Lu ; +diagnostics.update.obstype=obstype; +diagnostics.update.bkgd=bkgd; +diagnostics.update.fcst=fcst; + +% representer outputs: r.X_Y refers to sensitivity of model input variable Y +% to delta-perturbations of obs type X +diagnostics.r.H_h =r_H(1:nx,:); +diagnostics.r.H_H0 =r_H(nx+1,:); +diagnostics.r.H_theta0=r_H(nx+2,:); +diagnostics.r.H_kadrag=r_H(nx+3,:); +diagnostics.r.v_h =r_v(1:nx,:); +diagnostics.r.v_H0 =r_v(nx+1,:); +diagnostics.r.v_theta0=r_v(nx+2,:); +diagnostics.r.v_kadrag=r_v(nx+3,:); + +% R-matrix outputs: R.X_Y refers to sensitivity of obs type Y to +% delta-perturbations of obs type X +diagnostics.R.H_H=R_HH; +diagnostics.R.H_v=R_Hv; +diagnostics.R.v_H=R_vH; +diagnostics.R.v_v=R_vv; + +% additional diagnostic representers: rdiag.X_Y refers to sensitivity of +% model variable Y to delta-perturbations of obs type X +diagnostics.rdiag.H_H=r_HH; +diagnostics.rdiag.H_v=r_Hv; +diagnostics.rdiag.v_H=r_vH; +diagnostics.rdiag.v_v=r_vv; + +% adjoint outputs: ad.X_Y refers to sensitivity of model input Y to +% delta-perturbations of obs type X +diagnostics.ad.H_h =ad_H_h; +diagnostics.ad.H_H0 =ad_H_H0; +diagnostics.ad.H_theta0 =ad_H_theta0; +diagnostics.ad.H_ka_drag=ad_H_ka_drag; +diagnostics.ad.v_h =ad_v_h; +diagnostics.ad.v_H0 =ad_v_H0; +diagnostics.ad.v_theta0 =ad_v_theta0; +diagnostics.ad.v_ka_drag=ad_v_ka_drag; \ No newline at end of file diff --git a/bin/1DVar/old/assim_1dh.m b/bin/1DVar/old/assim_1dh.m new file mode 100644 index 0000000..5c358bf --- /dev/null +++ b/bin/1DVar/old/assim_1dh.m @@ -0,0 +1,409 @@ +function [posterior,diagnostics,representers]=assim_1dh(prior,obs,verb,bkgd) +% +% [posterior,diagnostics]=assim_1dh(prior,obs) +% +% Assimilate data into 1DH wave and current model. +% +% INPUTS: +% +% prior.x = model grid +% prior.h = bathymetry +% prior.theta0, prior.H0 = boundary conditions +% prior.ka_drag = bed roughness +% prior.tauw = wind stress in m2/s2 units (default = 0 if not included) +% +% prior.Ch, prior.Ctheta0, prior.CH0, prior.Cka = covariance matrices for h, +% theta0, H0, ka_drag +% +% obs.H.ind = model indeces for rms wave height observations +% obs.H.d = rms wave height data +% obs.H.e = error stdev for rms wave height +% obs.v.* = as in obs.H, but for longshore current +% +% NOTE, if any of obs.* are missing or empty, they will be ignored +% +% OUTPUTS: +% +% posterior = struct with same fields as prior but updated by assimilation +% +% diagnostics = struct with diagnostic fields for analyzing the update. See +% comments near end of this code for descriptions +% +addpath 'C:/cmtb/bin/1DVar/waveModel' + +if(~exist('verb')) + verb=1; +end + +hmin=.2; +nitermax=10; + +% unpack some variables +Ch=prior.Ch; +CH0=prior.CH0; +Ctheta0=prior.Ctheta0; +Cka=prior.Cka; +x=prior.x; +nx=length(x); +nt=length(prior); + +% if any obs types missing, set to empty +fld='Hv'; +noobs.ind=[]; +noobs.d=[]; +noobs.e=[]; +for i=1:length(fld) + if(~isfield(obs,fld(i))) + obs=setfield(obs,fld(i),noobs); + end +end + +% initialize prior model state using NL model +if(verb) + disp('running prior forward model...') +end +prior=waveModel(x,prior.H0,prior.theta0,prior); +if(verb) + disp('...done') +end + +% outer loop +eps=nan; +for n=1:nitermax + disp(['iteration ' num2str(n) ', itermax = ' num2str(nitermax) ', eps = ' num2str(eps)]) + % keyboard + + % define background and forecast states for this outer loop iteration + if(n==1) + if(~exist('bkgd')) + bkgd=prior; + else + disp('using provided bkgd state') + end + fcst=prior; + else + bkgd=posterior; + fcst=prior; + tl_h=fcst.h-bkgd.h; + tl_H0=fcst.H0-bkgd.H0; + tl_theta0=fcst.theta0-bkgd.theta0; + tl_ka_drag=fcst.ka_drag-bkgd.ka_drag; + [tl_H,tl_theta,tl_v]=tl_waveModel(x,tl_h,tl_H0,tl_theta0,tl_ka_drag,bkgd); + fcst.H=bkgd.H+tl_H; + fcst.theta=bkgd.theta+tl_theta; + fcst.v=bkgd.v+tl_v; + end + + % initialize representer sub-matrices + % + % LEGEND: + % + % R_XY = sensitivity of observation type Y, to delta-perturbations of + % observation X. These are sub-blocks of L*M*C*M'*L' (note, C is the + % prior covariance). + % + % r_X = sensitivity of model input vector phi = [h; H0; theta0; ka_drag]) + % to delta-perturbations of observation X. These are rows of C*M'*L'. + % + % r_XY = sensitivity of model variable Y to delta-perturbations of + % observation X. This is used for diagnostic purposes only, not for + % assimilation, and is output in struct 'rdiag'. These are just like r_X + % (above) but are for variables that aren't treated as model inputs + % (v,H,k,etc.). + % + % ad_X_Y = sensitivity of model input variable Y (one of the phi + % variables) to delta-perturbations of observation X. These are direct + % outputs of the adjoint model, i.e. M'*L', with no smoothing by the prior + % covariance. Stored for diagnostic purposes only. + % + clear R_* r_* rep_* + r_H=zeros(nx+3,length(obs.H.ind)); + r_v=zeros(nx+3,length(obs.v.ind)); + ad_H_h =zeros(length(obs.H.ind),nx); + ad_H_H0 =zeros(length(obs.H.ind),1); + ad_H_theta0 =zeros(length(obs.H.ind),1); + ad_H_ka_drag=zeros(length(obs.H.ind),1); + ad_v_h =zeros(length(obs.v.ind),nx); + ad_v_H0 =zeros(length(obs.v.ind),1); + ad_v_theta0 =zeros(length(obs.v.ind),1); + ad_v_ka_drag=zeros(length(obs.v.ind),1); + R_HH=zeros(length(obs.H.ind),length(obs.H.ind)); + R_Hv=zeros(length(obs.H.ind),length(obs.v.ind)); + R_vH=zeros(length(obs.v.ind),length(obs.H.ind)); + R_vv=zeros(length(obs.v.ind),length(obs.v.ind)); + r_HH=zeros(length(obs.H.ind),nx); + r_Hv=zeros(length(obs.H.ind),nx); + r_vH=zeros(length(obs.v.ind),nx); + r_vv=zeros(length(obs.v.ind),nx); + + % compute representers for wave height: apply delta-perturbations to + % observations of type X, to compute (a) sensitivity of model inputs psi + % (r_* matrices), and (b) sensitivity of observations of type Y (R_* + % matrices) + for i=1:length(obs.H.ind) + comb=zeros(nx,1); + comb(obs.H.ind(i))=1; % data functional (delta-fn, aka identity matrix) + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + r_H(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' + ad_H_h(i,:)=ad_h; + ad_H_H0(i,:)=ad_H0; + ad_H_theta0(i,:)=ad_theta0; + ad_H_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + r_HH(i,:)=tl_H; + r_Hv(i,:)=tl_v; + r_Hh(i,:)=Ch*ad_h; + R_HH(i,:)=tl_H(obs.H.ind); + R_Hv(i,:)=tl_v(obs.v.ind); + end + + % repeat to compute representers for longshore current + for i=1:length(obs.v.ind) + comb=zeros(nx,1); + comb(obs.v.ind(i))=1; % data functional (delta-fn, aka identity matrix) + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + r_v(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' + ad_v_h(i,:)=ad_h; + ad_v_H0(i,:)=ad_H0; + ad_v_theta0(i,:)=ad_theta0; + ad_v_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + r_vH(i,:)=tl_H; + r_vv(i,:)=tl_v; + r_vh(i,:)=Ch*ad_h; + R_vH(i,:)=tl_H(obs.H.ind); + R_vv(i,:)=tl_v(obs.v.ind); + end + + % assemble matrices for updating + Cd=diag([obs.H.e(:); + obs.v.e(:)].^2); + CMt=[r_H r_v]; + d=[obs.H.d(:); + obs.v.d(:)]; + Lu=[fcst.H(obs.H.ind); + fcst.v(obs.v.ind)]; + R=[R_HH R_Hv; + R_vH R_vv]; + obstype=[repmat('H',[length(obs.H.d) 1]); + repmat('v',[length(obs.v.d) 1])]; + + % compute the update + hedge=1; %tanh(n/5); % reduce magnitude of update for 1st few iterations + update=hedge*CMt*inv(R+Cd)*(d-Lu); + if(n>1) + update=.5*(u0+update); + end + u0=update; + posterior=prior; + posterior.H0=prior.H0+update(nx+1); + posterior.theta0=prior.theta0+update(nx+2); + posterior.ka_drag=max(1e-4,prior.ka_drag+update(nx+3)); + posterior.h = prior.h + update(1:nx); + posterior.h(posterior.h0) + eps=eps+(posterior.H0-bkgd.H0)^2/CH0; + end + if(Ctheta0>0) + eps=eps+(posterior.theta0-bkgd.theta0)^2/Ctheta0; + end + if(Cka>0) + eps=eps+(posterior.ka_drag-bkgd.ka_drag)^2/Cka; + end + if(eps<1e-4) + break; + end + +end % outer loop iterations + +% OPTIONAL: for diagnostics, repeat the representer calculations for both +% variables, but this time do ALL the gridpoints. These can be used for +% prior and posterior covariances for v, H. This is not used for the outer +% loop updates, so only need to do it once at the end +if(nargout>2) + disp('Calculating full representer matrix as requested (nargout>2)') + + adj_H_h =zeros(nx,nx); + adj_H_H0 =zeros(nx,1); + adj_H_theta0 =zeros(nx,1); + adj_H_ka_drag=zeros(nx,1); + adj_v_h =zeros(nx,nx); + adj_v_H0 =zeros(nx,1); + adj_v_theta0 =zeros(nx,1); + adj_v_ka_drag=zeros(nx,1); + rep_HH=zeros(nx); + rep_Hv=zeros(nx); + rep_Hh=zeros(nx); + rep_vH=zeros(nx); + rep_vv=zeros(nx); + rep_vh=zeros(nx); + rep_HH_post=zeros(nx); + rep_Hv_post=zeros(nx); + rep_Hh_post=zeros(nx); + rep_vH_post=zeros(nx); + rep_vv_post=zeros(nx); + rep_vh_post=zeros(nx); + + % wave height representers + for i=1:nx + comb=zeros(nx,1); + comb(i)=1; + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + adj_H_h(i,:)=ad_h; + adj_H_H0(i,:)=ad_H0; + adj_H_theta0(i,:)=ad_theta0; + adj_H_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + rep_HH(i,:)=tl_H; + rep_Hv(i,:)=tl_v; + rep_Hh(i,:)=Ch*ad_h; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); + rep_HH_post(i,:)=tl_H; + rep_Hv_post(i,:)=tl_v; + rep_Hh_post(i,:)=posterior.Ch*ad_h; + end + + % longshore current representers + for i=1:nx + comb=zeros(nx,1); + comb(i)=1; % data functional (delta-fn, aka identity matrix) + [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity + adj_v_h(i,:)=ad_h; + adj_v_H0(i,:)=ad_H0; + adj_v_theta0(i,:)=ad_theta0; + adj_v_ka_drag(i,:)=ad_ka_drag; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); + rep_vH(i,:)=tl_H; + rep_vv(i,:)=tl_v; + rep_vh(i,:)=Ch*ad_h; + [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); + rep_vH_post(i,:)=tl_H; + rep_vv_post(i,:)=tl_v; + rep_vh_post(i,:)=posterior.Ch*ad_h; + end + + representers.adj_H_h =adj_H_h ; + representers.adj_H_H0 =adj_H_H0 ; + representers.adj_H_theta0 =adj_H_theta0 ; + representers.adj_H_ka_drag=adj_H_ka_drag; + representers.adj_v_h =adj_v_h ; + representers.adj_v_H0 =adj_v_H0 ; + representers.adj_v_theta0 =adj_v_theta0 ; + representers.adj_v_ka_drag=adj_v_ka_drag; + representers.H_H=rep_HH; + representers.H_v=rep_Hv; + representers.H_h=rep_Hh; + representers.v_H=rep_vH; + representers.v_v=rep_vv; + representers.v_h=rep_vh; + representers.H_H_post=rep_HH_post; + representers.H_v_post=rep_Hv_post; + representers.H_h_post=rep_Hh_post; + representers.v_H_post=rep_vH_post; + representers.v_v_post=rep_vv_post; + representers.v_h_post=rep_vh_post; + +end + +%--------------------------------------- +% remaining code: reformat output variables in diagnostics struct, for +% convenience +%--------------------------------------- +clear diagnostics + +diagnostics.niter=n; +diagnostics.eps=eps; + +% terms in update equation +diagnostics.update.CMt=CMt; +diagnostics.update.R =R ; +diagnostics.update.Cd =Cd ; +diagnostics.update.d =d ; +diagnostics.update.Lu =Lu ; +diagnostics.update.obstype=obstype; +diagnostics.update.bkgd=bkgd; +diagnostics.update.fcst=fcst; + +% representer outputs: r.X_Y refers to sensitivity of model input variable Y +% to delta-perturbations of obs type X +diagnostics.r.H_h =r_H(1:nx,:); +diagnostics.r.H_H0 =r_H(nx+1,:); +diagnostics.r.H_theta0=r_H(nx+2,:); +diagnostics.r.H_kadrag=r_H(nx+3,:); +diagnostics.r.v_h =r_v(1:nx,:); +diagnostics.r.v_H0 =r_v(nx+1,:); +diagnostics.r.v_theta0=r_v(nx+2,:); +diagnostics.r.v_kadrag=r_v(nx+3,:); + +% R-matrix outputs: R.X_Y refers to sensitivity of obs type Y to +% delta-perturbations of obs type X +diagnostics.R.H_H=R_HH; +diagnostics.R.H_v=R_Hv; +diagnostics.R.v_H=R_vH; +diagnostics.R.v_v=R_vv; + +% additional diagnostic representers: rdiag.X_Y refers to sensitivity of +% model variable Y to delta-perturbations of obs type X +diagnostics.rdiag.H_H=r_HH; +diagnostics.rdiag.H_v=r_Hv; +diagnostics.rdiag.v_H=r_vH; +diagnostics.rdiag.v_v=r_vv; + +% adjoint outputs: ad.X_Y refers to sensitivity of model input Y to +% delta-perturbations of obs type X +diagnostics.ad.H_h =ad_H_h; +diagnostics.ad.H_H0 =ad_H_H0; +diagnostics.ad.H_theta0 =ad_H_theta0; +diagnostics.ad.H_ka_drag=ad_H_ka_drag; +diagnostics.ad.v_h =ad_v_h; +diagnostics.ad.v_H0 =ad_v_H0; +diagnostics.ad.v_theta0 =ad_v_theta0; +diagnostics.ad.v_ka_drag=ad_v_ka_drag; diff --git a/bin/1DVar/old/waveModel/ad_symmetryCheck.m b/bin/1DVar/old/waveModel/ad_symmetryCheck.m new file mode 100644 index 0000000..5aa8d74 --- /dev/null +++ b/bin/1DVar/old/waveModel/ad_symmetryCheck.m @@ -0,0 +1,36 @@ +% +% tests adjoint code: F'*AD*TL*F should be symmetric, +'ve definite +% +clear + +% get NL solution (background) +x=[100:10:1000]'; +bkgd.h=.01*x; +bkgd.H0=1; +bkgd.theta0=deg2rad(10); +bkgd.sigma=2*pi/10; +bkgd.ka_drag=0.015; +nx=length(x); +bkgd=waveModel(x,bkgd.H0,bkgd.theta0,bkgd); + +% apply TL and ADJ models for n instances of random forcing F (actually, IC +% perturbations) +eps = 0.05; +n=10; +F = eps*rand(nx,n); +F(end,:)=F(1,:); +for i=1:n + % TL model: u=TL*F + [tl_H,tl_theta,tl_v,tl_k]=tl_waveModel(x,F(:,i),0,0,0,bkgd); + % ADJ model: g=ADJ*(TL*F) + g(:,i)=ad_waveModel(x,tl_H,tl_theta,tl_v,tl_k,bkgd); +end + +% test whether result makes sense +A = F'*g; + +(A-A')/sum(diag(A)) % should be zeros to within roundoff + +%pcolor(A) +format short g +eig(A) % should be +'ve diff --git a/bin/1DVar/old/waveModel/ad_waveModel.m b/bin/1DVar/old/waveModel/ad_waveModel.m new file mode 100644 index 0000000..ca09ffe --- /dev/null +++ b/bin/1DVar/old/waveModel/ad_waveModel.m @@ -0,0 +1,361 @@ +function [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_k,bkgd) +% +% [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_eps_r,ad_k,bkgd) +% +% AD-code for tl_waveModel.m. Background state 'bkgd' can be a struct taken +% directly from output of waveModel.m, and 'tl_in' can be taken directly +% from output of tl_waveModel.m. +% +[g,alpha,beta,nu]=waveModelParams(); + +% grid +nx=length(x); +dx=diff(x(1:2)); + +% break out the bkgd vars +sigma=bkgd.sigma; +E =bkgd.E ; +Er =bkgd.Er ; +eps_b=bkgd.eps_b; +eps_r=bkgd.eps_r; +c =bkgd.c ; +cg =bkgd.cg ; +k =bkgd.k ; +h =bkgd.h ; +n =bkgd.n ; +theta=bkgd.theta; +sigma=bkgd.sigma; +x =bkgd.x ; +H =bkgd.H ; +dSxydx=bkgd.dSxydx; +Fy=bkgd.Fy; +v=bkgd.v; +H0=bkgd.H0; +theta0=bkgd.theta0; +gamma=bkgd.gamma; +Hm=bkgd.Hm; +Qb=bkgd.Qb; + +ka_drag=bkgd.ka_drag; + +refconst=sin(theta0)/c(nx); + +%----------------------------- +% begin adjoint code +%----------------------------- + +% initialize. All these variables are assumed to never be directly +% observed, else they would be provided as inputs +zz=zeros(nx,1); +ad_c=zz; +ad_Er=zz; +ad_E=zz; +ad_eps_b=zz; +ad_eps_r=zz; +ad_cg=zz; +ad_h=zz; +ad_n=zz; +% ad_k=zz; +ad_Qb=zz; +ad_Hm=zz; +ad_refconst=0; +ad_gamma=0; + +% bottom stress model following Ruessink et al. (2001), Feddersen et +% al. (2000) +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +urms=1.416*H*sigma./(4*sinh(k.*h)); +% B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); % old version of TL code, no mixing +B=a^2+(v./urms).^2; +dens = -urms.*Cd - v.^2.*Cd./urms./B; + +% mixing operator +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); + +% % OLD: incorrect version for with-mixing case +% if(nu>0) +% % v2: with mixing +% %3b tl_v = inv(diag((1+B)./v)+A)*tl_N; +% ad_N=inv(diag((1+B)./v)+A)'*ad_v; +% ad_v=0; +% %3a tl_N=tl_Fy./Fy - tl_Cd./Cd - tl_urms./urms.*(1-B); +% ad_Fy=ad_N./Fy; +% ad_Cd=-ad_N./Cd; +% ad_urms=-ad_N./urms.*(1-B); +% ad_N=0; +% NEW: corrected version for with-mixing case +if(nu>0) + % v2: with mixing + %3b tl_v = inv(diag(dens)+A)*tl_N; + ad_N=inv(diag(dens)+A)'*ad_v; +else + % v1: no mixing: + %3b tl_v=tl_N./dens; + ad_N=ad_v./dens; +end +ad_v=0; + +%3a tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; +ad_Fy=ad_N./sqrt(B); +ad_urms=ad_N.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)); +ad_Cd=ad_N.*v.*urms; +ad_N=0; + +%2 tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... +% -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); +ad_H = ad_H + 1.416*sigma*( ad_urms./(4*sinh(k.*h)) ); +ad_k = ad_k - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*h; +ad_h = ad_h - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*k; +ad_urms=0; + +% note below: ka_drag is scalar, so sum over gridpoints. Can see this by +% considering if the code was in a loop, then scalar ad_ka_drag would +% receive a contribution from each loop iteration +%1 tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); +ad_h = ad_h + 0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*ad_Cd); +ad_ka_drag = sum(0.015*(1/3)*(ka_drag./h).^(-2/3).*(ad_Cd./h)); % if correcting ka_drag +ad_Cd=0; + +% total force = radiation stress gradient + wind stress +% tl_Fy=tl_dSxydx; +ad_dSxydx=ad_Fy; +ad_Fy=0; + +% radiation stress gradient +if(beta>0) + % tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... + % +sin(theta)./c.^2.*eps_r.*tl_c ... + % -sin(theta)./c.*tl_eps_r; + ad_theta=ad_theta-cos(theta)./c.*eps_r.*ad_dSxydx; + ad_c=ad_c+sin(theta)./c.^2.*eps_r.*ad_dSxydx; + ad_eps_r=ad_eps_r-sin(theta)./c.*ad_dSxydx; + ad_dSxydx=0; +else + % tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... + % +sin(theta)./c.^2.*eps_b.*tl_c ... + % -sin(theta)./c.*tl_eps_b; + ad_theta=ad_theta-cos(theta)./c.*eps_b.*ad_dSxydx; + ad_c=ad_c+sin(theta)./c.^2.*eps_b.*ad_dSxydx; + ad_eps_b=ad_eps_b-sin(theta)./c.*ad_dSxydx; + ad_dSxydx=0; +end + +% stepping, explicit scheme +for i=1:(nx-1) + + % tl_H(i)=.5./sqrt(8/g*max(0,E(i)))*8/g.*tl_E(i); + if(E(i)==0) + ad_E(i)=0; + else + ad_E(i)=ad_E(i)+.5./sqrt(8/g*E(i))*8/g.*ad_H(i); + end + ad_H(i)=0; + + if(beta>0) + + % term 4 + nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); + nums2=dx*(eps_b(i+1)-eps_r(i+1)); + denoms=2*c(i)*cos(theta(i)); + %4 tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... + % - (nums1+nums2)/denoms^2*tl_denoms; + ad_nums1=ad_Er(i)/denoms; + ad_nums2=ad_Er(i)/denoms; + ad_denoms=-(nums1+nums2)/denoms^2*ad_Er(i); + ad_Er(i)=0; + %3 tl_denoms=2*tl_c(i)*cos(theta(i)) ... + % - 2*c(i)*sin(theta(i))*tl_theta(i); + ad_c(i)=ad_c(i)+2*cos(theta(i))*ad_denoms; + ad_theta(i)=ad_theta(i)-2*c(i)*sin(theta(i))*ad_denoms; + ad_denoms=0; + %2 tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); + ad_eps_b(i+1)=ad_eps_b(i+1)+dx*ad_nums2; + ad_eps_r(i+1)=ad_eps_r(i+1)-dx*ad_nums2; + ad_nums2=0; + %1 tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... + % + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... + % - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); + ad_Er(i+1)=ad_Er(i+1)+2*c(i+1)*cos(theta(i+1))*ad_nums1; + ad_c(i+1)=ad_c(i+1)+2*Er(i+1)*cos(theta(i+1))*ad_nums1; + ad_theta(i+1)=ad_theta(i+1)-2*Er(i+1)*c(i+1)*sin(theta(i+1))*ad_nums1; + ad_nums1=0; + + % term 3 + c1=2*g*sin(beta)/c(i+1); + c2=-2*g*sin(beta)*Er(i+1)/c(i+1)^2; + % tl_eps_r(i+1) = c1 * tl_Er(i+1) ... + % + c2 * tl_c(i+1); + ad_Er(i+1)=ad_Er(i+1)+c1*ad_eps_r(i+1); + ad_c(i+1) =ad_c(i+1) +c2*ad_eps_r(i+1); + ad_eps_r(i+1)=0; + + end + + % term 2 + nums1=cg(i+1)*E(i+1)*cos(theta(i+1)); + nums2=eps_b(i+1)*dx; + denoms=cg(i)*cos(theta(i)); + %4 tl_E(i) = tl_nums1/denoms ... + % - tl_nums2/denoms ... + % - (nums1-nums2)/denoms^2*tl_denoms; + ad_nums1=ad_E(i)/denoms; % note, consts initialized to zero + ad_nums2=-ad_E(i)/denoms; + ad_denoms=-(nums1-nums2)/denoms^2*ad_E(i); + ad_E(i)=0; + %3 tl_denoms=tl_cg(i)*cos(theta(i)) ... + % - cg(i)*sin(theta(i))*tl_theta(i); + ad_cg(i)=ad_cg(i)+cos(theta(i))*ad_denoms; + ad_theta(i)=ad_theta(i) - cg(i)*sin(theta(i))*ad_denoms; + ad_denoms=0; + %2 tl_nums2=tl_eps_b(i+1)*dx; + ad_eps_b(i+1)=ad_eps_b(i+1)+ad_nums2*dx; + ad_nums2=0; + %1 tl_nums1=tl_cg(i+1)*E(i+1)*cos(theta(i+1)) ... + % + cg(i+1)*tl_E(i+1)*cos(theta(i+1)) ... + % - cg(i+1)*E(i+1)*sin(theta(i+1))*tl_theta(i+1); + ad_cg(i+1)=ad_cg(i+1)+ad_nums1*E(i+1)*cos(theta(i+1)); + ad_E(i+1)=ad_E(i+1)+ad_nums1*cg(i+1)*cos(theta(i+1)); + ad_theta(i+1)=ad_theta(i+1)-ad_nums1*cg(i+1)*E(i+1)*sin(theta(i+1)); + ad_nums1=0; + + % term 1 + c1=alpha/4*g*(sigma/2/pi); + % tl_eps_b(i+1)=c1*tl_Qb(i+1)*Hm(i+1)^2 ... + % + 2*c1*Qb(i+1)*Hm(i+1)*tl_Hm; + ad_Qb(i+1)=ad_Qb(i+1)+c1*Hm(i+1)^2*ad_eps_b(i+1); + ad_Hm(i+1)=ad_Hm(i+1)+2*c1*Qb(i+1)*Hm(i+1)*ad_eps_b(i+1); + ad_eps_b(i+1)=0; + + % fraction of breaking waves, non-implicit approximation from SWAN code + B=H(i+1)/Hm(i+1); + if(B<=.5) + Qo=0; + else + Qo=(2*B-1)^2; + end + if(.20) + + % term 3 + % eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); + tl_eps_r(i+1)=2*g*tl_Er(i+1)*sin(beta)/c(i+1) ... + - 2*g*Er(i+1)*sin(beta)/c(i+1)^2*tl_c(i+1); + + % term 4 + nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); + nums2=dx*(eps_b(i+1)-eps_r(i+1)); + denoms=2*c(i)*cos(theta(i)); + tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... + + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... + - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); + tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); + tl_denoms=2*tl_c(i)*cos(theta(i)) ... + - 2*c(i)*sin(theta(i))*tl_theta(i); + % Er(i)=(nums1+nums2)/denoms; + tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... + - (nums1+nums2)/denoms^2*tl_denoms; + + end + + % H(i)=sqrt(8/g*E(i)); + if(E(i)==0) + tl_H(i)=0; + else + tl_H(i)=.5./sqrt(8/g*E(i))*8/g.*tl_E(i); + end + +end +tl_c=tl_c(:); +tl_H=tl_H(:); +tl_theta=tl_theta(:); + +% radiation stress gradient +if(beta>0) + % dSxydx = -sin(theta)./c.*eps_r; + tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... + +sin(theta)./c.^2.*eps_r.*tl_c ... + -sin(theta)./c.*tl_eps_r; +else + % dSxydx = -sin(theta)./c.*eps_b; + tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... + +sin(theta)./c.^2.*eps_b.*tl_c ... + -sin(theta)./c.*tl_eps_b; +end + +% total force = radiation stress gradient + wind stress +% Fy=dSxydx+tauw; +tl_Fy=tl_dSxydx; + +% define mixing operator +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); + +% bottom stress model following Ruessink et al. (2001), Feddersen et +% al. (2000). To get TL model, differentiate the eqn for v (i.e., the +% fsolve() line in waveModel.m) on both sides, then solve for +% tl_v +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); +urms=1.416*H*sigma./(4*sinh(k.*h)); +tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... + -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); +B=a^2+(v./urms).^2; +tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; +dens = -urms.*Cd - v.^2.*Cd./urms./B; +if(nu==0) + tl_v=tl_N./dens; +else + tl_v = inv(diag(dens)+A)*tl_N; % tl_N = (dens + A) * tl_v +end + + + + + +return; +%----------------------------------- +% OLD: incorrect derivation of tl_v, did not correctly incorporate mixing +%----------------------------------- + +% bottom stress model following Ruessink et al. (2001), Feddersen et +% al. (2000). To get TL model, differentiate the eqn for tau_b=Fy on both +% sides, then solve for tl_v... double-checked algebra on this, and verified +% that tl_v is consistent with a perturbed nonlinear model (but this check +% only applied to the version without mixing) +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); +urms=1.416*H*sigma./(4*sinh(k.*h)); +tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... + -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); +B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); +nums = tl_Fy./Fy ... + - tl_Cd./Cd ... + - tl_urms./urms.*(1-B); +dens = (1+B)./v; +tl_v = nums./dens; + +% v2: with mixing operator. Note previously without mixing I had worked out +% the TL form of tau_b(v)=Fy, to get +% +% nums = dens.*tl_v +% = diag(dens)*tl_v. +% +% Now just add in the mixing operator A to the same derivation, to get +% +% nums = ( diag(dens) + A )*tl_v. +% ---> tl_v = inv(...)*nums +% +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); +if(nu==0) + tl_v=nums./dens; +else + keyboard; + tl_v = pinv(diag(dens)+A)*nums; +end \ No newline at end of file diff --git a/bin/1DVar/old/waveModel/waveModel.m b/bin/1DVar/old/waveModel/waveModel.m new file mode 100644 index 0000000..fb555aa --- /dev/null +++ b/bin/1DVar/old/waveModel/waveModel.m @@ -0,0 +1,184 @@ +function out=waveModel(x,H0,theta0,out) +% +% out=waveModel(x,H0,theta0,in) +% +% Wave energy balance equation solver, explicit spatial stepping scheme. +% +% Breaking dissipation (eps_b) using TG1983 +% Roller energy (Er) and roller dissipation (eps_r) following Reniers & Battjes (1996) +% +% NOTE: input theta0 in radians +% +% 'in' will be appended/overwritten with new variables to create 'out', and +% should contain the following minimal inputs: +% +% in.{h,H0,theta0,sigma,tauw} +% +% note, tauw is optional, alongshore component of wind stress in m2/s2 units +% + +h =out.h ; +sigma =out.sigma ; +ka_drag=out.ka_drag; + +% wind stress implemented later, so use as optional argument to ensure +% backwards compatibility +if(isfield(out,'tauw')) + tauw=out.tauw; +else + tauw=0; +end + +[g,alpha,beta,nu]=waveModelParams(); + +% grid +nx=length(x); +dx=diff(x(1:2)); + +% dispersion +% k=fsolve(@(k)sigma^2-g*k.*tanh(k.*h),sigma./sqrt(g*h),optimset('Display','off')); +k=nan*h; +for i=1:nx + k(i)=fzero(@(k)sigma^2-g*k.*tanh(k.*h(i)),sigma./sqrt(g*h(i)),optimset('Display','off')); +end + +c=max(0,real(sigma./k)); +n=.5*(1+2*k.*h./sinh(2*k.*h)); +cg=n.*c; +refconst=sin(theta0)/c(nx); + +% gamma calculated based on deep water wave steepness (s0) following Battjes +% and Stive (1985), and also used by Ruessink et al. (2001) +L0=g/(2*pi*(sigma/2/pi)^2); +s0=H0/L0; +gamma=0.5+0.4*tanh(33*s0); + +% refraction +theta=asin(c.*refconst); + +% stepping, explicit scheme +E=zeros(nx,1); +Er=zeros(nx,1); +eps_b=zeros(nx,1); +eps_r=zeros(nx,1); +E(nx)=g/8*H0^2; +Er(nx)=0; +H(nx)=H0; +theta(nx)=theta0; %asin(c(nx).*refconst); + +for i=(nx-1):-1:1 + % max wave height + tharg=gamma/0.88.*k(i+1).*h(i+1); + Hm(i+1)=0.88./k(i+1).*tanh(tharg); + + % fraction of breaking waves, non-implicit approximation from SWAN code + B=H(i+1)/Hm(i+1); + if(B<=.5) + Qo=0; + else + Qo=(2*B-1)^2; + end + if(B<=.2) + Qb(i+1)=0; + elseif(.20) + eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); + Er(i)=(2*Er(i+1)*c(i+1)*cos(theta(i+1))+dx*(eps_b(i+1)-eps_r(i+1)))/(2*c(i)*cos(theta(i))); + if(Er(i)<0) + Er(i)=0; + end + end + + if(E(i)<.001) + E(i)=.001; + end + H(i)=sqrt(8/g*E(i)); + +end +c=c(:); +cg=cg(:); +k=k(:); +n=n(:); +theta=theta(:); +H=H(:); + +% radiation stress gradient +if(beta>0) % roller + dSxydx = -sin(theta)./c.*eps_r; +else + dSxydx=-sin(theta)./c.*eps_b; +end +dSxydx(dSxydx==0)=1e-6; % avoid singularity in TL model + +% bottom stress model following Ruessink et al. (2001), Feddersen et al. (2000) + +% total force = radiation stress gradient + wind stress +Fy=dSxydx+tauw; + +% v1: analytical solution, no mixing +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +urms=1.416*H.*sigma./(4*sinh(k.*h)); +v2 = sqrt( (a*Cd.*urms).^4 + 4*(Cd.*Fy).^2 )./(2*Cd.^2) - (a*urms).^2/2; +v=sqrt(v2).*sign(-Fy); + +% mixing operator +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); + +% v2: nonlinear solution with mixing. +test=A*v; +v0=v; +%disp('waveModel test 1.5') +%disp(test) +%disp(Fy) not zero +%disp(Cd) not zero +%disp(Cd.') not zero +%disp(urms) not zero +%disp(urms.') +%disp(v) not zero +%disp(v.') + +%v = fsolve(@(v)Fy + Cd.*urms.*v.*sqrt(a^2+(v./urms).^2) - A*v,v0,optimset('Display','off')); +%disp('waveModel test 2') + +% outputs struct +out.E =E ; +out.Er =Er ; +out.eps_b=eps_b; +out.eps_r=eps_r; +out.c=c; +out.cg=cg; +out.k=k; +out.h=h; +out.n=n; +out.theta=theta; +out.sigma=sigma; +out.x=x; +out.H=H; +out.gamma=gamma; +out.Hm=Hm; +out.Qb=Qb; +out.dSxydx=dSxydx; +out.Fy=Fy; +out.v=real(v); +out.ka_drag=ka_drag; diff --git a/bin/1DVar/old/waveModel/waveModelParams.m b/bin/1DVar/old/waveModel/waveModelParams.m new file mode 100644 index 0000000..99e80fb --- /dev/null +++ b/bin/1DVar/old/waveModel/waveModelParams.m @@ -0,0 +1,9 @@ +function [g,alpha,beta,nu]=waveModelParams(); +% +% common params for NL-TL-AD model +% + +g=9.8; +alpha=1; +beta=0.1; % roller parameter +nu=.5; diff --git a/bin/1DVar/waveModel/ad_symmetryCheck.m b/bin/1DVar/waveModel/ad_symmetryCheck.m new file mode 100644 index 0000000..5aa8d74 --- /dev/null +++ b/bin/1DVar/waveModel/ad_symmetryCheck.m @@ -0,0 +1,36 @@ +% +% tests adjoint code: F'*AD*TL*F should be symmetric, +'ve definite +% +clear + +% get NL solution (background) +x=[100:10:1000]'; +bkgd.h=.01*x; +bkgd.H0=1; +bkgd.theta0=deg2rad(10); +bkgd.sigma=2*pi/10; +bkgd.ka_drag=0.015; +nx=length(x); +bkgd=waveModel(x,bkgd.H0,bkgd.theta0,bkgd); + +% apply TL and ADJ models for n instances of random forcing F (actually, IC +% perturbations) +eps = 0.05; +n=10; +F = eps*rand(nx,n); +F(end,:)=F(1,:); +for i=1:n + % TL model: u=TL*F + [tl_H,tl_theta,tl_v,tl_k]=tl_waveModel(x,F(:,i),0,0,0,bkgd); + % ADJ model: g=ADJ*(TL*F) + g(:,i)=ad_waveModel(x,tl_H,tl_theta,tl_v,tl_k,bkgd); +end + +% test whether result makes sense +A = F'*g; + +(A-A')/sum(diag(A)) % should be zeros to within roundoff + +%pcolor(A) +format short g +eig(A) % should be +'ve diff --git a/bin/1DVar/waveModel/ad_waveModel.m b/bin/1DVar/waveModel/ad_waveModel.m new file mode 100644 index 0000000..e1def5f --- /dev/null +++ b/bin/1DVar/waveModel/ad_waveModel.m @@ -0,0 +1,362 @@ +function [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_k,bkgd) +% +% [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_eps_r,ad_k,bkgd) +% +% AD-code for tl_waveModel.m. Background state 'bkgd' can be a struct taken +% directly from output of waveModel.m, and 'tl_in' can be taken directly +% from output of tl_waveModel.m. +% + +[g,alpha,beta,nu]=waveModelParams(); + +% grid +nx=length(x); +dx=diff(x(1:2)); + +% break out the bkgd vars +sigma=bkgd.sigma; +E =bkgd.E ; +Er =bkgd.Er ; +eps_b=bkgd.eps_b; +eps_r=bkgd.eps_r; +c =bkgd.c ; +cg =bkgd.cg ; +k =bkgd.k ; +h =bkgd.h ; +n =bkgd.n ; +theta=bkgd.theta; +sigma=bkgd.sigma; +x =bkgd.x ; +H =bkgd.H ; +dSxydx=bkgd.dSxydx; +Fy=bkgd.Fy; +v=bkgd.v; +H0=bkgd.H0; +theta0=bkgd.theta0; +gamma=bkgd.gamma; +Hm=bkgd.Hm; +Qb=bkgd.Qb; + +ka_drag=bkgd.ka_drag; + +refconst=sin(theta0)/c(nx); + +%----------------------------- +% begin adjoint code +%----------------------------- + +% initialize. All these variables are assumed to never be directly +% observed, else they would be provided as inputs +zz=zeros(nx,1); +ad_c=zz; +ad_Er=zz; +ad_E=zz; +ad_eps_b=zz; +ad_eps_r=zz; +ad_cg=zz; +ad_h=zz; +ad_n=zz; +% ad_k=zz; +ad_Qb=zz; +ad_Hm=zz; +ad_refconst=0; +ad_gamma=0; + +% bottom stress model following Ruessink et al. (2001), Feddersen et +% al. (2000) +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +urms=1.416*H*sigma./(4*sinh(k.*h)); +% B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); % old version of TL code, no mixing +B=a^2+(v./urms).^2; +dens = -urms.*Cd - v.^2.*Cd./urms./B; + +% mixing operator +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); + +% % OLD: incorrect version for with-mixing case +% if(nu>0) +% % v2: with mixing +% %3b tl_v = inv(diag((1+B)./v)+A)*tl_N; +% ad_N=inv(diag((1+B)./v)+A)'*ad_v; +% ad_v=0; +% %3a tl_N=tl_Fy./Fy - tl_Cd./Cd - tl_urms./urms.*(1-B); +% ad_Fy=ad_N./Fy; +% ad_Cd=-ad_N./Cd; +% ad_urms=-ad_N./urms.*(1-B); +% ad_N=0; +% NEW: corrected version for with-mixing case +if(nu>0) + % v2: with mixing + %3b tl_v = inv(diag(dens)+A)*tl_N; + ad_N=inv(diag(dens)+A)'*ad_v; +else + % v1: no mixing: + %3b tl_v=tl_N./dens; + ad_N=ad_v./dens; +end +ad_v=0; + +%3a tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; +ad_Fy=ad_N./sqrt(B); +ad_urms=ad_N.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)); +ad_Cd=ad_N.*v.*urms; +ad_N=0; + +%2 tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... +% -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); +ad_H = ad_H + 1.416*sigma*( ad_urms./(4*sinh(k.*h)) ); +ad_k = ad_k - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*h; +ad_h = ad_h - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*k; +ad_urms=0; + +% note below: ka_drag is scalar, so sum over gridpoints. Can see this by +% considering if the code was in a loop, then scalar ad_ka_drag would +% receive a contribution from each loop iteration +%1 tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); +ad_h = ad_h + 0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*ad_Cd); +ad_ka_drag = sum(0.015*(1/3)*(ka_drag./h).^(-2/3).*(ad_Cd./h)); % if correcting ka_drag +ad_Cd=0; + +% total force = radiation stress gradient + wind stress +% tl_Fy=tl_dSxydx; +ad_dSxydx=ad_Fy; +ad_Fy=0; + +% radiation stress gradient +if(beta>0) + % tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... + % +sin(theta)./c.^2.*eps_r.*tl_c ... + % -sin(theta)./c.*tl_eps_r; + ad_theta=ad_theta-cos(theta)./c.*eps_r.*ad_dSxydx; + ad_c=ad_c+sin(theta)./c.^2.*eps_r.*ad_dSxydx; + ad_eps_r=ad_eps_r-sin(theta)./c.*ad_dSxydx; + ad_dSxydx=0; +else + % tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... + % +sin(theta)./c.^2.*eps_b.*tl_c ... + % -sin(theta)./c.*tl_eps_b; + ad_theta=ad_theta-cos(theta)./c.*eps_b.*ad_dSxydx; + ad_c=ad_c+sin(theta)./c.^2.*eps_b.*ad_dSxydx; + ad_eps_b=ad_eps_b-sin(theta)./c.*ad_dSxydx; + ad_dSxydx=0; +end + +% stepping, explicit scheme +for i=1:(nx-1) + + % tl_H(i)=.5./sqrt(8/g*max(0,E(i)))*8/g.*tl_E(i); + if(E(i)==0) + ad_E(i)=0; + else + ad_E(i)=ad_E(i)+.5./sqrt(8/g*E(i))*8/g.*ad_H(i); + end + ad_H(i)=0; + + if(beta>0) + + % term 4 + nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); + nums2=dx*(eps_b(i+1)-eps_r(i+1)); + denoms=2*c(i)*cos(theta(i)); + %4 tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... + % - (nums1+nums2)/denoms^2*tl_denoms; + ad_nums1=ad_Er(i)/denoms; + ad_nums2=ad_Er(i)/denoms; + ad_denoms=-(nums1+nums2)/denoms^2*ad_Er(i); + ad_Er(i)=0; + %3 tl_denoms=2*tl_c(i)*cos(theta(i)) ... + % - 2*c(i)*sin(theta(i))*tl_theta(i); + ad_c(i)=ad_c(i)+2*cos(theta(i))*ad_denoms; + ad_theta(i)=ad_theta(i)-2*c(i)*sin(theta(i))*ad_denoms; + ad_denoms=0; + %2 tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); + ad_eps_b(i+1)=ad_eps_b(i+1)+dx*ad_nums2; + ad_eps_r(i+1)=ad_eps_r(i+1)-dx*ad_nums2; + ad_nums2=0; + %1 tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... + % + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... + % - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); + ad_Er(i+1)=ad_Er(i+1)+2*c(i+1)*cos(theta(i+1))*ad_nums1; + ad_c(i+1)=ad_c(i+1)+2*Er(i+1)*cos(theta(i+1))*ad_nums1; + ad_theta(i+1)=ad_theta(i+1)-2*Er(i+1)*c(i+1)*sin(theta(i+1))*ad_nums1; + ad_nums1=0; + + % term 3 + c1=2*g*sin(beta)/c(i+1); + c2=-2*g*sin(beta)*Er(i+1)/c(i+1)^2; + % tl_eps_r(i+1) = c1 * tl_Er(i+1) ... + % + c2 * tl_c(i+1); + ad_Er(i+1)=ad_Er(i+1)+c1*ad_eps_r(i+1); + ad_c(i+1) =ad_c(i+1) +c2*ad_eps_r(i+1); + ad_eps_r(i+1)=0; + + end + + % term 2 + nums1=cg(i+1)*E(i+1)*cos(theta(i+1)); + nums2=eps_b(i+1)*dx; + denoms=cg(i)*cos(theta(i)); + %4 tl_E(i) = tl_nums1/denoms ... + % - tl_nums2/denoms ... + % - (nums1-nums2)/denoms^2*tl_denoms; + ad_nums1=ad_E(i)/denoms; % note, consts initialized to zero + ad_nums2=-ad_E(i)/denoms; + ad_denoms=-(nums1-nums2)/denoms^2*ad_E(i); + ad_E(i)=0; + %3 tl_denoms=tl_cg(i)*cos(theta(i)) ... + % - cg(i)*sin(theta(i))*tl_theta(i); + ad_cg(i)=ad_cg(i)+cos(theta(i))*ad_denoms; + ad_theta(i)=ad_theta(i) - cg(i)*sin(theta(i))*ad_denoms; + ad_denoms=0; + %2 tl_nums2=tl_eps_b(i+1)*dx; + ad_eps_b(i+1)=ad_eps_b(i+1)+ad_nums2*dx; + ad_nums2=0; + %1 tl_nums1=tl_cg(i+1)*E(i+1)*cos(theta(i+1)) ... + % + cg(i+1)*tl_E(i+1)*cos(theta(i+1)) ... + % - cg(i+1)*E(i+1)*sin(theta(i+1))*tl_theta(i+1); + ad_cg(i+1)=ad_cg(i+1)+ad_nums1*E(i+1)*cos(theta(i+1)); + ad_E(i+1)=ad_E(i+1)+ad_nums1*cg(i+1)*cos(theta(i+1)); + ad_theta(i+1)=ad_theta(i+1)-ad_nums1*cg(i+1)*E(i+1)*sin(theta(i+1)); + ad_nums1=0; + + % term 1 + c1=alpha/4*g*(sigma/2/pi); + % tl_eps_b(i+1)=c1*tl_Qb(i+1)*Hm(i+1)^2 ... + % + 2*c1*Qb(i+1)*Hm(i+1)*tl_Hm; + ad_Qb(i+1)=ad_Qb(i+1)+c1*Hm(i+1)^2*ad_eps_b(i+1); + ad_Hm(i+1)=ad_Hm(i+1)+2*c1*Qb(i+1)*Hm(i+1)*ad_eps_b(i+1); + ad_eps_b(i+1)=0; + + % fraction of breaking waves, non-implicit approximation from SWAN code + B=H(i+1)/Hm(i+1); + if(B<=.5) + Qo=0; + else + Qo=(2*B-1)^2; + end + if(.20) + + % term 3 + % eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); + tl_eps_r(i+1)=2*g*tl_Er(i+1)*sin(beta)/c(i+1) ... + - 2*g*Er(i+1)*sin(beta)/c(i+1)^2*tl_c(i+1); + + % term 4 + nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); + nums2=dx*(eps_b(i+1)-eps_r(i+1)); + denoms=2*c(i)*cos(theta(i)); + tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... + + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... + - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); + tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); + tl_denoms=2*tl_c(i)*cos(theta(i)) ... + - 2*c(i)*sin(theta(i))*tl_theta(i); + % Er(i)=(nums1+nums2)/denoms; + tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... + - (nums1+nums2)/denoms^2*tl_denoms; + + end + + % H(i)=sqrt(8/g*E(i)); + if(E(i)==0) + tl_H(i)=0; + else + tl_H(i)=.5./sqrt(8/g*E(i))*8/g.*tl_E(i); + end + +end +tl_c=tl_c(:); +tl_H=tl_H(:); +tl_theta=tl_theta(:); + +% radiation stress gradient +if(beta>0) + % dSxydx = -sin(theta)./c.*eps_r; + tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... + +sin(theta)./c.^2.*eps_r.*tl_c ... + -sin(theta)./c.*tl_eps_r; +else + % dSxydx = -sin(theta)./c.*eps_b; + tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... + +sin(theta)./c.^2.*eps_b.*tl_c ... + -sin(theta)./c.*tl_eps_b; +end + +% total force = radiation stress gradient + wind stress +% Fy=dSxydx+tauw; +tl_Fy=tl_dSxydx; + +% define mixing operator +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); + +% bottom stress model following Ruessink et al. (2001), Feddersen et +% al. (2000). To get TL model, differentiate the eqn for v (i.e., the +% fsolve() line in waveModel.m) on both sides, then solve for +% tl_v +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); +urms=1.416*H*sigma./(4*sinh(k.*h)); +tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... + -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); +B=a^2+(v./urms).^2; +tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; +dens = -urms.*Cd - v.^2.*Cd./urms./B; +if(nu==0) + tl_v=tl_N./dens; +else + tl_v = inv(diag(dens)+A)*tl_N; % tl_N = (dens + A) * tl_v +end + + + + + +return; +%----------------------------------- +% OLD: incorrect derivation of tl_v, did not correctly incorporate mixing +%----------------------------------- + +% bottom stress model following Ruessink et al. (2001), Feddersen et +% al. (2000). To get TL model, differentiate the eqn for tau_b=Fy on both +% sides, then solve for tl_v... double-checked algebra on this, and verified +% that tl_v is consistent with a perturbed nonlinear model (but this check +% only applied to the version without mixing) +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); +urms=1.416*H*sigma./(4*sinh(k.*h)); +tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... + -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); +B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); +nums = tl_Fy./Fy ... + - tl_Cd./Cd ... + - tl_urms./urms.*(1-B); +dens = (1+B)./v; +tl_v = nums./dens; + +% v2: with mixing operator. Note previously without mixing I had worked out +% the TL form of tau_b(v)=Fy, to get +% +% nums = dens.*tl_v +% = diag(dens)*tl_v. +% +% Now just add in the mixing operator A to the same derivation, to get +% +% nums = ( diag(dens) + A )*tl_v. +% ---> tl_v = inv(...)*nums +% +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); +if(nu==0) + tl_v=nums./dens; +else + keyboard; + tl_v = pinv(diag(dens)+A)*nums; +end diff --git a/bin/1DVar/waveModel/waveModel.m b/bin/1DVar/waveModel/waveModel.m new file mode 100644 index 0000000..98cf507 --- /dev/null +++ b/bin/1DVar/waveModel/waveModel.m @@ -0,0 +1,172 @@ +function out=waveModel(x,H0,theta0,out) +% +% out=waveModel(x,H0,theta0,in) +% +% Wave energy balance equation solver, explicit spatial stepping scheme. +% +% Breaking dissipation (eps_b) using TG1983 +% Roller energy (Er) and roller dissipation (eps_r) following Reniers & Battjes (1996) +% +% NOTE: input theta0 in radians +% +% 'in' will be appended/overwritten with new variables to create 'out', and +% should contain the following minimal inputs: +% +% in.{h,H0,theta0,sigma,tauw} +% +% note, tauw is optional, alongshore component of wind stress in m2/s2 units +% + +h =out.h ; +sigma =out.sigma ; +ka_drag=out.ka_drag; + +% wind stress implemented later, so use as optional argument to ensure +% backwards compatibility +if(isfield(out,'tauw')) + tauw=out.tauw; +else + tauw=0; +end + +[g,alpha,beta,nu]=waveModelParams(); + +% grid +nx=length(x); +dx=diff(x(1:2)); + +% dispersion +% k=fsolve(@(k)sigma^2-g*k.*tanh(k.*h),sigma./sqrt(g*h),optimset('Display','off')); +k=nan*h; +for i=1:nx + k(i)=fzero(@(k)sigma^2-g*k.*tanh(k.*h(i)),sigma./sqrt(g*h(i)),optimset('Display','off')); +end +c=max(0,real(sigma./k)); +n=.5*(1+2*k.*h./sinh(2*k.*h)); +cg=n.*c; +refconst=sin(theta0)/c(nx); + +% gamma calculated based on deep water wave steepness (s0) following Battjes +% and Stive (1985), and also used by Ruessink et al. (2001) +L0=g/(2*pi*(sigma/2/pi)^2); +s0=H0/L0; +gamma=0.5+0.4*tanh(33*s0); + +% refraction +theta=asin(c.*refconst); + +% stepping, explicit scheme +E=zeros(nx,1); +Er=zeros(nx,1); +eps_b=zeros(nx,1); +eps_r=zeros(nx,1); +E(nx)=g/8*H0^2; +Er(nx)=0; +H(nx)=H0; +theta(nx)=theta0; %asin(c(nx).*refconst); +for i=(nx-1):-1:1 + + % max wave height + tharg=gamma/0.88.*k(i+1).*h(i+1); + Hm(i+1)=0.88./k(i+1).*tanh(tharg); + + % fraction of breaking waves, non-implicit approximation from SWAN code + B=H(i+1)/Hm(i+1); + if(B<=.5) + Qo=0; + else + Qo=(2*B-1)^2; + end + if(B<=.2) + Qb(i+1)=0; + elseif(.20) + eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); + Er(i)=(2*Er(i+1)*c(i+1)*cos(theta(i+1))+dx*(eps_b(i+1)-eps_r(i+1)))/(2*c(i)*cos(theta(i))); + if(Er(i)<0) + Er(i)=0; + end + end + + if(E(i)<.001) + E(i)=.001; + end + H(i)=sqrt(8/g*E(i)); + +end +c=c(:); +cg=cg(:); +k=k(:); +n=n(:); +theta=theta(:); +H=H(:); + +% radiation stress gradient +if(beta>0) % roller + dSxydx = -sin(theta)./c.*eps_r; +else + dSxydx=-sin(theta)./c.*eps_b; +end +dSxydx(dSxydx==0)=1e-6; % avoid singularity in TL model + +% bottom stress model following Ruessink et al. (2001), Feddersen et al. (2000) + +% total force = radiation stress gradient + wind stress +Fy=dSxydx+tauw; + +% v1: analytical solution, no mixing +a=1.16; % empirical constant +Cd=0.015*(ka_drag./h).^(1/3); +urms=1.416*H.*sigma./(4*sinh(k.*h)); +v2 = sqrt( (a*Cd.*urms).^4 + 4*(Cd.*Fy).^2 )./(2*Cd.^2) - (a*urms).^2/2; +v=sqrt(v2).*sign(-Fy); + +% mixing operator +A=zeros(nx); +for i=2:nx-1 + A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); +end +A(1,1:2)=[-2 1]/dx^2*nu*h(1); +A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); + +% v2: nonlinear solution with mixing. +test=A*v; +v0=v; +v = fsolve(@(v)Fy + Cd.*urms.*v.*sqrt(a^2+(v./urms).^2) - A*v,v0,optimset('Display','off')); + +% outputs struct +out.E =E ; +out.Er =Er ; +out.eps_b=eps_b; +out.eps_r=eps_r; +out.c=c; +out.cg=cg; +out.k=k; +out.h=h; +out.n=n; +out.theta=theta; +out.sigma=sigma; +out.x=x; +out.H=H; +out.gamma=gamma; +out.Hm=Hm; +out.Qb=Qb; +out.dSxydx=dSxydx; +out.Fy=Fy; +out.v=real(v); +out.ka_drag=ka_drag; diff --git a/bin/1DVar/waveModel/waveModelParams.m b/bin/1DVar/waveModel/waveModelParams.m new file mode 100644 index 0000000..99e80fb --- /dev/null +++ b/bin/1DVar/waveModel/waveModelParams.m @@ -0,0 +1,9 @@ +function [g,alpha,beta,nu]=waveModelParams(); +% +% common params for NL-TL-AD model +% + +g=9.8; +alpha=1; +beta=0.1; % roller parameter +nu=.5; From a6a1fea687c78958c8c897584c7f6b20e77b730a Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 14 Dec 2020 11:58:14 -0500 Subject: [PATCH 02/26] adding example 1DVar yaml file --- .../1DVar_Input_example.yml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 yaml_files/TestBedExampleInputs/1DVar_Input_example.yml diff --git a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml new file mode 100644 index 0000000..1d89aae --- /dev/null +++ b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml @@ -0,0 +1,27 @@ +# # # # # # # # # # # # +# Data control # +# # # # # # # # # # # # +pFlag: False # turn plotting on +generateFlag: True # generate simulation input files (go get data, process, and write files) +runFlag: True # run the simulation +analyzeFlag: False # post process simulations (read files, post process data, make netCDF files, plot if desired) +bathyLoc: integrated_bathy # bathymetry source. OPTIONAL: defaults to FRF +######################## +#simulations parameters# +######################## +startTime: '2018-03-03T00:00:00Z' # project start time +endTime: '2018-03-12T00:00:00Z' # project End time not inclusive +simulationDuration: 24 # duration in hours how frequently to get new data, size of individual simulations +modelExecutable: C:/cmtb/bin/1DVar # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over +######################## +# path stuff # +######################## +modelSettings: + model: 1DVar + version_prefix: Base # controls switched with int the code + grid: None + +# this is the sim files, and plots +workingDirectory: C:/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels +# this is the base directory where netCDF files are output to, +netCDFdir: C:/cmtb/netCDF #optional will default to home/[whoami]/thredds_data -- this needs / From 30068639f327ce6cb81b5e731475520f8f465769 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 17 Dec 2020 11:42:03 -0500 Subject: [PATCH 03/26] updated for winds and new delta_T --- .gitmodules | 6 +- RunWorkflow_1DVar.py | 130 +++++++++++++----- .../1DVar_Input_example.yml | 6 +- 3 files changed, 100 insertions(+), 42 deletions(-) diff --git a/.gitmodules b/.gitmodules index 770389c..4cb98a9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "getdatatestbed"] path = getdatatestbed - url = git@github.com:SBFRF/getdatatestbed.git + url = https://github.com/SBFRF/getdatatestbed.git [submodule "testbedutils"] path = testbedutils - url = git@github.com:SBFRF/testbedutils.git + url = https://github.com/SBFRF/testbedutils.git [submodule "prepdata"] path = prepdata - url = git@github.com:SBFRF/prepdata.git + url = https://github.com/SBFRF/prepdata.git diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index c239973..8b06b52 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -17,14 +17,12 @@ Q = 0 def assim_currents(X, currents_object, obs, i): - #print("current object crosshore location ", currents_object['xFRF']) awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) - obs['v']['d'] = np.append(obs['v']['d'], matlab.float64([currents_object['aveN'][i]])) - obs['v']['ind'] = np.append(obs['v']['ind'], matlab.float64([awac_indice])) + obs['v']['d'] = np.append(obs['v']['d'], (matlab.float64([currents_object['aveV'][i]]))) + obs['v']['ind'] = np.append(obs['v']['ind'], (matlab.float64([awac_indice]))) return obs def assim_waves(X, waves_object, obs, i): - #print("waves object crosshore location ", waves_object['xFRF']) hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) obs['H']['d'] = np.append(obs['H']['d'], (matlab.float64([waves_object['Hs'][i]]))) obs['H']['ind'] = np.append(obs['H']['ind'], (matlab.float64([hs_indice]))) @@ -111,30 +109,42 @@ def Master_1DVar_run(inputDict): # fileHandling.displayStartInfo(projectStart, projectEnd, version_prefix, LOG_FILENAME, model) # ______________________________gather all data _____________________________ if generateFlag == True: - go = getObs(projectStart, projectEnd, server='chl') # initialize get observation + go = getObs(projectStart, projectEnd) # initialize get observation bathygo = getDataTestBed(projectStart, projectEnd) #bathyTransect = bathygo.getBathyTransectFromNC(method=1) # grab bathymetry transects bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects + pier_wind = go.getWind() + adop_35m_currents = go.getCurrents(gaugenumber='adop-3.5m', roundto=1) awac_45m_currents = go.getCurrents(gaugenumber='awac-4.5m', roundto=1) awac_6m_currents = go.getCurrents(gaugenumber='awac-6m', roundto=1) - awac_8m_currents = go.getCurrents(gaugenumber='awac-8m', roundto=1) + #awac_8m_currents = go.getCurrents(gaugenumber='awac-8m', roundto=1) #awac_11m_currents = go.getCurrents(gaugenumber='awac-11m', roundto=1) + spec100m = go.getWaveSpec(gaugenumber='xp100m', specOnly=False) # grab 100m array observations spec125m = go.getWaveSpec(gaugenumber='xp125m', specOnly=False) # grab 125m pressure array obs - spec150m = go.getWaveSpec(gaugenumber='xp150m', specOnly=False) # grab 150m pressure array obs + #spec150m = go.getWaveSpec(gaugenumber='xp150m', specOnly=False) # grab 150m pressure array obs + spec150m = None spec200m = go.getWaveSpec(gaugenumber='xp200m', specOnly=False) # grab 200m pressure array obs spec8m = go.getWaveSpec(gaugenumber='8m-array', specOnly=False) # grab 8m array observations - h = bathyTransect['elevation'][200,45:] - X = np.linspace(0, np.max(bathyTransect['xFRF']), len(h)) - delta_t = spec8m['time'][0] - bathyTransect['time'] - delta_t = delta_t.seconds + + speed = pier_wind['windspeed'] + direc = np.deg2rad(pier_wind['winddir']-71.8) + cd = 0.002 #reniers et al. 2004 + tauw = cd*(speed**2)*np.cos(direc) + wind_indice_skip = len(tauw)/len(dateStringList) + + h = bathyTransect['elevation'][200,35:] + X = np.linspace(35, np.max(bathyTransect['xFRF']), len(h)) + + delta_t = spec8m['epochtime'][0] - bathyTransect['time'].timestamp() #in days + # load example mat file to populate prior and observations with data for model run input = octave.load('./data/waveModels/1DVar/input_oct.mat') prior = input['prior'] - obs = input['obs'] + X = np.reshape(X, (len(X), 1)) h = np.reshape(h, (len(h), 1)) Q=0 @@ -146,14 +156,18 @@ def Master_1DVar_run(inputDict): # Prior.Cka is constant prior['x'] = X # populate with bathy cross-shore distance data prior['h'] = -h # populate with elevation data - prior['hErr'] = -h / 2 # populate with elevation error data (h/2 for now) - orig_obs_h = obs['H']['e'] - orig_obs_v = obs['v']['e'] - orig_obs_tauw = obs['tauw']['e'] + prior['hErr'] = .1 # populate with elevation error data .1 + + obs_indices_h = [] + obs_indices_v = [] + # ________________________________________________ RUN LOOP ________________________________________________ i = 0 print(dateStringList) for time in dateStringList: + obs = input['obs'] + obs['tauw'] = tauw[0] + print(obs['tauw']) try: print('-------------------------------Beginning Simulation {}-------------------------------'.format( DT.datetime.now())) @@ -164,13 +178,18 @@ def Master_1DVar_run(inputDict): print('Running {} Simulation'.format(model.upper())) dt = DT.datetime.now() - prior['theta0'] = spec8m['waveDp'][i]*(3.1415/180) # populate with theta0 data + prior['theta0'] = np.deg2rad(spec8m['waveDp'][i]-71.8) # populate with theta0 data prior['H0'] = spec8m['Hs'][i] # populate with offshore wave height data prior['sigma'] = 2 * np.pi * spec8m['peakf'][i] # calculate offshore sigma value""" print("theta: ", prior['theta0']) # select indices of bathymetry where we have Hs measurements obs['H']['d'] = [] obs['H']['ind'] = [] + if spec100m != None: + try: + obs = assim_waves(X, spec100m, obs, i) + except: + pass if spec125m != None: try: obs = assim_waves(X, spec125m, obs, i) @@ -192,7 +211,8 @@ def Master_1DVar_run(inputDict): except: pass - obs['H']['e'] = orig_obs_h + for obs_timestep_ind in obs['H']['ind']: + obs_indices_h.append(obs_timestep_ind) try: obs['H']['e'] = matlab.float64(obs['H']['e'][0][:len(obs['H']['d'])]) except: @@ -204,7 +224,6 @@ def Master_1DVar_run(inputDict): # select indices of bathymetry where we have v measurements obs['v']['d'] = [] obs['v']['ind'] = [] - obs['v']['e'] = orig_obs_v if adop_35m_currents != None: try: obs = assim_currents(X, adop_35m_currents, obs, i) @@ -220,26 +239,28 @@ def Master_1DVar_run(inputDict): obs = assim_currents(X, awac_6m_currents, obs, i) except: pass - if awac_8m_currents != None: + """if awac_8m_currents != None: try: obs = assim_currents(X, awac_8m_currents, obs, i) except: - pass + pass""" #if awac_11m_currents != None: #obs = assim_currents(X, awac_11m_currents, obs, i) + for obs_timestep_ind in obs['v']['ind']: + obs_indices_v.append(obs_timestep_ind) try: obs['v']['e'] = matlab.float64(obs['v']['e'][0][:len(obs['v']['d'])]) except: obs['v']['e'] = matlab.float64(obs['v']['e'][:len(obs['v']['d'])]) + print("Currents to Assimilate: ", obs['v']['d']) print("Current Indices to Assimilate: ", obs['v']['ind']) print("Current Errors to Assimilate: ", obs['v']['e']) + obs['tauw'] = tauw[int((i+1)*wind_indice_skip)] posterior, diagnostics = octave.feval('C:/cmtb/bin/1DVar/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ - - print('Simulation took %s ' % (DT.datetime.now() - dt)) prior = posterior @@ -258,26 +279,63 @@ def Master_1DVar_run(inputDict): logging.exception('\nERROR FOUND @ {}\n'.format(time), exc_info=True) os.chdir(modeldir) - - print(posterior['herr']) + obs_indices_h = np.unique(obs_indices_h) + obs_indices_v = np.unique(obs_indices_v) + print("Data assimilated at crosshore locations:") + print(obs_indices_h*(X[-1]/len(h))) + print(obs_indices_v*(X[-1]/len(h))) + Q = np.squeeze(np.transpose(Q)) fig = plt.figure(figsize=(6, 9)) - grid = gridspec.GridSpec(1, 3, figure=fig) + grid = gridspec.GridSpec(2, 2, figure=fig) ax0 = fig.add_subplot(grid[0, 0]) ax0.set_title("Prior") - ax0.set_ylim(ymax=-1, ymin=-6) - ax0.set_xlabel("Crosshore distance") + ax0.set_ylim(ymax=0, ymin=-11) + ax0.set_xlabel("Crosshore distance (m)") ax0.set_ylabel("Depth") - ax0.set_xlim(xmax=80) + ax0.set_xlim(xmax=1000) + ax0.plot(X, h, color='black') + for obs_loc in obs_indices_h: + ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + for obs_loc in obs_indices_v: + ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + ax1 = fig.add_subplot(grid[0, 1]) ax1.set_title("Posterior") - ax1.set_xlabel("Crosshore distance") + ax1.set_xlabel("Crosshore distance (m)") ax1.set_ylabel("Depth") - ax1.set_ylim(ymax=-1, ymin=-6) - ax1.set_xlim(xmax=80) - ax0.plot(h) - ax1.plot(-posterior['h']) - ax2 = fig.add_subplot(grid[0, 2]) - ax2.scatter(X, h+posterior['h']) + ax1.set_ylim(ymax=0, ymin=-11) + ax1.set_xlim(xmax=1000) + + ax1.errorbar(X, -posterior['h'], yerr=np.squeeze(posterior['hErr']), color='black', errorevery=5, capsize=2, ecolor='pink') + #ax1.plot(X, -posterior['h'], color='black') + + for obs_loc in obs_indices_h: + ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + for obs_loc in obs_indices_v: + ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + + ax2 = fig.add_subplot(grid[1, 0]) + ax2.set_title("prior - posterior") + ax2.scatter(X, h+posterior['h'], color="black") + ax2.set_xlabel("Crosshore distance (m)") + ax2.set_ylabel("Elevation change (m)") + ax2.set_xlim(xmax=1000) + + for obs_loc in obs_indices_h: + ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + temp = obs_loc + ax2.axvline(x=temp * (X[-1] / len(h)), color='r', linestyle='--', label="wave heights") + for obs_loc in obs_indices_v: + ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + temp = obs_loc + ax2.axvline(x=temp * (X[-1] / len(h)), color='orange', linestyle='--', label="currents") + + ax2.legend() + + ax3 = fig.add_subplot(grid[1, 1]) + ax3.plot(X, Q) + ax3.set_title("Q") + #plt.subplots_adjust(hspace=.25) plt.show() if __name__ == "__main__": diff --git a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml index 1d89aae..8f5f644 100644 --- a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml +++ b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml @@ -9,9 +9,9 @@ bathyLoc: integrated_bathy # bathymetry source. OPTIONAL: defa ######################## #simulations parameters# ######################## -startTime: '2018-03-03T00:00:00Z' # project start time -endTime: '2018-03-12T00:00:00Z' # project End time not inclusive -simulationDuration: 24 # duration in hours how frequently to get new data, size of individual simulations +startTime: '2016-07-28T00:00:00Z' # project start time +endTime: '2016-08-14T00:00:00Z' # project End time not inclusive +simulationDuration: 3 # duration in hours how frequently to get new data, size of individual simulations modelExecutable: C:/cmtb/bin/1DVar # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over ######################## # path stuff # From 4a8f498148f0bc66e25927645b3254933b5130fb Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 17 Dec 2020 14:47:54 -0500 Subject: [PATCH 04/26] updated with comments --- RunWorkflow_1DVar.py | 139 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 118 insertions(+), 21 deletions(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 8b06b52..8b37fc3 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -17,22 +17,62 @@ Q = 0 def assim_currents(X, currents_object, obs, i): + """Assimilates currents from the FRF-pulled current object into the format to be added to matlab observations matfile + + Args: + X: gridded crosshore distance data + currents_object: object returned from getWaveSpec in FRFgetData + obs: matfile containing observation data from example input + i: simulation number (to full different observation from waves_object at each time step + + Returns: + obs with new wave height data and indices written into it from current simulation timestep + + """ + awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) obs['v']['d'] = np.append(obs['v']['d'], (matlab.float64([currents_object['aveV'][i]]))) obs['v']['ind'] = np.append(obs['v']['ind'], (matlab.float64([awac_indice]))) return obs def assim_waves(X, waves_object, obs, i): + """Assimilates waves from the FRF-pulled waves objecst into the format to be added to matlab observations matfile + + Args: + X: gridded crosshore distance data + waves_object: object returned from getWaveSpec in FRFgetData + obs: matfile containing observation data from example input + i: simulation number (to full different observation from waves_object at each time step + + Returns: + obs with new wave height data and indices written into it from current simulation timestep + + """ + hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) obs['H']['d'] = np.append(obs['H']['d'], (matlab.float64([waves_object['Hs'][i]]))) obs['H']['ind'] = np.append(obs['H']['ind'], (matlab.float64([hs_indice]))) return obs def calculate_Ch(prior, spec8m, X, delta_t, Q): - # calculate Q(x,t) (measured process error) according to holman et al 2013 - # Q = Cq * Hmo ^ (-[(x-x0)/sigma_x]^2) - # deltat is difference from survey to assimilation step; - # add q each time-step to increase uncertainty as the Ch decreases in posterior posterior.ch + new S *N *S with just the tiny delta_t + """calculate Q(x,t) (measured process error) according to holman et al 2013 + Q = Cq * Hmo ^ (-[(x-x0)/sigma_x]^2) + deltat is difference from survey to assimilation step; + add q each time-step to increase uncertainty as the Ch decreases in posterior + posterior.ch + new S *N *S with just the tiny delta_t + + Args: + prior: the prior matfile to write Ch to + spec8m: offshore wave spectra object + X: gridded distance data in crosshore + delta_t: time difference in seconds between last simulation (or last measured bathy) and current timestep + Q: defined above + + Returns: + prior: with newly written Ch + Q: old Q + new Q + """ + Cq = 0.067 # from sandy duck experiment Hmo = np.mean(spec8m['Hs']) # significant wave height of highest 1/3 of waves x0 = 50 # x0 and sigma_x reflect the typical location of breaking waves at this particular beach @@ -50,12 +90,21 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): return prior, Q def find_nearest(array, value): + """This function will run CMS with any version prefix given start, end, and timestep. + + Args: + inputDict: a dictionary that is read from the input yaml + + Returns: + None + + """ array = np.asarray(array) idx = (np.abs(array - value)).argmin() return array[idx], idx def Master_1DVar_run(inputDict): - """This function will run CMS with any version prefix given start, end, and timestep. + """This function will run 1DVar given start, end, and timestep found in input yaml Args: inputDict: a dictionary that is read from the input yaml @@ -109,19 +158,25 @@ def Master_1DVar_run(inputDict): # fileHandling.displayStartInfo(projectStart, projectEnd, version_prefix, LOG_FILENAME, model) # ______________________________gather all data _____________________________ if generateFlag == True: - go = getObs(projectStart, projectEnd) # initialize get observation + # initialize get observation for measurements + go = getObs(projectStart, projectEnd) + + #initiliaze get observation for bathymetry bathygo = getDataTestBed(projectStart, projectEnd) #bathyTransect = bathygo.getBathyTransectFromNC(method=1) # grab bathymetry transects bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects + # pull wind data pier_wind = go.getWind() + #pull currents data adop_35m_currents = go.getCurrents(gaugenumber='adop-3.5m', roundto=1) awac_45m_currents = go.getCurrents(gaugenumber='awac-4.5m', roundto=1) awac_6m_currents = go.getCurrents(gaugenumber='awac-6m', roundto=1) #awac_8m_currents = go.getCurrents(gaugenumber='awac-8m', roundto=1) #awac_11m_currents = go.getCurrents(gaugenumber='awac-11m', roundto=1) + #pull waves data spec100m = go.getWaveSpec(gaugenumber='xp100m', specOnly=False) # grab 100m array observations spec125m = go.getWaveSpec(gaugenumber='xp125m', specOnly=False) # grab 125m pressure array obs #spec150m = go.getWaveSpec(gaugenumber='xp150m', specOnly=False) # grab 150m pressure array obs @@ -129,16 +184,19 @@ def Master_1DVar_run(inputDict): spec200m = go.getWaveSpec(gaugenumber='xp200m', specOnly=False) # grab 200m pressure array obs spec8m = go.getWaveSpec(gaugenumber='8m-array', specOnly=False) # grab 8m array observations - + #calculate tauw speed = pier_wind['windspeed'] direc = np.deg2rad(pier_wind['winddir']-71.8) cd = 0.002 #reniers et al. 2004 tauw = cd*(speed**2)*np.cos(direc) wind_indice_skip = len(tauw)/len(dateStringList) + # restrict bathy to only include points after indice 35, + # TODO: figure out why including indices before this gives fzero problems in the assim_1dh.m h = bathyTransect['elevation'][200,35:] X = np.linspace(35, np.max(bathyTransect['xFRF']), len(h)) + #calculate initial delta_t where it is elapsed time since the bathy was measured to first simulation timestep delta_t = spec8m['epochtime'][0] - bathyTransect['time'].timestamp() #in days # load example mat file to populate prior and observations with data for model run @@ -148,7 +206,10 @@ def Master_1DVar_run(inputDict): X = np.reshape(X, (len(X), 1)) h = np.reshape(h, (len(h), 1)) Q=0 + + #calculcate Ch prior, Q = calculate_Ch(prior, spec8m, X, delta_t, Q) + # populate prior with data # Prior.ka_drag is constant # Prior.Ctheta0 is constant @@ -156,8 +217,9 @@ def Master_1DVar_run(inputDict): # Prior.Cka is constant prior['x'] = X # populate with bathy cross-shore distance data prior['h'] = -h # populate with elevation data - prior['hErr'] = .1 # populate with elevation error data .1 + prior['hErr'] = .1 # populate with elevation error data currently using .1 m + #create lists for storing locations of observations for plotting obs_indices_h = [] obs_indices_v = [] @@ -165,9 +227,18 @@ def Master_1DVar_run(inputDict): i = 0 print(dateStringList) for time in dateStringList: + # initialize observation anew from example input each simulation timestep obs = input['obs'] - obs['tauw'] = tauw[0] - print(obs['tauw']) + + # write wind observations to observations matfile + if i == 0: + obs['tauw'] = tauw[0] + else: + try: + obs['tauw'] = tauw[int((i + 1) * wind_indice_skip)] + except: + obs['tauw'] = tauw[-1] + try: print('-------------------------------Beginning Simulation {}-------------------------------'.format( DT.datetime.now())) @@ -178,13 +249,18 @@ def Master_1DVar_run(inputDict): print('Running {} Simulation'.format(model.upper())) dt = DT.datetime.now() - prior['theta0'] = np.deg2rad(spec8m['waveDp'][i]-71.8) # populate with theta0 data - prior['H0'] = spec8m['Hs'][i] # populate with offshore wave height data - prior['sigma'] = 2 * np.pi * spec8m['peakf'][i] # calculate offshore sigma value""" + print("Wind Speed at timestep (m/s): ", obs['tauw']) + + # population the new "prior" during each timestep with FRF data + prior['theta0'] = np.deg2rad(spec8m['waveDp'][i]-71.8) # populate with theta0 data from 8m array + prior['H0'] = spec8m['Hs'][i] # populate with offshore wave height data from 8m array + prior['sigma'] = 2 * np.pi * spec8m['peakf'][i] # calculate offshore sigma value from 8m array print("theta: ", prior['theta0']) + # select indices of bathymetry where we have Hs measurements obs['H']['d'] = [] obs['H']['ind'] = [] + if spec100m != None: try: obs = assim_waves(X, spec100m, obs, i) @@ -211,19 +287,24 @@ def Master_1DVar_run(inputDict): except: pass - for obs_timestep_ind in obs['H']['ind']: - obs_indices_h.append(obs_timestep_ind) + # set wave observation errors, just using example errors for now try: obs['H']['e'] = matlab.float64(obs['H']['e'][0][:len(obs['H']['d'])]) except: obs['H']['e'] = matlab.float64(obs['H']['e'][:len(obs['H']['d'])]) + print("Wave Heights to Assimilate: ", obs['H']['d']) print("Wave Indices to Assimilate: ", obs['H']['ind']) print("Wave Errors to Assimilate: ", obs['H']['e']) + # add indices with assimilated wave heights to list for plotting + for obs_timestep_ind in obs['H']['ind']: + obs_indices_h.append(obs_timestep_ind) + # select indices of bathymetry where we have v measurements obs['v']['d'] = [] obs['v']['ind'] = [] + if adop_35m_currents != None: try: obs = assim_currents(X, adop_35m_currents, obs, i) @@ -243,12 +324,15 @@ def Master_1DVar_run(inputDict): try: obs = assim_currents(X, awac_8m_currents, obs, i) except: - pass""" - #if awac_11m_currents != None: - #obs = assim_currents(X, awac_11m_currents, obs, i) + pass + if awac_11m_currents != None: + try: + obs = assim_currents(X, awac_11m_currents, obs, i) + except: + pass + """ - for obs_timestep_ind in obs['v']['ind']: - obs_indices_v.append(obs_timestep_ind) + # set current observation errors, just using .1 m/s for now try: obs['v']['e'] = matlab.float64(obs['v']['e'][0][:len(obs['v']['d'])]) except: @@ -258,17 +342,26 @@ def Master_1DVar_run(inputDict): print("Current Indices to Assimilate: ", obs['v']['ind']) print("Current Errors to Assimilate: ", obs['v']['e']) - obs['tauw'] = tauw[int((i+1)*wind_indice_skip)] + # add indices with assimilated currents to list for plotting + for obs_timestep_ind in obs['v']['ind']: + obs_indices_v.append(obs_timestep_ind) + # run 1DVar model with prior and observation input + # set nout if desired to obtain diagnostics and representer matrices posterior, diagnostics = octave.feval('C:/cmtb/bin/1DVar/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ print('Simulation took %s ' % (DT.datetime.now() - dt)) + # make the output of the model the prior for the next timestep prior = posterior + + # find delta_t for the next timestep try: delta_t = spec8m['time'][i+1] - spec8m['time'][i] delta_t = delta_t.seconds except: pass + + #calculate Ch for the next timestep prior, Q = calculate_Ch(prior, spec8m, X, delta_t, Q) i += 1 @@ -279,6 +372,10 @@ def Master_1DVar_run(inputDict): logging.exception('\nERROR FOUND @ {}\n'.format(time), exc_info=True) os.chdir(modeldir) + # plot prior, posterior, difference, and Q + # plot hErr on the prior and posterior + # show areas with assimilated data on all plots + obs_indices_h = np.unique(obs_indices_h) obs_indices_v = np.unique(obs_indices_v) print("Data assimilated at crosshore locations:") From 6f84da61bd0ebf368afcc5175179cd6c440b2578 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 17 Feb 2021 13:36:24 -0500 Subject: [PATCH 05/26] Updates with workarounds that include newBranch of getdatatestbed --- .idea/dictionaries/Adam.xml | 7 + RunWorkflow_1DVar.py | 517 +++++++++--------- bin/1DVar/assim_1dh.m | 2 +- .../1DVar_Input_example.yml | 11 +- 4 files changed, 282 insertions(+), 255 deletions(-) create mode 100644 .idea/dictionaries/Adam.xml diff --git a/.idea/dictionaries/Adam.xml b/.idea/dictionaries/Adam.xml new file mode 100644 index 0000000..bfe787c --- /dev/null +++ b/.idea/dictionaries/Adam.xml @@ -0,0 +1,7 @@ + + + + assim + + + \ No newline at end of file diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 8b37fc3..297c694 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -16,44 +16,54 @@ import netCDF4 as nc Q = 0 + + def assim_currents(X, currents_object, obs, i): """Assimilates currents from the FRF-pulled current object into the format to be added to matlab observations matfile - + possible improvements include utilizing averaging instead of just pulling the measurement closest in time to each + time in dateStringList Args: X: gridded crosshore distance data currents_object: object returned from getWaveSpec in FRFgetData obs: matfile containing observation data from example input - i: simulation number (to full different observation from waves_object at each time step + i: simulation number (to full different observation from waves_object at each time step; waves are taken + every hour, while we might want to populate the observations with measurements taken every three hours.) Returns: obs with new wave height data and indices written into it from current simulation timestep """ - awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) - obs['v']['d'] = np.append(obs['v']['d'], (matlab.float64([currents_object['aveV'][i]]))) - obs['v']['ind'] = np.append(obs['v']['ind'], (matlab.float64([awac_indice]))) + obs['v']['d'] = np.append(obs['v']['d'], (matlab.float64([currents_object['aveV'][i]])), axis=0) + obs['v']['ind'] = np.append(obs['v']['ind'], (matlab.float64([awac_indice])), axis=0) + obs['v']['e'] = np.append(obs['v']['e'], .1) return obs + def assim_waves(X, waves_object, obs, i): - """Assimilates waves from the FRF-pulled waves objecst into the format to be added to matlab observations matfile + """Assimilates waves from the FRF-pulled waves objecst into the format to be added to matlab observations matfile. + possible improvements include utilizing averaging instead of just pulling the measurement closest in time to each + time in dateStringList Args: X: gridded crosshore distance data waves_object: object returned from getWaveSpec in FRFgetData obs: matfile containing observation data from example input - i: simulation number (to full different observation from waves_object at each time step + i: simulation number (to pull different observation from waves_object at each time step; waves are taken + every hour, while we might want to populate the observations with measurements taken every three hours.) + Returns: obs with new wave height data and indices written into it from current simulation timestep """ - hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) - obs['H']['d'] = np.append(obs['H']['d'], (matlab.float64([waves_object['Hs'][i]]))) - obs['H']['ind'] = np.append(obs['H']['ind'], (matlab.float64([hs_indice]))) + obs['H']['d'] = np.append(obs['H']['d'], (matlab.float64([waves_object['Hs'][i]])), axis=0) + obs['H']['ind'] = np.append(obs['H']['ind'], (matlab.float64([hs_indice])), axis=0) + obs['H']['e'] = np.append(obs['H']['e'], .2) return obs + def calculate_Ch(prior, spec8m, X, delta_t, Q): """calculate Q(x,t) (measured process error) according to holman et al 2013 Q = Cq * Hmo ^ (-[(x-x0)/sigma_x]^2) @@ -63,7 +73,7 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): Args: prior: the prior matfile to write Ch to - spec8m: offshore wave spectra object + waves_obj[-1]: offshore wave spectra object X: gridded distance data in crosshore delta_t: time difference in seconds between last simulation (or last measured bathy) and current timestep Q: defined above @@ -78,7 +88,7 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): x0 = 50 # x0 and sigma_x reflect the typical location of breaking waves at this particular beach sigma_x = 150 delta_t = delta_t/(60*60*24) - print("Delta t:", delta_t) + print("Delta t in hours:", delta_t*24) xx = np.meshgrid(X) Lx = 25 # decorrelation length scale, larger Lx leads to smoother results N = np.exp((-(xx - np.transpose(xx)) ** 2) / (2 * Lx)) @@ -89,6 +99,205 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): prior['Ch'] = Ch # populate elevation covariance return prior, Q + +def set_offshore_conditions(prior, waves_obj, element): + # population the new "prior" during each timestep with FRF data + prior['theta0'] = np.deg2rad(waves_obj[-1]['waveDp'][element] - 71.8) # populate with theta0 data from 8m array + print("Offshore Wave Direction: ", prior['theta0']) + prior['H0'] = waves_obj[-1]['Hs'][element] # populate with offshore wave height data from 8m array + prior['sigma'] = 2 * np.pi * waves_obj[-1]['peakf'][element] # calculate offshore sigma value from 8m array + return prior + + +def create_prior(): + prior = {} + prior['x'] = [] + prior['h'] = [] + prior['theta0'] = [] + prior['H0'] = [] + prior['ka_drag'] = .015 + prior['hErr'] = .1 + prior['Ctheta0'] = .0305 + prior['CH0'] = .01 + prior['Cka'] = 2.5*10**-5 + prior['Ch'] = [] + prior['sigma'] = [] + return prior + + +def create_obs(): + obs = {} + obs['H'] = {} + obs['v'] = {} + obs['H']['d'] = [] + obs['H']['ind'] = [] + obs['H']['e'] = [] + obs['v']['d'] = [] + obs['v']['ind'] = [] + obs['v']['e'] = [] + obs['tauw'] = [] + return obs + + +def preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateStringList, simulationDuration): + prior = create_prior() + obs, obs_indices_h, obs_indices_v = create_obs() + + # calculate tauw + speed = pier_wind['windspeed'] + direc = np.deg2rad(pier_wind['winddir'] - 71.8) + cd = 0.002 # reniers et al. 2004 + tauw = cd * (speed ** 2) * np.cos(direc) + wind_indice_skip = len(tauw) / len(dateStringList) + + # restrict bathy to only include points after indice 35, + zero_elev = np.argmax(bathyTransect['elevation'][200, :] < 0) + h = bathyTransect['elevation'][200, zero_elev:] + X = np.linspace(zero_elev, np.max(bathyTransect['xFRF']), len(h)) + + # calculate initial delta_t where it is elapsed time since the bathy was measured to first simulation timestep + delta_t = waves_obj[-1]['time'][0] - bathyTransect['time'] + #delta_t = bathyTransect['time'].timestamp() - waves_obj[-1]['epochtime'][0] + delta_t = delta_t.seconds + delta_t = np.abs(delta_t) + X = np.reshape(X, (len(X), 1)) + h = np.reshape(h, (len(h), 1)) + Q = 0 + + # populate prior with data appropriate for this time period + prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) + prior['x'] = X # populate with bathy cross-shore distance data + prior['h'] = -h # populate with elevation data + prior['hErr'] = .1 # populate with elevation error data currently using .1 m + + # populate obs with initial timestep data + + # create lists for storing locations of observations for plotting + obs_indices_h = [] + obs_indices_v = [] + obs_list = [] + + for element, time in enumerate(dateStringList): + obs = create_obs() + obs['tauw'] = tauw[0] + # populate each obs with tauw data + if element > 0: + try: + obs['tauw'] = tauw[int(element * wind_indice_skip)] + except: + obs['tauw'] = tauw[-1] + + for i in range(len(waves_obj)): + try: + # grab measurements exactly on times in dateStringList with element*simulationDuration, + # since waves are taken every hour + # no averaging done + obs = assim_waves(X, waves_obj[i], obs, element*simulationDuration) + except: + pass + + # add indices with assimilated wave heights to list for plotting + for obs_timestep_ind in obs['H']['ind']: + obs_indices_h.append(obs_timestep_ind) + + for i in range(len(current_obj)): + try: + # grab measurements exactly on times in dateStringList with element*simulationDuration/3 + # since currents are measured every 3 hours + # no averaging done + obs = assim_currents(X, current_obj[i], obs, int(element*simulationDuration/2)) + except: + pass + + # add indices with assimilated currents to list for plotting + for obs_timestep_ind in obs['v']['ind']: + obs_indices_v.append(obs_timestep_ind) + + obs_list = np.append(obs_list, obs) + + return X, h, Q, tauw, wind_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev + + +def plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zero_elev): + # plot prior, posterior, difference, and Q + # plot hErr on the prior and posterior + # show areas with assimilated data on all plots + + #grab bathy nearest enddate + bathygo = getDataTestBed(projectEnd, projectEnd) + bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects + print("Final Bathy Survey Date: ", bathyTransect['time']) + survey_h = bathyTransect['elevation'][200, zero_elev:] + + obs_indices_h = np.unique(obs_indices_h) + obs_indices_v = np.unique(obs_indices_v) + print("Data assimilated at crosshore locations:") + print(obs_indices_h*(X[-1]/len(h))) + print(obs_indices_v*(X[-1]/len(h))) + Q = np.squeeze(np.transpose(Q)) + fig = plt.figure(figsize=(6, 9)) + grid = gridspec.GridSpec(2, 2, figure=fig) + ax0 = fig.add_subplot(grid[0, 0]) + ax0.set_title("Prior") + ax0.set_ylim(ymax=0, ymin=-11) + ax0.set_xlabel("Crosshore distance (m)") + ax0.set_ylabel("Depth") + ax0.set_xlim(xmax=1000) + ax0.plot(X, h, color='black') + for obs_loc in obs_indices_h: + ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + for obs_loc in obs_indices_v: + ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + + ax1 = fig.add_subplot(grid[0, 1]) + ax1.set_title("Posterior") + ax1.set_xlabel("Crosshore distance (m)") + ax1.set_ylabel("Depth") + ax1.set_ylim(ymax=0, ymin=-11) + ax1.set_xlim(xmax=1000) + + ax1.errorbar(X, -posterior['h'], yerr=np.squeeze(posterior['hErr']), color='black', errorevery=5, capsize=2, ecolor='pink') + #ax1.plot(X, -posterior['h'], color='black') + + for obs_loc in obs_indices_h: + ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + for obs_loc in obs_indices_v: + ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + + ax2 = fig.add_subplot(grid[1, 0]) + ax2.set_title("prior - posterior") + ax2.scatter(X, -(h+posterior['h']), color="black") + ax2.set_xlabel("Crosshore distance (m)") + ax2.set_ylabel("Elevation change (m)") + ax2.set_xlim(xmax=1000) + + for obs_loc in obs_indices_h: + ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + temp = obs_loc + ax2.axvline(x=temp * (X[-1] / len(h)), color='r', linestyle='--', label="wave heights") + for obs_loc in obs_indices_v: + ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + temp = obs_loc + ax2.axvline(x=temp * (X[-1] / len(h)), color='orange', linestyle='--', label="currents") + ax2.legend() + + ax3 = fig.add_subplot(grid[1, 1]) + ax3.plot(X, survey_h, color='cyan', label="Survey") + ax3.plot(X, -posterior['h'], color='red', label="Posterior") + ax3.set_title("Posterior and Survey\n" + str(bathyTransect['time'])) + for obs_loc in obs_indices_h: + ax3.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') + temp = obs_loc + ax3.axvline(x=temp * (X[-1] / len(h)), color='r', linestyle='--', label="wave heights") + for obs_loc in obs_indices_v: + ax3.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') + temp = obs_loc + ax3.axvline(x=temp * (X[-1] / len(h)), color='orange', linestyle='--', label="currents") + ax3.legend() + #plt.subplots_adjust(hspace=.25) + plt.show() + + def find_nearest(array, value): """This function will run CMS with any version prefix given start, end, and timestep. @@ -103,6 +312,7 @@ def find_nearest(array, value): idx = (np.abs(array - value)).argmin() return array[idx], idx + def Master_1DVar_run(inputDict): """This function will run 1DVar given start, end, and timestep found in input yaml @@ -158,87 +368,40 @@ def Master_1DVar_run(inputDict): # fileHandling.displayStartInfo(projectStart, projectEnd, version_prefix, LOG_FILENAME, model) # ______________________________gather all data _____________________________ if generateFlag == True: - # initialize get observation for measurements - go = getObs(projectStart, projectEnd) - #initiliaze get observation for bathymetry + # initiliaze get observation for bathymetry bathygo = getDataTestBed(projectStart, projectEnd) - #bathyTransect = bathygo.getBathyTransectFromNC(method=1) # grab bathymetry transects bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects + print("Initial Bathy Survey Date: ", bathyTransect['time']) - # pull wind data - pier_wind = go.getWind() - - #pull currents data - adop_35m_currents = go.getCurrents(gaugenumber='adop-3.5m', roundto=1) - awac_45m_currents = go.getCurrents(gaugenumber='awac-4.5m', roundto=1) - awac_6m_currents = go.getCurrents(gaugenumber='awac-6m', roundto=1) - #awac_8m_currents = go.getCurrents(gaugenumber='awac-8m', roundto=1) - #awac_11m_currents = go.getCurrents(gaugenumber='awac-11m', roundto=1) - - #pull waves data - spec100m = go.getWaveSpec(gaugenumber='xp100m', specOnly=False) # grab 100m array observations - spec125m = go.getWaveSpec(gaugenumber='xp125m', specOnly=False) # grab 125m pressure array obs - #spec150m = go.getWaveSpec(gaugenumber='xp150m', specOnly=False) # grab 150m pressure array obs - spec150m = None - spec200m = go.getWaveSpec(gaugenumber='xp200m', specOnly=False) # grab 200m pressure array obs - spec8m = go.getWaveSpec(gaugenumber='8m-array', specOnly=False) # grab 8m array observations - - #calculate tauw - speed = pier_wind['windspeed'] - direc = np.deg2rad(pier_wind['winddir']-71.8) - cd = 0.002 #reniers et al. 2004 - tauw = cd*(speed**2)*np.cos(direc) - wind_indice_skip = len(tauw)/len(dateStringList) - - # restrict bathy to only include points after indice 35, - # TODO: figure out why including indices before this gives fzero problems in the assim_1dh.m - h = bathyTransect['elevation'][200,35:] - X = np.linspace(35, np.max(bathyTransect['xFRF']), len(h)) - - #calculate initial delta_t where it is elapsed time since the bathy was measured to first simulation timestep - delta_t = spec8m['epochtime'][0] - bathyTransect['time'].timestamp() #in days - - # load example mat file to populate prior and observations with data for model run - input = octave.load('./data/waveModels/1DVar/input_oct.mat') - prior = input['prior'] - - X = np.reshape(X, (len(X), 1)) - h = np.reshape(h, (len(h), 1)) - Q=0 - - #calculcate Ch - prior, Q = calculate_Ch(prior, spec8m, X, delta_t, Q) - - # populate prior with data - # Prior.ka_drag is constant - # Prior.Ctheta0 is constant - # Prior.CH0 is constant - # Prior.Cka is constant - prior['x'] = X # populate with bathy cross-shore distance data - prior['h'] = -h # populate with elevation data - prior['hErr'] = .1 # populate with elevation error data currently using .1 m - - #create lists for storing locations of observations for plotting - obs_indices_h = [] - obs_indices_v = [] - - # ________________________________________________ RUN LOOP ________________________________________________ - i = 0 - print(dateStringList) - for time in dateStringList: - # initialize observation anew from example input each simulation timestep - obs = input['obs'] - - # write wind observations to observations matfile - if i == 0: - obs['tauw'] = tauw[0] - else: + # initialize get observation for measurements + go = getObs(projectStart, projectEnd) + pier_wind = None + while pier_wind == None: + pier_wind = go.getWind() + + # pull currents + current_gauges = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m'] + current_obj = [] + for i in range(len(current_gauges)): try: - obs['tauw'] = tauw[int((i + 1) * wind_indice_skip)] + current_obj = np.append(current_obj, go.getCurrents(gaugenumber=current_gauges[i], roundto=1)) except: - obs['tauw'] = tauw[-1] + print("NetCDF DAP Error while grabbing current data from gauge: ", current_gauges[i]) + + # pull waves + #wave_gauges = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m', 'xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array'] + wave_gauges = ['xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array'] + waves_obj = [] + for i in range(len(wave_gauges)): + waves_obj = np.append(waves_obj, go.getWaveData(gaugenumber=wave_gauges[i], spec=False)) + #preprocess data + X, h, Q, tauw, wind_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev = \ + preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateStringList, simulationDuration) + + # ________________________________________________ RUN LOOP ________________________________________________ + for element, time in enumerate(dateStringList): try: print('-------------------------------Beginning Simulation {}-------------------------------'.format( DT.datetime.now())) @@ -249,106 +412,24 @@ def Master_1DVar_run(inputDict): print('Running {} Simulation'.format(model.upper())) dt = DT.datetime.now() - print("Wind Speed at timestep (m/s): ", obs['tauw']) - - # population the new "prior" during each timestep with FRF data - prior['theta0'] = np.deg2rad(spec8m['waveDp'][i]-71.8) # populate with theta0 data from 8m array - prior['H0'] = spec8m['Hs'][i] # populate with offshore wave height data from 8m array - prior['sigma'] = 2 * np.pi * spec8m['peakf'][i] # calculate offshore sigma value from 8m array - print("theta: ", prior['theta0']) - - # select indices of bathymetry where we have Hs measurements - obs['H']['d'] = [] - obs['H']['ind'] = [] - - if spec100m != None: - try: - obs = assim_waves(X, spec100m, obs, i) - except: - pass - if spec125m != None: - try: - obs = assim_waves(X, spec125m, obs, i) - except: - pass - if spec150m != None: - try: - obs = assim_waves(X, spec150m, obs, i) - except: - pass - if spec200m != None: - try: - obs = assim_waves(X, spec200m, obs, i) - except: - pass - if spec8m != None: - try: - obs = assim_waves(X, spec8m, obs, i) - except: - pass - - # set wave observation errors, just using example errors for now - try: - obs['H']['e'] = matlab.float64(obs['H']['e'][0][:len(obs['H']['d'])]) - except: - obs['H']['e'] = matlab.float64(obs['H']['e'][:len(obs['H']['d'])]) - - print("Wave Heights to Assimilate: ", obs['H']['d']) - print("Wave Indices to Assimilate: ", obs['H']['ind']) - print("Wave Errors to Assimilate: ", obs['H']['e']) - - # add indices with assimilated wave heights to list for plotting - for obs_timestep_ind in obs['H']['ind']: - obs_indices_h.append(obs_timestep_ind) - - # select indices of bathymetry where we have v measurements - obs['v']['d'] = [] - obs['v']['ind'] = [] - - if adop_35m_currents != None: - try: - obs = assim_currents(X, adop_35m_currents, obs, i) - except: - pass - if awac_45m_currents != None: - try: - obs = assim_currents(X, awac_45m_currents, obs, i) - except: - pass - if awac_6m_currents != None: - try: - obs = assim_currents(X, awac_6m_currents, obs, i) - except: - pass - """if awac_8m_currents != None: - try: - obs = assim_currents(X, awac_8m_currents, obs, i) - except: - pass - if awac_11m_currents != None: - try: - obs = assim_currents(X, awac_11m_currents, obs, i) - except: - pass - """ - - # set current observation errors, just using .1 m/s for now - try: - obs['v']['e'] = matlab.float64(obs['v']['e'][0][:len(obs['v']['d'])]) - except: - obs['v']['e'] = matlab.float64(obs['v']['e'][:len(obs['v']['d'])]) - - print("Currents to Assimilate: ", obs['v']['d']) - print("Current Indices to Assimilate: ", obs['v']['ind']) - print("Current Errors to Assimilate: ", obs['v']['e']) - - # add indices with assimilated currents to list for plotting - for obs_timestep_ind in obs['v']['ind']: - obs_indices_v.append(obs_timestep_ind) + # set offshore wave conditions used in prior based on new timestop of obs + # grab measurements exactly on times in dateStringList with element*simulationDuration, + # since waves are taken every hour + prior = set_offshore_conditions(prior, waves_obj, element*simulationDuration) + + #grab obs data for current timestep + obs = obs_list[element] + print("Assimilation no. " + str(element) + "/" + str(len(dateStringList))) + print("Assimilating Date: ", dateStringList[element]) + print("Wave heights (m) to assimilate: ", obs['H']['d']) + print("Wave height indices: ", obs['H']['ind']) + print("Currents (m/s) to assimilate: ", obs['v']['d']) + print("Current indices: ", obs['v']['ind']) + print("Tauw to assimilate: ", obs['tauw']) # run 1DVar model with prior and observation input # set nout if desired to obtain diagnostics and representer matrices - posterior, diagnostics = octave.feval('C:/cmtb/bin/1DVar/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ + posterior, diagnostics = octave.feval(modeldir + '/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ print('Simulation took %s ' % (DT.datetime.now() - dt)) # make the output of the model the prior for the next timestep @@ -356,15 +437,14 @@ def Master_1DVar_run(inputDict): # find delta_t for the next timestep try: - delta_t = spec8m['time'][i+1] - spec8m['time'][i] - delta_t = delta_t.seconds + delta_t = waves_obj[-1]['time'][int(element+simulationDuration)] - waves_obj[-1]['time'][element] + delta_t = np.abs(delta_t.seconds) except: pass #calculate Ch for the next timestep - prior, Q = calculate_Ch(prior, spec8m, X, delta_t, Q) + prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) - i += 1 print('-------------------------------SUCCESS-----------------------------------------') except Exception as e: @@ -372,68 +452,7 @@ def Master_1DVar_run(inputDict): logging.exception('\nERROR FOUND @ {}\n'.format(time), exc_info=True) os.chdir(modeldir) - # plot prior, posterior, difference, and Q - # plot hErr on the prior and posterior - # show areas with assimilated data on all plots - - obs_indices_h = np.unique(obs_indices_h) - obs_indices_v = np.unique(obs_indices_v) - print("Data assimilated at crosshore locations:") - print(obs_indices_h*(X[-1]/len(h))) - print(obs_indices_v*(X[-1]/len(h))) - Q = np.squeeze(np.transpose(Q)) - fig = plt.figure(figsize=(6, 9)) - grid = gridspec.GridSpec(2, 2, figure=fig) - ax0 = fig.add_subplot(grid[0, 0]) - ax0.set_title("Prior") - ax0.set_ylim(ymax=0, ymin=-11) - ax0.set_xlabel("Crosshore distance (m)") - ax0.set_ylabel("Depth") - ax0.set_xlim(xmax=1000) - ax0.plot(X, h, color='black') - for obs_loc in obs_indices_h: - ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - for obs_loc in obs_indices_v: - ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - - ax1 = fig.add_subplot(grid[0, 1]) - ax1.set_title("Posterior") - ax1.set_xlabel("Crosshore distance (m)") - ax1.set_ylabel("Depth") - ax1.set_ylim(ymax=0, ymin=-11) - ax1.set_xlim(xmax=1000) - - ax1.errorbar(X, -posterior['h'], yerr=np.squeeze(posterior['hErr']), color='black', errorevery=5, capsize=2, ecolor='pink') - #ax1.plot(X, -posterior['h'], color='black') - - for obs_loc in obs_indices_h: - ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - for obs_loc in obs_indices_v: - ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - - ax2 = fig.add_subplot(grid[1, 0]) - ax2.set_title("prior - posterior") - ax2.scatter(X, h+posterior['h'], color="black") - ax2.set_xlabel("Crosshore distance (m)") - ax2.set_ylabel("Elevation change (m)") - ax2.set_xlim(xmax=1000) - - for obs_loc in obs_indices_h: - ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - temp = obs_loc - ax2.axvline(x=temp * (X[-1] / len(h)), color='r', linestyle='--', label="wave heights") - for obs_loc in obs_indices_v: - ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - temp = obs_loc - ax2.axvline(x=temp * (X[-1] / len(h)), color='orange', linestyle='--', label="currents") - - ax2.legend() - - ax3 = fig.add_subplot(grid[1, 1]) - ax3.plot(X, Q) - ax3.set_title("Q") - #plt.subplots_adjust(hspace=.25) - plt.show() + plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zero_elev) if __name__ == "__main__": model = '1DVar' diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVar/assim_1dh.m index b8f37f5..8412fd0 100644 --- a/bin/1DVar/assim_1dh.m +++ b/bin/1DVar/assim_1dh.m @@ -29,7 +29,7 @@ % diagnostics = struct with diagnostic fields for analyzing the update. See % comments near end of this code for descriptions % -addpath 'C:/cmtb/bin/1DVar/waveModel' +addpath 'D:/cmtb/bin/1DVar/waveModel' warning('off','all'); if(~exist('verb')) verb=1; diff --git a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml index 8f5f644..3610bcc 100644 --- a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml +++ b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml @@ -9,10 +9,11 @@ bathyLoc: integrated_bathy # bathymetry source. OPTIONAL: defa ######################## #simulations parameters# ######################## -startTime: '2016-07-28T00:00:00Z' # project start time -endTime: '2016-08-14T00:00:00Z' # project End time not inclusive +startTime: '2016-11-05T00:00:00Z' # project start time +endTime: '2016-11-08T00:00:00Z' # project End time not inclusive + simulationDuration: 3 # duration in hours how frequently to get new data, size of individual simulations -modelExecutable: C:/cmtb/bin/1DVar # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over +modelExecutable: D:/cmtb/bin/1DVar # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over ######################## # path stuff # ######################## @@ -22,6 +23,6 @@ modelSettings: grid: None # this is the sim files, and plots -workingDirectory: C:/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels +workingDirectory: D:/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels # this is the base directory where netCDF files are output to, -netCDFdir: C:/cmtb/netCDF #optional will default to home/[whoami]/thredds_data -- this needs / +netCDFdir: D:/cmtb/netCDF #optional will default to home/[whoami]/thredds_data -- this needs / From 7f148afa366d747d52965c01722e2c2e07ed3f26 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 22 Mar 2021 14:14:55 -0400 Subject: [PATCH 06/26] updated to write mat files --- RunWorkflow_1DVar.py | 21 +++++++++++++++---- .../1DVar_Input_example.yml | 3 ++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 297c694..2887a36 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -14,6 +14,7 @@ import matlab import math import netCDF4 as nc +from scipy.io import loadmat, savemat Q = 0 @@ -269,7 +270,6 @@ def plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zer ax2.scatter(X, -(h+posterior['h']), color="black") ax2.set_xlabel("Crosshore distance (m)") ax2.set_ylabel("Elevation change (m)") - ax2.set_xlim(xmax=1000) for obs_loc in obs_indices_h: ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') @@ -335,6 +335,7 @@ def Master_1DVar_run(inputDict): pFlag = inputDict['pFlag'] model = inputDict.get('model', '1DVar') modeldir = inputDict['modelExecutable'] + matlabfiledir = inputDict['mainDirectory'] log = inputDict.get('logging', True) # __________________pre-processing checks________________________________ @@ -400,15 +401,21 @@ def Master_1DVar_run(inputDict): X, h, Q, tauw, wind_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev = \ preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateStringList, simulationDuration) + obs_dict = {} + obs_dict["struct"] = obs_list + + savemat("./data/matlab_files/initialprior.mat", prior) + savemat("./data/matlab_files/obslist.mat", obs_dict) + + # ________________________________________________ RUN LOOP ________________________________________________ for element, time in enumerate(dateStringList): try: print('-------------------------------Beginning Simulation {}-------------------------------'.format( DT.datetime.now())) - + if runFlag == True: # run model os.chdir(modeldir) # changing locations to where the model will be ran from for octave - print(os.getcwd()) print('Running {} Simulation'.format(model.upper())) dt = DT.datetime.now() @@ -427,11 +434,17 @@ def Master_1DVar_run(inputDict): print("Current indices: ", obs['v']['ind']) print("Tauw to assimilate: ", obs['tauw']) + # run 1DVar model with prior and observation input # set nout if desired to obtain diagnostics and representer matrices posterior, diagnostics = octave.feval(modeldir + '/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ print('Simulation took %s ' % (DT.datetime.now() - dt)) - + os.chdir(matlabfiledir) + # save matfile of this particular prior and obs + savemat("./data/matlab_files/prior_" + str(time[:13]) + ".mat", prior) + savemat("./data/matlab_files/obs_" + str(time[:13]) + ".mat", obs) + os.chdir(matlabfiledir) + savemat("./data/matlab_files/posterior_" + str(time[:13]) + ".mat", posterior) # make the output of the model the prior for the next timestep prior = posterior diff --git a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml index 3610bcc..257c55c 100644 --- a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml +++ b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml @@ -24,5 +24,6 @@ modelSettings: # this is the sim files, and plots workingDirectory: D:/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels +mainDirectory: D:/cmtb/ # Set to main filepath # this is the base directory where netCDF files are output to, -netCDFdir: D:/cmtb/netCDF #optional will default to home/[whoami]/thredds_data -- this needs / +netCDFdir: ./netCDF #optional will default to home/[whoami]/thredds_data -- this needs / From 6795c976232c9456ab9a1d3475d433a10cfb3c5d Mon Sep 17 00:00:00 2001 From: gwilsonOSU Date: Wed, 7 Apr 2021 15:01:45 -0700 Subject: [PATCH 07/26] fixed minor bugs in Ch+Q definitions; added shelf forcing dp/dy correction based on offshore v meas; added option for Ruessink et al 2003 wave breaking gamma formulation --- RunWorkflow_1DVar.py | 27 +++++++------ bin/1DVar/assim_1dh.m | 45 ++++++++++++++++++--- bin/1DVar/waveModel/ad_waveModel.m | 58 ++++++++++++++++----------- bin/1DVar/waveModel/tl_waveModel.m | 32 ++++++++++----- bin/1DVar/waveModel/waveModel.m | 21 ++++++---- bin/1DVar/waveModel/waveModelParams.m | 6 ++- 6 files changed, 129 insertions(+), 60 deletions(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 2887a36..7c0b8e8 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -80,8 +80,8 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): Q: defined above Returns: - prior: with newly written Ch - Q: old Q + new Q + prior: with newly written Ch = (old Ch) + Q + Q: process error """ Cq = 0.067 # from sandy duck experiment @@ -92,18 +92,18 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): print("Delta t in hours:", delta_t*24) xx = np.meshgrid(X) Lx = 25 # decorrelation length scale, larger Lx leads to smoother results - N = np.exp((-(xx - np.transpose(xx)) ** 2) / (2 * Lx)) - Q = Q + (Cq * Hmo) ** (np.power(-((X - x0) / sigma_x), 2))*delta_t + N = np.exp((-(xx - np.transpose(xx)) ** 2) / (2 * Lx**2)) + Q = (Cq * Hmo) ** (np.power(-((X - x0) / sigma_x), 2))*delta_t S = np.diag(np.sqrt(Q)) Ch = S * N * S - - prior['Ch'] = Ch # populate elevation covariance + if prior['Ch'] == []: + prior['Ch'] = np.zeros(shape=Ch.shape) + prior['Ch'] += Ch # add process error to prior Ch return prior, Q - def set_offshore_conditions(prior, waves_obj, element): # population the new "prior" during each timestep with FRF data - prior['theta0'] = np.deg2rad(waves_obj[-1]['waveDp'][element] - 71.8) # populate with theta0 data from 8m array + prior['theta0'] = np.deg2rad(waves_obj[-1]['waveDm'][element] - 71.8) # populate with theta0 data from 8m array print("Offshore Wave Direction: ", prior['theta0']) prior['H0'] = waves_obj[-1]['Hs'][element] # populate with offshore wave height data from 8m array prior['sigma'] = 2 * np.pi * waves_obj[-1]['peakf'][element] # calculate offshore sigma value from 8m array @@ -117,7 +117,7 @@ def create_prior(): prior['theta0'] = [] prior['H0'] = [] prior['ka_drag'] = .015 - prior['hErr'] = .1 + prior['hErr'] = [] prior['Ctheta0'] = .0305 prior['CH0'] = .01 prior['Cka'] = 2.5*10**-5 @@ -436,14 +436,15 @@ def Master_1DVar_run(inputDict): # run 1DVar model with prior and observation input - # set nout if desired to obtain diagnostics and representer matrices - posterior, diagnostics = octave.feval(modeldir + '/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ - print('Simulation took %s ' % (DT.datetime.now() - dt)) - os.chdir(matlabfiledir) + # set nout if desired to obtain diagnostics and representer matrices. # save matfile of this particular prior and obs + os.chdir(matlabfiledir) savemat("./data/matlab_files/prior_" + str(time[:13]) + ".mat", prior) savemat("./data/matlab_files/obs_" + str(time[:13]) + ".mat", obs) + posterior, diagnostics = octave.feval(modeldir + '/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ + print('Simulation took %s ' % (DT.datetime.now() - dt)) os.chdir(matlabfiledir) + # save matfile of the posterior result savemat("./data/matlab_files/posterior_" + str(time[:13]) + ".mat", posterior) # make the output of the model the prior for the next timestep prior = posterior diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVar/assim_1dh.m index 8412fd0..91f4866 100644 --- a/bin/1DVar/assim_1dh.m +++ b/bin/1DVar/assim_1dh.m @@ -29,7 +29,7 @@ % diagnostics = struct with diagnostic fields for analyzing the update. See % comments near end of this code for descriptions % -addpath 'D:/cmtb/bin/1DVar/waveModel' +addpath '/home/wilsongr/work/funded_projects/USCRP_DA/project/cmtb/bin/1DVar/waveModel/'; warning('off','all'); if(~exist('verb')) verb=1; @@ -58,6 +58,41 @@ end end +% Adjust prior tauw to account for possible pressure gradients. The eqn for +% v is copied from waveModel.m and directly inverted to solve for the force +% Fy, using alongshore current observations on the shelf (x>800m). This Fy +% is then assigned to tauw=Fy, replacing the input wind stress. It should +% be noted this total force Fy includes other contributions other than wind +% stress (dp/dy); so assigning it to tauw is an abuse of notation, but +% avoids the need for other coding changes such as adding a separate dp/dy +% term to waveModel.m. +imin=min(find(x>800)); +ind=find(obs.v.ind>imin); +if(isempty(ind)) + prior.tauw = obs.tauw; % default: if vshelf observations are unavailable, just use wind stress +else + vshelf = obs.v.d(ind); + h = interp1(x,prior.h,x(obs.v.ind(ind))); + g=9.8126; + for i=1:length(ind) + k(i)=fzero(@(k)prior.sigma^2-g*k.*tanh(k.*h(i)),prior.sigma./sqrt(g*h(i)),optimset('Display','off')); + end + a=1.16; % empirical constant + Cd=0.015*(prior.ka_drag./h).^(1/3); + urms=1.416*prior.H0.*prior.sigma./(4*sinh(k.*h)); + % v2 = sqrt( (a*Cd.*urms).^4 + 4*(Cd.*Fy).^2 )./(2*Cd.^2) - (a*urms).^2/2; % from waveModel.m + Fy = sqrt( ( ( ( vshelf.^2 + (a*urms).^2/2 ).*(2*Cd.^2) ).^2 - (a*Cd.*urms).^4 )./(4*Cd.^2) ); + Fy = -abs(Fy).*sign(mean(vshelf)); + prior.tauw = mean(Fy); % override user-input tauw with inverted version + clear h k a Cd urms + % ind=setdiff(1:length(obs.v.ind),ind); % optional: toss out obs used for calculating Fy above + % for fld={'ind','d','e'} + % fld=cell2mat(fld); + % this=getfield(obs.v,fld); + % obs.v=setfield(obs.v,fld,this(ind)); + % end +end + % initialize prior model state using NL model if(verb) disp('running prior forward model...') @@ -214,7 +249,7 @@ if(verb) %disp('plot') clf - subplot(221)%, hold on + subplot(221), hold on plot(prior.x,prior.h,'r') plot(prior.x,prior.h-sqrt(diag(prior.Ch)),'r--') plot(prior.x,prior.h+sqrt(diag(prior.Ch)),'r--') @@ -224,18 +259,18 @@ set(gca,'ydir','reverse') ylabel('h [m]') % legend('prior','inverted') - subplot(222)%, hold on + subplot(222), hold on plot(prior.x,prior.H,'r') plot(posterior.x,posterior.H,'b') errorbar(x(obs.H.ind),obs.H.d,obs.H.e,'ko') ylabel('H_{rms} [m]') title(['H0 prior=' num2str(prior.H0,2) 'm, final=' num2str(posterior.H0,2) 'm']); - subplot(223)%, hold on + subplot(223), hold on plot(prior.x,rad2deg(prior.theta),'r') plot(posterior.x,rad2deg(posterior.theta),'b') ylabel('theta [deg]') title(['theta0 prior=' num2str(rad2deg(prior.theta0),2) 'deg, final=' num2str(rad2deg(posterior.theta0),2) 'deg']); - subplot(224)%, hold on + subplot(224), hold on plot(prior.x,prior.v,'r') plot(posterior.x,posterior.v,'b') errorbar(x(obs.v.ind),obs.v.d,obs.v.e,'ko') diff --git a/bin/1DVar/waveModel/ad_waveModel.m b/bin/1DVar/waveModel/ad_waveModel.m index e1def5f..dd40bf0 100644 --- a/bin/1DVar/waveModel/ad_waveModel.m +++ b/bin/1DVar/waveModel/ad_waveModel.m @@ -7,7 +7,7 @@ % from output of tl_waveModel.m. % -[g,alpha,beta,nu]=waveModelParams(); +[g,alpha,beta,nu,gammaType]=waveModelParams(); % grid nx=length(x); @@ -60,7 +60,8 @@ ad_Qb=zz; ad_Hm=zz; ad_refconst=0; -ad_gamma=0; +ad_gamma=zz; +ad_tharg=0; % bottom stress model following Ruessink et al. (2001), Feddersen et % al. (2000) @@ -287,18 +288,17 @@ ad_B=0; % max wave height - % Hm=0.88./k.*tanh(gamma/0.88.*k.*h); - tharg=gamma/0.88.*k(i+1).*h(i+1); - % tl_Hm(i+1)=0.88*( -1./k(i+1).^2.*tanh(tharg).*tl_k(i+1) ... + tharg=gamma(i+1)/0.88.*k(i+1).*h(i+1); + %2 tl_Hm(i+1)=0.88*( -1./k(i+1).^2.*tanh(tharg).*tl_k(i+1) ... % + 1./k(i+1).*sech(tharg).^2.*tl_tharg ); - ad_k(i+1)=ad_k(i+1)-0.88*ad_Hm(i+1)./k(i+1).^2.*tanh(tharg); - ad_tharg=ad_Hm(i+1)*0.88./k(i+1).*sech(tharg).^2; % assume init = 0 + ad_k(i+1)=ad_k(i+1)- 0.88./k(i+1).^2.*tanh(tharg).*ad_Hm(i+1); + ad_tharg =ad_tharg + 0.88./k(i+1).*sech(tharg).^2.*ad_Hm(i+1); ad_Hm(i+1)=0; - % tl_tharg=gamma/0.88*( tl_k(i+1).*h(i+1) + k(i+1)*tl_h(i+1) ) ... - % + tl_gamma/0.88.*k(i+1).*h(i+1); - ad_k(i+1)=ad_k(i+1)+tharg./k(i+1)*ad_tharg; - ad_h(i+1)=ad_h(i+1)+tharg./h(i+1)*ad_tharg; - ad_gamma=ad_gamma+1/0.88.*k(i+1).*h(i+1)*ad_tharg; + %1 tl_tharg=gamma(i+1)/0.88*( tl_k(i+1).*h(i+1) + k(i+1)*tl_h(i+1) ) ... + % + tl_gamma(i+1)/0.88.*k(i+1).*h(i+1); + ad_k(i+1)=ad_k(i+1)+ gamma(i+1)/0.88*h(i+1) *ad_tharg; + ad_h(i+1)=ad_h(i+1)+ gamma(i+1)/0.88*k(i+1) *ad_tharg; + ad_gamma(i+1) =ad_gamma(i+1) + 1/0.88.*k(i+1).*h(i+1)*ad_tharg; ad_tharg=0; % refraction @@ -319,17 +319,29 @@ ad_H0=ad_H0+g/8*2*H0*ad_E(nx); % tl_Er(nx)=0; -% gamma calculated based on deep water wave steepness (s0) following Battjes -% and Stive (1985), and also used at FRF by Ruessink et al. (2001) -L0=g/(2*pi*(sigma/2/pi)^2); -s0=H0/L0; -gamma=0.5+0.4*tanh(33*s0); -%2 tl_gamma=0.4*sech(33*s0).^2.*33*tl_s0; -ad_s0=0.4*sech(33*s0).^2.*33*ad_gamma; -ad_gamma=0; -%1 tl_s0=tl_H0/L0; -ad_H0=ad_H0+ad_s0/L0; -ad_s0=0; +% gamma can be either calculated based on deep water wave steepness (s0) +% following Battjes and Stive (1985) (also used by Ruessink et al., 2001), +% or based on the empirical fit obtained for duck94 by Ruessink et +% al. (2003). +if(gammaType==2001) + L0=g/(2*pi*(omega/2/pi)^2); + s0=H0/L0; + %2 tl_gamma=0.4*sech(33*s0).^2.*33*tl_s0; + ad_s0=ad_s0+0.4*sech(33*s0).^2.*33*sum(ad_gamma); + ad_gamma=0; + %1 tl_s0=tl_H0/L0-H0/L0^2*tl_L0; + ad_H0=ad_H0+ad_s0/L0; + ad_L0=ad_L0-H0/L0^2*ad_s0; + ad_s0=0; + %0 tl_L0 = -2*2*pi*g/omega^3*tl_omega; + ad_omega = ad_omega-2*2*pi*g/omega^3*ad_L0; + ad_L0=0; +elseif(gammaType==2003) + % tl_gamma = 0.76*tl_k.*h + 0.76*k.*tl_h; + ad_k = ad_k + 0.76*h.*ad_gamma; + ad_h = ad_h + 0.76*k.*ad_gamma; + ad_gamma=0; +end % dispersion diff --git a/bin/1DVar/waveModel/tl_waveModel.m b/bin/1DVar/waveModel/tl_waveModel.m index a8418ae..82fc4b6 100644 --- a/bin/1DVar/waveModel/tl_waveModel.m +++ b/bin/1DVar/waveModel/tl_waveModel.m @@ -6,7 +6,7 @@ % directly from output of waveModel.m % -[g,alpha,beta,nu]=waveModelParams(); +[g,alpha,beta,nu,gammaType]=waveModelParams(); % grid nx=length(x); @@ -51,13 +51,23 @@ refconst=sin(theta0)/c(nx); tl_refconst=cos(theta0)/c(nx)*tl_theta0-sin(theta0)/c(nx)^2*tl_c(nx); -% gamma calculated based on deep water wave steepness (s0) following Battjes -% and Stive (1985), and also used at FRF by Ruessink et al. (2001) -L0=g/(2*pi*(sigma/2/pi)^2); -s0=H0/L0; -tl_s0=tl_H0/L0; -gamma=0.5+0.4*tanh(33*s0); -tl_gamma=0.4*sech(33*s0).^2.*33*tl_s0; +% gamma can be either calculated based on deep water wave steepness (s0) +% following Battjes and Stive (1985) (also used by Ruessink et al., 2001), +% or based on the empirical fit obtained for duck94 by Ruessink et +% al. (2003). +if(gammaType==2001) + L0=g/(2*pi*(omega/2/pi)^2); % = 2*pi*g/omega^2 + tl_L0 = -2*2*pi*g/omega^3*tl_omega; + s0=H0/L0; + tl_s0=tl_H0/L0-H0/L0^2*tl_L0; + gamma=0.5+0.4*tanh(33*s0); + tl_gamma=0.4*sech(33*s0).^2.*33*tl_s0; + gamma=ones(nx,1)*gamma; + tl_gamma=ones(nx,1)*gamma; +elseif(gammaType==2003) + gamma=0.76*k.*h+0.29; + tl_gamma = 0.76*tl_k.*h + 0.76*k.*tl_h; +end % stepping, explicit scheme tl_E=zeros(nx,1); @@ -75,9 +85,9 @@ tl_theta(i)=1./sqrt(1-(c(i).*refconst).^2).*( refconst.*tl_c(i) + tl_refconst.*c(i) ); % max wave height - tharg=gamma/0.88.*k(i+1).*h(i+1); - tl_tharg=gamma/0.88*( tl_k(i+1).*h(i+1) + k(i+1)*tl_h(i+1) ) ... - + tl_gamma/0.88.*k(i+1).*h(i+1); + tharg=gamma(i+1)/0.88.*k(i+1).*h(i+1); + tl_tharg=gamma(i+1)/0.88*( tl_k(i+1).*h(i+1) + k(i+1)*tl_h(i+1) ) ... + + tl_gamma(i+1)/0.88.*k(i+1).*h(i+1); % Hm(i+1)=0.88./k(i+1).*tanh(tharg); tl_Hm(i+1)=0.88*( -1./k(i+1).^2.*tanh(tharg).*tl_k(i+1) ... + 1./k(i+1).*sech(tharg).^2.*tl_tharg ); diff --git a/bin/1DVar/waveModel/waveModel.m b/bin/1DVar/waveModel/waveModel.m index 98cf507..384ae9a 100644 --- a/bin/1DVar/waveModel/waveModel.m +++ b/bin/1DVar/waveModel/waveModel.m @@ -29,7 +29,7 @@ tauw=0; end -[g,alpha,beta,nu]=waveModelParams(); +[g,alpha,beta,nu,gammaType]=waveModelParams(); % grid nx=length(x); @@ -46,11 +46,18 @@ cg=n.*c; refconst=sin(theta0)/c(nx); -% gamma calculated based on deep water wave steepness (s0) following Battjes -% and Stive (1985), and also used by Ruessink et al. (2001) -L0=g/(2*pi*(sigma/2/pi)^2); -s0=H0/L0; -gamma=0.5+0.4*tanh(33*s0); +% gamma can be either calculated based on deep water wave steepness (s0) +% following Battjes and Stive (1985) (also used by Ruessink et al., 2001), +% or based on the empirical fit obtained for duck94 by Ruessink et +% al. (2003). +if(gammaType==2001) + L0=g/(2*pi*(omega/2/pi)^2); + s0=H0/L0; + gamma=0.5+0.4*tanh(33*s0); + gamma=ones(nx,1)*gamma; +elseif(gammaType==2003) + gamma=0.76*k.*h+0.29; +end % refraction theta=asin(c.*refconst); @@ -67,7 +74,7 @@ for i=(nx-1):-1:1 % max wave height - tharg=gamma/0.88.*k(i+1).*h(i+1); + tharg=gamma(i+1)/0.88.*k(i+1).*h(i+1); Hm(i+1)=0.88./k(i+1).*tanh(tharg); % fraction of breaking waves, non-implicit approximation from SWAN code diff --git a/bin/1DVar/waveModel/waveModelParams.m b/bin/1DVar/waveModel/waveModelParams.m index 99e80fb..0647b8b 100644 --- a/bin/1DVar/waveModel/waveModelParams.m +++ b/bin/1DVar/waveModel/waveModelParams.m @@ -1,4 +1,4 @@ -function [g,alpha,beta,nu]=waveModelParams(); +function [g,alpha,beta,nu,gammaType]=waveModelParams(); % % common params for NL-TL-AD model % @@ -7,3 +7,7 @@ alpha=1; beta=0.1; % roller parameter nu=.5; + +% switch for breaker model. Use '2001' Battjes and Stive (1985) (as used by +% Ruessink et al. 2001), or '2003' for Ruessink et al. (2003). +gammaType=2003; From 0c56564f3cca73f02969d065072dd39698bc5ca0 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 15 Apr 2021 10:18:35 -0400 Subject: [PATCH 08/26] added step to skip assimilation if no offshore forcing (current@242) is found --- RunWorkflow_1DVar.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 7c0b8e8..362496b 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -101,6 +101,7 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): prior['Ch'] += Ch # add process error to prior Ch return prior, Q + def set_offshore_conditions(prior, waves_obj, element): # population the new "prior" during each timestep with FRF data prior['theta0'] = np.deg2rad(waves_obj[-1]['waveDm'][element] - 71.8) # populate with theta0 data from 8m array @@ -407,7 +408,6 @@ def Master_1DVar_run(inputDict): savemat("./data/matlab_files/initialprior.mat", prior) savemat("./data/matlab_files/obslist.mat", obs_dict) - # ________________________________________________ RUN LOOP ________________________________________________ for element, time in enumerate(dateStringList): try: @@ -432,6 +432,12 @@ def Master_1DVar_run(inputDict): print("Wave height indices: ", obs['H']['ind']) print("Currents (m/s) to assimilate: ", obs['v']['d']) print("Current indices: ", obs['v']['ind']) + if int(obs['v']['ind'][-1]) != 242: + print("No offshore forcing present in current measurements for this timestep -- skipping assimilation") + delta_t = waves_obj[-1]['time'][int(element + 2*simulationDuration)] - waves_obj[-1]['time'][element] + delta_t = np.abs(delta_t.seconds) + prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) + continue print("Tauw to assimilate: ", obs['tauw']) From d7a309abacc638e8ee87ede91e7f063d25193814 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 11 May 2021 21:52:11 -0400 Subject: [PATCH 09/26] Added local savings of prior/obs. Removed matlab dependency. Added tide to obs for use in future model update. --- RunWorkflow_1DVar.py | 50 +++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 362496b..9a3edd6 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -1,9 +1,4 @@ # -*- coding: utf-8 -*- -# import matplotlib -# matplotlib.use('Agg') -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec -from mpl_toolkits.mplot3d import Axes3D import os, getopt, sys, shutil, glob, logging, yaml, re, pickle import datetime as DT from subprocess import check_output @@ -11,11 +6,9 @@ from getdatatestbed.getDataFRF import getObs, getDataTestBed from testbedutils import fileHandling from oct2py import octave -import matlab -import math -import netCDF4 as nc -from scipy.io import loadmat, savemat - +from scipy.io import savemat +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec Q = 0 @@ -35,8 +28,8 @@ def assim_currents(X, currents_object, obs, i): """ awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) - obs['v']['d'] = np.append(obs['v']['d'], (matlab.float64([currents_object['aveV'][i]])), axis=0) - obs['v']['ind'] = np.append(obs['v']['ind'], (matlab.float64([awac_indice])), axis=0) + obs['v']['d'] = np.append(obs['v']['d'], float(currents_object['aveV'][i])) + obs['v']['ind'] = np.append(obs['v']['ind'], int(awac_indice)) obs['v']['e'] = np.append(obs['v']['e'], .1) return obs @@ -59,8 +52,8 @@ def assim_waves(X, waves_object, obs, i): """ hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) - obs['H']['d'] = np.append(obs['H']['d'], (matlab.float64([waves_object['Hs'][i]])), axis=0) - obs['H']['ind'] = np.append(obs['H']['ind'], (matlab.float64([hs_indice])), axis=0) + obs['H']['d'] = np.append(obs['H']['d'], float(waves_object['Hs'][i])) + obs['H']['ind'] = np.append(obs['H']['ind'], int(hs_indice)) obs['H']['e'] = np.append(obs['H']['e'], .2) return obs @@ -138,12 +131,12 @@ def create_obs(): obs['v']['ind'] = [] obs['v']['e'] = [] obs['tauw'] = [] + obs['tide'] = [] return obs -def preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateStringList, simulationDuration): +def preprocess_data(waves_obj, current_obj, tide_obj, pier_wind, bathyTransect, dateStringList, simulationDuration): prior = create_prior() - obs, obs_indices_h, obs_indices_v = create_obs() # calculate tauw speed = pier_wind['windspeed'] @@ -151,6 +144,7 @@ def preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateString cd = 0.002 # reniers et al. 2004 tauw = cd * (speed ** 2) * np.cos(direc) wind_indice_skip = len(tauw) / len(dateStringList) + tide_indice_skip = len(tide_obj) / len(dateStringList) # restrict bathy to only include points after indice 35, zero_elev = np.argmax(bathyTransect['elevation'][200, :] < 0) @@ -182,12 +176,15 @@ def preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateString for element, time in enumerate(dateStringList): obs = create_obs() obs['tauw'] = tauw[0] - # populate each obs with tauw data + obs['tide'] = tide_obj[0] + # populate each obs with tauw and tide data if element > 0: try: obs['tauw'] = tauw[int(element * wind_indice_skip)] + obs['tide'] = tide_obj[int(element * tide_indice_skip)] except: obs['tauw'] = tauw[-1] + obs['tide'] = tide_obj[-1] for i in range(len(waves_obj)): try: @@ -217,7 +214,7 @@ def preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateString obs_list = np.append(obs_list, obs) - return X, h, Q, tauw, wind_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev + return X, h, Q, tauw, wind_indice_skip, tide_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev def plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zero_elev): @@ -378,10 +375,16 @@ def Master_1DVar_run(inputDict): # initialize get observation for measurements go = getObs(projectStart, projectEnd) + + # pull wind pier_wind = None while pier_wind == None: pier_wind = go.getWind() + # pull water level + tides_list = [] + tides_list = np.append(tides_list, go.getWL()['WL']) + # pull currents current_gauges = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m'] current_obj = [] @@ -399,12 +402,11 @@ def Master_1DVar_run(inputDict): waves_obj = np.append(waves_obj, go.getWaveData(gaugenumber=wave_gauges[i], spec=False)) #preprocess data - X, h, Q, tauw, wind_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev = \ - preprocess_data(waves_obj, current_obj, pier_wind, bathyTransect, dateStringList, simulationDuration) + X, h, Q, tauw, wind_indice_skip, tide_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev = \ + preprocess_data(waves_obj, current_obj, tides_list, pier_wind, bathyTransect, dateStringList, simulationDuration) obs_dict = {} obs_dict["struct"] = obs_list - savemat("./data/matlab_files/initialprior.mat", prior) savemat("./data/matlab_files/obslist.mat", obs_dict) @@ -432,13 +434,17 @@ def Master_1DVar_run(inputDict): print("Wave height indices: ", obs['H']['ind']) print("Currents (m/s) to assimilate: ", obs['v']['d']) print("Current indices: ", obs['v']['ind']) - if int(obs['v']['ind'][-1]) != 242: + + offshore_indice = int(obs['v']['ind'][-1]) + print(offshore_indice) + if offshore_indice < 242: print("No offshore forcing present in current measurements for this timestep -- skipping assimilation") delta_t = waves_obj[-1]['time'][int(element + 2*simulationDuration)] - waves_obj[-1]['time'][element] delta_t = np.abs(delta_t.seconds) prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) continue print("Tauw to assimilate: ", obs['tauw']) + print("Tide: ", obs['tide']) # run 1DVar model with prior and observation input From 83de0f772b60c0fcfb6ee62b1369fea4ec18b089 Mon Sep 17 00:00:00 2001 From: gwilsonOSU Date: Fri, 28 May 2021 10:16:01 -0700 Subject: [PATCH 10/26] added tide correction to assim_1dh --- bin/1DVar/assim_1dh.m | 20 ++++++++++++++++--- getdatatestbed | 2 +- testbedutils | 2 +- .../1DVar_Input_example.yml | 6 +++--- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVar/assim_1dh.m index 91f4866..d3f1022 100644 --- a/bin/1DVar/assim_1dh.m +++ b/bin/1DVar/assim_1dh.m @@ -7,7 +7,7 @@ % INPUTS: % % prior.x = model grid -% prior.h = bathymetry +% prior.h = bathymetry relative to a constant vertical datum % prior.theta0, prior.H0 = boundary conditions % prior.ka_drag = bed roughness % prior.tauw = wind stress in m2/s2 units (default = 0 if not included) @@ -19,8 +19,10 @@ % obs.H.d = rms wave height data % obs.H.e = error stdev for rms wave height % obs.v.* = as in obs.H, but for longshore current +% obs.tide = tidal elevation in same vertical datum as h % -% NOTE, if any of obs.* are missing or empty, they will be ignored +% NOTE, if any of obs.* are missing or empty, they will be ignored. The +% only required field is obs.tide. % % OUTPUTS: % @@ -58,6 +60,14 @@ end end +% Adjust prior to add tide. This will be reversed at the end of this +% script, such that during assimilation h=depth, but input/output to the +% script is always h=navd88 +prior.h=prior.h+obs.tide; +if(exist('bkgd')) + bkgd.h=bkgd.h+obs.tide; +end + % Adjust prior tauw to account for possible pressure gradients. The eqn for % v is copied from waveModel.m and directly inverted to solve for the force % Fy, using alongshore current observations on the shelf (x>800m). This Fy @@ -302,6 +312,10 @@ end % outer loop iterations +% Remove tide from outputs, such that h is re navd88 instead of TWL +prior.h=prior.h-obs.tide; +posterior.h=posterior.h-obs.tide; + % OPTIONAL: for diagnostics, repeat the representer calculations for both % variables, but this time do ALL the gridpoints. These can be used for % prior and posterior covariances for v, H. This is not used for the outer @@ -444,4 +458,4 @@ diagnostics.ad.v_h =ad_v_h; diagnostics.ad.v_H0 =ad_v_H0; diagnostics.ad.v_theta0 =ad_v_theta0; -diagnostics.ad.v_ka_drag=ad_v_ka_drag; \ No newline at end of file +diagnostics.ad.v_ka_drag=ad_v_ka_drag; diff --git a/getdatatestbed b/getdatatestbed index aa897bf..bce22e8 160000 --- a/getdatatestbed +++ b/getdatatestbed @@ -1 +1 @@ -Subproject commit aa897bfa4948c578738d6ec55043ae955e85eb2f +Subproject commit bce22e812a454c8d9a7a341b2833b26ac9c0a463 diff --git a/testbedutils b/testbedutils index 2fb3f92..9bf6a42 160000 --- a/testbedutils +++ b/testbedutils @@ -1 +1 @@ -Subproject commit 2fb3f92632a9677bfb1b96d6e786141d8d5fa128 +Subproject commit 9bf6a42c6a5131a5c232e3102815a3649d52997c diff --git a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml index 257c55c..0fbb1e8 100644 --- a/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml +++ b/yaml_files/TestBedExampleInputs/1DVar_Input_example.yml @@ -13,7 +13,7 @@ startTime: '2016-11-05T00:00:00Z' # project start time endTime: '2016-11-08T00:00:00Z' # project End time not inclusive simulationDuration: 3 # duration in hours how frequently to get new data, size of individual simulations -modelExecutable: D:/cmtb/bin/1DVar # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over +modelExecutable: /home/wilsongr/work/funded_projects/USCRP_DA/project/cmtb/bin/1DVar # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over ######################## # path stuff # ######################## @@ -23,7 +23,7 @@ modelSettings: grid: None # this is the sim files, and plots -workingDirectory: D:/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels -mainDirectory: D:/cmtb/ # Set to main filepath +workingDirectory: /home/wilsongr/work/funded_projects/USCRP_DA/project/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels +mainDirectory: /home/wilsongr/work/funded_projects/USCRP_DA/project/cmtb/ # Set to main filepath # this is the base directory where netCDF files are output to, netCDFdir: ./netCDF #optional will default to home/[whoami]/thredds_data -- this needs / From 564faa100f8776decec1127a72ce3a93c402b37c Mon Sep 17 00:00:00 2001 From: Greg Wilson Date: Tue, 8 Jun 2021 17:08:43 -0700 Subject: [PATCH 11/26] bugfix for Ch calculation --- RunWorkflow_1DVar.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 9a3edd6..c13fa6f 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -30,7 +30,7 @@ def assim_currents(X, currents_object, obs, i): awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) obs['v']['d'] = np.append(obs['v']['d'], float(currents_object['aveV'][i])) obs['v']['ind'] = np.append(obs['v']['ind'], int(awac_indice)) - obs['v']['e'] = np.append(obs['v']['e'], .1) + obs['v']['e'] = np.append(obs['v']['e'], .2) return obs @@ -54,13 +54,13 @@ def assim_waves(X, waves_object, obs, i): hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) obs['H']['d'] = np.append(obs['H']['d'], float(waves_object['Hs'][i])) obs['H']['ind'] = np.append(obs['H']['ind'], int(hs_indice)) - obs['H']['e'] = np.append(obs['H']['e'], .2) + obs['H']['e'] = np.append(obs['H']['e'], .05) return obs def calculate_Ch(prior, spec8m, X, delta_t, Q): """calculate Q(x,t) (measured process error) according to holman et al 2013 - Q = Cq * Hmo ^ (-[(x-x0)/sigma_x]^2) + Q = Cq * Hmo^2 * exp(-[(x-x0)/sigma_x]^2) deltat is difference from survey to assimilation step; add q each time-step to increase uncertainty as the Ch decreases in posterior posterior.ch + new S *N *S with just the tiny delta_t @@ -84,9 +84,9 @@ def calculate_Ch(prior, spec8m, X, delta_t, Q): delta_t = delta_t/(60*60*24) print("Delta t in hours:", delta_t*24) xx = np.meshgrid(X) - Lx = 25 # decorrelation length scale, larger Lx leads to smoother results + Lx = 50 # decorrelation length scale, larger Lx leads to smoother results N = np.exp((-(xx - np.transpose(xx)) ** 2) / (2 * Lx**2)) - Q = (Cq * Hmo) ** (np.power(-((X - x0) / sigma_x), 2))*delta_t + Q = Cq * Hmo**2. * np.exp(np.power(-((X - x0) / sigma_x), 2))*delta_t S = np.diag(np.sqrt(Q)) Ch = S * N * S if prior['Ch'] == []: @@ -227,6 +227,7 @@ def plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zer bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects print("Final Bathy Survey Date: ", bathyTransect['time']) survey_h = bathyTransect['elevation'][200, zero_elev:] + savemat("./data/matlab_files/finalBathyTransect.mat", bathyTransect) obs_indices_h = np.unique(obs_indices_h) obs_indices_v = np.unique(obs_indices_v) @@ -446,7 +447,6 @@ def Master_1DVar_run(inputDict): print("Tauw to assimilate: ", obs['tauw']) print("Tide: ", obs['tide']) - # run 1DVar model with prior and observation input # set nout if desired to obtain diagnostics and representer matrices. # save matfile of this particular prior and obs From 9ec4ab9e5dcafe0b896c5c8ee3cd6ba6ab576410 Mon Sep 17 00:00:00 2001 From: Greg Wilson Date: Tue, 8 Jun 2021 17:09:13 -0700 Subject: [PATCH 12/26] added ability to assimilate h --- bin/1DVar/assim_1dh.m | 225 +++++++++++++++++++++++++++--------------- 1 file changed, 148 insertions(+), 77 deletions(-) diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVar/assim_1dh.m index d3f1022..a4332f1 100644 --- a/bin/1DVar/assim_1dh.m +++ b/bin/1DVar/assim_1dh.m @@ -38,7 +38,7 @@ end hmin=.2; -nitermax=500; +nitermax=50; % unpack some variables Ch=prior.Ch; @@ -50,7 +50,7 @@ nt=length(prior); % if any obs types missing, set to empty -fld='Hv'; +fld='Hvh'; noobs.ind=[]; noobs.d=[]; noobs.e=[]; @@ -63,9 +63,16 @@ % Adjust prior to add tide. This will be reversed at the end of this % script, such that during assimilation h=depth, but input/output to the % script is always h=navd88 +if(obs.tide<=-999) + warning('got -999 (nodata) for tide, setting to zero instead') + obs.tide=0; +end prior.h=prior.h+obs.tide; +prior.h(prior.h2) + disp('Calculating full representer matrix as requested (nargout>2)') adj_H_h =zeros(nx,nx); @@ -404,58 +530,3 @@ representers.v_h_post=rep_vh_post; end - -%--------------------------------------- -% remaining code: reformat output variables in diagnostics struct, for -% convenience -%--------------------------------------- -clear diagnostics - -diagnostics.niter=n; -diagnostics.eps=eps; - -% terms in update equation -diagnostics.update.CMt=CMt; -diagnostics.update.R =R ; -diagnostics.update.Cd =Cd ; -diagnostics.update.d =d ; -diagnostics.update.Lu =Lu ; -diagnostics.update.obstype=obstype; -diagnostics.update.bkgd=bkgd; -diagnostics.update.fcst=fcst; - -% representer outputs: r.X_Y refers to sensitivity of model input variable Y -% to delta-perturbations of obs type X -diagnostics.r.H_h =r_H(1:nx,:); -diagnostics.r.H_H0 =r_H(nx+1,:); -diagnostics.r.H_theta0=r_H(nx+2,:); -diagnostics.r.H_kadrag=r_H(nx+3,:); -diagnostics.r.v_h =r_v(1:nx,:); -diagnostics.r.v_H0 =r_v(nx+1,:); -diagnostics.r.v_theta0=r_v(nx+2,:); -diagnostics.r.v_kadrag=r_v(nx+3,:); - -% R-matrix outputs: R.X_Y refers to sensitivity of obs type Y to -% delta-perturbations of obs type X -diagnostics.R.H_H=R_HH; -diagnostics.R.H_v=R_Hv; -diagnostics.R.v_H=R_vH; -diagnostics.R.v_v=R_vv; - -% additional diagnostic representers: rdiag.X_Y refers to sensitivity of -% model variable Y to delta-perturbations of obs type X -diagnostics.rdiag.H_H=r_HH; -diagnostics.rdiag.H_v=r_Hv; -diagnostics.rdiag.v_H=r_vH; -diagnostics.rdiag.v_v=r_vv; - -% adjoint outputs: ad.X_Y refers to sensitivity of model input Y to -% delta-perturbations of obs type X -diagnostics.ad.H_h =ad_H_h; -diagnostics.ad.H_H0 =ad_H_H0; -diagnostics.ad.H_theta0 =ad_H_theta0; -diagnostics.ad.H_ka_drag=ad_H_ka_drag; -diagnostics.ad.v_h =ad_v_h; -diagnostics.ad.v_H0 =ad_v_H0; -diagnostics.ad.v_theta0 =ad_v_theta0; -diagnostics.ad.v_ka_drag=ad_v_ka_drag; From 17e0760cc5d778428ea8c8b9897d5899dfc7404e Mon Sep 17 00:00:00 2001 From: Greg Wilson Date: Tue, 8 Jun 2021 17:10:14 -0700 Subject: [PATCH 13/26] bugfix for offshore BC for mixing --- bin/1DVar/waveModel/ad_waveModel.m | 2 +- bin/1DVar/waveModel/tl_waveModel.m | 2 +- bin/1DVar/waveModel/waveModel.m | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/1DVar/waveModel/ad_waveModel.m b/bin/1DVar/waveModel/ad_waveModel.m index dd40bf0..6348de7 100644 --- a/bin/1DVar/waveModel/ad_waveModel.m +++ b/bin/1DVar/waveModel/ad_waveModel.m @@ -78,7 +78,7 @@ A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); end A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); +A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); % % OLD: incorrect version for with-mixing case % if(nu>0) diff --git a/bin/1DVar/waveModel/tl_waveModel.m b/bin/1DVar/waveModel/tl_waveModel.m index 82fc4b6..82d1554 100644 --- a/bin/1DVar/waveModel/tl_waveModel.m +++ b/bin/1DVar/waveModel/tl_waveModel.m @@ -196,7 +196,7 @@ A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); end A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); +A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); % bottom stress model following Ruessink et al. (2001), Feddersen et % al. (2000). To get TL model, differentiate the eqn for v (i.e., the diff --git a/bin/1DVar/waveModel/waveModel.m b/bin/1DVar/waveModel/waveModel.m index 384ae9a..25f551d 100644 --- a/bin/1DVar/waveModel/waveModel.m +++ b/bin/1DVar/waveModel/waveModel.m @@ -149,10 +149,9 @@ A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); end A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); +A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); % v2: nonlinear solution with mixing. -test=A*v; v0=v; v = fsolve(@(v)Fy + Cd.*urms.*v.*sqrt(a^2+(v./urms).^2) - A*v,v0,optimset('Display','off')); From 6c78b4f3285d40d754c899eaabbc6766a5820a2d Mon Sep 17 00:00:00 2001 From: Greg Wilson Date: Tue, 22 Jun 2021 16:43:16 -0700 Subject: [PATCH 14/26] assim_1dh.m now supports sediment transport persistence model. RunWorkflow_1DVar.py will need to be updated to reflect this --- bin/1DVar/assim_1dh.m | 141 +++++++++++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 35 deletions(-) diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVar/assim_1dh.m index a4332f1..4f5476a 100644 --- a/bin/1DVar/assim_1dh.m +++ b/bin/1DVar/assim_1dh.m @@ -1,6 +1,6 @@ -function [posterior,diagnostics,representers]=assim_1dh(prior,obs,verb,bkgd) +function [posterior,forecast,diagnostics,representers]=assim_1dh(prior,obs,dt,verb) % -% [posterior,diagnostics]=assim_1dh(prior,obs) +% [posterior,forecast,diagnostics]=assim_1dh(prior,obs,dt) % % Assimilate data into 1DH wave and current model. % @@ -15,6 +15,28 @@ % prior.Ch, prior.Ctheta0, prior.CH0, prior.Cka = covariance matrices for h, % theta0, H0, ka_drag % +% prior.sedmodel = string to indicate which sediment transport model is used +% to forecast the bathymetry to time t+dt. At time of +% writing, the only supported model is 'persistence'. More +% models are planned for future versions. +% +% prior.sedparams = list of sediment transport parameters required by the +% requested sedmodel. TODO, this list needs to be +% documented for each sedmodel. +% +% CASE-1: sedmodel='persistence' +% +% This is a persistence model for h, and covariance is updated using the +% "process error" defined by Holman et al. (2013) eqn. (9). See their +% paper for definitions of required input fields listed below; the +% exception is the parameter 'Lx' which provides a finite spatial +% decorrelation length for the process error. +% +% prior.sedparams.Cq (recommended 0.067 m2/day) +% prior.sedparams.x0 (recommended 50 m) +% prior.sedparams.sigma_x (recommended 150 m) +% prior.sedparams.Lx (recommended 25 m) +% % obs.H.ind = model indeces for rms wave height observations % obs.H.d = rms wave height data % obs.H.e = error stdev for rms wave height @@ -24,9 +46,23 @@ % NOTE, if any of obs.* are missing or empty, they will be ignored. The % only required field is obs.tide. % +% dt = time in days for which to provide a bathymetry forecast. Set dt=0 to +% disable forecasts. +% % OUTPUTS: % -% posterior = struct with same fields as prior but updated by assimilation +% posterior = struct with same fields as prior but updated by assimilation. +% +% forecast = struct that has similar fields as prior and posterior, but for +% future time t+dt. These can be used in a subsequent +% assimilation cycle at time t+dt. Importantly, h and Ch in the +% forecast are calculated using a sediment transport model, which +% can be selected using the input prior.sedmodel. +% +% NOTE, setting dt=0 will disable the forecast step, in which case +% forecast=[]. This is recommended in cases where the forecast is not +% useful, since calculating the forecast can take a considerable amount +% of time depending on the sediment transport model being used. % % diagnostics = struct with diagnostic fields for analyzing the update. See % comments near end of this code for descriptions @@ -70,10 +106,6 @@ prior.h=prior.h+obs.tide; prior.h(prior.h0) + if(~isfield(prior,'sedmodel')) + error('prior.sedmodel is required as input, see header of assim_1dh.m for details') + end + + % For the forecast step, we just want to predict bathymetry. Other + % non-bathymetry variables are not updated as part of the forecast step, + % and so they and their covariances remain constant. Doing this + % bookkeeping here will make it easier to re-use the forecast as the prior + % for time t+dt. + forecast=struct; + forecast.x =prior.x ; + forecast.ka_drag =prior.ka_drag ; + forecast.sedmodel=prior.sedmodel; + forecast.Ctheta0 =prior.Ctheta0 ; + forecast.CH0 =prior.CH0 ; + forecast.Cka =prior.Cka ; + + % Case-1: 'persistence' + if(strcmp(prior.sedmodel,'persistence')) + forecast.hp = posterior.h; + Q = ( prior.sedparams.Cq * (sqrt(2)*posterior.H0)^2 ) ... + * exp(-( (x-prior.sedparams.x0) / prior.sedparams.sigma_x ).^2) ... + * dt; + S = diag(sqrt(Q)); + xx = meshgrid(x); + N = exp((-(xx - xx').^2)/(2*prior.sedparams.Lx^2)); + forecast.Ch = posterior.Ch + S*N*S; + + % TODO: other sediment transport codes will go here... + % elseif(strcmp(prior.sedmodel,'vanderA')) + % .... + % elseif(strcmp(prior.sedmodel,'dubarbier')) + % .... + % elseif(strcmp(prior.sedmodel,'soulsbyVanRijn')) + % .... + % + + else + error(['The requested sediment transport model is not supported (prior.sedmodel = ' prior.sedmodel ')']) + end + +end + %--------------------------------------- % reformat output variables in diagnostics struct, for convenience %--------------------------------------- @@ -397,7 +468,7 @@ diagnostics.Lu =Lu ; diagnostics.obstype=obstype; diagnostics.bkgd=bkgd; -diagnostics.fcst=fcst; +diagnostics.pred=pred; % representer outputs: r.X_Y refers to sensitivity of model input variable Y % to delta-perturbations of obs type X From 8007383daa13dd651749078ba1c7a6705c7052fa Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 2 Sep 2021 11:32:57 -0400 Subject: [PATCH 15/26] sedparam preparation --- RunWorkflow_1DVar.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index c13fa6f..1ed8452 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -108,7 +108,7 @@ def create_prior(): prior = {} prior['x'] = [] prior['h'] = [] - prior['theta0'] = [] + prior['theta0'] = 0.0 prior['H0'] = [] prior['ka_drag'] = .015 prior['hErr'] = [] @@ -116,7 +116,13 @@ def create_prior(): prior['CH0'] = .01 prior['Cka'] = 2.5*10**-5 prior['Ch'] = [] - prior['sigma'] = [] + prior['sigma'] = 0.067 + prior['sedmodel'] = "persistence" + prior['sedparams'] = {} + prior['sedparams']['Cq'] = 0.067 + prior['sedparams']['x0'] = 50.0 + prior['sedparams']['sigma_x'] = 150.0 + prior['sedparams']['Lx'] = 25.0 return prior @@ -453,7 +459,7 @@ def Master_1DVar_run(inputDict): os.chdir(matlabfiledir) savemat("./data/matlab_files/prior_" + str(time[:13]) + ".mat", prior) savemat("./data/matlab_files/obs_" + str(time[:13]) + ".mat", obs) - posterior, diagnostics = octave.feval(modeldir + '/assim_1dh.m', prior, obs, 1, nout=2)#, representers = \ + posterior = octave.feval(modeldir + '/assim_1dh.m', prior, obs, delta_t, 1, nout=1)#diagnostics, representers = \ print('Simulation took %s ' % (DT.datetime.now() - dt)) os.chdir(matlabfiledir) # save matfile of the posterior result @@ -469,7 +475,7 @@ def Master_1DVar_run(inputDict): pass #calculate Ch for the next timestep - prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) + #prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) print('-------------------------------SUCCESS-----------------------------------------') From ca3fb0e75c2416d9a7e6a2c5ab712183cc839192 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Sep 2021 14:22:19 -0400 Subject: [PATCH 16/26] netcdf output updates --- bin/{1DVar => 1DVarS}/assim_1dh.m | 8 ++++---- bin/{1DVar => 1DVarS}/old/assim_1dh.m | 0 bin/{1DVar => 1DVarS}/old/waveModel/ad_symmetryCheck.m | 0 bin/{1DVar => 1DVarS}/old/waveModel/ad_waveModel.m | 0 bin/{1DVar => 1DVarS}/old/waveModel/tl_waveModel.m | 0 bin/{1DVar => 1DVarS}/old/waveModel/waveModel.m | 0 bin/{1DVar => 1DVarS}/old/waveModel/waveModelParams.m | 0 bin/{1DVar => 1DVarS}/waveModel/ad_symmetryCheck.m | 0 bin/{1DVar => 1DVarS}/waveModel/ad_waveModel.m | 0 bin/{1DVar => 1DVarS}/waveModel/tl_waveModel.m | 0 bin/{1DVar => 1DVarS}/waveModel/waveModel.m | 4 +++- bin/{1DVar => 1DVarS}/waveModel/waveModelParams.m | 0 12 files changed, 7 insertions(+), 5 deletions(-) rename bin/{1DVar => 1DVarS}/assim_1dh.m (98%) rename bin/{1DVar => 1DVarS}/old/assim_1dh.m (100%) rename bin/{1DVar => 1DVarS}/old/waveModel/ad_symmetryCheck.m (100%) rename bin/{1DVar => 1DVarS}/old/waveModel/ad_waveModel.m (100%) rename bin/{1DVar => 1DVarS}/old/waveModel/tl_waveModel.m (100%) rename bin/{1DVar => 1DVarS}/old/waveModel/waveModel.m (100%) rename bin/{1DVar => 1DVarS}/old/waveModel/waveModelParams.m (100%) rename bin/{1DVar => 1DVarS}/waveModel/ad_symmetryCheck.m (100%) rename bin/{1DVar => 1DVarS}/waveModel/ad_waveModel.m (100%) rename bin/{1DVar => 1DVarS}/waveModel/tl_waveModel.m (100%) rename bin/{1DVar => 1DVarS}/waveModel/waveModel.m (98%) rename bin/{1DVar => 1DVarS}/waveModel/waveModelParams.m (100%) diff --git a/bin/1DVar/assim_1dh.m b/bin/1DVarS/assim_1dh.m similarity index 98% rename from bin/1DVar/assim_1dh.m rename to bin/1DVarS/assim_1dh.m index 4f5476a..8eea455 100644 --- a/bin/1DVar/assim_1dh.m +++ b/bin/1DVarS/assim_1dh.m @@ -67,7 +67,7 @@ % diagnostics = struct with diagnostic fields for analyzing the update. See % comments near end of this code for descriptions % -addpath '/home/wilsongr/work/funded_projects/USCRP_DA/project/cmtb/bin/1DVar/waveModel/'; +addpath 'D:/1DVarS_CMTB/bin/1DVar/waveModel/'; warning('off','all'); if(~exist('verb')) verb=1; @@ -430,11 +430,11 @@ if(strcmp(prior.sedmodel,'persistence')) forecast.hp = posterior.h; Q = ( prior.sedparams.Cq * (sqrt(2)*posterior.H0)^2 ) ... - * exp(-( (x-prior.sedparams.x0) / prior.sedparams.sigma_x ).^2) ... + * exp(-( (x-double(prior.sedparams.x0)) / double(prior.sedparams.sigma_x )).^2) ... * dt; S = diag(sqrt(Q)); xx = meshgrid(x); - N = exp((-(xx - xx').^2)/(2*prior.sedparams.Lx^2)); + N = exp((-(xx - xx').^2)/(2*double(prior.sedparams.Lx)^2)); forecast.Ch = posterior.Ch + S*N*S; % TODO: other sediment transport codes will go here... @@ -462,7 +462,7 @@ % terms in update equation diagnostics.CMt=CMt; -diagnostics.R =R ; +diagnostics.Ro =R ; diagnostics.Cd =Cd ; diagnostics.d =d ; diagnostics.Lu =Lu ; diff --git a/bin/1DVar/old/assim_1dh.m b/bin/1DVarS/old/assim_1dh.m similarity index 100% rename from bin/1DVar/old/assim_1dh.m rename to bin/1DVarS/old/assim_1dh.m diff --git a/bin/1DVar/old/waveModel/ad_symmetryCheck.m b/bin/1DVarS/old/waveModel/ad_symmetryCheck.m similarity index 100% rename from bin/1DVar/old/waveModel/ad_symmetryCheck.m rename to bin/1DVarS/old/waveModel/ad_symmetryCheck.m diff --git a/bin/1DVar/old/waveModel/ad_waveModel.m b/bin/1DVarS/old/waveModel/ad_waveModel.m similarity index 100% rename from bin/1DVar/old/waveModel/ad_waveModel.m rename to bin/1DVarS/old/waveModel/ad_waveModel.m diff --git a/bin/1DVar/old/waveModel/tl_waveModel.m b/bin/1DVarS/old/waveModel/tl_waveModel.m similarity index 100% rename from bin/1DVar/old/waveModel/tl_waveModel.m rename to bin/1DVarS/old/waveModel/tl_waveModel.m diff --git a/bin/1DVar/old/waveModel/waveModel.m b/bin/1DVarS/old/waveModel/waveModel.m similarity index 100% rename from bin/1DVar/old/waveModel/waveModel.m rename to bin/1DVarS/old/waveModel/waveModel.m diff --git a/bin/1DVar/old/waveModel/waveModelParams.m b/bin/1DVarS/old/waveModel/waveModelParams.m similarity index 100% rename from bin/1DVar/old/waveModel/waveModelParams.m rename to bin/1DVarS/old/waveModel/waveModelParams.m diff --git a/bin/1DVar/waveModel/ad_symmetryCheck.m b/bin/1DVarS/waveModel/ad_symmetryCheck.m similarity index 100% rename from bin/1DVar/waveModel/ad_symmetryCheck.m rename to bin/1DVarS/waveModel/ad_symmetryCheck.m diff --git a/bin/1DVar/waveModel/ad_waveModel.m b/bin/1DVarS/waveModel/ad_waveModel.m similarity index 100% rename from bin/1DVar/waveModel/ad_waveModel.m rename to bin/1DVarS/waveModel/ad_waveModel.m diff --git a/bin/1DVar/waveModel/tl_waveModel.m b/bin/1DVarS/waveModel/tl_waveModel.m similarity index 100% rename from bin/1DVar/waveModel/tl_waveModel.m rename to bin/1DVarS/waveModel/tl_waveModel.m diff --git a/bin/1DVar/waveModel/waveModel.m b/bin/1DVarS/waveModel/waveModel.m similarity index 98% rename from bin/1DVar/waveModel/waveModel.m rename to bin/1DVarS/waveModel/waveModel.m index 25f551d..99ec4a1 100644 --- a/bin/1DVar/waveModel/waveModel.m +++ b/bin/1DVarS/waveModel/waveModel.m @@ -121,7 +121,7 @@ k=k(:); n=n(:); theta=theta(:); -H=H(:); +H=double(H(:)); % radiation stress gradient if(beta>0) % roller @@ -152,6 +152,8 @@ A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); % v2: nonlinear solution with mixing. +Fy = double(Fy); +v = double(v); v0=v; v = fsolve(@(v)Fy + Cd.*urms.*v.*sqrt(a^2+(v./urms).^2) - A*v,v0,optimset('Display','off')); diff --git a/bin/1DVar/waveModel/waveModelParams.m b/bin/1DVarS/waveModel/waveModelParams.m similarity index 100% rename from bin/1DVar/waveModel/waveModelParams.m rename to bin/1DVarS/waveModel/waveModelParams.m From 427dd3f98e12a1445bc43e7827e3074144bb753c Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 15 Sep 2021 14:30:06 -0400 Subject: [PATCH 17/26] added script to pull altimeter data --- altimeter_pull.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 altimeter_pull.py diff --git a/altimeter_pull.py b/altimeter_pull.py new file mode 100644 index 0000000..6dac4bb --- /dev/null +++ b/altimeter_pull.py @@ -0,0 +1,21 @@ +import numpy as np +import os, yaml, datetime +from getdatatestbed.getDataFRF import getObs, getDataTestBed + +startTime = datetime.datetime(2015, 1, 1, 0, 0, 0) +endTime = datetime.datetime(2021, 10, 1, 0, 0, 0) + +go = getObs(startTime, endTime) + +print(go.d1) +print(go.d2) +altAll = {} +altimeterlist = ['Alt769-150', 'Alt769-200', 'Alt769-250', 'Alt769-300', 'Alt769-350', + 'Alt861-150', 'Alt861-200', 'Alt861-250', 'Alt861-300', 'Alt861-350', + 'Alt940-150', 'Alt940-200', 'Alt940-250', 'Alt940-300', 'Alt940-340'] + +for altimeter in altimeterlist: + print(altimeter) + altAll[str(altimeter)] = go.getALT(gaugeName=altimeter) + +np.save('./data/altimeter2015-2021.npy', altAll) \ No newline at end of file From 108b578907ba012018c8a3f3a5bf5faf6eed1280 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 16 Sep 2021 11:05:28 -0400 Subject: [PATCH 18/26] NetCDF setup for Dunex/Future Work --- RunWorkflow_1DVar.py | 112 ++++-- .../1DVarS_Input_example.yml | 29 ++ .../morphModels/1dvars/1dvars_global.yml | 58 +++ yaml_files/morphModels/1dvars/1dvars_var.yml | 340 ++++++++++++++++++ 4 files changed, 504 insertions(+), 35 deletions(-) create mode 100644 yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml create mode 100644 yaml_files/morphModels/1dvars/1dvars_global.yml create mode 100644 yaml_files/morphModels/1dvars/1dvars_var.yml diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py index 1ed8452..453c841 100644 --- a/RunWorkflow_1DVar.py +++ b/RunWorkflow_1DVar.py @@ -4,11 +4,12 @@ from subprocess import check_output import numpy as np from getdatatestbed.getDataFRF import getObs, getDataTestBed -from testbedutils import fileHandling +from testbedutils import fileHandling, py2netCDF as p2nc from oct2py import octave from scipy.io import savemat import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec +import netCDF4 Q = 0 @@ -28,9 +29,9 @@ def assim_currents(X, currents_object, obs, i): """ awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) - obs['v']['d'] = np.append(obs['v']['d'], float(currents_object['aveV'][i])) - obs['v']['ind'] = np.append(obs['v']['ind'], int(awac_indice)) - obs['v']['e'] = np.append(obs['v']['e'], .2) + obs['vd'] = np.append(obs['vd'], float(currents_object['aveV'][i])) + obs['vind'] = np.append(obs['vind'], int(awac_indice)) + obs['ve'] = np.append(obs['ve'], .2) return obs @@ -52,9 +53,9 @@ def assim_waves(X, waves_object, obs, i): """ hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) - obs['H']['d'] = np.append(obs['H']['d'], float(waves_object['Hs'][i])) - obs['H']['ind'] = np.append(obs['H']['ind'], int(hs_indice)) - obs['H']['e'] = np.append(obs['H']['e'], .05) + obs['Hd'] = np.append(obs['Hd'], float(waves_object['Hs'][i])) + obs['Hind'] = np.append(obs['Hind'], int(hs_indice)) + obs['He'] = np.append(obs['He'], .05) return obs @@ -106,6 +107,8 @@ def set_offshore_conditions(prior, waves_obj, element): def create_prior(): prior = {} + prior['measurements'] = [] + prior['time'] = [] prior['x'] = [] prior['h'] = [] prior['theta0'] = 0.0 @@ -123,36 +126,64 @@ def create_prior(): prior['sedparams']['x0'] = 50.0 prior['sedparams']['sigma_x'] = 150.0 prior['sedparams']['Lx'] = 25.0 + prior['Cq'] = 0.067 + prior['x0'] = 50.0 + prior['sigma_x'] = 150.0 + prior['Lx'] = 25.0 return prior def create_obs(): obs = {} + obs['measurements'] = [] + obs['constant'] = [] obs['H'] = {} obs['v'] = {} - obs['H']['d'] = [] - obs['H']['ind'] = [] - obs['H']['e'] = [] - obs['v']['d'] = [] - obs['v']['ind'] = [] - obs['v']['e'] = [] + obs['Hd'] = [] + obs['Hind'] = [] + obs['He'] = [] + obs['vd'] = [] + obs['vind'] = [] + obs['ve'] = [] obs['tauw'] = [] obs['tide'] = [] return obs +def write_netcdf(time, posterior, obs, pathBase): + obs.pop('H') + obs.pop('v') + posterior['Hd'] = obs['Hd'] + posterior['Hind'] = obs['Hind'] + posterior['He'] = obs['He'] + posterior['vd'] = obs['vd'] + posterior['vind'] = obs['vind'] + posterior['ve'] = obs['ve'] + #posterior = posterior ** obs + time = ''.join(''.join(time.split(':')).split('-')) + varYaml = "./yaml_files/morphModels/1dvars/1dvars_var.yml" + globalYaml = "./yaml_files/morphModels/1dvars/1dvars_global.yml" + p2nc.makenc_generic(os.path.join(pathBase, 'cmtb_morph_1dvar_{}.nc'.format(time)), globalYaml, varYaml, posterior) + + def preprocess_data(waves_obj, current_obj, tide_obj, pier_wind, bathyTransect, dateStringList, simulationDuration): prior = create_prior() # calculate tauw + if pier_wind == None: + pier_wind = {} + pier_wind['windspeed'] = np.asarray([np.random.randint(-1, 1) for i in range(4000)]) + pier_wind['winddir'] = np.asarray([np.random.randint(55, 90) for i in range(4000)]) + direc = np.deg2rad(pier_wind['winddir'] - 71.8) + else: + direc = np.deg2rad(pier_wind['winddir'] - 71.8) speed = pier_wind['windspeed'] - direc = np.deg2rad(pier_wind['winddir'] - 71.8) cd = 0.002 # reniers et al. 2004 tauw = cd * (speed ** 2) * np.cos(direc) wind_indice_skip = len(tauw) / len(dateStringList) tide_indice_skip = len(tide_obj) / len(dateStringList) - # restrict bathy to only include points after indice 35, + # restrict bathy to only include points where h is negative zero_elev = np.argmax(bathyTransect['elevation'][200, :] < 0) h = bathyTransect['elevation'][200, zero_elev:] X = np.linspace(zero_elev, np.max(bathyTransect['xFRF']), len(h)) @@ -202,7 +233,7 @@ def preprocess_data(waves_obj, current_obj, tide_obj, pier_wind, bathyTransect, pass # add indices with assimilated wave heights to list for plotting - for obs_timestep_ind in obs['H']['ind']: + for obs_timestep_ind in obs['Hind']: obs_indices_h.append(obs_timestep_ind) for i in range(len(current_obj)): @@ -215,7 +246,7 @@ def preprocess_data(waves_obj, current_obj, tide_obj, pier_wind, bathyTransect, pass # add indices with assimilated currents to list for plotting - for obs_timestep_ind in obs['v']['ind']: + for obs_timestep_ind in obs['vind']: obs_indices_v.append(obs_timestep_ind) obs_list = np.append(obs_list, obs) @@ -338,7 +369,7 @@ def Master_1DVar_run(inputDict): runFlag = inputDict['runFlag'] analyzeFlag = inputDict['analyzeFlag'] pFlag = inputDict['pFlag'] - model = inputDict.get('model', '1DVar') + model = inputDict.get('model', '1DVarS') modeldir = inputDict['modelExecutable'] matlabfiledir = inputDict['mainDirectory'] log = inputDict.get('logging', True) @@ -399,15 +430,18 @@ def Master_1DVar_run(inputDict): try: current_obj = np.append(current_obj, go.getCurrents(gaugenumber=current_gauges[i], roundto=1)) except: + current_obj = np.append(np.nan) print("NetCDF DAP Error while grabbing current data from gauge: ", current_gauges[i]) # pull waves #wave_gauges = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m', 'xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array'] - wave_gauges = ['xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array'] + wave_gauges = ['xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array', 'paros-200-940m', 'paros-250-940m', + 'paros-340x-940y-top', 'sig940-300'] waves_obj = [] for i in range(len(wave_gauges)): + print(wave_gauges[i]) waves_obj = np.append(waves_obj, go.getWaveData(gaugenumber=wave_gauges[i], spec=False)) - + print(len(waves_obj)) #preprocess data X, h, Q, tauw, wind_indice_skip, tide_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev = \ preprocess_data(waves_obj, current_obj, tides_list, pier_wind, bathyTransect, dateStringList, simulationDuration) @@ -432,19 +466,19 @@ def Master_1DVar_run(inputDict): # grab measurements exactly on times in dateStringList with element*simulationDuration, # since waves are taken every hour prior = set_offshore_conditions(prior, waves_obj, element*simulationDuration) - + prior['time'] = dateStringList[element] #grab obs data for current timestep obs = obs_list[element] print("Assimilation no. " + str(element) + "/" + str(len(dateStringList))) print("Assimilating Date: ", dateStringList[element]) - print("Wave heights (m) to assimilate: ", obs['H']['d']) - print("Wave height indices: ", obs['H']['ind']) - print("Currents (m/s) to assimilate: ", obs['v']['d']) - print("Current indices: ", obs['v']['ind']) + print("Wave heights (m) to assimilate: ", obs['Hd']) + print("Wave height indices: ", obs['Hind']) + print("Currents (m/s) to assimilate: ", obs['vd']) + print("Current indices: ", obs['vind']) - offshore_indice = int(obs['v']['ind'][-1]) + offshore_indice = int(obs['vind'][-1]) print(offshore_indice) - if offshore_indice < 242: + if offshore_indice < 239: print("No offshore forcing present in current measurements for this timestep -- skipping assimilation") delta_t = waves_obj[-1]['time'][int(element + 2*simulationDuration)] - waves_obj[-1]['time'][element] delta_t = np.abs(delta_t.seconds) @@ -456,14 +490,21 @@ def Master_1DVar_run(inputDict): # run 1DVar model with prior and observation input # set nout if desired to obtain diagnostics and representer matrices. # save matfile of this particular prior and obs - os.chdir(matlabfiledir) + """os.chdir(matlabfiledir) savemat("./data/matlab_files/prior_" + str(time[:13]) + ".mat", prior) - savemat("./data/matlab_files/obs_" + str(time[:13]) + ".mat", obs) + savemat("./data/matlab_files/obs_" + str(time[:13]) + ".mat", obs)""" + obs['H']['d'] = obs['Hd'] + obs['H']['ind'] = obs['Hind'] + obs['H']['e'] = obs['He'] + obs['v']['d'] = obs['vd'] + obs['v']['ind'] = obs['vind'] + obs['v']['e'] = obs['ve'] posterior = octave.feval(modeldir + '/assim_1dh.m', prior, obs, delta_t, 1, nout=1)#diagnostics, representers = \ print('Simulation took %s ' % (DT.datetime.now() - dt)) os.chdir(matlabfiledir) # save matfile of the posterior result - savemat("./data/matlab_files/posterior_" + str(time[:13]) + ".mat", posterior) + pathBase = 'D:/1DVarS_CMTB/data/netCDF_files/' + write_netcdf(dateStringList[element], posterior, obs, pathBase) # make the output of the model the prior for the next timestep prior = posterior @@ -474,9 +515,6 @@ def Master_1DVar_run(inputDict): except: pass - #calculate Ch for the next timestep - #prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) - print('-------------------------------SUCCESS-----------------------------------------') except Exception as e: @@ -486,14 +524,18 @@ def Master_1DVar_run(inputDict): plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zero_elev) + if __name__ == "__main__": - model = '1DVar' + model = '1DVarS' opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) print('___________________________________\n___________________________________\n___________________' '________________\n') print('USACE FRF Coastal Model Test Bed : {}'.format(model)) - args = ['./yaml_files/TestBedExampleInputs/1DVar_Input_example.yml'] + """nctest = netCDF4.Dataset('./data/netCDF_files/cmtb_morph_1dvar_20210901T000000Z.nc') + print(nctest.variables.keys()) + print(nctest.variables['h'][:])""" + args = ['./yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml'] try: # assume the user gave the path yamlLoc = args[0] diff --git a/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml new file mode 100644 index 0000000..d6da89f --- /dev/null +++ b/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml @@ -0,0 +1,29 @@ +# # # # # # # # # # # # +# Data control # +# # # # # # # # # # # # +pFlag: False # turn plotting on +generateFlag: True # generate simulation input files (go get data, process, and write files) +runFlag: True # run the simulation +analyzeFlag: False # post process simulations (read files, post process data, make netCDF files, plot if desired) +bathyLoc: integrated_bathy # bathymetry source. OPTIONAL: defaults to FRF +######################## +#simulations parameters# +######################## +startTime: '2021-09-01T00:00:00Z' # project start time +endTime: '2021-09-03T00:00:00Z' # project End time not inclusive + +simulationDuration: 3 # duration in hours how frequently to get new data, size of individual simulations +modelExecutable: D:/1DVarS_CMTBv2/bin/1DVarS # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over +######################## +# path stuff # +######################## +modelSettings: + model: 1DVarS + version_prefix: Base # controls switched with int the code + grid: None + +# this is the sim files, and plots +workingDirectory: D:/1DVarS_CMTBv2/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels +mainDirectory: D:/1DVarS_CMTBv2/ # Set to main filepath +# this is the base directory where netCDF files are output to, +netCDFdir: ./data/netCDF_files/ #optional will default to home/[whoami]/thredds_data -- this needs / diff --git a/yaml_files/morphModels/1dvars/1dvars_global.yml b/yaml_files/morphModels/1dvars/1dvars_global.yml new file mode 100644 index 0000000..eaeb105 --- /dev/null +++ b/yaml_files/morphModels/1dvars/1dvars_global.yml @@ -0,0 +1,58 @@ +featureType: grid +title: Observations +summary: > + Observations coinciding with the model run. + This component of the CMTB is composed of a 1D Variotional Morphological model developed by + Dr. Greg Wilson @ Oregon State University: +history: posterior created as output of assimilated data for CMTB +source: CMTB 1DVar simulations +platform: FRF Cluster - Crunchy +sourceUrl: (local files) +standard_name_vocabulary: CFv25 +Metadata_Conventions: Unidata Dataset Discovery v1.0, CF-1.6 +metadata_link: N/A +Conventions: CF-1.6 +creator_name: USACE/CHL/COAB +creator_url: http://frf.usace.army.mil +creator_email: frfwebmaster@usace.army.mil +license: These data may be redistributed and used without restriction. Data are intended for scholarly use by the research community, with the express agreement that users will properly acknowledge the USACE Field Research Facility and the supporting investigator(s). Use or reproduction of these data for commercial purposes is prohibited without prior written permission. +keywords_vocabulary: FGlobal Change Master Directory (GCMD) Earth Science Keywords; CF Standard Name Table (v23, 23 March 2013) +keywords: Oceans > Ocean Waves > Wave Frequency, Oceans > Ocean Waves > Wave Height, Oceans > Ocean Waves > Wave Period, Oceans > Ocean Waves > Wave Spectra, Oceans > Ocean Waves > Wave Direction, DOD > DOD/USARMY/USACE/CHL/FRF > Field Research Facility, Coastal And Hydraulics Laboratory,U. S. Army Corps Of Engineers, U.S. Army, U. S. Department Of Defense, Oregon State University, College of Earth and Ocean Sciences, sea_surface_wave_variance_spectral_density, sea_surface_wave_significant_height, sea_surface_wave_from_direction, sea_surface_wave_directional_variance_spectral_density +processing: realtime +organization: USACE/CHL/COAB/OSU +publisher_url: http://frf.usace.army.mil +infoUrl: http://frf.usace.army.mil +publisher_email: frfwebmaster@usace.army.mil +publisher_name: USACE/CHL/COAB +format_version: v1.0 +institution: USACE/CHL/COAB +contact: USACE/CHL/COAB +contact_info: USACE/CHL/COAB +contact_role: Owner +contributor_name: USACE/CHL/COAB +contributor_role: USACE/CHL/COAB +naming_authority: FRF +origin: USACE/CHL/COAB +acknowledgement: Data are provided by the Field Research Facility; Coastal Observations & Analysis Branch; US Army Corps of Engineers, Duck, North Carolina. +project: USACE/COAB observations +id: FRF_CMTB_CSHORE_Fixed +processing_level: L1 +geospatial_vertical_units: m +geospatial_vertical_resolution: 0 +geospatial_vertical_min: 0 +geospatial_vertical_max: 0 +geospatial_vertical_origin: sea surface +geospatial_lat_min: to be updated +geospatial_lat_max: to be updated +geospatial_lat_units: degrees_north +geospatial_lon_min: to be updated +geospatial_lon_max: to be updated +geospatial_lon_units: degrees_east +geospatial_vertical_positive: up +time_coverage_start: '2017-05-01T22:00:00Z' +time_coverage_end: '2017-05-01T22:00:00Z' +deployment_start: '2017-05-01T22:00:00Z' +instrument: N/A +cross_shore_angle_units: degrees +cross_shore_angle_description: cross shore angle at the USACE FRF site in DUCK, NC, clockwise from true north +cross_shore_angle: 71.8 diff --git a/yaml_files/morphModels/1dvars/1dvars_var.yml b/yaml_files/morphModels/1dvars/1dvars_var.yml new file mode 100644 index 0000000..78e9d1e --- /dev/null +++ b/yaml_files/morphModels/1dvars/1dvars_var.yml @@ -0,0 +1,340 @@ +# CSHORE template + +_variables: ['time', 'measurements', 'constant', + 'x', 'h', 'theta0', 'H0', 'ka_drag', 'hErr', 'constant', + 'Ctheta0', 'CH0', 'Cka', 'Ch', 'sigma', + 'sedmodel', 'Cq', 'x0', 'sigma_x', 'Lx', + 'Hd', 'Hind', 'He', + 'vd', 'vind', 've', + 'tauw', 'tide'] +_attributes: ['notes'] +_dimensions: ['time', 'x', 'constant', 'measurements'] + +time: + name: 'time' + units: 'datetime' + standard_name: 'datetime' + long_name: 'UTC Model Time' + data_type: 'S1' + dim: ['time'] + calendar: 'gregorian' + fill_value: '-999.99' + +constant: + name: 'constant' + units: '' + standard_name: '' + long_name: 'constant of the same type' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'constant' + coordinates: 'xFRF yFRF' + description: 'constant of the same type' + +measurements: + name: 'measurements' + units: '' + standard_name: '' + long_name: 'measurements of the same type' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'measurements' + coordinates: 'xFRF yFRF' + description: 'measurements of the same type' + +# hydrology +Hd: + name: 'Hd' + units: 'm' + standard_name: '' + long_name: 'Significant Wave Height' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'Wave Height' + coordinates: 'xFRF yFRF' + description: 'Significant Wave Height from colocated sensor' + +Hind: + name: 'Hind' + units: '' + standard_name: '' + long_name: 'wave height indice' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'Wave Height Indice' + coordinates: 'xFRF yFRF' + description: 'Indice of Significant Wave Height from colocated sensor' + +He: + name: 'He' + units: 'm' + standard_name: '' + long_name: 'Error in Significant Wave Height' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'Wave Height' + coordinates: 'xFRF yFRF' + description: 'Error in Significant Wave Height from colocated sensor' + +vd: + name: 'vd' + units: 'm/s' + standard_name: '' + long_name: 'Current' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'current' + coordinates: 'xFRF yFRF' + description: 'current velocity from colocated sensor' + +vind: + name: 'vind' + units: '' + standard_name: '' + long_name: 'curre t indice' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'current Indice' + coordinates: 'xFRF yFRF' + description: 'Indice of current from colocated sensor' + +ve: + name: 've' + units: 'm/s' + standard_name: '' + long_name: 'Error in current' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'current' + coordinates: 'xFRF yFRF' + description: 'Error in current from colocated sensor' + +tauw: + name: 'tauw' + units: 'N/m2' + standard_name: '' + long_name: 'tauw' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'current' + coordinates: 'xFRF yFRF' + description: 'tauw' + +tide: + name: 'tide' + units: 'm' + standard_name: '' + long_name: 'tide elevation' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'current' + coordinates: 'xFRF yFRF' + description: 'tide' + +sedmodel: + name: 'sedmodel' + units: 'string' + standard_name: 'Sediment Model Type' + long_name: 'Sediment Model Type for Assimilation' + data_type: 'S1' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'sedmodel' + coordinates: '' + description: 'Sediment Model Type for Assimilation' + +# morphology +x: + name: 'x' + units: 'm' + standard_name: '' + long_name: 'cross-shore cell for the model bathymetry' + data_type: 'f8' + dim: ['x'] + fill_value: '-999.99' + short_name: 'bathy' + coordinates: 'x' + description: 'cross-shore position in x coordinates' + +h: + name: 'h' + units: 'm' + standard_name: '' + long_name: 'bed elevation for the model bathymetry' + data_type: 'f8' + dim: ['x'] + fill_value: '-999.99' + short_name: 'bathy' + coordinates: 'x' + description: 'Bottom elevation for the 1dvars model bathymetry at each time-step' + +hErr: + name: 'hErr' + units: 'm' + standard_name: '' + long_name: 'bed elevation for the model bathymetry' + data_type: 'f8' + dim: ['x'] + fill_value: '-999.99' + short_name: 'bathy' + coordinates: 'x' + description: 'Bottom elevation error for the 1dvars model bathymetry at each time-step' + + +# hydrology +theta0: + name: 'theta0' + units: 'Degrees' + standard_name: '' + long_name: 'Mean Wave Direction measured from true north' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'Mean Direction' + coordinates: 'x yFRF' + description: 'Mean wave Direction along the Modeled Transect from CMTB 1dvar model run' + +H0: + name: 'H0' + units: 'm' + standard_name: '' + long_name: 'Significant Wave Height' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'Wave Height' + coordinates: 'x yFRF' + description: 'Offshore Significant Wave Height from the 8m array at time' + +ka_drag: + name: 'ka_drag' + units: '' + standard_name: '' + long_name: 'ka_drag' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'ka_drag' + coordinates: 'x yFRF' + description: 'ka_drag constant' + +#covariances + +Ch: + name: 'Ch' + units: '' + standard_name: '' + long_name: 'Covriance of bed elevation for the model bathymetry' + data_type: 'f8' + dim: ['x', 'x'] + fill_value: '-999.99' + short_name: 'covariance of bathy' + coordinates: '' + description: 'covariance of Bottom elevation for the 1dvars model bathymetry at each time-step' + +Ctheta0: + name: 'Ctheta0' + units: '' + standard_name: '' + long_name: 'Covariance of Mean Wave Direction measured from true north' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'Covariance of Mean Direction' + coordinates: 'x yFRF' + description: 'Covariance of Mean wave Direction along the Modeled Transect from CMTB 1dvar model run' + +CH0: + name: 'CH0' + units: '' + standard_name: '' + long_name: 'Covariance of Significant Wave Height' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'Covariance of Wave Height' + coordinates: 'x yFRF' + description: 'Covariance of Offshore Significant Wave Height from the 8m array at time' + +Cka: + name: 'Cka' + units: '' + standard_name: 'Covariance of ka_drag' + long_name: 'Covariance of ka_drag' + data_type: 'f8' + dim: ['measurements'] + fill_value: '-999.99' + short_name: 'Covariance of ka_drag' + coordinates: 'x yFRF' + description: 'Covariance of ka_drag constant' + +sigma: + name: 'sigma' + units: '' + standard_name: 'sigma' + long_name: 'sigma' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'sigma' + coordinates: 'x yFRF' + description: 'sigma' + +#sediment stuff +Cq: + name: 'Cq' + units: '' + standard_name: 'Cq' + long_name: 'Cq' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'Cq' + coordinates: 'x yFRF' + description: 'Cq' + +x0: + name: 'x0' + units: '' + standard_name: 'x0' + long_name: 'x0' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'x0' + coordinates: 'x yFRF' + description: 'x0' + +sigma_x: + name: 'sigma_x' + units: '' + standard_name: 'sigma_x' + long_name: 'sigma_x' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'sigma_x' + coordinates: 'x yFRF' + description: 'sigma_x' + +Lx: + name: 'Lx' + units: '' + standard_name: 'Lx' + long_name: 'Lx' + data_type: 'f8' + dim: ['constant'] + fill_value: '-999.99' + short_name: 'Lx' + coordinates: 'x yFRF' + description: 'Lx' \ No newline at end of file From 3e0d0301c37a149cf9c1b2629cec1c0cd8b7b7bd Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 4 Oct 2021 10:52:20 -0400 Subject: [PATCH 19/26] 1dvars v2 working with genericWorkflow --- genericWorkFlow.py | 53 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/genericWorkFlow.py b/genericWorkFlow.py index f7052c5..d87b32a 100755 --- a/genericWorkFlow.py +++ b/genericWorkFlow.py @@ -5,7 +5,7 @@ import datetime as DT import numpy as np from frontback import frontBackNEW -from getdatatestbed.getDataFRF import getObs +from getdatatestbed.getDataFRF import getObs, getDataTestBed from testbedutils import fileHandling from prepdata import writeRunRead as wrrClass import glob @@ -57,15 +57,29 @@ def Master_workFlow(inputDict): if generateFlag == True: go = getObs(projectStart-DT.timedelta(hours=3), projectEnd+DT.timedelta(hours=3)) # initialize get observation if modelName in ['ww3']: - gauge = 'waverider-26m' + wave_gauge = 'waverider-26m' + current_gauge = None elif modelName.lower() in ['swash', 'funwave']: - gauge = '8m-array' + wave_gauge = '8m-array' + current_gauge = None elif modelName.lower() in ['cshore']: - gauge = 'awac-6m' + wave_gauge = 'awac-6m' + current_gauge = None + elif modelName.lower() in ['1dvars']: + wave_gauge = ['xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array', 'paros-200-940m', 'paros-250-940m', + 'paros-340x-940y-top', 'sig940-300'] + current_gauge = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m'] + else: + print("Model name: " + modelName + "not found.") + wave_gauge = None + current_gauge = None - rawspec = go.getWaveData(gaugenumber=gauge, spec=True) + if wave_gauge != None: + rawspec = go.getWaveData(gaugenumber=wave_gauge, spec=True) rawWL = go.getWL() rawwind = go.getWind(gaugenumber=0) + if current_gauge != None: + rawcurrent = go.getCurrents(gaugenumber=current_gauge, roundto=1) # ________________________________________________ RUN LOOP ________________________________________________ # run the process through each of the above dates @@ -105,7 +119,6 @@ def Master_workFlow(inputDict): allWL=rawWL, allWave=rawspec, wrr=wrr) - elif modelName in ['swash']: if generateFlag is True: @@ -144,6 +157,29 @@ def Master_workFlow(inputDict): allCTD=rawctd, wrr=wrr) gridFname = None + elif modelName in ['1dvars']: + wrr = wrrClass.var1dsIO('1dvars', workingDirectory=workingDirectory, testName=testName, + versionPrefix=version_prefix, + startTime=DT.datetime.strptime(time, '%Y-%m-%dT%H:%M:%SZ'), + simulatedRunTime=inputDict['simulationDuration'], + endTime=DT.datetime.strptime(time, '%Y-%m-%dT%H:%M:%SZ') + DT.timedelta( + hours=inputDict['simulationDuration']), runFlag=runFlag, + generateFlag=generateFlag, readFlag=analyzeFlag, pbsFlag=pbsFlag) + if generateFlag is True: + bathygo = getDataTestBed(projectStart, projectEnd) + rawbathy = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects + wavePacket, windPacket, wlPacket, bathyPacket, currentPacket, gridFname, wrr = frontBackNEW.var1dsSimSetup(time, + inputDict=inputDict, + allWind=rawwind, + allWL=rawWL, + allWave=rawspec, + allBathy=rawbathy, + allCurrent=rawcurrent, + wrr=wrr) + ctdPacket = None + updateBathy = None + + if generateFlag is True: print(" TODO: TY you're handing me back the same prepdata packets from all frontBacks") @@ -161,9 +197,11 @@ def Master_workFlow(inputDict): wrr.hpcCores = inputDict['hpcSettings']['hpcCores'] wrr.hpcNodes = inputDict['hpcSettings']['hpcNodes'] # write simulation files (if assigned) + print(gridFname) wrr.writeAllFiles(wavePacket=wavePacket, windPacket=windPacket, wlPacket=wlPacket, bathyPacket=bathyPacket, gridfname=gridFname, - ctdPacket=ctdPacket, updateBathy=updateBathy) + ctdPacket=ctdPacket, currentPacket=currentPacket, + updateBathy=updateBathy) # run simulation (as appropriate) if runFlag is True: @@ -222,5 +260,4 @@ def Master_workFlow(inputDict): except: raise IOError('Input YAML file required. See yaml_files/TestBedExampleInputs/[model]_Input_example for example ' 'yaml file.') - Master_workFlow(inputDict=inputDict) From ed28aca2badf506aea7dd8769a14b22ac2087c06 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 7 Oct 2021 09:05:57 -0400 Subject: [PATCH 20/26] v2 fully working --- bin/1DVarS/assim_1dh.m | 2 +- frontback/frontBackNEW.py | 21 +++++++++++++++---- genericWorkFlow.py | 11 +++++----- getdatatestbed | 2 +- prepdata | 2 +- testbedutils | 2 +- .../1DVarS_Input_example.yml | 19 ++++++++++------- 7 files changed, 38 insertions(+), 21 deletions(-) diff --git a/bin/1DVarS/assim_1dh.m b/bin/1DVarS/assim_1dh.m index 8eea455..af82a67 100644 --- a/bin/1DVarS/assim_1dh.m +++ b/bin/1DVarS/assim_1dh.m @@ -67,7 +67,7 @@ % diagnostics = struct with diagnostic fields for analyzing the update. See % comments near end of this code for descriptions % -addpath 'D:/1DVarS_CMTB/bin/1DVar/waveModel/'; +addpath './bin/1DVarS/waveModel/'; warning('off','all'); if(~exist('verb')) verb=1; diff --git a/frontback/frontBackNEW.py b/frontback/frontBackNEW.py index f6325d2..7b3c948 100644 --- a/frontback/frontBackNEW.py +++ b/frontback/frontBackNEW.py @@ -14,6 +14,7 @@ from prepdata import py2netCDF as p2nc from prepdata import postData + def ww3simSetup(startTimeString, inputDict, allWind , allWL, allWave, wrr): """This Function is the master call for the data preparation for the Coastal Model Test Bed (CMTB) for ww3. @@ -108,6 +109,7 @@ def ww3simSetup(startTimeString, inputDict, allWind , allWL, allWave, wrr): return wavepacket, windpacket, wlpacket, bathy, gridFname, wrr + def swashSimSetup(startTimeString, inputDict, allWind, allWL, allWave, wrr): """This Function is the master call for the data preparation for the Coastal Model Test Bed (CMTB) and the Swash wave/FLow model @@ -166,6 +168,21 @@ def swashSimSetup(startTimeString, inputDict, allWind, allWL, allWave, wrr): return wavepacket, None, WLpacket, gridDict, None, wrr + +def var1dsSimSetup(startTimeString, inputDict, allWind, allWL, allWave, allBathy, allCurrent, wrr): + endTime = inputDict['endTime'] + startTime = inputDict['startTime'] + dateStartList, wrr.dateStringList, projectStart, projectEnd = fileHandling.createTimeInfo(startTime, endTime, + simulationDuration=3) + wrr.timestep = inputDict['modelSettings']['timestep'] + gridFname = '' + ## ___________WATER LEVEL__________________ + prepdata = PrepDataTools() + WLpacket = prepdata.prep_WL(allWL, allWind['epochtime']) + + return allWave, allWind, WLpacket, allBathy, allCurrent, gridFname, wrr + + def genericPostProcess(startTime, inputDict, spatialData, pointData, wrr): """This runs the post process script for Wave Watch 3. @@ -274,10 +291,6 @@ class objects as listed below: print("is this necessary??") - - - - def cshoreSimSetup(startTimeString, inputDict, allWave, allBathy, allWL, allWind, allCTD, wrr): """Author: David Young Association: USACE CHL Field Research Facility diff --git a/genericWorkFlow.py b/genericWorkFlow.py index d87b32a..523e362 100755 --- a/genericWorkFlow.py +++ b/genericWorkFlow.py @@ -198,10 +198,10 @@ def Master_workFlow(inputDict): wrr.hpcNodes = inputDict['hpcSettings']['hpcNodes'] # write simulation files (if assigned) print(gridFname) - wrr.writeAllFiles(wavePacket=wavePacket, windPacket=windPacket, wlPacket=wlPacket, - bathyPacket=bathyPacket, gridfname=gridFname, - ctdPacket=ctdPacket, currentPacket=currentPacket, - updateBathy=updateBathy) + wrr.writeAllFiles(wavePacket, windPacket, wlPacket, + bathyPacket, gridFname, + ctdPacket, currentPacket, + updateBathy) # run simulation (as appropriate) if runFlag is True: @@ -214,7 +214,7 @@ def Master_workFlow(inputDict): wrr=wrr) # if it's a live run, move the plots to the output directory - if plotFlag is True and DT.date.today() == projectEnd or inputDict['slack'] is not None: + """if plotFlag is True and DT.date.today() == projectEnd or inputDict['slack'] is not None: from testbedutils import cmtbSlack moveFnames = glob.glob(wrr.plottingDirectory + '/CMTB*.png') moveFnames.extend(glob.glob(wrr.plottingDirectory + '/CMTB*.gif')) @@ -232,6 +232,7 @@ def Master_workFlow(inputDict): for file in moveFnames: shutil.move(file, liveFileMoveToDirectory) print('moved {} to {} '.format(file, liveFileMoveToDirectory)) + """ print('------------------SUCCESS-----------------------------------------') except Exception as e: diff --git a/getdatatestbed b/getdatatestbed index 751b245..54586f1 160000 --- a/getdatatestbed +++ b/getdatatestbed @@ -1 +1 @@ -Subproject commit 751b2451e91eb109759553d28c8a7639c5cd95c1 +Subproject commit 54586f1f8e4960fd7cb97f577bc025f64491a26f diff --git a/prepdata b/prepdata index fd544cd..104a6a5 160000 --- a/prepdata +++ b/prepdata @@ -1 +1 @@ -Subproject commit fd544cd549f2d39ef04e7a6d7900fe09db6ce8c8 +Subproject commit 104a6a5035211ffe79bb2926d18ae80a9bf16627 diff --git a/testbedutils b/testbedutils index b12bbba..de361e1 160000 --- a/testbedutils +++ b/testbedutils @@ -1 +1 @@ -Subproject commit b12bbba4803d0a9cb3935c7f138ed3226c3390d7 +Subproject commit de361e18abab5b80131567d4c90045392439d866 diff --git a/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml b/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml index d6da89f..c569f86 100644 --- a/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml +++ b/yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml @@ -1,29 +1,32 @@ # # # # # # # # # # # # # Data control # # # # # # # # # # # # # -pFlag: False # turn plotting on +plotFlag: False # turn plotting on generateFlag: True # generate simulation input files (go get data, process, and write files) runFlag: True # run the simulation analyzeFlag: False # post process simulations (read files, post process data, make netCDF files, plot if desired) bathyLoc: integrated_bathy # bathymetry source. OPTIONAL: defaults to FRF +logging: False +slack: None ######################## #simulations parameters# ######################## -startTime: '2021-09-01T00:00:00Z' # project start time -endTime: '2021-09-03T00:00:00Z' # project End time not inclusive +startTime: '2021-09-09T00:00:00Z' # project start time +endTime: '2021-09-09T06:00:00Z' # project End time not inclusive -simulationDuration: 3 # duration in hours how frequently to get new data, size of individual simulations -modelExecutable: D:/1DVarS_CMTBv2/bin/1DVarS # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over +simulationDuration: 144 #number of hours between startTime and endTime, only used for metadata +modelExecutable: D:/cmtb/bin/1DVarS # path to the model REQUIRED# this is the directory where simulation files and QA/QC plots are made and existing architecture takes over ######################## # path stuff # ######################## modelSettings: - model: 1DVarS + modelName: 1dvars version_prefix: Base # controls switched with int the code grid: None + timestep: 3 # duration in hours how frequently to get new data, size of individual simulations # this is the sim files, and plots -workingDirectory: D:/1DVarS_CMTBv2/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels -mainDirectory: D:/1DVarS_CMTBv2/ # Set to main filepath +workingDirectory: D:/cmtb/data/waveModels/1DVar # REQUIRED usually [cmtbRoot]/data/waveModels +mainDirectory: D:/cmtb/ # Set to main filepath # this is the base directory where netCDF files are output to, netCDFdir: ./data/netCDF_files/ #optional will default to home/[whoami]/thredds_data -- this needs / From c258fa0466d3baca36bf955a1433b86ecc767641 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 7 Oct 2021 14:56:54 -0400 Subject: [PATCH 21/26] deleted extra filed from git repo --- .gitmodules | 9 - .idea/.gitignore | 2 - .idea/cmtb.iml | 28 - .idea/codeStyles/codeStyleConfig.xml | 5 - .idea/dictionaries/Adam.xml | 7 - .idea/encodings.xml | 4 - .idea/inspectionProfiles/Project_Default.xml | 30 - .idea/misc.xml | 8 - .idea/modules.xml | 8 - .idea/other.xml | 8 - .idea/vcs.xml | 9 - RunWorkflow_1DVar.py | 554 ----------------- altimeter_pull.py | 21 - bin/1DVarS/assim_1dh.m | 603 ------------------- bin/1DVarS/old/assim_1dh.m | 409 ------------- bin/1DVarS/old/waveModel/ad_symmetryCheck.m | 36 -- bin/1DVarS/old/waveModel/ad_waveModel.m | 361 ----------- bin/1DVarS/old/waveModel/tl_waveModel.m | 257 -------- bin/1DVarS/old/waveModel/waveModel.m | 184 ------ bin/1DVarS/old/waveModel/waveModelParams.m | 9 - bin/1DVarS/waveModel/ad_symmetryCheck.m | 36 -- bin/1DVarS/waveModel/ad_waveModel.m | 374 ------------ bin/1DVarS/waveModel/tl_waveModel.m | 269 --------- bin/1DVarS/waveModel/waveModel.m | 180 ------ bin/1DVarS/waveModel/waveModelParams.m | 13 - 25 files changed, 3424 deletions(-) delete mode 100644 .gitmodules delete mode 100644 .idea/.gitignore delete mode 100644 .idea/cmtb.iml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/dictionaries/Adam.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/other.xml delete mode 100644 .idea/vcs.xml delete mode 100644 RunWorkflow_1DVar.py delete mode 100644 altimeter_pull.py delete mode 100644 bin/1DVarS/assim_1dh.m delete mode 100644 bin/1DVarS/old/assim_1dh.m delete mode 100644 bin/1DVarS/old/waveModel/ad_symmetryCheck.m delete mode 100644 bin/1DVarS/old/waveModel/ad_waveModel.m delete mode 100644 bin/1DVarS/old/waveModel/tl_waveModel.m delete mode 100644 bin/1DVarS/old/waveModel/waveModel.m delete mode 100644 bin/1DVarS/old/waveModel/waveModelParams.m delete mode 100644 bin/1DVarS/waveModel/ad_symmetryCheck.m delete mode 100644 bin/1DVarS/waveModel/ad_waveModel.m delete mode 100644 bin/1DVarS/waveModel/tl_waveModel.m delete mode 100644 bin/1DVarS/waveModel/waveModel.m delete mode 100644 bin/1DVarS/waveModel/waveModelParams.m diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 4cb98a9..0000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "getdatatestbed"] - path = getdatatestbed - url = https://github.com/SBFRF/getdatatestbed.git -[submodule "testbedutils"] - path = testbedutils - url = https://github.com/SBFRF/testbedutils.git -[submodule "prepdata"] - path = prepdata - url = https://github.com/SBFRF/prepdata.git diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 5c98b42..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Default ignored files -/workspace.xml \ No newline at end of file diff --git a/.idea/cmtb.iml b/.idea/cmtb.iml deleted file mode 100644 index 34ddd3f..0000000 --- a/.idea/cmtb.iml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/dictionaries/Adam.xml b/.idea/dictionaries/Adam.xml deleted file mode 100644 index bfe787c..0000000 --- a/.idea/dictionaries/Adam.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - assim - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 15a15b2..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index c88595c..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 789e113..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index bb0aad5..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml deleted file mode 100644 index a847ac6..0000000 --- a/.idea/other.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 512b7a8..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/RunWorkflow_1DVar.py b/RunWorkflow_1DVar.py deleted file mode 100644 index 453c841..0000000 --- a/RunWorkflow_1DVar.py +++ /dev/null @@ -1,554 +0,0 @@ -# -*- coding: utf-8 -*- -import os, getopt, sys, shutil, glob, logging, yaml, re, pickle -import datetime as DT -from subprocess import check_output -import numpy as np -from getdatatestbed.getDataFRF import getObs, getDataTestBed -from testbedutils import fileHandling, py2netCDF as p2nc -from oct2py import octave -from scipy.io import savemat -import matplotlib.pyplot as plt -import matplotlib.gridspec as gridspec -import netCDF4 -Q = 0 - - -def assim_currents(X, currents_object, obs, i): - """Assimilates currents from the FRF-pulled current object into the format to be added to matlab observations matfile - possible improvements include utilizing averaging instead of just pulling the measurement closest in time to each - time in dateStringList - Args: - X: gridded crosshore distance data - currents_object: object returned from getWaveSpec in FRFgetData - obs: matfile containing observation data from example input - i: simulation number (to full different observation from waves_object at each time step; waves are taken - every hour, while we might want to populate the observations with measurements taken every three hours.) - - Returns: - obs with new wave height data and indices written into it from current simulation timestep - - """ - awac_value, awac_indice = find_nearest(X, currents_object['xFRF']) - obs['vd'] = np.append(obs['vd'], float(currents_object['aveV'][i])) - obs['vind'] = np.append(obs['vind'], int(awac_indice)) - obs['ve'] = np.append(obs['ve'], .2) - return obs - - -def assim_waves(X, waves_object, obs, i): - """Assimilates waves from the FRF-pulled waves objecst into the format to be added to matlab observations matfile. - possible improvements include utilizing averaging instead of just pulling the measurement closest in time to each - time in dateStringList - - Args: - X: gridded crosshore distance data - waves_object: object returned from getWaveSpec in FRFgetData - obs: matfile containing observation data from example input - i: simulation number (to pull different observation from waves_object at each time step; waves are taken - every hour, while we might want to populate the observations with measurements taken every three hours.) - - - Returns: - obs with new wave height data and indices written into it from current simulation timestep - - """ - hs_value, hs_indice = find_nearest(X, waves_object['xFRF']) - obs['Hd'] = np.append(obs['Hd'], float(waves_object['Hs'][i])) - obs['Hind'] = np.append(obs['Hind'], int(hs_indice)) - obs['He'] = np.append(obs['He'], .05) - return obs - - -def calculate_Ch(prior, spec8m, X, delta_t, Q): - """calculate Q(x,t) (measured process error) according to holman et al 2013 - Q = Cq * Hmo^2 * exp(-[(x-x0)/sigma_x]^2) - deltat is difference from survey to assimilation step; - add q each time-step to increase uncertainty as the Ch decreases in posterior - posterior.ch + new S *N *S with just the tiny delta_t - - Args: - prior: the prior matfile to write Ch to - waves_obj[-1]: offshore wave spectra object - X: gridded distance data in crosshore - delta_t: time difference in seconds between last simulation (or last measured bathy) and current timestep - Q: defined above - - Returns: - prior: with newly written Ch = (old Ch) + Q - Q: process error - """ - - Cq = 0.067 # from sandy duck experiment - Hmo = np.mean(spec8m['Hs']) # significant wave height of highest 1/3 of waves - x0 = 50 # x0 and sigma_x reflect the typical location of breaking waves at this particular beach - sigma_x = 150 - delta_t = delta_t/(60*60*24) - print("Delta t in hours:", delta_t*24) - xx = np.meshgrid(X) - Lx = 50 # decorrelation length scale, larger Lx leads to smoother results - N = np.exp((-(xx - np.transpose(xx)) ** 2) / (2 * Lx**2)) - Q = Cq * Hmo**2. * np.exp(np.power(-((X - x0) / sigma_x), 2))*delta_t - S = np.diag(np.sqrt(Q)) - Ch = S * N * S - if prior['Ch'] == []: - prior['Ch'] = np.zeros(shape=Ch.shape) - prior['Ch'] += Ch # add process error to prior Ch - return prior, Q - - -def set_offshore_conditions(prior, waves_obj, element): - # population the new "prior" during each timestep with FRF data - prior['theta0'] = np.deg2rad(waves_obj[-1]['waveDm'][element] - 71.8) # populate with theta0 data from 8m array - print("Offshore Wave Direction: ", prior['theta0']) - prior['H0'] = waves_obj[-1]['Hs'][element] # populate with offshore wave height data from 8m array - prior['sigma'] = 2 * np.pi * waves_obj[-1]['peakf'][element] # calculate offshore sigma value from 8m array - return prior - - -def create_prior(): - prior = {} - prior['measurements'] = [] - prior['time'] = [] - prior['x'] = [] - prior['h'] = [] - prior['theta0'] = 0.0 - prior['H0'] = [] - prior['ka_drag'] = .015 - prior['hErr'] = [] - prior['Ctheta0'] = .0305 - prior['CH0'] = .01 - prior['Cka'] = 2.5*10**-5 - prior['Ch'] = [] - prior['sigma'] = 0.067 - prior['sedmodel'] = "persistence" - prior['sedparams'] = {} - prior['sedparams']['Cq'] = 0.067 - prior['sedparams']['x0'] = 50.0 - prior['sedparams']['sigma_x'] = 150.0 - prior['sedparams']['Lx'] = 25.0 - prior['Cq'] = 0.067 - prior['x0'] = 50.0 - prior['sigma_x'] = 150.0 - prior['Lx'] = 25.0 - return prior - - -def create_obs(): - obs = {} - obs['measurements'] = [] - obs['constant'] = [] - obs['H'] = {} - obs['v'] = {} - obs['Hd'] = [] - obs['Hind'] = [] - obs['He'] = [] - obs['vd'] = [] - obs['vind'] = [] - obs['ve'] = [] - obs['tauw'] = [] - obs['tide'] = [] - return obs - - -def write_netcdf(time, posterior, obs, pathBase): - obs.pop('H') - obs.pop('v') - posterior['Hd'] = obs['Hd'] - posterior['Hind'] = obs['Hind'] - posterior['He'] = obs['He'] - posterior['vd'] = obs['vd'] - posterior['vind'] = obs['vind'] - posterior['ve'] = obs['ve'] - #posterior = posterior ** obs - time = ''.join(''.join(time.split(':')).split('-')) - varYaml = "./yaml_files/morphModels/1dvars/1dvars_var.yml" - globalYaml = "./yaml_files/morphModels/1dvars/1dvars_global.yml" - p2nc.makenc_generic(os.path.join(pathBase, 'cmtb_morph_1dvar_{}.nc'.format(time)), globalYaml, varYaml, posterior) - - -def preprocess_data(waves_obj, current_obj, tide_obj, pier_wind, bathyTransect, dateStringList, simulationDuration): - prior = create_prior() - - # calculate tauw - if pier_wind == None: - pier_wind = {} - pier_wind['windspeed'] = np.asarray([np.random.randint(-1, 1) for i in range(4000)]) - pier_wind['winddir'] = np.asarray([np.random.randint(55, 90) for i in range(4000)]) - direc = np.deg2rad(pier_wind['winddir'] - 71.8) - else: - direc = np.deg2rad(pier_wind['winddir'] - 71.8) - speed = pier_wind['windspeed'] - cd = 0.002 # reniers et al. 2004 - tauw = cd * (speed ** 2) * np.cos(direc) - wind_indice_skip = len(tauw) / len(dateStringList) - tide_indice_skip = len(tide_obj) / len(dateStringList) - - # restrict bathy to only include points where h is negative - zero_elev = np.argmax(bathyTransect['elevation'][200, :] < 0) - h = bathyTransect['elevation'][200, zero_elev:] - X = np.linspace(zero_elev, np.max(bathyTransect['xFRF']), len(h)) - - # calculate initial delta_t where it is elapsed time since the bathy was measured to first simulation timestep - delta_t = waves_obj[-1]['time'][0] - bathyTransect['time'] - #delta_t = bathyTransect['time'].timestamp() - waves_obj[-1]['epochtime'][0] - delta_t = delta_t.seconds - delta_t = np.abs(delta_t) - X = np.reshape(X, (len(X), 1)) - h = np.reshape(h, (len(h), 1)) - Q = 0 - - # populate prior with data appropriate for this time period - prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) - prior['x'] = X # populate with bathy cross-shore distance data - prior['h'] = -h # populate with elevation data - prior['hErr'] = .1 # populate with elevation error data currently using .1 m - - # populate obs with initial timestep data - - # create lists for storing locations of observations for plotting - obs_indices_h = [] - obs_indices_v = [] - obs_list = [] - - for element, time in enumerate(dateStringList): - obs = create_obs() - obs['tauw'] = tauw[0] - obs['tide'] = tide_obj[0] - # populate each obs with tauw and tide data - if element > 0: - try: - obs['tauw'] = tauw[int(element * wind_indice_skip)] - obs['tide'] = tide_obj[int(element * tide_indice_skip)] - except: - obs['tauw'] = tauw[-1] - obs['tide'] = tide_obj[-1] - - for i in range(len(waves_obj)): - try: - # grab measurements exactly on times in dateStringList with element*simulationDuration, - # since waves are taken every hour - # no averaging done - obs = assim_waves(X, waves_obj[i], obs, element*simulationDuration) - except: - pass - - # add indices with assimilated wave heights to list for plotting - for obs_timestep_ind in obs['Hind']: - obs_indices_h.append(obs_timestep_ind) - - for i in range(len(current_obj)): - try: - # grab measurements exactly on times in dateStringList with element*simulationDuration/3 - # since currents are measured every 3 hours - # no averaging done - obs = assim_currents(X, current_obj[i], obs, int(element*simulationDuration/2)) - except: - pass - - # add indices with assimilated currents to list for plotting - for obs_timestep_ind in obs['vind']: - obs_indices_v.append(obs_timestep_ind) - - obs_list = np.append(obs_list, obs) - - return X, h, Q, tauw, wind_indice_skip, tide_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev - - -def plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zero_elev): - # plot prior, posterior, difference, and Q - # plot hErr on the prior and posterior - # show areas with assimilated data on all plots - - #grab bathy nearest enddate - bathygo = getDataTestBed(projectEnd, projectEnd) - bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects - print("Final Bathy Survey Date: ", bathyTransect['time']) - survey_h = bathyTransect['elevation'][200, zero_elev:] - savemat("./data/matlab_files/finalBathyTransect.mat", bathyTransect) - - obs_indices_h = np.unique(obs_indices_h) - obs_indices_v = np.unique(obs_indices_v) - print("Data assimilated at crosshore locations:") - print(obs_indices_h*(X[-1]/len(h))) - print(obs_indices_v*(X[-1]/len(h))) - Q = np.squeeze(np.transpose(Q)) - fig = plt.figure(figsize=(6, 9)) - grid = gridspec.GridSpec(2, 2, figure=fig) - ax0 = fig.add_subplot(grid[0, 0]) - ax0.set_title("Prior") - ax0.set_ylim(ymax=0, ymin=-11) - ax0.set_xlabel("Crosshore distance (m)") - ax0.set_ylabel("Depth") - ax0.set_xlim(xmax=1000) - ax0.plot(X, h, color='black') - for obs_loc in obs_indices_h: - ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - for obs_loc in obs_indices_v: - ax0.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - - ax1 = fig.add_subplot(grid[0, 1]) - ax1.set_title("Posterior") - ax1.set_xlabel("Crosshore distance (m)") - ax1.set_ylabel("Depth") - ax1.set_ylim(ymax=0, ymin=-11) - ax1.set_xlim(xmax=1000) - - ax1.errorbar(X, -posterior['h'], yerr=np.squeeze(posterior['hErr']), color='black', errorevery=5, capsize=2, ecolor='pink') - #ax1.plot(X, -posterior['h'], color='black') - - for obs_loc in obs_indices_h: - ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - for obs_loc in obs_indices_v: - ax1.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - - ax2 = fig.add_subplot(grid[1, 0]) - ax2.set_title("prior - posterior") - ax2.scatter(X, -(h+posterior['h']), color="black") - ax2.set_xlabel("Crosshore distance (m)") - ax2.set_ylabel("Elevation change (m)") - - for obs_loc in obs_indices_h: - ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - temp = obs_loc - ax2.axvline(x=temp * (X[-1] / len(h)), color='r', linestyle='--', label="wave heights") - for obs_loc in obs_indices_v: - ax2.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - temp = obs_loc - ax2.axvline(x=temp * (X[-1] / len(h)), color='orange', linestyle='--', label="currents") - ax2.legend() - - ax3 = fig.add_subplot(grid[1, 1]) - ax3.plot(X, survey_h, color='cyan', label="Survey") - ax3.plot(X, -posterior['h'], color='red', label="Posterior") - ax3.set_title("Posterior and Survey\n" + str(bathyTransect['time'])) - for obs_loc in obs_indices_h: - ax3.axvline(x=obs_loc*(X[-1]/len(h)), color='r', linestyle='--') - temp = obs_loc - ax3.axvline(x=temp * (X[-1] / len(h)), color='r', linestyle='--', label="wave heights") - for obs_loc in obs_indices_v: - ax3.axvline(x=obs_loc*(X[-1]/len(h)), color='orange', linestyle='--') - temp = obs_loc - ax3.axvline(x=temp * (X[-1] / len(h)), color='orange', linestyle='--', label="currents") - ax3.legend() - #plt.subplots_adjust(hspace=.25) - plt.show() - - -def find_nearest(array, value): - """This function will run CMS with any version prefix given start, end, and timestep. - - Args: - inputDict: a dictionary that is read from the input yaml - - Returns: - None - - """ - array = np.asarray(array) - idx = (np.abs(array - value)).argmin() - return array[idx], idx - - -def Master_1DVar_run(inputDict): - """This function will run 1DVar given start, end, and timestep found in input yaml - - Args: - inputDict: a dictionary that is read from the input yaml - - Returns: - None - - """ - ## unpack input Dictionary - version_prefix = inputDict['modelSettings']['version_prefix'] - endTime = inputDict['endTime'] - startTime = inputDict['startTime'] - simulationDuration = inputDict['simulationDuration'] - workingDir = os.path.join(inputDict['workingDirectory'], 'waveModels') - generateFlag = inputDict['generateFlag'] - runFlag = inputDict['runFlag'] - analyzeFlag = inputDict['analyzeFlag'] - pFlag = inputDict['pFlag'] - model = inputDict.get('model', '1DVarS') - modeldir = inputDict['modelExecutable'] - matlabfiledir = inputDict['mainDirectory'] - log = inputDict.get('logging', True) - - # __________________pre-processing checks________________________________ - fileHandling.checkVersionPrefix(model, inputDict) - # __________________input directories________________________________ - baseDir = os.getcwd() # location of working directory - # check executable - if inputDict['modelExecutable'].startswith(baseDir): # change to relative path - inputDict['modelExecutable'] = re.sub(baseDir, '', inputDict['modelExecutable']) - workingDirectory = os.path.join(workingDir, model.lower(), version_prefix) - inputDict['netCDFdir'] = os.path.join(inputDict['netCDFdir'], 'waveModels') - inputDict['path_prefix'] = workingDirectory - # ______________________ Logging ____________________________ - # auto generated Log file using start_end time? - # LOG_FILENAME = fileHandling.logFileLogic(workingDirectory, version_prefix, startTime, endTime, log=log) - # __________________get time list to loop over________________________________ - try: - projectEnd = DT.datetime.strptime(endTime, '%Y-%m-%dT%H:%M:%SZ') - projectStart = DT.datetime.strptime(startTime, '%Y-%m-%dT%H:%M:%SZ') - except TypeError: # if input date was parsed as datetime - projectEnd = endTime - projectStart = startTime - # This is the portion that creates a list of simulation end times - dt_DT = DT.timedelta(0, simulationDuration * 60 * 60) # timestep in datetime - # make List of Datestring items, for simulations - dateStartList = [projectStart] - dateStringList = [dateStartList[0].strftime("%Y-%m-%dT%H:%M:%SZ")] - for i in range(int(np.ceil((projectEnd - projectStart).total_seconds() / dt_DT.total_seconds())) - 1): - dateStartList.append(dateStartList[-1] + dt_DT) - dateStringList.append(dateStartList[-1].strftime("%Y-%m-%dT%H:%M:%SZ")) - # fileHandling.displayStartInfo(projectStart, projectEnd, version_prefix, LOG_FILENAME, model) - # ______________________________gather all data _____________________________ - if generateFlag == True: - - # initiliaze get observation for bathymetry - bathygo = getDataTestBed(projectStart, projectEnd) - bathyTransect = bathygo.getBathyIntegratedTransect(method=1) # grab bathymetry transects - print("Initial Bathy Survey Date: ", bathyTransect['time']) - - # initialize get observation for measurements - go = getObs(projectStart, projectEnd) - - # pull wind - pier_wind = None - while pier_wind == None: - pier_wind = go.getWind() - - # pull water level - tides_list = [] - tides_list = np.append(tides_list, go.getWL()['WL']) - - # pull currents - current_gauges = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m'] - current_obj = [] - for i in range(len(current_gauges)): - try: - current_obj = np.append(current_obj, go.getCurrents(gaugenumber=current_gauges[i], roundto=1)) - except: - current_obj = np.append(np.nan) - print("NetCDF DAP Error while grabbing current data from gauge: ", current_gauges[i]) - - # pull waves - #wave_gauges = ['adop-3.5m', 'awac-4.5m', 'awac-6m', 'awac-8m', 'awac-11m', 'xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array'] - wave_gauges = ['xp100m', 'xp125m', 'xp150m', 'xp200m', '8m-array', 'paros-200-940m', 'paros-250-940m', - 'paros-340x-940y-top', 'sig940-300'] - waves_obj = [] - for i in range(len(wave_gauges)): - print(wave_gauges[i]) - waves_obj = np.append(waves_obj, go.getWaveData(gaugenumber=wave_gauges[i], spec=False)) - print(len(waves_obj)) - #preprocess data - X, h, Q, tauw, wind_indice_skip, tide_indice_skip, delta_t, prior, obs_list, obs_indices_h, obs_indices_v, zero_elev = \ - preprocess_data(waves_obj, current_obj, tides_list, pier_wind, bathyTransect, dateStringList, simulationDuration) - - obs_dict = {} - obs_dict["struct"] = obs_list - savemat("./data/matlab_files/initialprior.mat", prior) - savemat("./data/matlab_files/obslist.mat", obs_dict) - - # ________________________________________________ RUN LOOP ________________________________________________ - for element, time in enumerate(dateStringList): - try: - print('-------------------------------Beginning Simulation {}-------------------------------'.format( - DT.datetime.now())) - - if runFlag == True: # run model - os.chdir(modeldir) # changing locations to where the model will be ran from for octave - print('Running {} Simulation'.format(model.upper())) - dt = DT.datetime.now() - - # set offshore wave conditions used in prior based on new timestop of obs - # grab measurements exactly on times in dateStringList with element*simulationDuration, - # since waves are taken every hour - prior = set_offshore_conditions(prior, waves_obj, element*simulationDuration) - prior['time'] = dateStringList[element] - #grab obs data for current timestep - obs = obs_list[element] - print("Assimilation no. " + str(element) + "/" + str(len(dateStringList))) - print("Assimilating Date: ", dateStringList[element]) - print("Wave heights (m) to assimilate: ", obs['Hd']) - print("Wave height indices: ", obs['Hind']) - print("Currents (m/s) to assimilate: ", obs['vd']) - print("Current indices: ", obs['vind']) - - offshore_indice = int(obs['vind'][-1]) - print(offshore_indice) - if offshore_indice < 239: - print("No offshore forcing present in current measurements for this timestep -- skipping assimilation") - delta_t = waves_obj[-1]['time'][int(element + 2*simulationDuration)] - waves_obj[-1]['time'][element] - delta_t = np.abs(delta_t.seconds) - prior, Q = calculate_Ch(prior, waves_obj[-1], X, delta_t, Q) - continue - print("Tauw to assimilate: ", obs['tauw']) - print("Tide: ", obs['tide']) - - # run 1DVar model with prior and observation input - # set nout if desired to obtain diagnostics and representer matrices. - # save matfile of this particular prior and obs - """os.chdir(matlabfiledir) - savemat("./data/matlab_files/prior_" + str(time[:13]) + ".mat", prior) - savemat("./data/matlab_files/obs_" + str(time[:13]) + ".mat", obs)""" - obs['H']['d'] = obs['Hd'] - obs['H']['ind'] = obs['Hind'] - obs['H']['e'] = obs['He'] - obs['v']['d'] = obs['vd'] - obs['v']['ind'] = obs['vind'] - obs['v']['e'] = obs['ve'] - posterior = octave.feval(modeldir + '/assim_1dh.m', prior, obs, delta_t, 1, nout=1)#diagnostics, representers = \ - print('Simulation took %s ' % (DT.datetime.now() - dt)) - os.chdir(matlabfiledir) - # save matfile of the posterior result - pathBase = 'D:/1DVarS_CMTB/data/netCDF_files/' - write_netcdf(dateStringList[element], posterior, obs, pathBase) - # make the output of the model the prior for the next timestep - prior = posterior - - # find delta_t for the next timestep - try: - delta_t = waves_obj[-1]['time'][int(element+simulationDuration)] - waves_obj[-1]['time'][element] - delta_t = np.abs(delta_t.seconds) - except: - pass - - print('-------------------------------SUCCESS-----------------------------------------') - - except Exception as e: - print('<< ERROR >> HAPPENED IN THIS TIME STEP\n{}'.format(e)) - logging.exception('\nERROR FOUND @ {}\n'.format(time), exc_info=True) - os.chdir(modeldir) - - plot_1DVar(X, h, Q, posterior, obs_indices_h, obs_indices_v, projectEnd, zero_elev) - - -if __name__ == "__main__": - model = '1DVarS' - opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) - print('___________________________________\n___________________________________\n___________________' - '________________\n') - print('USACE FRF Coastal Model Test Bed : {}'.format(model)) - - """nctest = netCDF4.Dataset('./data/netCDF_files/cmtb_morph_1dvar_20210901T000000Z.nc') - print(nctest.variables.keys()) - print(nctest.variables['h'][:])""" - args = ['./yaml_files/TestBedExampleInputs/1DVarS_Input_example.yml'] - try: - # assume the user gave the path - yamlLoc = args[0] - if os.path.exists('.cmtbSettings'): - with open('.cmtbSettings', 'r') as fid: - a = yaml.safe_load(fid) - with open(os.path.join(yamlLoc), 'r') as f: - inputDict = yaml.safe_load(f) - inputDict.update(a) - - except: - raise IOError( - 'Input YAML file required. See yaml_files/TestBedExampleInputs/{}_Input_example for example yaml file.'.format( - model)) - - Master_1DVar_run(inputDict=inputDict) diff --git a/altimeter_pull.py b/altimeter_pull.py deleted file mode 100644 index 6dac4bb..0000000 --- a/altimeter_pull.py +++ /dev/null @@ -1,21 +0,0 @@ -import numpy as np -import os, yaml, datetime -from getdatatestbed.getDataFRF import getObs, getDataTestBed - -startTime = datetime.datetime(2015, 1, 1, 0, 0, 0) -endTime = datetime.datetime(2021, 10, 1, 0, 0, 0) - -go = getObs(startTime, endTime) - -print(go.d1) -print(go.d2) -altAll = {} -altimeterlist = ['Alt769-150', 'Alt769-200', 'Alt769-250', 'Alt769-300', 'Alt769-350', - 'Alt861-150', 'Alt861-200', 'Alt861-250', 'Alt861-300', 'Alt861-350', - 'Alt940-150', 'Alt940-200', 'Alt940-250', 'Alt940-300', 'Alt940-340'] - -for altimeter in altimeterlist: - print(altimeter) - altAll[str(altimeter)] = go.getALT(gaugeName=altimeter) - -np.save('./data/altimeter2015-2021.npy', altAll) \ No newline at end of file diff --git a/bin/1DVarS/assim_1dh.m b/bin/1DVarS/assim_1dh.m deleted file mode 100644 index af82a67..0000000 --- a/bin/1DVarS/assim_1dh.m +++ /dev/null @@ -1,603 +0,0 @@ -function [posterior,forecast,diagnostics,representers]=assim_1dh(prior,obs,dt,verb) -% -% [posterior,forecast,diagnostics]=assim_1dh(prior,obs,dt) -% -% Assimilate data into 1DH wave and current model. -% -% INPUTS: -% -% prior.x = model grid -% prior.h = bathymetry relative to a constant vertical datum -% prior.theta0, prior.H0 = boundary conditions -% prior.ka_drag = bed roughness -% prior.tauw = wind stress in m2/s2 units (default = 0 if not included) -% -% prior.Ch, prior.Ctheta0, prior.CH0, prior.Cka = covariance matrices for h, -% theta0, H0, ka_drag -% -% prior.sedmodel = string to indicate which sediment transport model is used -% to forecast the bathymetry to time t+dt. At time of -% writing, the only supported model is 'persistence'. More -% models are planned for future versions. -% -% prior.sedparams = list of sediment transport parameters required by the -% requested sedmodel. TODO, this list needs to be -% documented for each sedmodel. -% -% CASE-1: sedmodel='persistence' -% -% This is a persistence model for h, and covariance is updated using the -% "process error" defined by Holman et al. (2013) eqn. (9). See their -% paper for definitions of required input fields listed below; the -% exception is the parameter 'Lx' which provides a finite spatial -% decorrelation length for the process error. -% -% prior.sedparams.Cq (recommended 0.067 m2/day) -% prior.sedparams.x0 (recommended 50 m) -% prior.sedparams.sigma_x (recommended 150 m) -% prior.sedparams.Lx (recommended 25 m) -% -% obs.H.ind = model indeces for rms wave height observations -% obs.H.d = rms wave height data -% obs.H.e = error stdev for rms wave height -% obs.v.* = as in obs.H, but for longshore current -% obs.tide = tidal elevation in same vertical datum as h -% -% NOTE, if any of obs.* are missing or empty, they will be ignored. The -% only required field is obs.tide. -% -% dt = time in days for which to provide a bathymetry forecast. Set dt=0 to -% disable forecasts. -% -% OUTPUTS: -% -% posterior = struct with same fields as prior but updated by assimilation. -% -% forecast = struct that has similar fields as prior and posterior, but for -% future time t+dt. These can be used in a subsequent -% assimilation cycle at time t+dt. Importantly, h and Ch in the -% forecast are calculated using a sediment transport model, which -% can be selected using the input prior.sedmodel. -% -% NOTE, setting dt=0 will disable the forecast step, in which case -% forecast=[]. This is recommended in cases where the forecast is not -% useful, since calculating the forecast can take a considerable amount -% of time depending on the sediment transport model being used. -% -% diagnostics = struct with diagnostic fields for analyzing the update. See -% comments near end of this code for descriptions -% -addpath './bin/1DVarS/waveModel/'; -warning('off','all'); -if(~exist('verb')) - verb=1; -end - -hmin=.2; -nitermax=50; - -% unpack some variables -Ch=prior.Ch; -CH0=prior.CH0; -Ctheta0=prior.Ctheta0; -Cka=prior.Cka; -x=prior.x; -nx=length(x); -nt=length(prior); - -% if any obs types missing, set to empty -fld='Hvh'; -noobs.ind=[]; -noobs.d=[]; -noobs.e=[]; -for i=1:length(fld) - if(~isfield(obs,fld(i))) - obs=setfield(obs,fld(i),noobs); - end -end - -% Adjust prior to add tide. This will be reversed at the end of this -% script, such that during assimilation h=depth, but input/output to the -% script is always h=navd88 -if(obs.tide<=-999) - warning('got -999 (nodata) for tide, setting to zero instead') - obs.tide=0; -end -prior.h=prior.h+obs.tide; -prior.h(prior.h800m). This Fy -% is then assigned to tauw=Fy, replacing the input wind stress. It should -% be noted this total force Fy includes other contributions other than wind -% stress (dp/dy); so assigning it to tauw is an abuse of notation, but -% avoids the need for other coding changes such as adding a separate dp/dy -% term to waveModel.m. -imin=min(find(x>800)); -ind=find(obs.v.ind>imin); -if(isempty(ind)) - prior.tauw = obs.tauw; % default: if vshelf observations are unavailable, just use wind stress -else - vshelf = obs.v.d(ind); - h = interp1(x,prior.h,x(obs.v.ind(ind))); - g=9.8126; - for i=1:length(ind) - k(i)=fzero(@(k)prior.sigma^2-g*k.*tanh(k.*h(i)),prior.sigma./sqrt(g*h(i)),optimset('Display','off')); - end - a=1.16; % empirical constant - Cd=0.015*(prior.ka_drag./h).^(1/3); - urms=1.416*prior.H0.*prior.sigma./(4*sinh(k.*h)); - % v2 = sqrt( (a*Cd.*urms).^4 + 4*(Cd.*Fy).^2 )./(2*Cd.^2) - (a*urms).^2/2; % from waveModel.m - Fy = sqrt( ( ( ( vshelf.^2 + (a*urms).^2/2 ).*(2*Cd.^2) ).^2 - (a*Cd.*urms).^4 )./(4*Cd.^2) ); - Fy = -abs(Fy).*sign(mean(vshelf)); - prior.tauw = mean(Fy); % override user-input tauw with inverted version - clear h k a Cd urms - ind=setdiff(1:length(obs.v.ind),ind); % optional: toss out obs used for calculating Fy above - for fld={'ind','d','e'} - fld=cell2mat(fld); - this=getfield(obs.v,fld); - obs.v=setfield(obs.v,fld,this(ind)); - end -end - -% initialize prior model state using NL model -if(verb) - disp('running prior forward model...') -end -prior=waveModel(x,prior.H0,prior.theta0,prior); -if(verb) - disp('...done') -end - -% outer loop -eps=nan; -for n=1:nitermax - disp(['iteration ' num2str(n) ', itermax = ' num2str(nitermax) ', eps = ' num2str(eps)]) - - % define background (variable name 'bkgd') and predicted ('pred') state - % vectors for this outer loop iteration. Note, earlier versions of this - % code used the variable name 'fcst' in place of 'pred'. This was changed - % to avoid confusion with the "forecasted" bathymetry at time t+dt (which - % was added to the code later). - if(n==1) - if(~exist('bkgd')) - bkgd=prior; - else - disp('using provided bkgd state') - end - pred=prior; - else - bkgd=posterior; - pred=prior; - tl_h=pred.h-bkgd.h; - tl_H0=pred.H0-bkgd.H0; - tl_theta0=pred.theta0-bkgd.theta0; - tl_ka_drag=pred.ka_drag-bkgd.ka_drag; - [tl_H,tl_theta,tl_v]=tl_waveModel(x,tl_h,tl_H0,tl_theta0,tl_ka_drag,bkgd); - pred.H=bkgd.H+tl_H; - pred.theta=bkgd.theta+tl_theta; - pred.v=bkgd.v+tl_v; - end - - % initialize representer sub-matrices - % - % LEGEND: - % - % R_XY = sensitivity of observation type Y, to delta-perturbations of - % observation X. These are sub-blocks of L*M*C*M'*L' (note, C is the - % prior covariance). - % - % r_X = sensitivity of model input vector phi = [h; H0; theta0; ka_drag]) - % to delta-perturbations of observation X. These are rows of C*M'*L'. - % - % r_XY = sensitivity of model variable Y to delta-perturbations of - % observation X. This is used for diagnostic purposes only, not for - % assimilation, and is output in struct 'rdiag'. These are just like r_X - % (above) but are for variables that aren't treated as model inputs - % (v,H,k,etc.). - % - % ad_X_Y = sensitivity of model input variable Y (one of the phi - % variables) to delta-perturbations of observation X. These are direct - % outputs of the adjoint model, i.e. M'*L', with no smoothing by the prior - % covariance. Stored for diagnostic purposes only. - % - clear R_* r_* rep_* - r_H=zeros(nx+3,length(obs.H.ind)); - r_v=zeros(nx+3,length(obs.v.ind)); - r_h=zeros(nx+3,length(obs.h.ind)); - ad_H_h =zeros(length(obs.H.ind),nx); - ad_H_H0 =zeros(length(obs.H.ind),1); - ad_H_theta0 =zeros(length(obs.H.ind),1); - ad_H_ka_drag=zeros(length(obs.H.ind),1); - ad_v_h =zeros(length(obs.v.ind),nx); - ad_v_H0 =zeros(length(obs.v.ind),1); - ad_v_theta0 =zeros(length(obs.v.ind),1); - ad_v_ka_drag=zeros(length(obs.v.ind),1); - R_HH=zeros(length(obs.H.ind),length(obs.H.ind)); - R_Hv=zeros(length(obs.H.ind),length(obs.v.ind)); - R_Hh=zeros(length(obs.H.ind),length(obs.h.ind)); - R_vH=zeros(length(obs.v.ind),length(obs.H.ind)); - R_vv=zeros(length(obs.v.ind),length(obs.v.ind)); - R_vh=zeros(length(obs.v.ind),length(obs.h.ind)); - R_hH=zeros(length(obs.h.ind),length(obs.H.ind)); - R_hv=zeros(length(obs.h.ind),length(obs.v.ind)); - R_hh=zeros(length(obs.h.ind),length(obs.h.ind)); - r_HH=zeros(length(obs.H.ind),nx); - r_Hv=zeros(length(obs.H.ind),nx); - r_Hh=zeros(length(obs.H.ind),nx); - r_vH=zeros(length(obs.v.ind),nx); - r_vv=zeros(length(obs.v.ind),nx); - r_vh=zeros(length(obs.v.ind),nx); - r_hH=zeros(length(obs.h.ind),nx); - r_hv=zeros(length(obs.h.ind),nx); - r_hh=zeros(length(obs.h.ind),nx); - - % compute representers for wave height: apply delta-perturbations to - % observations of type X, to compute (a) sensitivity of model inputs psi - % (r_* matrices), and (b) sensitivity of observations of type Y (R_* - % matrices) - for i=1:length(obs.H.ind) - comb=zeros(nx,1); - comb(obs.H.ind(i))=1; % data functional (delta-fn, aka identity matrix) - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - r_H(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' - ad_H_h(i,:)=ad_h; - ad_H_H0(i,:)=ad_H0; - ad_H_theta0(i,:)=ad_theta0; - ad_H_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - tl_h=Ch*ad_h; - r_HH(i,:)=tl_H; - r_Hv(i,:)=tl_v; - r_Hh(i,:)=Ch*ad_h; - R_HH(i,:)=tl_H(obs.H.ind); - R_Hv(i,:)=tl_v(obs.v.ind); - R_Hh(i,:)=tl_h(obs.h.ind); - end - - % repeat to compute representers for longshore current (v) - for i=1:length(obs.v.ind) - comb=zeros(nx,1); - comb(obs.v.ind(i))=1; % data functional (delta-fn, aka identity matrix) - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - r_v(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' - ad_v_h(i,:)=ad_h; - ad_v_H0(i,:)=ad_H0; - ad_v_theta0(i,:)=ad_theta0; - ad_v_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - tl_h=Ch*ad_h; - r_vH(i,:)=tl_H; - r_vv(i,:)=tl_v; - r_vh(i,:)=Ch*ad_h; - R_vH(i,:)=tl_H(obs.H.ind); - R_vv(i,:)=tl_v(obs.v.ind); - R_vh(i,:)=tl_h(obs.h.ind); - end - - % repeat to compute representers for water depth (h) - for i=1:length(obs.h.ind) - comb=zeros(nx,1); - comb(obs.h.ind(i))=1; % data functional (delta-fn, aka identity matrix) - ad_h=comb; - ad_H0=0; - ad_theta0=0; - ad_ka_drag=0; - r_h(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' - ad_h_h(i,:)=ad_h; - ad_h_H0(i,:)=ad_H0; - ad_h_theta0(i,:)=ad_theta0; - ad_h_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - tl_h=Ch*ad_h; - r_hH(i,:)=tl_H; - r_hv(i,:)=tl_v; - r_hh(i,:)=Ch*ad_h; - R_hH(i,:)=tl_H(obs.H.ind); - R_hv(i,:)=tl_v(obs.v.ind); - R_hh(i,:)=tl_h(obs.h.ind); - end - - % assemble matrices for updating - Cd=diag([obs.H.e(:); - obs.v.e(:); - obs.h.e(:)].^2); - CMt=[r_H r_v r_h]; - d=[obs.H.d(:); - obs.v.d(:) - obs.h.d(:)]; - Lu=[pred.H(obs.H.ind); - pred.v(obs.v.ind) - pred.h(obs.h.ind)]; - R=[R_HH R_Hv R_Hh; - R_vH R_vv R_vh; - R_hH R_hv R_hh]; - obstype=[repmat('H',[length(obs.H.d) 1]); - repmat('v',[length(obs.v.d) 1]); - repmat('h',[length(obs.h.d) 1])]; - - % compute the update - hedge=1; %tanh(n/5); % reduce magnitude of update for 1st few iterations - update=hedge*CMt*inv(R+Cd)*(d-Lu); - if(n>1) - update=.5*(u0+update); - end - u0=update; - posterior=prior; - posterior.H0=prior.H0+update(nx+1); - posterior.theta0=prior.theta0+update(nx+2); - posterior.ka_drag=max(1e-4,prior.ka_drag+update(nx+3)); - posterior.h = prior.h + update(1:nx); - posterior.h(posterior.h0) - % eps=eps+(posterior.H0-bkgd.H0)^2/CH0; - %end - %if(Ctheta0>0) - % eps=eps+(posterior.theta0-bkgd.theta0)^2/Ctheta0; - %end - %if(Cka>0) - % eps=eps+(posterior.ka_drag-bkgd.ka_drag)^2/Cka; - %end - if(eps<1e-4) - disp(eps) - break; - end - -end % outer loop iterations - -% Remove tide from outputs, such that h is re navd88 instead of TWL -prior.h=prior.h-obs.tide; -posterior.h=posterior.h-obs.tide; - -%--------------------------------------- -% Forecast step: If requested, predict the bathymetry and its covariance at -% time t+dt. -%--------------------------------------- - -if(~isempty(dt) & dt>0) - if(~isfield(prior,'sedmodel')) - error('prior.sedmodel is required as input, see header of assim_1dh.m for details') - end - - % For the forecast step, we just want to predict bathymetry. Other - % non-bathymetry variables are not updated as part of the forecast step, - % and so they and their covariances remain constant. Doing this - % bookkeeping here will make it easier to re-use the forecast as the prior - % for time t+dt. - forecast=struct; - forecast.x =prior.x ; - forecast.ka_drag =prior.ka_drag ; - forecast.sedmodel=prior.sedmodel; - forecast.Ctheta0 =prior.Ctheta0 ; - forecast.CH0 =prior.CH0 ; - forecast.Cka =prior.Cka ; - - % Case-1: 'persistence' - if(strcmp(prior.sedmodel,'persistence')) - forecast.hp = posterior.h; - Q = ( prior.sedparams.Cq * (sqrt(2)*posterior.H0)^2 ) ... - * exp(-( (x-double(prior.sedparams.x0)) / double(prior.sedparams.sigma_x )).^2) ... - * dt; - S = diag(sqrt(Q)); - xx = meshgrid(x); - N = exp((-(xx - xx').^2)/(2*double(prior.sedparams.Lx)^2)); - forecast.Ch = posterior.Ch + S*N*S; - - % TODO: other sediment transport codes will go here... - % elseif(strcmp(prior.sedmodel,'vanderA')) - % .... - % elseif(strcmp(prior.sedmodel,'dubarbier')) - % .... - % elseif(strcmp(prior.sedmodel,'soulsbyVanRijn')) - % .... - % - - else - error(['The requested sediment transport model is not supported (prior.sedmodel = ' prior.sedmodel ')']) - end - -end - -%--------------------------------------- -% reformat output variables in diagnostics struct, for convenience -%--------------------------------------- -clear diagnostics - -diagnostics.niter=n; -diagnostics.eps=eps; - -% terms in update equation -diagnostics.CMt=CMt; -diagnostics.Ro =R ; -diagnostics.Cd =Cd ; -diagnostics.d =d ; -diagnostics.Lu =Lu ; -diagnostics.obstype=obstype; -diagnostics.bkgd=bkgd; -diagnostics.pred=pred; - -% representer outputs: r.X_Y refers to sensitivity of model input variable Y -% to delta-perturbations of obs type X -diagnostics.r.H_h =r_H(1:nx,:); -diagnostics.r.H_H0 =r_H(nx+1,:); -diagnostics.r.H_theta0=r_H(nx+2,:); -diagnostics.r.H_kadrag=r_H(nx+3,:); -diagnostics.r.v_h =r_v(1:nx,:); -diagnostics.r.v_H0 =r_v(nx+1,:); -diagnostics.r.v_theta0=r_v(nx+2,:); -diagnostics.r.v_kadrag=r_v(nx+3,:); - -% R-matrix outputs: R.X_Y refers to sensitivity of obs type Y to -% delta-perturbations of obs type X -diagnostics.R.H_H=R_HH; -diagnostics.R.H_v=R_Hv; -diagnostics.R.v_H=R_vH; -diagnostics.R.v_v=R_vv; - -% additional diagnostic representers: rdiag.X_Y refers to sensitivity of -% model variable Y to delta-perturbations of obs type X -diagnostics.rdiag.H_H=r_HH; -diagnostics.rdiag.H_v=r_Hv; -diagnostics.rdiag.v_H=r_vH; -diagnostics.rdiag.v_v=r_vv; - -% adjoint outputs: ad.X_Y refers to sensitivity of model input Y to -% delta-perturbations of obs type X -diagnostics.ad.H_h =ad_H_h; -diagnostics.ad.H_H0 =ad_H_H0; -diagnostics.ad.H_theta0 =ad_H_theta0; -diagnostics.ad.H_ka_drag=ad_H_ka_drag; -diagnostics.ad.v_h =ad_v_h; -diagnostics.ad.v_H0 =ad_v_H0; -diagnostics.ad.v_theta0 =ad_v_theta0; -diagnostics.ad.v_ka_drag=ad_v_ka_drag; - -%--------------------------------------- -% OPTIONAL: If diagnostics output was requested, repeat the representer -% calculations for both variables, but this time do ALL the gridpoints. -% These can be used for prior and posterior covariances for v, H. This is -% not used for the outer loop updates, so only need to do it once at the end -% -% NOTE: This is in the process of being split off into its own code, -% assim_1dh_representers.m -%--------------------------------------- - -if(nargout>2) - - disp('Calculating full representer matrix as requested (nargout>2)') - - adj_H_h =zeros(nx,nx); - adj_H_H0 =zeros(nx,1); - adj_H_theta0 =zeros(nx,1); - adj_H_ka_drag=zeros(nx,1); - adj_v_h =zeros(nx,nx); - adj_v_H0 =zeros(nx,1); - adj_v_theta0 =zeros(nx,1); - adj_v_ka_drag=zeros(nx,1); - rep_HH=zeros(nx); - rep_Hv=zeros(nx); - rep_Hh=zeros(nx); - rep_vH=zeros(nx); - rep_vv=zeros(nx); - rep_vh=zeros(nx); - rep_HH_post=zeros(nx); - rep_Hv_post=zeros(nx); - rep_Hh_post=zeros(nx); - rep_vH_post=zeros(nx); - rep_vv_post=zeros(nx); - rep_vh_post=zeros(nx); - - % wave height representers - for i=1:nx - comb=zeros(nx,1); - comb(i)=1; - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - adj_H_h(i,:)=ad_h; - adj_H_H0(i,:)=ad_H0; - adj_H_theta0(i,:)=ad_theta0; - adj_H_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - rep_HH(i,:)=tl_H; - rep_Hv(i,:)=tl_v; - rep_Hh(i,:)=Ch*ad_h; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); - rep_HH_post(i,:)=tl_H; - rep_Hv_post(i,:)=tl_v; - rep_Hh_post(i,:)=posterior.Ch*ad_h; - end - - % longshore current representers - for i=1:nx - comb=zeros(nx,1); - comb(i)=1; % data functional (delta-fn, aka identity matrix) - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - adj_v_h(i,:)=ad_h; - adj_v_H0(i,:)=ad_H0; - adj_v_theta0(i,:)=ad_theta0; - adj_v_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - rep_vH(i,:)=tl_H; - rep_vv(i,:)=tl_v; - rep_vh(i,:)=Ch*ad_h; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); - rep_vH_post(i,:)=tl_H; - rep_vv_post(i,:)=tl_v; - rep_vh_post(i,:)=posterior.Ch*ad_h; - end - - representers.adj_H_h =adj_H_h ; - representers.adj_H_H0 =adj_H_H0 ; - representers.adj_H_theta0 =adj_H_theta0 ; - representers.adj_H_ka_drag=adj_H_ka_drag; - representers.adj_v_h =adj_v_h ; - representers.adj_v_H0 =adj_v_H0 ; - representers.adj_v_theta0 =adj_v_theta0 ; - representers.adj_v_ka_drag=adj_v_ka_drag; - representers.H_H=rep_HH; - representers.H_v=rep_Hv; - representers.H_h=rep_Hh; - representers.v_H=rep_vH; - representers.v_v=rep_vv; - representers.v_h=rep_vh; - representers.H_H_post=rep_HH_post; - representers.H_v_post=rep_Hv_post; - representers.H_h_post=rep_Hh_post; - representers.v_H_post=rep_vH_post; - representers.v_v_post=rep_vv_post; - representers.v_h_post=rep_vh_post; - -end diff --git a/bin/1DVarS/old/assim_1dh.m b/bin/1DVarS/old/assim_1dh.m deleted file mode 100644 index 5c358bf..0000000 --- a/bin/1DVarS/old/assim_1dh.m +++ /dev/null @@ -1,409 +0,0 @@ -function [posterior,diagnostics,representers]=assim_1dh(prior,obs,verb,bkgd) -% -% [posterior,diagnostics]=assim_1dh(prior,obs) -% -% Assimilate data into 1DH wave and current model. -% -% INPUTS: -% -% prior.x = model grid -% prior.h = bathymetry -% prior.theta0, prior.H0 = boundary conditions -% prior.ka_drag = bed roughness -% prior.tauw = wind stress in m2/s2 units (default = 0 if not included) -% -% prior.Ch, prior.Ctheta0, prior.CH0, prior.Cka = covariance matrices for h, -% theta0, H0, ka_drag -% -% obs.H.ind = model indeces for rms wave height observations -% obs.H.d = rms wave height data -% obs.H.e = error stdev for rms wave height -% obs.v.* = as in obs.H, but for longshore current -% -% NOTE, if any of obs.* are missing or empty, they will be ignored -% -% OUTPUTS: -% -% posterior = struct with same fields as prior but updated by assimilation -% -% diagnostics = struct with diagnostic fields for analyzing the update. See -% comments near end of this code for descriptions -% -addpath 'C:/cmtb/bin/1DVar/waveModel' - -if(~exist('verb')) - verb=1; -end - -hmin=.2; -nitermax=10; - -% unpack some variables -Ch=prior.Ch; -CH0=prior.CH0; -Ctheta0=prior.Ctheta0; -Cka=prior.Cka; -x=prior.x; -nx=length(x); -nt=length(prior); - -% if any obs types missing, set to empty -fld='Hv'; -noobs.ind=[]; -noobs.d=[]; -noobs.e=[]; -for i=1:length(fld) - if(~isfield(obs,fld(i))) - obs=setfield(obs,fld(i),noobs); - end -end - -% initialize prior model state using NL model -if(verb) - disp('running prior forward model...') -end -prior=waveModel(x,prior.H0,prior.theta0,prior); -if(verb) - disp('...done') -end - -% outer loop -eps=nan; -for n=1:nitermax - disp(['iteration ' num2str(n) ', itermax = ' num2str(nitermax) ', eps = ' num2str(eps)]) - % keyboard - - % define background and forecast states for this outer loop iteration - if(n==1) - if(~exist('bkgd')) - bkgd=prior; - else - disp('using provided bkgd state') - end - fcst=prior; - else - bkgd=posterior; - fcst=prior; - tl_h=fcst.h-bkgd.h; - tl_H0=fcst.H0-bkgd.H0; - tl_theta0=fcst.theta0-bkgd.theta0; - tl_ka_drag=fcst.ka_drag-bkgd.ka_drag; - [tl_H,tl_theta,tl_v]=tl_waveModel(x,tl_h,tl_H0,tl_theta0,tl_ka_drag,bkgd); - fcst.H=bkgd.H+tl_H; - fcst.theta=bkgd.theta+tl_theta; - fcst.v=bkgd.v+tl_v; - end - - % initialize representer sub-matrices - % - % LEGEND: - % - % R_XY = sensitivity of observation type Y, to delta-perturbations of - % observation X. These are sub-blocks of L*M*C*M'*L' (note, C is the - % prior covariance). - % - % r_X = sensitivity of model input vector phi = [h; H0; theta0; ka_drag]) - % to delta-perturbations of observation X. These are rows of C*M'*L'. - % - % r_XY = sensitivity of model variable Y to delta-perturbations of - % observation X. This is used for diagnostic purposes only, not for - % assimilation, and is output in struct 'rdiag'. These are just like r_X - % (above) but are for variables that aren't treated as model inputs - % (v,H,k,etc.). - % - % ad_X_Y = sensitivity of model input variable Y (one of the phi - % variables) to delta-perturbations of observation X. These are direct - % outputs of the adjoint model, i.e. M'*L', with no smoothing by the prior - % covariance. Stored for diagnostic purposes only. - % - clear R_* r_* rep_* - r_H=zeros(nx+3,length(obs.H.ind)); - r_v=zeros(nx+3,length(obs.v.ind)); - ad_H_h =zeros(length(obs.H.ind),nx); - ad_H_H0 =zeros(length(obs.H.ind),1); - ad_H_theta0 =zeros(length(obs.H.ind),1); - ad_H_ka_drag=zeros(length(obs.H.ind),1); - ad_v_h =zeros(length(obs.v.ind),nx); - ad_v_H0 =zeros(length(obs.v.ind),1); - ad_v_theta0 =zeros(length(obs.v.ind),1); - ad_v_ka_drag=zeros(length(obs.v.ind),1); - R_HH=zeros(length(obs.H.ind),length(obs.H.ind)); - R_Hv=zeros(length(obs.H.ind),length(obs.v.ind)); - R_vH=zeros(length(obs.v.ind),length(obs.H.ind)); - R_vv=zeros(length(obs.v.ind),length(obs.v.ind)); - r_HH=zeros(length(obs.H.ind),nx); - r_Hv=zeros(length(obs.H.ind),nx); - r_vH=zeros(length(obs.v.ind),nx); - r_vv=zeros(length(obs.v.ind),nx); - - % compute representers for wave height: apply delta-perturbations to - % observations of type X, to compute (a) sensitivity of model inputs psi - % (r_* matrices), and (b) sensitivity of observations of type Y (R_* - % matrices) - for i=1:length(obs.H.ind) - comb=zeros(nx,1); - comb(obs.H.ind(i))=1; % data functional (delta-fn, aka identity matrix) - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - r_H(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' - ad_H_h(i,:)=ad_h; - ad_H_H0(i,:)=ad_H0; - ad_H_theta0(i,:)=ad_theta0; - ad_H_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - r_HH(i,:)=tl_H; - r_Hv(i,:)=tl_v; - r_Hh(i,:)=Ch*ad_h; - R_HH(i,:)=tl_H(obs.H.ind); - R_Hv(i,:)=tl_v(obs.v.ind); - end - - % repeat to compute representers for longshore current - for i=1:length(obs.v.ind) - comb=zeros(nx,1); - comb(obs.v.ind(i))=1; % data functional (delta-fn, aka identity matrix) - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - r_v(:,i)=[Ch*ad_h; CH0*ad_H0; Ctheta0*ad_theta0; Cka*ad_ka_drag]; % = C*M' - ad_v_h(i,:)=ad_h; - ad_v_H0(i,:)=ad_H0; - ad_v_theta0(i,:)=ad_theta0; - ad_v_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - r_vH(i,:)=tl_H; - r_vv(i,:)=tl_v; - r_vh(i,:)=Ch*ad_h; - R_vH(i,:)=tl_H(obs.H.ind); - R_vv(i,:)=tl_v(obs.v.ind); - end - - % assemble matrices for updating - Cd=diag([obs.H.e(:); - obs.v.e(:)].^2); - CMt=[r_H r_v]; - d=[obs.H.d(:); - obs.v.d(:)]; - Lu=[fcst.H(obs.H.ind); - fcst.v(obs.v.ind)]; - R=[R_HH R_Hv; - R_vH R_vv]; - obstype=[repmat('H',[length(obs.H.d) 1]); - repmat('v',[length(obs.v.d) 1])]; - - % compute the update - hedge=1; %tanh(n/5); % reduce magnitude of update for 1st few iterations - update=hedge*CMt*inv(R+Cd)*(d-Lu); - if(n>1) - update=.5*(u0+update); - end - u0=update; - posterior=prior; - posterior.H0=prior.H0+update(nx+1); - posterior.theta0=prior.theta0+update(nx+2); - posterior.ka_drag=max(1e-4,prior.ka_drag+update(nx+3)); - posterior.h = prior.h + update(1:nx); - posterior.h(posterior.h0) - eps=eps+(posterior.H0-bkgd.H0)^2/CH0; - end - if(Ctheta0>0) - eps=eps+(posterior.theta0-bkgd.theta0)^2/Ctheta0; - end - if(Cka>0) - eps=eps+(posterior.ka_drag-bkgd.ka_drag)^2/Cka; - end - if(eps<1e-4) - break; - end - -end % outer loop iterations - -% OPTIONAL: for diagnostics, repeat the representer calculations for both -% variables, but this time do ALL the gridpoints. These can be used for -% prior and posterior covariances for v, H. This is not used for the outer -% loop updates, so only need to do it once at the end -if(nargout>2) - disp('Calculating full representer matrix as requested (nargout>2)') - - adj_H_h =zeros(nx,nx); - adj_H_H0 =zeros(nx,1); - adj_H_theta0 =zeros(nx,1); - adj_H_ka_drag=zeros(nx,1); - adj_v_h =zeros(nx,nx); - adj_v_H0 =zeros(nx,1); - adj_v_theta0 =zeros(nx,1); - adj_v_ka_drag=zeros(nx,1); - rep_HH=zeros(nx); - rep_Hv=zeros(nx); - rep_Hh=zeros(nx); - rep_vH=zeros(nx); - rep_vv=zeros(nx); - rep_vh=zeros(nx); - rep_HH_post=zeros(nx); - rep_Hv_post=zeros(nx); - rep_Hh_post=zeros(nx); - rep_vH_post=zeros(nx); - rep_vv_post=zeros(nx); - rep_vh_post=zeros(nx); - - % wave height representers - for i=1:nx - comb=zeros(nx,1); - comb(i)=1; - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,comb,0*comb,0*comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - adj_H_h(i,:)=ad_h; - adj_H_H0(i,:)=ad_H0; - adj_H_theta0(i,:)=ad_theta0; - adj_H_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - rep_HH(i,:)=tl_H; - rep_Hv(i,:)=tl_v; - rep_Hh(i,:)=Ch*ad_h; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); - rep_HH_post(i,:)=tl_H; - rep_Hv_post(i,:)=tl_v; - rep_Hh_post(i,:)=posterior.Ch*ad_h; - end - - % longshore current representers - for i=1:nx - comb=zeros(nx,1); - comb(i)=1; % data functional (delta-fn, aka identity matrix) - [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,0*comb,0*comb,comb,0*comb,bkgd); % I*M', where M is the TL model and I is identity - adj_v_h(i,:)=ad_h; - adj_v_H0(i,:)=ad_H0; - adj_v_theta0(i,:)=ad_theta0; - adj_v_ka_drag(i,:)=ad_ka_drag; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,Ch*ad_h,CH0*ad_H0,Ctheta0*ad_theta0,Cka*ad_ka_drag,bkgd); - rep_vH(i,:)=tl_H; - rep_vv(i,:)=tl_v; - rep_vh(i,:)=Ch*ad_h; - [tl_H,tl_a,tl_v,tl_k]=tl_waveModel(x,posterior.Ch*ad_h,posterior.CH0*ad_H0,posterior.Ctheta0*ad_theta0,posterior.Cka*ad_ka_drag,bkgd); - rep_vH_post(i,:)=tl_H; - rep_vv_post(i,:)=tl_v; - rep_vh_post(i,:)=posterior.Ch*ad_h; - end - - representers.adj_H_h =adj_H_h ; - representers.adj_H_H0 =adj_H_H0 ; - representers.adj_H_theta0 =adj_H_theta0 ; - representers.adj_H_ka_drag=adj_H_ka_drag; - representers.adj_v_h =adj_v_h ; - representers.adj_v_H0 =adj_v_H0 ; - representers.adj_v_theta0 =adj_v_theta0 ; - representers.adj_v_ka_drag=adj_v_ka_drag; - representers.H_H=rep_HH; - representers.H_v=rep_Hv; - representers.H_h=rep_Hh; - representers.v_H=rep_vH; - representers.v_v=rep_vv; - representers.v_h=rep_vh; - representers.H_H_post=rep_HH_post; - representers.H_v_post=rep_Hv_post; - representers.H_h_post=rep_Hh_post; - representers.v_H_post=rep_vH_post; - representers.v_v_post=rep_vv_post; - representers.v_h_post=rep_vh_post; - -end - -%--------------------------------------- -% remaining code: reformat output variables in diagnostics struct, for -% convenience -%--------------------------------------- -clear diagnostics - -diagnostics.niter=n; -diagnostics.eps=eps; - -% terms in update equation -diagnostics.update.CMt=CMt; -diagnostics.update.R =R ; -diagnostics.update.Cd =Cd ; -diagnostics.update.d =d ; -diagnostics.update.Lu =Lu ; -diagnostics.update.obstype=obstype; -diagnostics.update.bkgd=bkgd; -diagnostics.update.fcst=fcst; - -% representer outputs: r.X_Y refers to sensitivity of model input variable Y -% to delta-perturbations of obs type X -diagnostics.r.H_h =r_H(1:nx,:); -diagnostics.r.H_H0 =r_H(nx+1,:); -diagnostics.r.H_theta0=r_H(nx+2,:); -diagnostics.r.H_kadrag=r_H(nx+3,:); -diagnostics.r.v_h =r_v(1:nx,:); -diagnostics.r.v_H0 =r_v(nx+1,:); -diagnostics.r.v_theta0=r_v(nx+2,:); -diagnostics.r.v_kadrag=r_v(nx+3,:); - -% R-matrix outputs: R.X_Y refers to sensitivity of obs type Y to -% delta-perturbations of obs type X -diagnostics.R.H_H=R_HH; -diagnostics.R.H_v=R_Hv; -diagnostics.R.v_H=R_vH; -diagnostics.R.v_v=R_vv; - -% additional diagnostic representers: rdiag.X_Y refers to sensitivity of -% model variable Y to delta-perturbations of obs type X -diagnostics.rdiag.H_H=r_HH; -diagnostics.rdiag.H_v=r_Hv; -diagnostics.rdiag.v_H=r_vH; -diagnostics.rdiag.v_v=r_vv; - -% adjoint outputs: ad.X_Y refers to sensitivity of model input Y to -% delta-perturbations of obs type X -diagnostics.ad.H_h =ad_H_h; -diagnostics.ad.H_H0 =ad_H_H0; -diagnostics.ad.H_theta0 =ad_H_theta0; -diagnostics.ad.H_ka_drag=ad_H_ka_drag; -diagnostics.ad.v_h =ad_v_h; -diagnostics.ad.v_H0 =ad_v_H0; -diagnostics.ad.v_theta0 =ad_v_theta0; -diagnostics.ad.v_ka_drag=ad_v_ka_drag; diff --git a/bin/1DVarS/old/waveModel/ad_symmetryCheck.m b/bin/1DVarS/old/waveModel/ad_symmetryCheck.m deleted file mode 100644 index 5aa8d74..0000000 --- a/bin/1DVarS/old/waveModel/ad_symmetryCheck.m +++ /dev/null @@ -1,36 +0,0 @@ -% -% tests adjoint code: F'*AD*TL*F should be symmetric, +'ve definite -% -clear - -% get NL solution (background) -x=[100:10:1000]'; -bkgd.h=.01*x; -bkgd.H0=1; -bkgd.theta0=deg2rad(10); -bkgd.sigma=2*pi/10; -bkgd.ka_drag=0.015; -nx=length(x); -bkgd=waveModel(x,bkgd.H0,bkgd.theta0,bkgd); - -% apply TL and ADJ models for n instances of random forcing F (actually, IC -% perturbations) -eps = 0.05; -n=10; -F = eps*rand(nx,n); -F(end,:)=F(1,:); -for i=1:n - % TL model: u=TL*F - [tl_H,tl_theta,tl_v,tl_k]=tl_waveModel(x,F(:,i),0,0,0,bkgd); - % ADJ model: g=ADJ*(TL*F) - g(:,i)=ad_waveModel(x,tl_H,tl_theta,tl_v,tl_k,bkgd); -end - -% test whether result makes sense -A = F'*g; - -(A-A')/sum(diag(A)) % should be zeros to within roundoff - -%pcolor(A) -format short g -eig(A) % should be +'ve diff --git a/bin/1DVarS/old/waveModel/ad_waveModel.m b/bin/1DVarS/old/waveModel/ad_waveModel.m deleted file mode 100644 index ca09ffe..0000000 --- a/bin/1DVarS/old/waveModel/ad_waveModel.m +++ /dev/null @@ -1,361 +0,0 @@ -function [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_k,bkgd) -% -% [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_eps_r,ad_k,bkgd) -% -% AD-code for tl_waveModel.m. Background state 'bkgd' can be a struct taken -% directly from output of waveModel.m, and 'tl_in' can be taken directly -% from output of tl_waveModel.m. -% -[g,alpha,beta,nu]=waveModelParams(); - -% grid -nx=length(x); -dx=diff(x(1:2)); - -% break out the bkgd vars -sigma=bkgd.sigma; -E =bkgd.E ; -Er =bkgd.Er ; -eps_b=bkgd.eps_b; -eps_r=bkgd.eps_r; -c =bkgd.c ; -cg =bkgd.cg ; -k =bkgd.k ; -h =bkgd.h ; -n =bkgd.n ; -theta=bkgd.theta; -sigma=bkgd.sigma; -x =bkgd.x ; -H =bkgd.H ; -dSxydx=bkgd.dSxydx; -Fy=bkgd.Fy; -v=bkgd.v; -H0=bkgd.H0; -theta0=bkgd.theta0; -gamma=bkgd.gamma; -Hm=bkgd.Hm; -Qb=bkgd.Qb; - -ka_drag=bkgd.ka_drag; - -refconst=sin(theta0)/c(nx); - -%----------------------------- -% begin adjoint code -%----------------------------- - -% initialize. All these variables are assumed to never be directly -% observed, else they would be provided as inputs -zz=zeros(nx,1); -ad_c=zz; -ad_Er=zz; -ad_E=zz; -ad_eps_b=zz; -ad_eps_r=zz; -ad_cg=zz; -ad_h=zz; -ad_n=zz; -% ad_k=zz; -ad_Qb=zz; -ad_Hm=zz; -ad_refconst=0; -ad_gamma=0; - -% bottom stress model following Ruessink et al. (2001), Feddersen et -% al. (2000) -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -urms=1.416*H*sigma./(4*sinh(k.*h)); -% B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); % old version of TL code, no mixing -B=a^2+(v./urms).^2; -dens = -urms.*Cd - v.^2.*Cd./urms./B; - -% mixing operator -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); - -% % OLD: incorrect version for with-mixing case -% if(nu>0) -% % v2: with mixing -% %3b tl_v = inv(diag((1+B)./v)+A)*tl_N; -% ad_N=inv(diag((1+B)./v)+A)'*ad_v; -% ad_v=0; -% %3a tl_N=tl_Fy./Fy - tl_Cd./Cd - tl_urms./urms.*(1-B); -% ad_Fy=ad_N./Fy; -% ad_Cd=-ad_N./Cd; -% ad_urms=-ad_N./urms.*(1-B); -% ad_N=0; -% NEW: corrected version for with-mixing case -if(nu>0) - % v2: with mixing - %3b tl_v = inv(diag(dens)+A)*tl_N; - ad_N=inv(diag(dens)+A)'*ad_v; -else - % v1: no mixing: - %3b tl_v=tl_N./dens; - ad_N=ad_v./dens; -end -ad_v=0; - -%3a tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; -ad_Fy=ad_N./sqrt(B); -ad_urms=ad_N.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)); -ad_Cd=ad_N.*v.*urms; -ad_N=0; - -%2 tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... -% -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); -ad_H = ad_H + 1.416*sigma*( ad_urms./(4*sinh(k.*h)) ); -ad_k = ad_k - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*h; -ad_h = ad_h - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*k; -ad_urms=0; - -% note below: ka_drag is scalar, so sum over gridpoints. Can see this by -% considering if the code was in a loop, then scalar ad_ka_drag would -% receive a contribution from each loop iteration -%1 tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); -ad_h = ad_h + 0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*ad_Cd); -ad_ka_drag = sum(0.015*(1/3)*(ka_drag./h).^(-2/3).*(ad_Cd./h)); % if correcting ka_drag -ad_Cd=0; - -% total force = radiation stress gradient + wind stress -% tl_Fy=tl_dSxydx; -ad_dSxydx=ad_Fy; -ad_Fy=0; - -% radiation stress gradient -if(beta>0) - % tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... - % +sin(theta)./c.^2.*eps_r.*tl_c ... - % -sin(theta)./c.*tl_eps_r; - ad_theta=ad_theta-cos(theta)./c.*eps_r.*ad_dSxydx; - ad_c=ad_c+sin(theta)./c.^2.*eps_r.*ad_dSxydx; - ad_eps_r=ad_eps_r-sin(theta)./c.*ad_dSxydx; - ad_dSxydx=0; -else - % tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... - % +sin(theta)./c.^2.*eps_b.*tl_c ... - % -sin(theta)./c.*tl_eps_b; - ad_theta=ad_theta-cos(theta)./c.*eps_b.*ad_dSxydx; - ad_c=ad_c+sin(theta)./c.^2.*eps_b.*ad_dSxydx; - ad_eps_b=ad_eps_b-sin(theta)./c.*ad_dSxydx; - ad_dSxydx=0; -end - -% stepping, explicit scheme -for i=1:(nx-1) - - % tl_H(i)=.5./sqrt(8/g*max(0,E(i)))*8/g.*tl_E(i); - if(E(i)==0) - ad_E(i)=0; - else - ad_E(i)=ad_E(i)+.5./sqrt(8/g*E(i))*8/g.*ad_H(i); - end - ad_H(i)=0; - - if(beta>0) - - % term 4 - nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); - nums2=dx*(eps_b(i+1)-eps_r(i+1)); - denoms=2*c(i)*cos(theta(i)); - %4 tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... - % - (nums1+nums2)/denoms^2*tl_denoms; - ad_nums1=ad_Er(i)/denoms; - ad_nums2=ad_Er(i)/denoms; - ad_denoms=-(nums1+nums2)/denoms^2*ad_Er(i); - ad_Er(i)=0; - %3 tl_denoms=2*tl_c(i)*cos(theta(i)) ... - % - 2*c(i)*sin(theta(i))*tl_theta(i); - ad_c(i)=ad_c(i)+2*cos(theta(i))*ad_denoms; - ad_theta(i)=ad_theta(i)-2*c(i)*sin(theta(i))*ad_denoms; - ad_denoms=0; - %2 tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); - ad_eps_b(i+1)=ad_eps_b(i+1)+dx*ad_nums2; - ad_eps_r(i+1)=ad_eps_r(i+1)-dx*ad_nums2; - ad_nums2=0; - %1 tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... - % + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... - % - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); - ad_Er(i+1)=ad_Er(i+1)+2*c(i+1)*cos(theta(i+1))*ad_nums1; - ad_c(i+1)=ad_c(i+1)+2*Er(i+1)*cos(theta(i+1))*ad_nums1; - ad_theta(i+1)=ad_theta(i+1)-2*Er(i+1)*c(i+1)*sin(theta(i+1))*ad_nums1; - ad_nums1=0; - - % term 3 - c1=2*g*sin(beta)/c(i+1); - c2=-2*g*sin(beta)*Er(i+1)/c(i+1)^2; - % tl_eps_r(i+1) = c1 * tl_Er(i+1) ... - % + c2 * tl_c(i+1); - ad_Er(i+1)=ad_Er(i+1)+c1*ad_eps_r(i+1); - ad_c(i+1) =ad_c(i+1) +c2*ad_eps_r(i+1); - ad_eps_r(i+1)=0; - - end - - % term 2 - nums1=cg(i+1)*E(i+1)*cos(theta(i+1)); - nums2=eps_b(i+1)*dx; - denoms=cg(i)*cos(theta(i)); - %4 tl_E(i) = tl_nums1/denoms ... - % - tl_nums2/denoms ... - % - (nums1-nums2)/denoms^2*tl_denoms; - ad_nums1=ad_E(i)/denoms; % note, consts initialized to zero - ad_nums2=-ad_E(i)/denoms; - ad_denoms=-(nums1-nums2)/denoms^2*ad_E(i); - ad_E(i)=0; - %3 tl_denoms=tl_cg(i)*cos(theta(i)) ... - % - cg(i)*sin(theta(i))*tl_theta(i); - ad_cg(i)=ad_cg(i)+cos(theta(i))*ad_denoms; - ad_theta(i)=ad_theta(i) - cg(i)*sin(theta(i))*ad_denoms; - ad_denoms=0; - %2 tl_nums2=tl_eps_b(i+1)*dx; - ad_eps_b(i+1)=ad_eps_b(i+1)+ad_nums2*dx; - ad_nums2=0; - %1 tl_nums1=tl_cg(i+1)*E(i+1)*cos(theta(i+1)) ... - % + cg(i+1)*tl_E(i+1)*cos(theta(i+1)) ... - % - cg(i+1)*E(i+1)*sin(theta(i+1))*tl_theta(i+1); - ad_cg(i+1)=ad_cg(i+1)+ad_nums1*E(i+1)*cos(theta(i+1)); - ad_E(i+1)=ad_E(i+1)+ad_nums1*cg(i+1)*cos(theta(i+1)); - ad_theta(i+1)=ad_theta(i+1)-ad_nums1*cg(i+1)*E(i+1)*sin(theta(i+1)); - ad_nums1=0; - - % term 1 - c1=alpha/4*g*(sigma/2/pi); - % tl_eps_b(i+1)=c1*tl_Qb(i+1)*Hm(i+1)^2 ... - % + 2*c1*Qb(i+1)*Hm(i+1)*tl_Hm; - ad_Qb(i+1)=ad_Qb(i+1)+c1*Hm(i+1)^2*ad_eps_b(i+1); - ad_Hm(i+1)=ad_Hm(i+1)+2*c1*Qb(i+1)*Hm(i+1)*ad_eps_b(i+1); - ad_eps_b(i+1)=0; - - % fraction of breaking waves, non-implicit approximation from SWAN code - B=H(i+1)/Hm(i+1); - if(B<=.5) - Qo=0; - else - Qo=(2*B-1)^2; - end - if(.20) - - % term 3 - % eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); - tl_eps_r(i+1)=2*g*tl_Er(i+1)*sin(beta)/c(i+1) ... - - 2*g*Er(i+1)*sin(beta)/c(i+1)^2*tl_c(i+1); - - % term 4 - nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); - nums2=dx*(eps_b(i+1)-eps_r(i+1)); - denoms=2*c(i)*cos(theta(i)); - tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... - + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... - - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); - tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); - tl_denoms=2*tl_c(i)*cos(theta(i)) ... - - 2*c(i)*sin(theta(i))*tl_theta(i); - % Er(i)=(nums1+nums2)/denoms; - tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... - - (nums1+nums2)/denoms^2*tl_denoms; - - end - - % H(i)=sqrt(8/g*E(i)); - if(E(i)==0) - tl_H(i)=0; - else - tl_H(i)=.5./sqrt(8/g*E(i))*8/g.*tl_E(i); - end - -end -tl_c=tl_c(:); -tl_H=tl_H(:); -tl_theta=tl_theta(:); - -% radiation stress gradient -if(beta>0) - % dSxydx = -sin(theta)./c.*eps_r; - tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... - +sin(theta)./c.^2.*eps_r.*tl_c ... - -sin(theta)./c.*tl_eps_r; -else - % dSxydx = -sin(theta)./c.*eps_b; - tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... - +sin(theta)./c.^2.*eps_b.*tl_c ... - -sin(theta)./c.*tl_eps_b; -end - -% total force = radiation stress gradient + wind stress -% Fy=dSxydx+tauw; -tl_Fy=tl_dSxydx; - -% define mixing operator -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); - -% bottom stress model following Ruessink et al. (2001), Feddersen et -% al. (2000). To get TL model, differentiate the eqn for v (i.e., the -% fsolve() line in waveModel.m) on both sides, then solve for -% tl_v -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); -urms=1.416*H*sigma./(4*sinh(k.*h)); -tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... - -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); -B=a^2+(v./urms).^2; -tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; -dens = -urms.*Cd - v.^2.*Cd./urms./B; -if(nu==0) - tl_v=tl_N./dens; -else - tl_v = inv(diag(dens)+A)*tl_N; % tl_N = (dens + A) * tl_v -end - - - - - -return; -%----------------------------------- -% OLD: incorrect derivation of tl_v, did not correctly incorporate mixing -%----------------------------------- - -% bottom stress model following Ruessink et al. (2001), Feddersen et -% al. (2000). To get TL model, differentiate the eqn for tau_b=Fy on both -% sides, then solve for tl_v... double-checked algebra on this, and verified -% that tl_v is consistent with a perturbed nonlinear model (but this check -% only applied to the version without mixing) -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); -urms=1.416*H*sigma./(4*sinh(k.*h)); -tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... - -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); -B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); -nums = tl_Fy./Fy ... - - tl_Cd./Cd ... - - tl_urms./urms.*(1-B); -dens = (1+B)./v; -tl_v = nums./dens; - -% v2: with mixing operator. Note previously without mixing I had worked out -% the TL form of tau_b(v)=Fy, to get -% -% nums = dens.*tl_v -% = diag(dens)*tl_v. -% -% Now just add in the mixing operator A to the same derivation, to get -% -% nums = ( diag(dens) + A )*tl_v. -% ---> tl_v = inv(...)*nums -% -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); -if(nu==0) - tl_v=nums./dens; -else - keyboard; - tl_v = pinv(diag(dens)+A)*nums; -end \ No newline at end of file diff --git a/bin/1DVarS/old/waveModel/waveModel.m b/bin/1DVarS/old/waveModel/waveModel.m deleted file mode 100644 index fb555aa..0000000 --- a/bin/1DVarS/old/waveModel/waveModel.m +++ /dev/null @@ -1,184 +0,0 @@ -function out=waveModel(x,H0,theta0,out) -% -% out=waveModel(x,H0,theta0,in) -% -% Wave energy balance equation solver, explicit spatial stepping scheme. -% -% Breaking dissipation (eps_b) using TG1983 -% Roller energy (Er) and roller dissipation (eps_r) following Reniers & Battjes (1996) -% -% NOTE: input theta0 in radians -% -% 'in' will be appended/overwritten with new variables to create 'out', and -% should contain the following minimal inputs: -% -% in.{h,H0,theta0,sigma,tauw} -% -% note, tauw is optional, alongshore component of wind stress in m2/s2 units -% - -h =out.h ; -sigma =out.sigma ; -ka_drag=out.ka_drag; - -% wind stress implemented later, so use as optional argument to ensure -% backwards compatibility -if(isfield(out,'tauw')) - tauw=out.tauw; -else - tauw=0; -end - -[g,alpha,beta,nu]=waveModelParams(); - -% grid -nx=length(x); -dx=diff(x(1:2)); - -% dispersion -% k=fsolve(@(k)sigma^2-g*k.*tanh(k.*h),sigma./sqrt(g*h),optimset('Display','off')); -k=nan*h; -for i=1:nx - k(i)=fzero(@(k)sigma^2-g*k.*tanh(k.*h(i)),sigma./sqrt(g*h(i)),optimset('Display','off')); -end - -c=max(0,real(sigma./k)); -n=.5*(1+2*k.*h./sinh(2*k.*h)); -cg=n.*c; -refconst=sin(theta0)/c(nx); - -% gamma calculated based on deep water wave steepness (s0) following Battjes -% and Stive (1985), and also used by Ruessink et al. (2001) -L0=g/(2*pi*(sigma/2/pi)^2); -s0=H0/L0; -gamma=0.5+0.4*tanh(33*s0); - -% refraction -theta=asin(c.*refconst); - -% stepping, explicit scheme -E=zeros(nx,1); -Er=zeros(nx,1); -eps_b=zeros(nx,1); -eps_r=zeros(nx,1); -E(nx)=g/8*H0^2; -Er(nx)=0; -H(nx)=H0; -theta(nx)=theta0; %asin(c(nx).*refconst); - -for i=(nx-1):-1:1 - % max wave height - tharg=gamma/0.88.*k(i+1).*h(i+1); - Hm(i+1)=0.88./k(i+1).*tanh(tharg); - - % fraction of breaking waves, non-implicit approximation from SWAN code - B=H(i+1)/Hm(i+1); - if(B<=.5) - Qo=0; - else - Qo=(2*B-1)^2; - end - if(B<=.2) - Qb(i+1)=0; - elseif(.20) - eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); - Er(i)=(2*Er(i+1)*c(i+1)*cos(theta(i+1))+dx*(eps_b(i+1)-eps_r(i+1)))/(2*c(i)*cos(theta(i))); - if(Er(i)<0) - Er(i)=0; - end - end - - if(E(i)<.001) - E(i)=.001; - end - H(i)=sqrt(8/g*E(i)); - -end -c=c(:); -cg=cg(:); -k=k(:); -n=n(:); -theta=theta(:); -H=H(:); - -% radiation stress gradient -if(beta>0) % roller - dSxydx = -sin(theta)./c.*eps_r; -else - dSxydx=-sin(theta)./c.*eps_b; -end -dSxydx(dSxydx==0)=1e-6; % avoid singularity in TL model - -% bottom stress model following Ruessink et al. (2001), Feddersen et al. (2000) - -% total force = radiation stress gradient + wind stress -Fy=dSxydx+tauw; - -% v1: analytical solution, no mixing -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -urms=1.416*H.*sigma./(4*sinh(k.*h)); -v2 = sqrt( (a*Cd.*urms).^4 + 4*(Cd.*Fy).^2 )./(2*Cd.^2) - (a*urms).^2/2; -v=sqrt(v2).*sign(-Fy); - -% mixing operator -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); - -% v2: nonlinear solution with mixing. -test=A*v; -v0=v; -%disp('waveModel test 1.5') -%disp(test) -%disp(Fy) not zero -%disp(Cd) not zero -%disp(Cd.') not zero -%disp(urms) not zero -%disp(urms.') -%disp(v) not zero -%disp(v.') - -%v = fsolve(@(v)Fy + Cd.*urms.*v.*sqrt(a^2+(v./urms).^2) - A*v,v0,optimset('Display','off')); -%disp('waveModel test 2') - -% outputs struct -out.E =E ; -out.Er =Er ; -out.eps_b=eps_b; -out.eps_r=eps_r; -out.c=c; -out.cg=cg; -out.k=k; -out.h=h; -out.n=n; -out.theta=theta; -out.sigma=sigma; -out.x=x; -out.H=H; -out.gamma=gamma; -out.Hm=Hm; -out.Qb=Qb; -out.dSxydx=dSxydx; -out.Fy=Fy; -out.v=real(v); -out.ka_drag=ka_drag; diff --git a/bin/1DVarS/old/waveModel/waveModelParams.m b/bin/1DVarS/old/waveModel/waveModelParams.m deleted file mode 100644 index 99e80fb..0000000 --- a/bin/1DVarS/old/waveModel/waveModelParams.m +++ /dev/null @@ -1,9 +0,0 @@ -function [g,alpha,beta,nu]=waveModelParams(); -% -% common params for NL-TL-AD model -% - -g=9.8; -alpha=1; -beta=0.1; % roller parameter -nu=.5; diff --git a/bin/1DVarS/waveModel/ad_symmetryCheck.m b/bin/1DVarS/waveModel/ad_symmetryCheck.m deleted file mode 100644 index 5aa8d74..0000000 --- a/bin/1DVarS/waveModel/ad_symmetryCheck.m +++ /dev/null @@ -1,36 +0,0 @@ -% -% tests adjoint code: F'*AD*TL*F should be symmetric, +'ve definite -% -clear - -% get NL solution (background) -x=[100:10:1000]'; -bkgd.h=.01*x; -bkgd.H0=1; -bkgd.theta0=deg2rad(10); -bkgd.sigma=2*pi/10; -bkgd.ka_drag=0.015; -nx=length(x); -bkgd=waveModel(x,bkgd.H0,bkgd.theta0,bkgd); - -% apply TL and ADJ models for n instances of random forcing F (actually, IC -% perturbations) -eps = 0.05; -n=10; -F = eps*rand(nx,n); -F(end,:)=F(1,:); -for i=1:n - % TL model: u=TL*F - [tl_H,tl_theta,tl_v,tl_k]=tl_waveModel(x,F(:,i),0,0,0,bkgd); - % ADJ model: g=ADJ*(TL*F) - g(:,i)=ad_waveModel(x,tl_H,tl_theta,tl_v,tl_k,bkgd); -end - -% test whether result makes sense -A = F'*g; - -(A-A')/sum(diag(A)) % should be zeros to within roundoff - -%pcolor(A) -format short g -eig(A) % should be +'ve diff --git a/bin/1DVarS/waveModel/ad_waveModel.m b/bin/1DVarS/waveModel/ad_waveModel.m deleted file mode 100644 index 6348de7..0000000 --- a/bin/1DVarS/waveModel/ad_waveModel.m +++ /dev/null @@ -1,374 +0,0 @@ -function [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_k,bkgd) -% -% [ad_h,ad_H0,ad_theta0,ad_ka_drag]=ad_waveModel(x,ad_H,ad_theta,ad_v,ad_eps_r,ad_k,bkgd) -% -% AD-code for tl_waveModel.m. Background state 'bkgd' can be a struct taken -% directly from output of waveModel.m, and 'tl_in' can be taken directly -% from output of tl_waveModel.m. -% - -[g,alpha,beta,nu,gammaType]=waveModelParams(); - -% grid -nx=length(x); -dx=diff(x(1:2)); - -% break out the bkgd vars -sigma=bkgd.sigma; -E =bkgd.E ; -Er =bkgd.Er ; -eps_b=bkgd.eps_b; -eps_r=bkgd.eps_r; -c =bkgd.c ; -cg =bkgd.cg ; -k =bkgd.k ; -h =bkgd.h ; -n =bkgd.n ; -theta=bkgd.theta; -sigma=bkgd.sigma; -x =bkgd.x ; -H =bkgd.H ; -dSxydx=bkgd.dSxydx; -Fy=bkgd.Fy; -v=bkgd.v; -H0=bkgd.H0; -theta0=bkgd.theta0; -gamma=bkgd.gamma; -Hm=bkgd.Hm; -Qb=bkgd.Qb; - -ka_drag=bkgd.ka_drag; - -refconst=sin(theta0)/c(nx); - -%----------------------------- -% begin adjoint code -%----------------------------- - -% initialize. All these variables are assumed to never be directly -% observed, else they would be provided as inputs -zz=zeros(nx,1); -ad_c=zz; -ad_Er=zz; -ad_E=zz; -ad_eps_b=zz; -ad_eps_r=zz; -ad_cg=zz; -ad_h=zz; -ad_n=zz; -% ad_k=zz; -ad_Qb=zz; -ad_Hm=zz; -ad_refconst=0; -ad_gamma=zz; -ad_tharg=0; - -% bottom stress model following Ruessink et al. (2001), Feddersen et -% al. (2000) -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -urms=1.416*H*sigma./(4*sinh(k.*h)); -% B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); % old version of TL code, no mixing -B=a^2+(v./urms).^2; -dens = -urms.*Cd - v.^2.*Cd./urms./B; - -% mixing operator -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); - -% % OLD: incorrect version for with-mixing case -% if(nu>0) -% % v2: with mixing -% %3b tl_v = inv(diag((1+B)./v)+A)*tl_N; -% ad_N=inv(diag((1+B)./v)+A)'*ad_v; -% ad_v=0; -% %3a tl_N=tl_Fy./Fy - tl_Cd./Cd - tl_urms./urms.*(1-B); -% ad_Fy=ad_N./Fy; -% ad_Cd=-ad_N./Cd; -% ad_urms=-ad_N./urms.*(1-B); -% ad_N=0; -% NEW: corrected version for with-mixing case -if(nu>0) - % v2: with mixing - %3b tl_v = inv(diag(dens)+A)*tl_N; - ad_N=inv(diag(dens)+A)'*ad_v; -else - % v1: no mixing: - %3b tl_v=tl_N./dens; - ad_N=ad_v./dens; -end -ad_v=0; - -%3a tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; -ad_Fy=ad_N./sqrt(B); -ad_urms=ad_N.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)); -ad_Cd=ad_N.*v.*urms; -ad_N=0; - -%2 tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... -% -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); -ad_H = ad_H + 1.416*sigma*( ad_urms./(4*sinh(k.*h)) ); -ad_k = ad_k - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*h; -ad_h = ad_h - 1.416*sigma*H./(4*sinh(k.*h).^2).*cosh(k.*h).*ad_urms.*k; -ad_urms=0; - -% note below: ka_drag is scalar, so sum over gridpoints. Can see this by -% considering if the code was in a loop, then scalar ad_ka_drag would -% receive a contribution from each loop iteration -%1 tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); -ad_h = ad_h + 0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*ad_Cd); -ad_ka_drag = sum(0.015*(1/3)*(ka_drag./h).^(-2/3).*(ad_Cd./h)); % if correcting ka_drag -ad_Cd=0; - -% total force = radiation stress gradient + wind stress -% tl_Fy=tl_dSxydx; -ad_dSxydx=ad_Fy; -ad_Fy=0; - -% radiation stress gradient -if(beta>0) - % tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... - % +sin(theta)./c.^2.*eps_r.*tl_c ... - % -sin(theta)./c.*tl_eps_r; - ad_theta=ad_theta-cos(theta)./c.*eps_r.*ad_dSxydx; - ad_c=ad_c+sin(theta)./c.^2.*eps_r.*ad_dSxydx; - ad_eps_r=ad_eps_r-sin(theta)./c.*ad_dSxydx; - ad_dSxydx=0; -else - % tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... - % +sin(theta)./c.^2.*eps_b.*tl_c ... - % -sin(theta)./c.*tl_eps_b; - ad_theta=ad_theta-cos(theta)./c.*eps_b.*ad_dSxydx; - ad_c=ad_c+sin(theta)./c.^2.*eps_b.*ad_dSxydx; - ad_eps_b=ad_eps_b-sin(theta)./c.*ad_dSxydx; - ad_dSxydx=0; -end - -% stepping, explicit scheme -for i=1:(nx-1) - - % tl_H(i)=.5./sqrt(8/g*max(0,E(i)))*8/g.*tl_E(i); - if(E(i)==0) - ad_E(i)=0; - else - ad_E(i)=ad_E(i)+.5./sqrt(8/g*E(i))*8/g.*ad_H(i); - end - ad_H(i)=0; - - if(beta>0) - - % term 4 - nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); - nums2=dx*(eps_b(i+1)-eps_r(i+1)); - denoms=2*c(i)*cos(theta(i)); - %4 tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... - % - (nums1+nums2)/denoms^2*tl_denoms; - ad_nums1=ad_Er(i)/denoms; - ad_nums2=ad_Er(i)/denoms; - ad_denoms=-(nums1+nums2)/denoms^2*ad_Er(i); - ad_Er(i)=0; - %3 tl_denoms=2*tl_c(i)*cos(theta(i)) ... - % - 2*c(i)*sin(theta(i))*tl_theta(i); - ad_c(i)=ad_c(i)+2*cos(theta(i))*ad_denoms; - ad_theta(i)=ad_theta(i)-2*c(i)*sin(theta(i))*ad_denoms; - ad_denoms=0; - %2 tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); - ad_eps_b(i+1)=ad_eps_b(i+1)+dx*ad_nums2; - ad_eps_r(i+1)=ad_eps_r(i+1)-dx*ad_nums2; - ad_nums2=0; - %1 tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... - % + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... - % - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); - ad_Er(i+1)=ad_Er(i+1)+2*c(i+1)*cos(theta(i+1))*ad_nums1; - ad_c(i+1)=ad_c(i+1)+2*Er(i+1)*cos(theta(i+1))*ad_nums1; - ad_theta(i+1)=ad_theta(i+1)-2*Er(i+1)*c(i+1)*sin(theta(i+1))*ad_nums1; - ad_nums1=0; - - % term 3 - c1=2*g*sin(beta)/c(i+1); - c2=-2*g*sin(beta)*Er(i+1)/c(i+1)^2; - % tl_eps_r(i+1) = c1 * tl_Er(i+1) ... - % + c2 * tl_c(i+1); - ad_Er(i+1)=ad_Er(i+1)+c1*ad_eps_r(i+1); - ad_c(i+1) =ad_c(i+1) +c2*ad_eps_r(i+1); - ad_eps_r(i+1)=0; - - end - - % term 2 - nums1=cg(i+1)*E(i+1)*cos(theta(i+1)); - nums2=eps_b(i+1)*dx; - denoms=cg(i)*cos(theta(i)); - %4 tl_E(i) = tl_nums1/denoms ... - % - tl_nums2/denoms ... - % - (nums1-nums2)/denoms^2*tl_denoms; - ad_nums1=ad_E(i)/denoms; % note, consts initialized to zero - ad_nums2=-ad_E(i)/denoms; - ad_denoms=-(nums1-nums2)/denoms^2*ad_E(i); - ad_E(i)=0; - %3 tl_denoms=tl_cg(i)*cos(theta(i)) ... - % - cg(i)*sin(theta(i))*tl_theta(i); - ad_cg(i)=ad_cg(i)+cos(theta(i))*ad_denoms; - ad_theta(i)=ad_theta(i) - cg(i)*sin(theta(i))*ad_denoms; - ad_denoms=0; - %2 tl_nums2=tl_eps_b(i+1)*dx; - ad_eps_b(i+1)=ad_eps_b(i+1)+ad_nums2*dx; - ad_nums2=0; - %1 tl_nums1=tl_cg(i+1)*E(i+1)*cos(theta(i+1)) ... - % + cg(i+1)*tl_E(i+1)*cos(theta(i+1)) ... - % - cg(i+1)*E(i+1)*sin(theta(i+1))*tl_theta(i+1); - ad_cg(i+1)=ad_cg(i+1)+ad_nums1*E(i+1)*cos(theta(i+1)); - ad_E(i+1)=ad_E(i+1)+ad_nums1*cg(i+1)*cos(theta(i+1)); - ad_theta(i+1)=ad_theta(i+1)-ad_nums1*cg(i+1)*E(i+1)*sin(theta(i+1)); - ad_nums1=0; - - % term 1 - c1=alpha/4*g*(sigma/2/pi); - % tl_eps_b(i+1)=c1*tl_Qb(i+1)*Hm(i+1)^2 ... - % + 2*c1*Qb(i+1)*Hm(i+1)*tl_Hm; - ad_Qb(i+1)=ad_Qb(i+1)+c1*Hm(i+1)^2*ad_eps_b(i+1); - ad_Hm(i+1)=ad_Hm(i+1)+2*c1*Qb(i+1)*Hm(i+1)*ad_eps_b(i+1); - ad_eps_b(i+1)=0; - - % fraction of breaking waves, non-implicit approximation from SWAN code - B=H(i+1)/Hm(i+1); - if(B<=.5) - Qo=0; - else - Qo=(2*B-1)^2; - end - if(.20) - - % term 3 - % eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); - tl_eps_r(i+1)=2*g*tl_Er(i+1)*sin(beta)/c(i+1) ... - - 2*g*Er(i+1)*sin(beta)/c(i+1)^2*tl_c(i+1); - - % term 4 - nums1=2*Er(i+1)*c(i+1)*cos(theta(i+1)); - nums2=dx*(eps_b(i+1)-eps_r(i+1)); - denoms=2*c(i)*cos(theta(i)); - tl_nums1=2*tl_Er(i+1)*c(i+1)*cos(theta(i+1)) ... - + 2*Er(i+1)*tl_c(i+1)*cos(theta(i+1)) ... - - 2*Er(i+1)*c(i+1)*sin(theta(i+1))*tl_theta(i+1); - tl_nums2=dx*(tl_eps_b(i+1)-tl_eps_r(i+1)); - tl_denoms=2*tl_c(i)*cos(theta(i)) ... - - 2*c(i)*sin(theta(i))*tl_theta(i); - % Er(i)=(nums1+nums2)/denoms; - tl_Er(i)=(tl_nums1+tl_nums2)/denoms ... - - (nums1+nums2)/denoms^2*tl_denoms; - - end - - % H(i)=sqrt(8/g*E(i)); - if(E(i)==0) - tl_H(i)=0; - else - tl_H(i)=.5./sqrt(8/g*E(i))*8/g.*tl_E(i); - end - -end -tl_c=tl_c(:); -tl_H=tl_H(:); -tl_theta=tl_theta(:); - -% radiation stress gradient -if(beta>0) - % dSxydx = -sin(theta)./c.*eps_r; - tl_dSxydx = -cos(theta)./c.*eps_r.*tl_theta ... - +sin(theta)./c.^2.*eps_r.*tl_c ... - -sin(theta)./c.*tl_eps_r; -else - % dSxydx = -sin(theta)./c.*eps_b; - tl_dSxydx = -cos(theta)./c.*eps_b.*tl_theta ... - +sin(theta)./c.^2.*eps_b.*tl_c ... - -sin(theta)./c.*tl_eps_b; -end - -% total force = radiation stress gradient + wind stress -% Fy=dSxydx+tauw; -tl_Fy=tl_dSxydx; - -% define mixing operator -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); - -% bottom stress model following Ruessink et al. (2001), Feddersen et -% al. (2000). To get TL model, differentiate the eqn for v (i.e., the -% fsolve() line in waveModel.m) on both sides, then solve for -% tl_v -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); -urms=1.416*H*sigma./(4*sinh(k.*h)); -tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... - -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); -B=a^2+(v./urms).^2; -tl_N = tl_Fy./sqrt(B) + tl_urms.*(v.*Cd-v.^3.*Cd./(urms.^2.*B)) + tl_Cd.*v.*urms; -dens = -urms.*Cd - v.^2.*Cd./urms./B; -if(nu==0) - tl_v=tl_N./dens; -else - tl_v = inv(diag(dens)+A)*tl_N; % tl_N = (dens + A) * tl_v -end - - - - - -return; -%----------------------------------- -% OLD: incorrect derivation of tl_v, did not correctly incorporate mixing -%----------------------------------- - -% bottom stress model following Ruessink et al. (2001), Feddersen et -% al. (2000). To get TL model, differentiate the eqn for tau_b=Fy on both -% sides, then solve for tl_v... double-checked algebra on this, and verified -% that tl_v is consistent with a perturbed nonlinear model (but this check -% only applied to the version without mixing) -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -tl_Cd=0.015*(1/3)*(ka_drag./h).^(-2/3).*(-ka_drag./h.^2.*tl_h+tl_ka_drag./h); -urms=1.416*H*sigma./(4*sinh(k.*h)); -tl_urms=1.416*sigma*( tl_H./(4*sinh(k.*h)) ... - -H./(4*sinh(k.*h).^2).*cosh(k.*h).*( tl_k.*h+k.*tl_h ) ); -B=Cd./Fy.*v.^3./urms./sqrt(a^2+(v./urms).^2); -nums = tl_Fy./Fy ... - - tl_Cd./Cd ... - - tl_urms./urms.*(1-B); -dens = (1+B)./v; -tl_v = nums./dens; - -% v2: with mixing operator. Note previously without mixing I had worked out -% the TL form of tau_b(v)=Fy, to get -% -% nums = dens.*tl_v -% = diag(dens)*tl_v. -% -% Now just add in the mixing operator A to the same derivation, to get -% -% nums = ( diag(dens) + A )*tl_v. -% ---> tl_v = inv(...)*nums -% -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=[1 -2]/dx^2*nu*h(nx); -if(nu==0) - tl_v=nums./dens; -else - keyboard; - tl_v = pinv(diag(dens)+A)*nums; -end diff --git a/bin/1DVarS/waveModel/waveModel.m b/bin/1DVarS/waveModel/waveModel.m deleted file mode 100644 index 99ec4a1..0000000 --- a/bin/1DVarS/waveModel/waveModel.m +++ /dev/null @@ -1,180 +0,0 @@ -function out=waveModel(x,H0,theta0,out) -% -% out=waveModel(x,H0,theta0,in) -% -% Wave energy balance equation solver, explicit spatial stepping scheme. -% -% Breaking dissipation (eps_b) using TG1983 -% Roller energy (Er) and roller dissipation (eps_r) following Reniers & Battjes (1996) -% -% NOTE: input theta0 in radians -% -% 'in' will be appended/overwritten with new variables to create 'out', and -% should contain the following minimal inputs: -% -% in.{h,H0,theta0,sigma,tauw} -% -% note, tauw is optional, alongshore component of wind stress in m2/s2 units -% - -h =out.h ; -sigma =out.sigma ; -ka_drag=out.ka_drag; - -% wind stress implemented later, so use as optional argument to ensure -% backwards compatibility -if(isfield(out,'tauw')) - tauw=out.tauw; -else - tauw=0; -end - -[g,alpha,beta,nu,gammaType]=waveModelParams(); - -% grid -nx=length(x); -dx=diff(x(1:2)); - -% dispersion -% k=fsolve(@(k)sigma^2-g*k.*tanh(k.*h),sigma./sqrt(g*h),optimset('Display','off')); -k=nan*h; -for i=1:nx - k(i)=fzero(@(k)sigma^2-g*k.*tanh(k.*h(i)),sigma./sqrt(g*h(i)),optimset('Display','off')); -end -c=max(0,real(sigma./k)); -n=.5*(1+2*k.*h./sinh(2*k.*h)); -cg=n.*c; -refconst=sin(theta0)/c(nx); - -% gamma can be either calculated based on deep water wave steepness (s0) -% following Battjes and Stive (1985) (also used by Ruessink et al., 2001), -% or based on the empirical fit obtained for duck94 by Ruessink et -% al. (2003). -if(gammaType==2001) - L0=g/(2*pi*(omega/2/pi)^2); - s0=H0/L0; - gamma=0.5+0.4*tanh(33*s0); - gamma=ones(nx,1)*gamma; -elseif(gammaType==2003) - gamma=0.76*k.*h+0.29; -end - -% refraction -theta=asin(c.*refconst); - -% stepping, explicit scheme -E=zeros(nx,1); -Er=zeros(nx,1); -eps_b=zeros(nx,1); -eps_r=zeros(nx,1); -E(nx)=g/8*H0^2; -Er(nx)=0; -H(nx)=H0; -theta(nx)=theta0; %asin(c(nx).*refconst); -for i=(nx-1):-1:1 - - % max wave height - tharg=gamma(i+1)/0.88.*k(i+1).*h(i+1); - Hm(i+1)=0.88./k(i+1).*tanh(tharg); - - % fraction of breaking waves, non-implicit approximation from SWAN code - B=H(i+1)/Hm(i+1); - if(B<=.5) - Qo=0; - else - Qo=(2*B-1)^2; - end - if(B<=.2) - Qb(i+1)=0; - elseif(.20) - eps_r(i+1)=2*g*Er(i+1)*sin(beta)/c(i+1); - Er(i)=(2*Er(i+1)*c(i+1)*cos(theta(i+1))+dx*(eps_b(i+1)-eps_r(i+1)))/(2*c(i)*cos(theta(i))); - if(Er(i)<0) - Er(i)=0; - end - end - - if(E(i)<.001) - E(i)=.001; - end - H(i)=sqrt(8/g*E(i)); - -end -c=c(:); -cg=cg(:); -k=k(:); -n=n(:); -theta=theta(:); -H=double(H(:)); - -% radiation stress gradient -if(beta>0) % roller - dSxydx = -sin(theta)./c.*eps_r; -else - dSxydx=-sin(theta)./c.*eps_b; -end -dSxydx(dSxydx==0)=1e-6; % avoid singularity in TL model - -% bottom stress model following Ruessink et al. (2001), Feddersen et al. (2000) - -% total force = radiation stress gradient + wind stress -Fy=dSxydx+tauw; - -% v1: analytical solution, no mixing -a=1.16; % empirical constant -Cd=0.015*(ka_drag./h).^(1/3); -urms=1.416*H.*sigma./(4*sinh(k.*h)); -v2 = sqrt( (a*Cd.*urms).^4 + 4*(Cd.*Fy).^2 )./(2*Cd.^2) - (a*urms).^2/2; -v=sqrt(v2).*sign(-Fy); - -% mixing operator -A=zeros(nx); -for i=2:nx-1 - A(i,i+[-1:1])=[1 -2 1]/dx^2*nu*h(i); -end -A(1,1:2)=[-2 1]/dx^2*nu*h(1); -A(nx,nx-1:nx)=0; % [1 -2]/dx^2*nu*h(nx); - -% v2: nonlinear solution with mixing. -Fy = double(Fy); -v = double(v); -v0=v; -v = fsolve(@(v)Fy + Cd.*urms.*v.*sqrt(a^2+(v./urms).^2) - A*v,v0,optimset('Display','off')); - -% outputs struct -out.E =E ; -out.Er =Er ; -out.eps_b=eps_b; -out.eps_r=eps_r; -out.c=c; -out.cg=cg; -out.k=k; -out.h=h; -out.n=n; -out.theta=theta; -out.sigma=sigma; -out.x=x; -out.H=H; -out.gamma=gamma; -out.Hm=Hm; -out.Qb=Qb; -out.dSxydx=dSxydx; -out.Fy=Fy; -out.v=real(v); -out.ka_drag=ka_drag; diff --git a/bin/1DVarS/waveModel/waveModelParams.m b/bin/1DVarS/waveModel/waveModelParams.m deleted file mode 100644 index 0647b8b..0000000 --- a/bin/1DVarS/waveModel/waveModelParams.m +++ /dev/null @@ -1,13 +0,0 @@ -function [g,alpha,beta,nu,gammaType]=waveModelParams(); -% -% common params for NL-TL-AD model -% - -g=9.8; -alpha=1; -beta=0.1; % roller parameter -nu=.5; - -% switch for breaker model. Use '2001' Battjes and Stive (1985) (as used by -% Ruessink et al. 2001), or '2003' for Ruessink et al. (2003). -gammaType=2003; From c5a1d86c536ed75a1dd6d273a375f1ad65e2136f Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 7 Oct 2021 15:00:35 -0400 Subject: [PATCH 22/26] deleted extra filed from git repo, added old back in --- .gitmodules | 9 ++++++ .idea/.gitignore | 2 ++ .idea/.name | 1 + .idea/cmtb.iml | 28 ++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++++ .idea/encodings.xml | 4 +++ .idea/inspectionProfiles/Project_Default.xml | 30 ++++++++++++++++++++ .idea/misc.xml | 7 +++++ .idea/modules.xml | 8 ++++++ .idea/other.xml | 8 ++++++ .idea/vcs.xml | 9 ++++++ 11 files changed, 111 insertions(+) create mode 100644 .gitmodules create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/cmtb.iml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/other.xml create mode 100644 .idea/vcs.xml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..805d9fd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "getdatatestbed"] + path = getdatatestbed + url = https://github.com/SBFRF/getdatatestbed.git +[submodule "testbedutils"] + path = testbedutils + url = https://github.com/SBFRF/testbedutils.git +[submodule "prepdata"] + path = prepdata + url = https://github.com/collins-frf/prepdata.git diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..838cda8 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +genericWorkFlow.py \ No newline at end of file diff --git a/.idea/cmtb.iml b/.idea/cmtb.iml new file mode 100644 index 0000000..46e6208 --- /dev/null +++ b/.idea/cmtb.iml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..c88595c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,30 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..33513a5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..bb0aad5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..a847ac6 --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..512b7a8 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file From 3a8c9fba16cca3a265fcaeec86f246ff9482a1e3 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 14 Oct 2021 09:19:46 -0400 Subject: [PATCH 23/26] fixed wrr call, removed .gitmodules from branch --- .gitmodules | 9 --------- genericWorkFlow.py | 18 +++++++++++------- 2 files changed, 11 insertions(+), 16 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 805d9fd..0000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "getdatatestbed"] - path = getdatatestbed - url = https://github.com/SBFRF/getdatatestbed.git -[submodule "testbedutils"] - path = testbedutils - url = https://github.com/SBFRF/testbedutils.git -[submodule "prepdata"] - path = prepdata - url = https://github.com/collins-frf/prepdata.git diff --git a/genericWorkFlow.py b/genericWorkFlow.py index 523e362..651a578 100755 --- a/genericWorkFlow.py +++ b/genericWorkFlow.py @@ -197,11 +197,10 @@ def Master_workFlow(inputDict): wrr.hpcCores = inputDict['hpcSettings']['hpcCores'] wrr.hpcNodes = inputDict['hpcSettings']['hpcNodes'] # write simulation files (if assigned) - print(gridFname) - wrr.writeAllFiles(wavePacket, windPacket, wlPacket, - bathyPacket, gridFname, - ctdPacket, currentPacket, - updateBathy) + wrr.writeAllFiles(wavePacket=wavePacket, windPacket=windPacket, wlPacket=wlPacket, + bathyPacket=bathyPacket, gridFname=gridFname, + ctdPacket=ctdPacket, currentPacket=currentPacket, + updateBathy=updateBathy) # run simulation (as appropriate) if runFlag is True: @@ -213,8 +212,13 @@ def Master_workFlow(inputDict): frontBackNEW.genericPostProcess(time, inputDict, spatialData=spatialData, pointData=savePointData, wrr=wrr) + print(plotFlag) + print(DT.date.today()) + print(inputDict['slack']) + print(inputDict['slack'] is not None) + #TODO: As seen by the print statement above, the following if statement evaluates as True no matter the input # if it's a live run, move the plots to the output directory - """if plotFlag is True and DT.date.today() == projectEnd or inputDict['slack'] is not None: + if plotFlag is True and DT.date.today() == projectEnd or inputDict['slack'] is not None: from testbedutils import cmtbSlack moveFnames = glob.glob(wrr.plottingDirectory + '/CMTB*.png') moveFnames.extend(glob.glob(wrr.plottingDirectory + '/CMTB*.gif')) @@ -232,7 +236,7 @@ def Master_workFlow(inputDict): for file in moveFnames: shutil.move(file, liveFileMoveToDirectory) print('moved {} to {} '.format(file, liveFileMoveToDirectory)) - """ + print('------------------SUCCESS-----------------------------------------') except Exception as e: From e1c2f864c689a27c96f37587fb1773a004286e72 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Nov 2021 15:01:17 -0400 Subject: [PATCH 24/26] updates for pflag and slack --- genericWorkFlow.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/genericWorkFlow.py b/genericWorkFlow.py index 651a578..2040174 100755 --- a/genericWorkFlow.py +++ b/genericWorkFlow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import matplotlib -matplotlib.use('Agg') +matplotlib.use('TkAgg') import os, getopt, sys, shutil, glob, logging, yaml, re, pickle import datetime as DT import numpy as np @@ -179,8 +179,6 @@ def Master_workFlow(inputDict): ctdPacket = None updateBathy = None - - if generateFlag is True: print(" TODO: TY you're handing me back the same prepdata packets from all frontBacks") print('TODO: document Packets coming from sim-setup') From a08e0e4a544afeba6f9fc61873c06ed9e78fad15 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Nov 2021 15:14:36 -0400 Subject: [PATCH 25/26] gitmodules added --- .gitmodules | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..805d9fd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "getdatatestbed"] + path = getdatatestbed + url = https://github.com/SBFRF/getdatatestbed.git +[submodule "testbedutils"] + path = testbedutils + url = https://github.com/SBFRF/testbedutils.git +[submodule "prepdata"] + path = prepdata + url = https://github.com/collins-frf/prepdata.git From ab2444d7a51e0bd4ce6f3a4aabf81f5d45abc227 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 17 Nov 2021 11:50:58 -0500 Subject: [PATCH 26/26] slack logic fix --- genericWorkFlow.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/genericWorkFlow.py b/genericWorkFlow.py index 2040174..5ffebfe 100755 --- a/genericWorkFlow.py +++ b/genericWorkFlow.py @@ -210,30 +210,26 @@ def Master_workFlow(inputDict): frontBackNEW.genericPostProcess(time, inputDict, spatialData=spatialData, pointData=savePointData, wrr=wrr) - print(plotFlag) - print(DT.date.today()) - print(inputDict['slack']) - print(inputDict['slack'] is not None) #TODO: As seen by the print statement above, the following if statement evaluates as True no matter the input # if it's a live run, move the plots to the output directory - if plotFlag is True and DT.date.today() == projectEnd or inputDict['slack'] is not None: + if plotFlag is True and DT.date.today() == projectEnd: from testbedutils import cmtbSlack moveFnames = glob.glob(wrr.plottingDirectory + '/CMTB*.png') moveFnames.extend(glob.glob(wrr.plottingDirectory + '/CMTB*.gif')) + else: + # move files + moveFnames = glob.glob(wrr.plottingDirectory + 'CMTB*.png') + moveFnames.extend(glob.glob(wrr.plottingDirectory + '/CMTB*.gif')) + liveFileMoveToDirectory = '/mnt/gaia/cmtb' + for file in moveFnames: + shutil.move(file, liveFileMoveToDirectory) + print('moved {} to {} '.format(file, liveFileMoveToDirectory)) + + if inputDict['slack'] is True: + myslack = cmtbSlack.slack('testbedutils/slackSettings.yml') # initialize + myslack.postMessageWithFiles(f"checkout {wrr.modelName} simulations from {wrr.dateString}", + moveFnames) - if inputDict['slack'] is not None: - myslack = cmtbSlack.slack('testbedutils/slackSettings.yml') # initialize - myslack.postMessageWithFiles(f"checkout {wrr.modelName} simulations from {wrr.dateString}", - moveFnames) - - else: - # move files - moveFnames = glob.glob(wrr.plottingDirectory + 'CMTB*.png') - moveFnames.extend(glob.glob(wrr.plottingDirectory + '/CMTB*.gif')) - liveFileMoveToDirectory = '/mnt/gaia/cmtb' - for file in moveFnames: - shutil.move(file, liveFileMoveToDirectory) - print('moved {} to {} '.format(file, liveFileMoveToDirectory)) print('------------------SUCCESS-----------------------------------------')