XBeach
|
00001 ! Everything in this module should be handled by xmaster, so only call using xmaster 00002 module spectral_wave_bc_module 00003 use typesandkinds, only: slen 00004 use constants, only: pi, compi 00005 use paramsconst 00006 implicit none 00007 save 00008 00009 private 00010 public lastwaveelevation, n_index_loc, bcfiles, bccount, nspectra, reuseall, spectrumendtime 00011 public spectral_wave_bc 00012 00013 type spectrum ! These are related to input spectra 00014 real*8,dimension(:,:),pointer :: S ! 2D variance density spectrum 00015 real*8,dimension(:),pointer :: f,ang ! 1D frequency and direction vectors for S 00016 real*8,dimension(:),pointer :: Sf ! S integrated over all directions 00017 real*8,dimension(:),pointer :: Sd ! S integrated over all frequencies 00018 integer :: nf,nang ! number of frequencies and angles 00019 real*8 :: df,dang ! frequency and angle step size 00020 real*8 :: hm0,fp,dir0,scoeff ! imposed significant wave height, peak frequency, 00021 ! main wave angle and spreading coefficient 00022 real*8 :: trep,dirm ! representative period and mean wave direction 00023 endtype spectrum 00024 type shortspectrum 00025 real*8,dimension(:),pointer :: Sf ! S integrated over all directions 00026 ! real*8,dimension(:),pointer :: Srep ! S at frequency/direction locations of wave train components 00027 ! real*8 :: dangrep ! representative angle step size for Srep 00028 endtype shortspectrum 00029 type waveparamsnew ! These are related to the generated bc series 00030 real*8 :: h0 ! average water depth on offshore boundary 00031 integer :: K ! number of wave train components 00032 real*8 :: rtbc,dtbc ! duration and time step to be written to boundary condition file 00033 real*8 :: rtin,dtin ! duration and time step for the internal time axis, based on Fourier 00034 ! limitations and the number of wave train components (wp%K) 00035 real*8,dimension(:),pointer :: tin ! internal time axis 00036 real*8,dimension(:),pointer :: taperf,taperw ! internal taper function for flow and waves 00037 integer :: tslen ! internal time axis length (is always even, not odd) 00038 integer :: tslenbc ! time axis length for boundary condition file 00039 logical :: dtchanged ! quick check to see if dtin == dtbc (useful for interpolation purpose) 00040 real*8,dimension(:),pointer :: fgen,thetagen,phigen,kgen,wgen ! frequency, angle, phase, 00041 ! wave number, radian frequency 00042 ! of wavetrain components for boundary signal 00043 real*8 :: dfgen ! frequency grid size in the generated components 00044 type(shortspectrum),dimension(:),pointer :: vargen ! This is the variance for each wave train at each spectrum location 00045 ! is stored 00046 type(shortspectrum),dimension(:),pointer :: vargenq ! This is the variance for each wave train at each spectrum location 00047 ! is stored, which is not scaled and is used for the generation of bound waves 00048 !real*8,dimension(:),pointer :: danggen ! Representative integration angle for Srep to Sf, per offshore grid point 00049 real*8,dimension(:,:),pointer :: A ! Amplitude, per wave train component, per offshore grid point A(ny+1,K) 00050 real*8,dimension(:,:),pointer :: Sfinterp ! S integrated over all directions at frequency locations of fgen, 00051 ! per offshore grid point Sfinterp(ny+1,K) 00052 real*8,dimension(:,:),pointer :: Sfinterpq ! S integrated over all directions at frequency locations of fgen, 00053 ! per offshore grid point Sfinterpq(ny+1,K), uncorrected for generation of bound waves 00054 real*8,dimension(:),pointer :: Hm0interp ! Hm0 per offshore point, based on intergration of Sfinterp. 00055 ! Used to scale spectrum 00056 ! final time series 00057 integer, dimension(:),pointer :: Findex ! Index of wave train component locations on frequency/Fourier axis 00058 integer, dimension(:),pointer :: WDindex ! Index of wave train component locations on wave directional bin axis 00059 integer, dimension(:),pointer :: PRindex ! Index of wave train components to be phase-resolved (rather than 00060 ! using energy balance) 00061 double complex,dimension(:,:),pointer :: CompFn ! Fourier components of the wave trains 00062 character(slen) :: Efilename,qfilename,nhfilename,Esfilename 00063 real*8,dimension(:,:),pointer :: zsits ! time series of total surface elevation for nonhspectrum==1 00064 real*8,dimension(:,:),pointer :: uits,vits ! time series of depth-averaged horizontal velocity nonhspectrum==1 00065 real*8,dimension(:,:),pointer :: duits,dvits! time series of depth-averaged horizontal velocity nonhspectrum==1 00066 00067 real*8,dimension(:,:),pointer :: wits ! time series of depth-averaged vertical velocity for nonhspectrum==1 ?? 00068 endtype waveparamsnew 00069 type filenames ! Place to store multiple file names 00070 character(slen) :: fname ! file name of boundary condition file 00071 integer :: listline ! read position in FILELIST files 00072 logical :: reuse = .false. ! indicate to reuse this file every rtbc cycle 00073 endtype filenames 00074 00075 ! These are for administration purposes and are initialized in initialize.F90 00076 integer,dimension(:),allocatable :: n_index_loc ! y-index locations of all input spectra, set in init spectrum 00077 integer :: nspectra ! number of input spectrs, set in init spectrum 00078 type(filenames),dimension(:),allocatable :: bcfiles ! input wave spectrum files 00079 logical :: reuseall ! switch to reuse all of the wave boundary conditions 00080 integer,save :: bccount ! number of times boundary conditions have been generated, set in init spectrum 00081 integer,save :: fidelist,fidqlist,fidnhlist,fideslist ! file identifiers for ebcflist and qbcflist 00082 real*8 :: spectrumendtime ! end time of boundary condition written to administration file 00083 real*8,dimension(:,:),allocatable :: lastwaveelevation ! wave height at the end of the last spectrum 00084 integer :: ind_end_taper ! index of where the taper function equals rtbc 00085 ! These parameters control a lot how the spectra are handled. They could be put in params.txt, 00086 ! but most users will want to keep these at their default values anyway 00087 integer,parameter,private :: nfint = 801 ! size of standard 2D spectrum in frequency dimension 00088 integer,parameter,private :: naint = 401 ! size of standard 2D spectrum in angular dimension 00089 integer,parameter,private :: Kmin = 200 ! minimum number of wave train components 00090 real*8,parameter,private :: wdmax = 5.d0 ! maximum depth*reliable angular wave frequency that can be resolved by 00091 ! nonhydrostatic wave model. All frequencies above this are removed 00092 ! from nonhspectrum generation 00093 ! Constants, cannot be modified 00094 real*8,parameter,private :: par_pi = 4.d0*atan(1.d0) 00095 00096 contains 00097 00098 subroutine spectral_wave_bc(s,par,curline) 00099 use params, only: parameters 00100 use spaceparamsdef, only: spacepars 00101 use logging_module, only: writelog 00102 use filefunctions, only: create_new_fid 00103 use xmpi_module 00104 00105 implicit none 00106 type(spacepars),intent(inout) :: s 00107 type(parameters),intent(inout) :: par 00108 integer,intent(out) :: curline 00109 type(spectrum),dimension(:),allocatable :: specin,specinterp 00110 type(spectrum) :: combspec 00111 type(waveparamsnew) :: wp ! Most will be deallocated, but some variables useful to keep? 00112 integer :: iloc 00113 integer :: iostat 00114 real*8 :: spectrumendtimeold,fmax 00115 real*8,dimension(:),allocatable :: spectrumendtimearray 00116 real*8,save :: rtbc_local,dtbc_local 00117 real*8,save :: maindir_local 00118 00119 spectrumendtimeold = -123 00120 00121 !fidqlist = -123 00122 !fidnhlist = -123 00123 !fidelist = -123 00124 ! Update the number of boundary conditions generated by this module 00125 bccount = bccount+1 00126 00127 ! Arnold: check if this is the last computational timestep. If so, don't recalculate 00128 ! boundary conditions 00129 if (par%t>(par%tstop-par%dt)) then 00130 return 00131 end if 00132 00133 ! wwvv copied call to set_bcfilenames to here: (see later) 00134 call set_bcfilenames(wp) 00135 if (xmaster) then 00136 if (reuseall) then 00137 ! Wave boundary conditions have already been computed 00138 ! Filename, rtbc, dtbc, etc. still kept in memory. 00139 00140 ! The end time for the boundary conditions calculated in the last call 00141 ! is equal to the old end time, plus the duration of the boundary conditions 00142 ! created at the last call 00143 spectrumendtimeold = spectrumendtime ! Robert: need this in case new run with 00144 ! instat='reuse' does not have exact same 00145 ! time steps as original simulation 00146 spectrumendtime = spectrumendtime + rtbc_local 00147 00148 else 00149 call writelog('l','','--------------------------------') 00150 call writelog('ls','','Calculating spectral wave boundary conditions ') 00151 call writelog('l','','--------------------------------') 00152 00153 ! allocate temporary input storage 00154 if (.not. allocated(specin)) then 00155 allocate(specin(nspectra)) 00156 allocate(specinterp(nspectra)) 00157 allocate(spectrumendtimearray(nspectra)) 00158 endif 00159 00160 ! calculate the average offshore water depth, which is used in various 00161 ! computation in this module 00162 wp%h0 = calculate_average_water_depth(s) 00163 00164 ! Read through input spectra files 00165 fmax = 1.d0 ! assume 1Hz as maximum frequency. Increase in loop below if needed. 00166 spectrumendtimearray = spectrumendtime 00167 do iloc = 1,nspectra 00168 call writelog('sl','(a,i0)','Reading spectrum at location ',iloc) 00169 ! read input files until endtime exceeds current time. 00170 ! necessary in case of external time management 00171 00172 do while (par%t .ge. spectrumendtimearray(iloc)) 00173 call read_spectrum_input(par,wp,bcfiles(iloc),specin(iloc)) 00174 00175 ! update end time 00176 if (iloc==1) then 00177 spectrumendtimeold = spectrumendtime 00178 spectrumendtime = spectrumendtime + wp%rtbc 00179 endif 00180 spectrumendtimearray(iloc) = spectrumendtimearray(iloc)+wp%rtbc 00181 end do 00182 fmax = max(fmax,maxval(specin(iloc)%f)) 00183 enddo 00184 do iloc = 1,nspectra 00185 call writelog('sl','(a,i0)','Interpreting spectrum at location ',iloc) 00186 ! Interpolate input 2D spectrum to standard 2D spectrum 00187 call interpolate_spectrum(specin(iloc),specinterp(iloc),par,fmax) 00188 call writelog('sl','','Values calculated from interpolated spectrum:') 00189 call writelog('sl','(a,f0.2,a)','Hm0 = ',specinterp(iloc)%hm0,' m') 00190 call writelog('sl','(a,f0.2,a)','Trep = ',specinterp(iloc)%trep,' s') 00191 call writelog('sl','(a,f0.2,a)','Mean dir = ',specinterp(iloc)%dirm,' degN') 00192 enddo 00193 00194 ! Determine whether all the spectra are to be reused, which implies that the global reuseall should be 00195 ! set to true (no further computations required in future calls) 00196 call set_reuseall 00197 00198 ! Determine the file names of the output boundary condition time series 00199 ! wwvv copied call to set_bcfilenames to start of subroutine. 00200 ! to make sure that filenames are set, regardless the value of reuseall 00201 call set_bcfilenames(wp) 00202 00203 ! calculate the mean combined spectra (used for combined Trep, determination of wave components, etc.) 00204 ! now still uses simple averaging, but could be improved to use weighting for distance etc. 00205 call generate_combined_spectrum(specinterp,combspec,par%px,par%trepfac,par%Tm01switch) 00206 par%Trep = combspec%trep 00207 call writelog('sl','(a,f0.2,a)','Overall Trep from all spectra calculated: ',par%Trep,' s') 00208 00209 if (par%single_dir==1) then 00210 call set_stationary_spectrum (s,par,wp,combspec) 00211 endif 00212 00213 ! Wave trains that are used by XBeach. The number of wave trains, their frequencies and directions 00214 ! are based on the combined spectra of all the locations to ensure all wave conditions are 00215 ! represented in the XBeach model 00216 ! call generate_wavetrain_components(combspec,wp,par) 00217 ! max statement below needed for compiler in case of 1D simulation without cyclic boundaries 00218 call generate_wavetrain_components(combspec,wp,par,s%xz(1,1),s%yz(1,1),s%xz(1,max(s%ny-2,1)),s%yz(1,max(s%ny-2,1))) 00219 00220 ! We can now apply a correction to the wave train components if necessary. This section can be 00221 ! improved later 00222 if (nspectra==1 .and. specin(1)%scoeff>1000.d0) then 00223 ! this can be used both for Jonswap and vardens input 00224 wp%thetagen = specin(1)%dir0 00225 endif 00226 00227 ! Set up time axis, including the time axis for output to boundary condition files and an 00228 ! internal time axis, which may differ in length to the output time axis 00229 call generate_wave_time_axis(par,wp) 00230 00231 ! Determine the variance for each wave train component, at every spectrum location point 00232 call generate_wave_train_variance(wp,specinterp) 00233 00234 ! Determine the amplitude of each wave train component, at every point along the 00235 ! offshore boundary 00236 call generate_wave_train_properties_per_offshore_point(wp,s,par%nonhspectrum,par%swkhmin) 00237 00238 ! Generate Fourier components for all wave train component, at every point along 00239 ! the offshore boundary 00240 call generate_wave_train_Fourier(wp,s,par) 00241 00242 ! time series of short wave energy or surface elevation 00243 if (par%nonhspectrum==0) then 00244 ! Distribute all wave train components among the wave direction bins. Also rearrage 00245 ! the randomly drawn wave directions to match the centres of the wave bins if the 00246 ! user-defined nspr is set on. 00247 call distribute_wave_train_directions(wp,s,par%px,par%nspr,.false.) 00248 00249 ! if we want to send some low-frequency swell waves into the model in the NLSWE then 00250 ! separate here into a mix of wave action balance and NLSWE components 00251 if (par%swkhmin>0.d0) then 00252 ! recalculate Trep 00253 call tpDcalc(sum(wp%Sfinterp,DIM=1)/(s%ny+1)*(1-wp%PRindex),wp%fgen,par%Trep,par%trepfac,par%Tm01switch) 00254 call writelog('sl','','Trep recomputed to account only for components in wave action balance.') 00255 call writelog('sl','(a,f0.2,a)','New Trep in wave action balance: ',par%Trep,' s') 00256 ! 00257 ! Calculate the wave energy envelope per offshore grid point and write to output file 00258 call generate_ebcf(wp,s,par) ! note, this subroutine will account for only non-phase-resolved components 00259 ! Generate time series of surface elevation and horizontal velocity, only for phase-resolved components 00260 call generate_swts(wp,s,par) 00261 else 00262 ! Only do wave action balance stuff 00263 ! 00264 ! Calculate the wave energy envelope per offshore grid point and write to output file 00265 call generate_ebcf(wp,s,par) 00266 endif ! swkhmin>0.d0 00267 else 00268 ! If user set nspr on, then force all short wave components to head along computational 00269 ! x-axis. 00270 call distribute_wave_train_directions(wp,s,par%px,par%nspr,.true.) 00271 ! Generate time series of surface elevation and horizontal velocity 00272 call generate_swts(wp,s,par) 00273 endif 00274 00275 ! Calculate the bound long wave from the wave train components and write to output file 00276 if ((par%nonhspectrum==0) .or. (par%nonhspectrum==1 .and. par%order>1)) then 00277 call generate_qbcf(wp,s,par) 00278 endif 00279 00280 ! Calculate second order bound components. 00281 if (par%nonhspectrum==1 .and. par%highcomp==1 .and. par%order>1) then 00282 call generate_secondorder(par,s, wp) 00283 endif 00284 00285 00286 ! Write non-hydrostatic time series of combined short and long waves if necessary 00287 if (par%nonhspectrum==1) then 00288 call generate_nhtimeseries_file(wp,par) 00289 endif 00290 ! 00291 ! 00292 ! Save key variables to memory, in case of 'reuseall' 00293 rtbc_local = wp%rtbc 00294 dtbc_local = wp%dtbc 00295 maindir_local = combspec%dir0 00296 ! 00297 ! 00298 ! Deallocate a lot of memory 00299 deallocate(specin,specinterp,spectrumendtimearray) 00300 deallocate(wp%tin,wp%taperf,wp%taperw) 00301 deallocate(wp%fgen,wp%thetagen,wp%phigen,wp%kgen,wp%wgen) 00302 deallocate(wp%vargen) 00303 deallocate(wp%vargenq) 00304 deallocate(wp%Sfinterp) 00305 deallocate(wp%Sfinterpq) 00306 deallocate(wp%Hm0interp) 00307 deallocate(wp%A) 00308 deallocate(wp%Findex) 00309 deallocate(wp%CompFn) 00310 deallocate(wp%PRindex) 00311 if (par%nonhspectrum==0) then 00312 deallocate(wp%WDindex) 00313 if (par%swkhmin>0.d0) then 00314 deallocate(wp%zsits) 00315 deallocate(wp%uits) 00316 endif 00317 else 00318 deallocate(wp%zsits) 00319 deallocate(wp%uits) 00320 endif 00321 ! Send message to screen and log 00322 call writelog('l','','--------------------------------') 00323 call writelog('ls','','Spectral wave boundary conditions complete ') 00324 call writelog('l','','--------------------------------') 00325 endif ! reuseall 00326 00327 ! Collect new file identifiers for administration list files 00328 call generate_admin_files(par,wp,rtbc_local,dtbc_local,maindir_local,spectrumendtimeold,spectrumendtime) 00329 00330 ! Set output of the correct line 00331 curline = bccount 00332 endif 00333 00334 end subroutine spectral_wave_bc 00335 00336 ! -------------------------------------------------------------- 00337 ! ---------------- Read input spectra files -------------------- 00338 ! -------------------------------------------------------------- 00339 subroutine read_spectrum_input(par,wp,fn,specin) 00340 use params, only: parameters 00341 use filefunctions, only: create_new_fid 00342 use logging_module, only: report_file_read_error 00343 use paramsconst 00344 00345 implicit none 00346 ! Interface 00347 type(parameters),intent(in) :: par 00348 type(waveparamsnew),intent(inout) :: wp 00349 type(filenames),intent(inout) :: fn 00350 type(spectrum),intent(inout) :: specin 00351 ! internal 00352 integer :: fid 00353 character(8) :: testline 00354 character(slen) :: readfile 00355 logical :: filelist 00356 integer :: i,ier 00357 00358 ! check the first line of the boundary condition file for FILELIST keyword 00359 fid = create_new_fid() 00360 open(fid,file=fn%fname,status='old',form='formatted') 00361 read(fid,*,iostat=ier)testline 00362 if (ier .ne. 0) then 00363 call report_file_read_error(fn%fname) 00364 endif 00365 if (trim(testline)=='FILELIST') then 00366 filelist = .true. 00367 ! move listline off its default position of zero to the first row 00368 if (fn%listline==0) then 00369 fn%listline = 1 00370 endif 00371 fn%reuse = .false. 00372 else 00373 filelist = .false. 00374 if (par%wbctype /= WBCTYPE_JONS_TABLE) then 00375 fn%reuse = .true. 00376 else 00377 fn%reuse = .false. 00378 endif 00379 endif 00380 close(fid) 00381 ! If file has list of spectra, read through the lines to find the correct 00382 ! spectrum file name and rtbc and dtbc. Else the filename and rtbc, dtbc are in 00383 ! params.txt 00384 if (filelist) then 00385 fid = create_new_fid() 00386 open(fid,file=fn%fname,status='old',form='formatted') 00387 do i=1,fn%listline 00388 read(fid,*)testline ! old stuff, not needed anymore 00389 enddo 00390 read(fid,*,iostat=ier)wp%rtbc,wp%dtbc,readfile ! new boundary condition 00391 if (ier .ne. 0) then 00392 call report_file_read_error(fn%fname) 00393 endif 00394 ! we have to adjust this to morphological time, as done in params.txt 00395 if (par%morfacopt==1) then 00396 wp%rtbc = wp%rtbc/max(par%morfac,1.d0) 00397 endif 00398 fn%listline = fn%listline + 1 ! move one on from the last time we opened this file 00399 close(fid) 00400 else 00401 wp%rtbc = par%rt ! already set to morphological time 00402 wp%dtbc = par%dtbc 00403 readfile = fn%fname 00404 endif 00405 00406 ! based on the value of instat, we need to read either Jonswap, Swan or vardens files 00407 ! note: jons_table is also handeled by read_jonswap_file subroutine 00408 !select case (par%instat(1:4)) 00409 select case(par%wbctype) 00410 case (WBCTYPE_PARAMETRIC, WBCTYPE_JONS_TABLE) 00411 ! wp type sent in to receive rtbc and dtbc from jons_table file 00412 ! fn%listline sent in to find correct row in jons_table file 00413 ! pfff.. 00414 call read_jonswap_file(par,wp,readfile,fn%listline,specin) 00415 !case ('swan') 00416 case (WBCTYPE_SWAN) 00417 call read_swan_file(par,readfile,specin) 00418 !case ('vard') 00419 case (WBCTYPE_VARDENS) 00420 call read_vardens_file(par,readfile,specin) 00421 endselect 00422 00423 end subroutine read_spectrum_input 00424 00425 ! -------------------------------------------------------------- 00426 ! ------------------- Read JONSWAP files ----------------------- 00427 ! -------------------------------------------------------------- 00428 subroutine read_jonswap_file(par,wp,readfile,listline,specin) 00429 00430 use readkey_module, only: readkey_int, readkey_dbl, readkey_dblvec, isSetParameter, readkey_intvec, readkey 00431 use params, only: parameters 00432 use logging_module, only: writelog, report_file_read_error 00433 use filefunctions, only: create_new_fid 00434 use wave_functions_module, only: iteratedispersion 00435 use xmpi_module 00436 use paramsconst 00437 00438 IMPLICIT NONE 00439 00440 ! Input / output variables 00441 type(parameters), INTENT(IN) :: par 00442 type(waveparamsnew),intent(inout) :: wp 00443 character(len=*), intent(IN) :: readfile 00444 integer, intent(INOUT) :: listline 00445 type(spectrum),intent(inout) :: specin 00446 00447 ! Internal variables 00448 integer :: i,ii,ier,ip,ind 00449 integer :: nmodal 00450 integer :: fid 00451 integer :: forcepartition = -123 00452 real*8,dimension(:),allocatable :: x, y, Dd, tempdir 00453 real*8,dimension(:),allocatable :: Hm0,fp,gam,mainang,scoeff 00454 integer,dimension(:),allocatable :: tma 00455 real*8 :: dfj, fnyq 00456 real*8 :: Tp 00457 real*8 :: L,L0,sigmatma,k,n 00458 character(len=80) :: dummystring 00459 type(spectrum),dimension(:),allocatable :: multinomalspec 00460 00461 ! First part: read JONSWAP parameter data 00462 00463 ! Check whether spectrum characteristics or table should be used 00464 if (par%wbctype /= WBCTYPE_JONS_TABLE) then 00465 ! Use spectrum characteristics 00466 call writelog('sl','','waveparams: Reading from: ',trim(readfile),' ...') 00467 ! 00468 ! First read if the spectrum is multinodal, and how many peaks there should be 00469 ! 00470 nmodal = readkey_int (readfile, 'nmodal', 1, 1, 4, bcast=.false.) 00471 if (nmodal<1) then 00472 call writelog('lswe','','Error: number of spectral partions may not be less than 1 in ',trim(readfile)) 00473 call halt_program 00474 endif 00475 ! 00476 ! Allocate space for all spectral parameters 00477 ! 00478 allocate(Hm0(nmodal)) 00479 allocate(fp(nmodal)) 00480 allocate(gam(nmodal)) 00481 allocate(mainang(nmodal)) 00482 allocate(scoeff(nmodal)) 00483 allocate(tma(nmodal)) 00484 ! 00485 ! Read the spectral parameters for all spectrum components 00486 ! 00487 ! Wave height (required) 00488 Hm0 = readkey_dblvec(readfile, 'Hm0',nmodal,nmodal, 0.0d0, 0.0d0, 5.0d0, bcast=.false.,required=.true. ) 00489 ! 00490 ! Wave period (required) 00491 ! allow both Tp and fp specification to bring in line with params.txt 00492 if (isSetParameter(readfile,'Tp',bcast=.false.) .and. .not. isSetParameter(readfile,'fp',bcast=.false.)) then 00493 fp = 1.d0/readkey_dblvec(readfile, 'Tp',nmodal,nmodal, 12.5d0, 2.5d0, 20.0d0, bcast=.false.) 00494 elseif (isSetParameter(readfile,'fp',bcast=.false.) .and. .not. isSetParameter(readfile,'Tp',bcast=.false.)) then 00495 fp = readkey_dblvec(readfile, 'fp',nmodal,nmodal, 0.08d0,0.0625d0, 0.4d0, bcast=.false.) 00496 elseif (.not. isSetParameter(readfile,'fp',bcast=.false.) .and. .not. isSetParameter(readfile,'Tp',bcast=.false.)) then 00497 call writelog('lswe','','Error: missing required value for parameter ''Tp'' or ''fp'' in ',trim(readfile)) 00498 call halt_program 00499 else 00500 fp = 1.d0/readkey_dblvec(readfile, 'Tp',nmodal,nmodal, 12.5d0, 2.5d0, 20.0d0, bcast=.false.) 00501 call writelog('lsw','','Warning: selecting to read peak period (Tp) instead of frequency (fp) in ',trim(readfile)) 00502 endif 00503 ! 00504 ! Wave spreading in frequency domain (peakedness) 00505 ! 00506 gam = readkey_dblvec(readfile, 'gammajsp',nmodal,nmodal, 3.3d0, 1.0d0, 5.0d0, bcast=.false.) 00507 ! 00508 ! Wave spreading in directional domain 00509 ! 00510 scoeff = readkey_dblvec(readfile, 's',nmodal,nmodal, 10.0d0, 1.0d0, 1000.0d0, bcast=.false.) 00511 ! 00512 ! TMA 00513 ! 00514 tma = readkey_intvec(readfile, 'tma',nmodal,nmodal, 0, 0, 1, bcast=.false.) 00515 ! 00516 ! Main wave direction 00517 ! allow both mainang and dir0 specification to bring in line with params.txt 00518 if (isSetParameter(readfile,'mainang',bcast=.false.) .and. & 00519 .not. isSetParameter(readfile,'dir0',bcast=.false.)) then 00520 mainang = readkey_dblvec(readfile, 'mainang',nmodal,nmodal, & 00521 270.0d0, 0.0d0, 360.0d0, bcast=.false.) 00522 elseif (isSetParameter(readfile,'dir0',bcast=.false.) .and. & 00523 .not. isSetParameter(readfile,'mainang',bcast=.false.)) then 00524 mainang = readkey_dblvec(readfile, 'dir0',nmodal,nmodal, & 00525 270.0d0, 0.0d0, 360.0d0, bcast=.false.) 00526 elseif (.not. isSetParameter(readfile,'dir0',bcast=.false.) .and. & 00527 .not. isSetParameter(readfile,'mainang',bcast=.false.)) then 00528 mainang = 270.d0 00529 else 00530 mainang = readkey_dblvec(readfile, 'mainang',nmodal,nmodal, 270.0d0, 0.0d0, 360.0d0, bcast=.false.) 00531 call writelog('lsw','','Warning: selecting to read ''mainang'' instead of ''dir0'' in ',trim(readfile)) 00532 endif 00533 ! 00534 ! Nyquist parameters used only in this subroutine 00535 ! are not read individually for each spectrum partition 00536 fnyq = readkey_dbl (readfile, 'fnyq',max(0.3d0,3.d0*maxval(fp)), 0.2d0, 1.0d0, bcast=.false. ) 00537 dfj = readkey_dbl (readfile, 'dfj', fnyq/200, fnyq/1000, fnyq/20, bcast=.false. ) 00538 ! 00539 ! Finally check if XBeach should accept even the most stupid partioning (sets error level to warning 00540 ! level when computing partition overlap 00541 if (nmodal>1) then 00542 forcepartition = readkey_int (readfile, 'forcepartition', 0, 0, 1, bcast=.false.) 00543 endif 00544 ! check for other strange values in this file 00545 call readkey(readfile,'checkparams',dummystring) 00546 else 00547 nmodal = 1 00548 allocate(Hm0(nmodal)) 00549 allocate(fp(nmodal)) 00550 allocate(gam(nmodal)) 00551 allocate(mainang(nmodal)) 00552 allocate(scoeff(nmodal)) 00553 allocate(tma(nmodal)) 00554 ! Use spectrum table 00555 fid = create_new_fid() 00556 call writelog('sl','','waveparams: Reading from table: ',trim(readfile),' ...') 00557 open(fid,file=readfile,status='old',form='formatted') 00558 ! read junk up to the correct line in the file 00559 do i=1,listline 00560 read(fid,*,iostat=ier)dummystring 00561 if (ier .ne. 0) then 00562 call report_file_read_error(readfile) 00563 endif 00564 enddo 00565 read(fid,*,iostat=ier)Hm0(1),Tp,mainang(1),gam(1),scoeff(1),wp%rtbc,wp%dtbc 00566 if (ier .ne. 0) then 00567 call writelog('lswe','','Error reading file ',trim(readfile)) 00568 close(fid) 00569 call halt_program 00570 endif 00571 ! move the line pointer in the file 00572 listline = listline+1 00573 ! convert to morphological time 00574 if (par%morfacopt==1) then 00575 wp%rtbc = wp%rtbc/max(par%morfac,1.d0) 00576 endif 00577 fp(1)=1.d0/Tp 00578 fnyq=3.d0*fp(1) 00579 dfj=fp(1)/50 00580 tma(1) = 0 00581 close(fid) 00582 endif 00583 ! 00584 ! Second part: generate 2D spectrum from input parameters 00585 ! 00586 ! Define number of frequency bins by defining an array of the necessary length 00587 ! using the Nyquist frequency and frequency step size 00588 specin%nf = ceiling((fnyq-dfj)/dfj) 00589 specin%df = dfj 00590 ! 00591 ! Define array with actual eqidistant frequency bins 00592 allocate(specin%f(specin%nf)) 00593 do i=1,specin%nf 00594 specin%f(i)=i*dfj 00595 enddo 00596 ! 00597 ! we need a normalised frequency and variance vector for JONSWAP generation 00598 allocate(x(size(specin%f))) 00599 allocate(y(size(specin%f))) 00600 ! 00601 ! Define 200 directions relative to main angle running from 0 to 2*pi 00602 ! we also need a temporary vector for direction distribution 00603 specin%nang = naint 00604 allocate(tempdir(specin%nang)) 00605 allocate(Dd(specin%nang)) 00606 allocate(specin%ang(specin%nang)) 00607 specin%dang = 2*par%px/(naint-1) 00608 do i=1,specin%nang 00609 specin%ang(i)=(i-1)*specin%dang 00610 enddo 00611 ! 00612 ! Generate density spectrum for each spectrum partition 00613 ! 00614 allocate(multinomalspec(nmodal)) 00615 ! 00616 do ip=1,nmodal 00617 ! relative frequenct vector 00618 x=specin%f/fp(ip) 00619 ! Calculate unscaled and non-directional JONSWAP spectrum using 00620 ! peak-enhancement factor and pre-determined frequency bins 00621 call jonswapgk(x,gam(ip),y) 00622 ! Determine scaled and non-directional JONSWAP spectrum using the JONSWAP 00623 ! characteristics 00624 if (tma(ip)==1) then 00625 do ii=1,specin%nf 00626 L0 = par%g*(1/specin%f(ii))**2/2/par%px ! deep water wave length 00627 L = iteratedispersion(L0,L0,par%px,wp%h0) 00628 if (L<0.d0) then 00629 call writelog('lsw','','No dispersion convergence found for wave train ',i, & 00630 ' in boundary condition generation') 00631 L = -L 00632 endif 00633 k = 2*par%px/L 00634 ! h = wp%h0 00635 n = 0.5d0*(1+k*wp%h0*((1-tanh(k*wp%h0)**2)/(tanh(k*wp%h0)))) 00636 sigmatma = ((1/(2.d0*n))*tanh(k*wp%h0)**2) 00637 y(ii) = y(ii) * sigmatma 00638 enddo 00639 endif 00640 y=(Hm0(ip)/(4.d0*sqrt(sum(y)*dfj)))**2*y 00641 ! Convert main angle from degrees to radians and from nautical convention to 00642 ! internal grid 00643 mainang(ip)=(1.5d0*par%px)-mainang(ip)*par%px/180 00644 ! Make sure the main angle is defined between 0 and 2*pi 00645 do while (mainang(ip)>2*par%px .or. mainang(ip)<0.d0) !Robert en Ap 00646 if (mainang(ip)>2*par%px) then 00647 mainang(ip)=mainang(ip)-2*par%px 00648 elseif (mainang(ip)<0.d0) then 00649 mainang(ip)=mainang(ip)+2*par%px 00650 endif 00651 enddo 00652 ! Convert 200 directions relative to main angle to directions relative to 00653 ! internal grid 00654 ! Bas: apparently division by 2 for cosine law happens already here 00655 tempdir = (specin%ang-mainang(ip))/2 00656 ! Make sure all directions around the main angle are defined between 0 and 2*pi 00657 do while (any(tempdir>2*par%px) .or. any(tempdir<0.d0)) 00658 where (tempdir>2*par%px) 00659 tempdir=tempdir-2*par%px 00660 elsewhere (tempdir<0.d0) 00661 tempdir=tempdir+2*par%px 00662 endwhere 00663 enddo 00664 ! Calculate directional spreading based on cosine law 00665 Dd = dcos(tempdir)**(2*nint(scoeff(ip))) ! Robert: apparently nint is needed here, else MATH error 00666 ! Scale directional spreading to have a surface of unity by dividing by its 00667 ! own surface 00668 Dd = Dd / (sum(Dd)*specin%dang) 00669 ! Define two-dimensional variance density spectrum array and distribute 00670 ! variance density for each frequency over directional bins 00671 allocate(multinomalspec(ip)%S(specin%nf,specin%nang)) 00672 do i=1,specin%nang 00673 do ii=1,specin%nf 00674 multinomalspec(ip)%S(ii,i)=y(ii)*Dd(i) 00675 end do 00676 end do 00677 enddo ! 1,nmodal 00678 do ip=1,nmodal 00679 write(*,*)'Hm0 = ',4*sqrt(sum(multinomalspec(ip)%S)*specin%dang*specin%df) 00680 enddo 00681 ! 00682 ! Combine spectrum partitions so that the total spectrum is correct 00683 ! 00684 if (nmodal==1) then 00685 ! Set all the useful parameters and arrays 00686 specin%Hm0 = Hm0(1) 00687 specin%fp = fp(1) 00688 specin%dir0 = mainang(1) 00689 specin%scoeff = scoeff(1) 00690 allocate(specin%S(specin%nf,specin%nang)) 00691 specin%S = multinomalspec(1)%S 00692 else 00693 specin%Hm0 = sqrt(sum(Hm0**2)) ! total wave height 00694 ind = maxval(maxloc(Hm0)) ! spectrum that is largest 00695 specin%fp = fp(ind) ! not really used in further calculation 00696 specin%dir0 = mainang(ind) ! again not really used in further calculation 00697 ! if all scoeff>1000 then all waves should be in the same direction exactly 00698 if (all(scoeff>=1024.d0) .and. all(mainang==mainang(ind))) then 00699 specin%scoeff = 1024.d0 00700 else 00701 specin%scoeff = min(scoeff(ind),999.d0) 00702 endif 00703 ! 00704 ! Now set total spectrum by summation of all subspectra, no checks 00705 allocate(specin%S(specin%nf,specin%nang)) 00706 specin%S = 0.d0 00707 do ip=1,nmodal 00708 specin%S = specin%S+multinomalspec(ip)%S 00709 enddo 00710 !! Now we have to loop over all partitioned spectra. Where two or more spectra 00711 !! overlap, only the largest is counted, and all others are set to zero. Afterwards 00712 !! all spectra are scaled so that the total energy is maintained. Since the scaling 00713 !! affects where spectra overlap, this loop is repeated until only minor changes 00714 !! in the scaling occur. Warnings or errors are given if the spectrum is scaled too 00715 !! much, i.e. spectra overlap too much. 00716 !! 00717 !! Allocate space 00718 !allocate(scaledspec(nmodal)) ! used to store scaled density spectra 00719 !do ip=1,nmodal 00720 ! allocate(scaledspec(ip)%S(specin%nf,specin%nang)) 00721 ! scaledspec(ip)%S = multinomalspec(ip)%S 00722 !enddo 00723 !allocate(scalefac1(nmodal)) ! this is the scaling factor required to maintain Hm0 00724 ! ! including that parts of the spectrum are set to zero 00725 !scalefac1 = 1.d0 00726 !allocate(scalefac2(nmodal)) ! this is the scaling factor required to maintain Hm0 00727 ! ! in the previous iteration 00728 !scalefac2 = 1.d0 00729 !allocate(avgscale(nmodal)) 00730 !avgscale = 1.d0 00731 !! these are convergence criteria 00732 !newconv = 0.d0 00733 !oldconv = huge(0.d0) 00734 !cont = .true. 00735 !allocate(tempmax(nmodal)) ! used to store maximum value in f,theta space 00736 !allocate(oldvariance(nmodal)) ! used to store the sum of variance in the original partitions 00737 !do ip=1,nmodal 00738 ! oldvariance(ip) = sum(multinomalspec(ip)%S) 00739 !enddo 00740 !allocate(newvariance(nmodal)) ! used to store the sum of variance in the new partitions 00741 !! 00742 !! Start convergence loop 00743 !do while (cont) 00744 ! avgscale = (avgscale+scalefac1)/2 00745 ! ! First scale the spectra 00746 ! do ip=1,nmodal 00747 ! ! scale the spectrum using less than half the additional scale factor, this to 00748 ! ! ensure the method does not become unstable 00749 ! ! scaledspec(ip)%S = multinomalspec(ip)%S*(0.51d0+scalefac1(ip)*0.49d0) 00750 ! ! scaledspec(ip)%S = multinomalspec(ip)%S*(scalefac1(ip)+scalefac2(ip))/2 00751 ! scaledspec(ip)%S = multinomalspec(ip)%S*avgscale(ip) 00752 ! 00753 ! enddo 00754 ! do i=1,specin%nang 00755 ! do ii=1,specin%nf 00756 ! ! vector of variance densities at this point in f,theta space 00757 ! do ip=1,nmodal 00758 ! tempmax(ip) = scaledspec(ip)%S(ii,i) 00759 ! end do 00760 ! ! All spectra other than the one that is greatest in this point 00761 ! ! is set to zero. In case multiple spectra are equal largest, 00762 ! ! the first spectrum in the list is chosen (minval(maxloc)) 00763 ! ind = minval(maxloc(tempmax)) 00764 ! do ip=1,nmodal 00765 ! if (ip/=ind) then 00766 ! scaledspec(ip)%S(ii,i) = 0.d0 00767 ! endif 00768 ! enddo 00769 ! enddo 00770 ! end do 00771 ! ! 00772 ! ! Now rescale adjusted partition spectra so that they match the incident Hm0 00773 ! scalefac2 = scalefac1 ! keep previous results 00774 ! do ip=1,nmodal 00775 ! newvariance(ip) = sum(scaledspec(ip)%S) 00776 ! if (newvariance(ip)>0.01d0*oldvariance(ip)) then 00777 ! scalefac1(ip) = oldvariance(ip)/newvariance(ip) ! want to maximise to a factor 2 00778 ! ! else can generate rediculous results 00779 ! else 00780 ! scalefac1(ip) = 0.d0 ! completely remove this spectrum 00781 ! endif 00782 ! enddo 00783 ! ! 00784 ! ! check convergence criteria (if error is increasing, we have passed best fit) 00785 ! newconv = maxval(abs(scalefac2-scalefac1)/scalefac1) 00786 ! if (newconv<0.0001d0 .or. abs(newconv-oldconv)<0.0001d0) then 00787 ! cont = .false. 00788 ! endif 00789 ! oldconv = newconv 00790 !end do 00791 !! Ensure full energy conservation by scaling now according to full scalefac, not just the 00792 !! half used in the iteration loop 00793 !do ip=1,nmodal 00794 ! scaledspec(ip)%S = multinomalspec(ip)%S*scalefac1(ip) 00795 !enddo 00796 !! 00797 !! Now check the total scaling that has taken place across the spectrum, also accounting 00798 !! for the fact that some parts of the spectra are set to zero. This differs from the 00799 !! scalefac1 and scalefac2 vectors in the loop that only adjust the part of the spectrum 00800 !! that is not zero. 00801 !do ip=1,nmodal 00802 ! write(*,*)'Hm0m = ',4*sqrt(sum(scaledspec(ip)%S)*specin%dang*specin%df) 00803 !enddo 00804 !do ip=1,nmodal 00805 ! indvec = maxloc(scaledspec(ip)%S) 00806 ! scalefac1(ip) = scaledspec(ip)%S(indvec(1),indvec(2)) / & 00807 ! multinomalspec(ip)%S(indvec(1),indvec(2))-1.d0 00808 !enddo 00809 !! 00810 !! Warning and/or error criteria here if spectra overlap each other too much 00811 !do ip=1,nmodal 00812 ! if(scalefac1(ip)>0.5d0) then 00813 ! if (forcepartition==1) then 00814 ! call writelog('lsw','(a,f0.0,a,i0,a,a,a)', & 00815 ! 'Warning: ',scalefac1(ip)*100,'% of energy in spectrum partition ''',ip, & 00816 ! ''' in ', trim(readfile),' is overlapped by other partitions') 00817 ! call writelog('lsw','',' Check spectral partitioning in ',trim(readfile)) 00818 ! else 00819 ! call writelog('lswe','(a,f0.0,a,i0,a,a,a)', & 00820 ! 'Error: ',scalefac1(ip)*100,'% of energy in spectrum partition ''',ip, & 00821 ! ''' in ', trim(readfile),' is overlapped by other partitions') 00822 ! call writelog('lswe','','This spectrum overlaps too much with another spectrum partition.', & 00823 ! ' Check spectral partitioning in ',trim(readfile)) 00824 ! call writelog('lswe','','If the partitioning should be carried out regardles of energy loss, ', & 00825 ! 'set ''forcepartition = 1'' in ',trim(readfile)) 00826 ! call halt_program 00827 ! endif 00828 ! elseif (scalefac1(ip)>0.2d0 .and. scalefac1(ip)<=0.5d0) then 00829 ! call writelog('lsw','(a,f0.0,a,i0,a,a,a)', & 00830 ! 'Warning: ',scalefac1(ip)*100,'% of energy in spectrum partition ''',ip, & 00831 ! ''' in ', trim(readfile),' is overlapped by other partitions') 00832 ! call writelog('lsw','',' Check spectral partitioning in ',trim(readfile)) 00833 ! elseif (scalefac1(ip)<0.d0) then 00834 ! call writelog('lsw','(a,i0,a,a,a)','Warning: spectrum partition ''',ip,''' in ',trim(readfile), & 00835 ! ' has been removed') 00836 ! call writelog('lsw','','This spectrum is entirely overlapped by another spectrum partition.',& 00837 ! ' Check spectral partitioning in ',trim(readfile)) 00838 ! endif 00839 !enddo 00840 !! 00841 !! Now set total spectrum 00842 !allocate(specin%S(specin%nf,specin%nang)) 00843 !specin%S = 0.d0 00844 !do ip=1,nmodal 00845 ! specin%S = specin%S+scaledspec(ip)%S 00846 !enddo 00847 ! 00848 !deallocate(scaledspec) 00849 !deallocate(tempmax) 00850 !deallocate(scalefac1,scalefac2) 00851 !deallocate(newvariance,oldvariance) 00852 endif 00853 00854 ! We need frequency spectrum to ensure Sf remains correct between interpoloation 00855 ! routines 00856 allocate(specin%Sf(specin%nf)) 00857 specin%Sf=0.d0 00858 do i=1,specin%nf 00859 do ii=1,specin%nang 00860 if (ii==1) then 00861 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(2)-specin%ang(1)) 00862 elseif (ii==specin%nang) then 00863 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(ii)-specin%ang(ii-1)) 00864 else 00865 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(ii+1)-specin%ang(ii-1))/2 00866 endif 00867 enddo 00868 enddo 00869 00870 deallocate (Dd) 00871 deallocate(Hm0,fp,gam,mainang,scoeff,tma) 00872 deallocate (x,y) 00873 deallocate(tempdir) 00874 ! TODO dereference pointers first.... 00875 do ip=1,nmodal 00876 deallocate(multinomalspec(ip)%S) 00877 end do 00878 deallocate(multinomalspec) 00879 00880 00881 end subroutine read_jonswap_file 00882 00883 00884 00885 ! ----------------------------------------------------------- 00886 ! --------- JONSWAP unscaled JONSWAP spectrum -------------- 00887 ! -------------(used by read_jonswap_files)------------------ 00888 subroutine jonswapgk(x,gam,y) 00889 00890 IMPLICIT NONE 00891 ! Required input: - x : nondimensional frequency, divided by the peak frequency 00892 ! - gam : peak enhancement factor, optional parameter (DEFAULT 3.3) 00893 ! - y is output : nondimensional relative spectral density, equal to one at the peak 00894 00895 real*8, INTENT(IN) :: gam 00896 real*8,dimension(:), INTENT(IN) :: x 00897 real*8,dimension(:), INTENT(INOUT) :: y 00898 00899 ! Internal variables 00900 real*8,dimension(size(x)) :: xa, sigma, fac1, fac2, fac3, temp 00901 00902 xa=abs(x) 00903 00904 where (xa==0) 00905 xa=1e-20 00906 end where 00907 00908 sigma=xa 00909 00910 where (sigma<1.) 00911 sigma=0.07 00912 end where 00913 00914 where (sigma>=1.) 00915 sigma=0.09 00916 end where 00917 00918 temp=0*xa+1 00919 00920 fac1=xa**(-5) 00921 fac2=exp(-1.25*(xa**(-4))) 00922 fac3=(gam*temp)**(exp(-((xa-1)**2)/(2*(sigma**2)))) 00923 00924 y=fac1*fac2*fac3 00925 y=y/maxval(y) 00926 00927 return 00928 00929 end subroutine jonswapgk 00930 00931 00932 ! -------------------------------------------------------------- 00933 ! ----------------------Read SWAN files ------------------------ 00934 ! -------------------------------------------------------------- 00935 subroutine read_swan_file(par,readfile,specin) 00936 00937 use params, only: parameters 00938 use logging_module, only: writelog, report_file_read_error 00939 use filefunctions, only: create_new_fid 00940 use xmpi_module, only: Halt_Program 00941 use math_tools, only: flipv,flipa 00942 00943 IMPLICIT NONE 00944 00945 ! Input / output variables 00946 type(parameters), INTENT(IN) :: par 00947 character(len=*), intent(IN) :: readfile 00948 type(spectrum),intent(inout) :: specin 00949 00950 ! Internal variables 00951 character(6) :: rtext 00952 real*8 :: factor,exc 00953 integer :: fid,switch 00954 integer :: i,ii,ier,ier2,ier3 00955 logical :: flipped 00956 00957 flipped =.false. 00958 switch = 0 00959 00960 call writelog('sl','','Reading from SWAN file: ',trim(readfile),' ...') 00961 fid = create_new_fid() 00962 open(fid,file=readfile,form='formatted',status='old') 00963 00964 ! Read file until RFREQ or AFREQ is found 00965 do while (switch==0) 00966 read(fid,'(a)',iostat=ier)rtext 00967 if (ier .ne. 0) then 00968 call report_file_read_error(readfile) 00969 endif 00970 if (rtext == 'RFREQ ') then 00971 switch = 1 00972 elseif (rtext == 'AFREQ ') then 00973 switch = 2 00974 end if 00975 end do 00976 00977 ! Read nfreq and f 00978 ! Note f is not monotonically increasing in most simulations 00979 read(fid,*,iostat=ier)specin%nf 00980 if (ier .ne. 0) then 00981 call report_file_read_error(readfile) 00982 endif 00983 allocate(specin%f(specin%nf)) 00984 do i=1,specin%nf 00985 read(fid,*,iostat=ier)specin%f(i) 00986 if (ier .ne. 0) then 00987 call report_file_read_error(readfile) 00988 endif 00989 end do 00990 00991 ! Convert to absolute frequencies: 00992 ! STILL TO BE DONE 00993 if (switch == 1) then 00994 specin%f = specin%f 00995 else 00996 specin%f = specin%f 00997 end if 00998 00999 ! Read CDIR or NDIR 01000 read(fid,'(a)',iostat=ier)rtext 01001 if (ier .ne. 0) then 01002 call report_file_read_error(readfile) 01003 endif 01004 if (rtext == 'NDIR ') then 01005 switch = 1 01006 elseif (rtext == 'CDIR ') then 01007 switch = 2 01008 else 01009 call writelog('ewls','', 'SWAN directional bins keyword not found') 01010 call halt_program 01011 endif 01012 01013 ! Read ndir, theta 01014 read(fid,*,iostat=ier)specin%nang 01015 if (ier .ne. 0) then 01016 call report_file_read_error(readfile) 01017 endif 01018 allocate(specin%ang(specin%nang)) 01019 do i=1,specin%nang 01020 read(fid,*,iostat=ier)specin%ang(i) 01021 if (ier .ne. 0) then 01022 call report_file_read_error(readfile) 01023 endif 01024 end do 01025 01026 ! Convert angles to cartesian degrees relative to East 01027 if (switch == 1) then 01028 ! nautical to cartesian East 01029 specin%ang = 270.d0-specin%ang 01030 else 01031 ! cartesian to cartesian East 01032 specin%ang = specin%ang-par%dthetaS_XB 01033 ! dthetaS_XB is the (counter-clockwise) angle in the degrees to rotate from the x-axis in SWAN to the 01034 ! x-axis pointing East 01035 end if 01036 01037 ! Ensure angles are increasing instead of decreasing 01038 if (specin%ang(2)<specin%ang(1)) then 01039 call flipv(specin%ang,size(specin%ang)) 01040 flipped=.true. 01041 end if 01042 01043 ! convert to radians 01044 specin%ang=specin%ang*par%px/180 01045 specin%dang=specin%ang(2)-specin%ang(1) 01046 01047 ! Skip Quant, next line, read VaDens or EnDens 01048 read(fid,'(a)',iostat=ier)rtext 01049 read(fid,'(a)',iostat=ier2)rtext 01050 read(fid,'(a)',iostat=ier3)rtext 01051 if (ier+ier2+ier3 .ne. 0) then 01052 call report_file_read_error(readfile) 01053 endif 01054 if (rtext == 'VaDens') then 01055 switch = 1 01056 elseif (rtext == 'EnDens') then 01057 switch = 2 01058 else 01059 call writelog('slwe','', 'SWAN VaDens/EnDens keyword not found') 01060 call halt_program 01061 end if 01062 read(fid,'(a)',iostat=ier)rtext 01063 read(fid,*,iostat=ier2)exc 01064 if (ier+ier2 .ne. 0) then 01065 call report_file_read_error(readfile) 01066 endif 01067 01068 i=0 01069 ! Find FACTOR keyword 01070 do while (i==0) 01071 read(fid,'(a)',iostat=ier)rtext 01072 if (ier .ne. 0) then 01073 call report_file_read_error(readfile) 01074 endif 01075 if (rtext == 'FACTOR') then 01076 i=1 01077 elseif (rtext == 'ZERO ') then 01078 call writelog('lswe','','Zero energy density input for this point') 01079 call halt_program 01080 elseif (rtext == 'NODATA') then 01081 call writelog('lwse','','SWAN file has no data for this point') 01082 call halt_program 01083 end if 01084 end do 01085 read(fid,*,iostat=ier)factor 01086 if (ier .ne. 0) then 01087 call report_file_read_error(readfile) 01088 endif 01089 01090 ! Read 2D S array 01091 allocate(specin%S(specin%nf,specin%nang)) 01092 do i=1,specin%nf 01093 read(fid,*,iostat=ier)(specin%S(i,ii),ii=1,specin%nang) 01094 if (ier .ne. 0) then 01095 call report_file_read_error(readfile) 01096 endif 01097 end do 01098 01099 ! Finished reading file 01100 close(fid) 01101 01102 ! Replace exception value 01103 where (specin%S == exc) 01104 specin%S = 0.d0 01105 endwhere 01106 01107 ! If angles were decreasing, flip S_array as also dir is flipped 01108 if (flipped) then 01109 call flipa(specin%S,specin%nf,specin%nang,2) 01110 end if 01111 01112 ! multiply by SWAN output factor 01113 specin%S=specin%S*factor 01114 01115 ! Convert from energy density to variance density 01116 if (switch == 2) then 01117 specin%S=specin%S/(par%rho*par%g) 01118 end if 01119 01120 ! Convert to m2/Hz/rad 01121 specin%S=specin%S*180/par%px 01122 01123 ! We need a value for spreading. The assumption is that it is less than 1000 01124 ! This way, wp%fgen will not be set to just one angle. 01125 specin%scoeff = -1.d0 01126 ! We need to know if hm0 was set explicitly, not the case for Swan files 01127 specin%hm0 = -1.d0 01128 01129 ! We need frequency spectrum to ensure Sf remains correct between interpoloation 01130 ! routines 01131 allocate(specin%Sf(specin%nf)) 01132 specin%Sf=0.d0 01133 do i=1,specin%nf 01134 do ii=1,specin%nang 01135 if (ii==1) then 01136 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(2)-specin%ang(1)) 01137 elseif (ii==specin%nang) then 01138 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(ii)-specin%ang(ii-1)) 01139 else 01140 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(ii+1)-specin%ang(ii-1))/2 01141 endif 01142 enddo 01143 enddo 01144 01145 end subroutine read_swan_file 01146 01147 01148 ! -------------------------------------------------------------- 01149 ! -----------------Read variance density files ----------------- 01150 ! -------------------------------------------------------------- 01151 subroutine read_vardens_file(par,readfile,specin) 01152 01153 use params, only: parameters 01154 use logging_module, only: writelog, report_file_read_error 01155 use filefunctions, only: create_new_fid 01156 use xmpi_module, only: Halt_Program 01157 use math_tools, only: flipv,flipa 01158 01159 IMPLICIT NONE 01160 01161 ! Input / output variables 01162 type(parameters), INTENT(IN) :: par 01163 character(len=*), intent(IN) :: readfile 01164 type(spectrum),intent(inout) :: specin 01165 01166 ! Internal variables 01167 integer :: fid,i,ii,nnz,ier 01168 01169 ! Open file to start read 01170 call writelog('sl','','Reading from vardens file: ',trim(readfile),' ...') 01171 fid = create_new_fid() 01172 open(fid,file=readfile,form='formatted',status='old') 01173 01174 ! Read number of frequencies and frequency vector 01175 read(fid,*,iostat=ier)specin%nf 01176 if (ier .ne. 0) then 01177 call report_file_read_error(readfile) 01178 endif 01179 allocate(specin%f(specin%nf)) 01180 do i=1,specin%nf 01181 read(fid,*,iostat=ier)specin%f(i) 01182 if (ier .ne. 0) then 01183 call report_file_read_error(readfile) 01184 endif 01185 end do 01186 01187 ! Read number of angles and angles vector 01188 read(fid,*,iostat=ier)specin%nang 01189 if (ier .ne. 0) then 01190 call report_file_read_error(readfile) 01191 endif 01192 allocate(specin%ang(specin%nang)) 01193 do i=1,specin%nang 01194 read(fid,*,iostat=ier)specin%ang(i) 01195 if (ier .ne. 0) then 01196 call report_file_read_error(readfile) 01197 endif 01198 end do 01199 01200 ! Convert from degrees to rad 01201 specin%ang=specin%ang*par%px/180 01202 specin%dang=specin%ang(2)-specin%ang(1) 01203 01204 ! Read 2D S array 01205 allocate(specin%S(specin%nf,specin%nang)) 01206 do i=1,specin%nf 01207 read(fid,*,iostat=ier)(specin%S(i,ii),ii=1,specin%nang) 01208 if (ier .ne. 0) then 01209 call report_file_read_error(readfile) 01210 endif 01211 end do 01212 01213 ! Finished reading file 01214 close(fid) 01215 01216 ! Convert to m2/Hz/rad 01217 specin%S=specin%S*180/par%px 01218 01219 ! We need a value for spreading. The assumption is that it is less than 1000 01220 ! if more than one direction column has variance. If only one column has variance 01221 ! we assume the model requires long crested waves, and wp%thetagen should be exactly 01222 ! equal to the input direction. We then also need to find the dominant angle. 01223 ! First flatten spectrum to direction only, then determine if all but one are zero, 01224 ! use the direction of the non-zero as the main wave direction, set spreading to 01225 ! a high value (greater than 1000). Else set spreading to a negative value. 01226 allocate(specin%Sd(specin%nang)) 01227 specin%Sd = sum(specin%S,DIM = 1) 01228 nnz = count(specin%Sd>0.d0) 01229 if (nnz == 1) then 01230 specin%scoeff = 1024.d0 01231 specin%dir0 = specin%ang(minval(maxloc(specin%Sd))) 01232 else 01233 specin%scoeff = -1.d0 01234 endif 01235 01236 ! We need frequency spectrum to ensure Sf remains correct between interpoloation 01237 ! routines 01238 allocate(specin%Sf(specin%nf)) 01239 specin%Sf=0.d0 01240 do i=1,specin%nf 01241 do ii=1,specin%nang 01242 if (ii==1) then 01243 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(2)-specin%ang(1)) 01244 elseif (ii==specin%nang) then 01245 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(ii)-specin%ang(ii-1)) 01246 else 01247 specin%Sf(i) = specin%Sf(i)+specin%S(i,ii)*abs(specin%ang(ii+1)-specin%ang(ii-1))/2 01248 endif 01249 enddo 01250 enddo 01251 01252 01253 ! We need to know if hm0 was set explicitly, not the case for vardens files 01254 specin%hm0 = -1.d0 01255 01256 end subroutine read_vardens_file 01257 01258 01259 ! -------------------------------------------------------------- 01260 ! ------------- Interpolate to standard spectrum --------------- 01261 ! -------------------------------------------------------------- 01262 subroutine interpolate_spectrum(specin,specinterp,par,fmax) 01263 01264 use interp, only: linear_interp, interp_in_cyclic_function, interp_using_trapez_rule 01265 use params, only: parameters 01266 01267 IMPLICIT NONE 01268 01269 ! input/output 01270 type(spectrum),intent(in) :: specin 01271 type(spectrum),intent(inout) :: specinterp 01272 type(parameters),intent(in) :: par 01273 real*8, intent(in) :: fmax 01274 ! internal 01275 integer :: i,j,dummy 01276 real*8 :: m0,df,dang 01277 ! real*8,dimension(naint) :: Sd 01278 real*8 :: hm0pre,hm0post,Sfnow,factor,xcycle 01279 real*8, dimension(:,:),allocatable :: Stemp 01280 01281 ! allocate size of f,ang,Sf and S arrays in specinterp 01282 allocate(specinterp%f(nfint)) 01283 allocate(specinterp%ang(naint)) 01284 allocate(specinterp%Sf(nfint)) 01285 allocate(specinterp%Sd(naint)) 01286 allocate(specinterp%S(nfint,naint)) 01287 allocate(Stemp(specin%nf,naint)) 01288 01289 ! fill f and ang arrays 01290 specinterp%nf = nfint 01291 specinterp%df = fmax/(nfint-1) ! this usually gives a range of 0-1Hz, 01292 ! with 1/800Hz resolution 01293 do i=1,nfint 01294 specinterp%f(i)=(i-1)*specinterp%df 01295 enddo 01296 specinterp%nang = naint 01297 specinterp%dang = 2*par%px/(naint-1) ! this is exactly the same as in the JONSWAP construction 01298 do i=1,specinterp%nang 01299 specinterp%ang(i)=(i-1)*specinterp%dang 01300 enddo 01301 01302 ! If hm0 was set explicitly, then use that, else calculate hm0 01303 if (specin%hm0>0.d0) then 01304 hm0pre = specin%hm0 01305 else 01306 ! pre-interpolation hm0 value (can be on a non-monotonic f,ang grid) 01307 m0 = 0 01308 do j=1,specin%nang 01309 do i=1,specin%nf 01310 df = specin%f(max(2,i))-specin%f(max(1,i-1)) 01311 dang = specin%ang(max(2,j))-specin%ang(max(1,j-1)) 01312 m0 = m0 + specin%S(i,j)*df*dang 01313 enddo 01314 enddo 01315 hm0pre = 4*sqrt(m0) 01316 endif 01317 01318 ! interpolation (no extrapolation) of input 2D spectrum to standard 2D spectrum 01319 ! do j=1,naint 01320 ! do i=1,nfint 01321 ! call linear_interp_2d(specin%f,specin%nf,specin%ang,specin%nang,specin%S, & 01322 ! specinterp%f(i),specinterp%ang(j),specinterp%S(i,j),'interp',0.d0) 01323 ! enddo 01324 ! enddo 01325 xcycle=2.d0*par%px 01326 do i=1,specin%nf 01327 call interp_in_cyclic_function(specin%ang,specin%S(i,:),specin%nang,xcycle,specinterp%ang,naint,Stemp(i,:)) 01328 enddo 01329 do j=1,naint 01330 do i=1,nfint 01331 if (specinterp%f(i)>specin%f(specin%nf).or.specinterp%f(i)<specin%f(1) ) then 01332 specinterp%S(i,j)=0.d0 01333 else 01334 call linear_interp(specin%f,Stemp(:,j),specin%nf,specinterp%f(i),specinterp%S(i,j),dummy) 01335 endif 01336 end do 01337 enddo 01338 01339 deallocate(Stemp) 01340 ! hm0 post (is always on a monotonic f,ang grid 01341 m0 = sum(specinterp%S)*specinterp%df*specinterp%dang 01342 hm0post = 4*sqrt(m0) 01343 01344 ! correct the wave energy to ensure hm0post == hm0pre 01345 ! specinterp%S = (hm0pre/hm0post)**2*specinterp%S 01346 ! This is done later using Sf 01347 01348 ! calculate 1D spectrum, summed over directions 01349 specinterp%Sf = sum(specinterp%S, DIM = 2)*specinterp%dang 01350 01351 ! correct the wave energy by setting Sfpost == Sfpre 01352 do i=1,nfint 01353 if (specinterp%f(i)>=minval(specin%f) .and. specinterp%f(i)<=maxval(specin%f)) then 01354 call LINEAR_INTERP(specin%f,specin%Sf,specin%nf,specinterp%f(i),Sfnow,dummy) 01355 if (specinterp%Sf(i)>0.d0 .and. Sfnow>0.d0) then 01356 factor = Sfnow/specinterp%Sf(i) 01357 specinterp%Sf(i) = specinterp%Sf(i)*factor 01358 specinterp%S(i,:) = specinterp%S(i,:)*factor 01359 elseif (specinterp%Sf(i)==0.d0 .and. Sfnow>=0.d0) then 01360 specinterp%Sf(i) = Sfnow 01361 dummy = maxval(maxloc(specinterp%S(i,:))) 01362 specinterp%S(i,dummy) = Sfnow/specinterp%dang 01363 else 01364 specinterp%Sf(i) = 0.d0 01365 specinterp%S(i,:) = 0.d0 01366 endif 01367 endif 01368 enddo 01369 01370 ! calculate other wave statistics from interpolated spectrum 01371 m0 = sum(specinterp%Sf)*specinterp%df 01372 specinterp%hm0 = 4*sqrt(m0) 01373 specinterp%Sd = sum(specinterp%S, DIM = 1)*specinterp%df 01374 i = maxval(maxloc(specinterp%Sd)) 01375 specinterp%dir0 = 270.d0 - specinterp%ang(i)*180/par%px ! converted back into nautical degrees 01376 i = maxval(maxloc(specinterp%Sf)) 01377 specinterp%fp = specinterp%f(i) 01378 call tpDcalc(specinterp%Sf,specinterp%f,specinterp%trep,par%trepfac,par%Tm01switch) 01379 specinterp%dirm = 270.d0-180.d0/par%px*atan2( sum(sin(specinterp%ang)*specinterp%Sd)/sum(specinterp%Sd),& 01380 sum(cos(specinterp%ang)*specinterp%Sd)/sum(specinterp%Sd) ) 01381 01382 specinterp%dirm = mod(specinterp%dirm,360.d0) 01383 01384 end subroutine interpolate_spectrum 01385 01386 ! -------------------------------------------------------------- 01387 ! ----------- Small subroutine to determine if the ------------ 01388 ! ---------- global reuseall should be true or false ----------- 01389 subroutine set_reuseall 01390 01391 implicit none 01392 ! internal 01393 integer :: i 01394 01395 ! set default to reuse all boundary conditions 01396 reuseall = .true. 01397 ! find any spectrum that cannot be reused, then change global 01398 ! reuseall to false 01399 do i=1,nspectra 01400 if (.not. bcfiles(i)%reuse) then 01401 reuseall = .false. 01402 endif 01403 enddo 01404 01405 end subroutine set_reuseall 01406 01407 ! -------------------------------------------------------------- 01408 ! ----------- Small subroutine to set the filenames ------------ 01409 ! ----------- of the boundary condition output files ----------- 01410 subroutine set_bcfilenames(wp) 01411 01412 implicit none 01413 ! input/output 01414 type(waveparamsnew),intent(inout) :: wp 01415 ! internal 01416 integer :: i1,i2,i3,i4,i5 01417 01418 if (reuseall) then 01419 wp%Efilename = 'E_reuse.bcf' 01420 wp%Esfilename = 'Es_reuse.bcf' 01421 wp%qfilename = 'q_reuse.bcf' 01422 wp%nhfilename = 'nh_reuse.bcf' 01423 else 01424 i1=floor(real(bccount)/10000) 01425 i2=floor(real(bccount-i1*10000)/1000) 01426 i3=floor(real(bccount-i1*10000-i2*1000)/100) 01427 i4=floor(real(bccount-i1*10000-i2*1000-i3*100)/10) 01428 i5=bccount-i1*10000-i2*1000-i3*100-i4*10 01429 wp%Efilename='E_series'//char(48+i1)//char(48+i2)//char(48+i3)//char(48+i4)//char(48+i5)//'.bcf' 01430 wp%Esfilename='Es_series'//char(48+i1)//char(48+i2)//char(48+i3)//char(48+i4)//char(48+i5)//'.bcf' 01431 wp%qfilename='q_series'//char(48+i1)//char(48+i2)//char(48+i3)//char(48+i4)//char(48+i5)//'.bcf' 01432 wp%nhfilename='nh_series'//char(48+i1)//char(48+i2)//char(48+i3)//char(48+i4)//char(48+i5)//'.bcf' 01433 endif 01434 01435 end subroutine set_bcfilenames 01436 01437 ! -------------------------------------------------------------- 01438 ! ----------- Merge all separate spectra into one -------------- 01439 ! -------------- average spectrum for other use ---------------- 01440 subroutine generate_combined_spectrum(specinterp,combspec,px,trepfac,Tm01switch) 01441 01442 implicit none 01443 type(spectrum),dimension(nspectra),intent(in) :: specinterp 01444 type(spectrum),intent(inout) :: combspec 01445 real*8, intent(in) :: px,trepfac 01446 integer,intent(in) :: Tm01switch 01447 integer :: iloc 01448 ! real*8,dimension(naint) :: Sd 01449 real*8,dimension(3) :: peakSd,peakang 01450 real*8,dimension(:),allocatable :: tempSf 01451 01452 allocate(combspec%f(nfint)) 01453 allocate(combspec%Sf(nfint)) 01454 allocate(combspec%Sd(naint)) 01455 allocate(combspec%ang(naint)) 01456 allocate(combspec%S(nfint,naint)) 01457 combspec%f=specinterp(1)%f 01458 combspec%nf=nfint 01459 combspec%df=specinterp(1)%df 01460 combspec%ang=specinterp(1)%ang 01461 combspec%nang=naint 01462 combspec%dang=specinterp(1)%dang 01463 combspec%trep = 0.d0 01464 combspec%S=0.d0 01465 combspec%Sf=0.d0 01466 01467 do iloc = 1,nspectra 01468 !combspec%trep = combspec%trep+specinterp(iloc)%trep/nspectra 01469 combspec%S = combspec%S +specinterp(iloc)%S /nspectra 01470 combspec%Sf = combspec%Sf +specinterp(iloc)%Sf /nspectra 01471 call tpDcalc(combspec%Sf,combspec%f,combspec%trep,trepfac,Tm01switch) 01472 enddo 01473 ! 01474 ! Calculate peak wave angle 01475 ! 01476 ! frequency integrated variance array 01477 combspec%Sd = sum(combspec%S,DIM=1)*combspec%df 01478 ! peak location of f-int array233333333 01479 iloc = maxval(maxloc(combspec%Sd)) 01480 ! pick two neighbouring directional bins, including effect of closing circle at 01481 ! 0 and 2pi rad 01482 if (iloc>1 .and. iloc<naint) then 01483 peakSd = (/combspec%Sd(iloc-1),combspec%Sd(iloc),combspec%Sd(iloc+1)/) 01484 peakang = (/combspec%ang(iloc-1),combspec%ang(iloc),combspec%ang(iloc+1)/) 01485 elseif (iloc==1) then 01486 peakSd = (/combspec%Sd(naint),combspec%Sd(1),combspec%Sd(2)/) 01487 peakang = (/combspec%ang(naint)-2*px,combspec%ang(1),combspec%ang(2)/) 01488 elseif (iloc==naint) then 01489 peakSd = (/combspec%Sd(naint-1),combspec%Sd(naint),combspec%Sd(1)/) 01490 peakang = (/combspec%ang(naint-1),combspec%ang(naint),combspec%ang(1)+2*px/) 01491 endif 01492 ! dir0 calculated as mean over peak and two neighbouring cells 01493 combspec%dir0 = sum(peakSd*peakang)/sum(peakSd) 01494 ! return to 0<=dir0<=2*px 01495 if (combspec%dir0>2*px) then 01496 combspec%dir0 = combspec%dir0-2*px 01497 elseif (combspec%dir0<0) then 01498 combspec%dir0 = combspec%dir0+2*px 01499 endif 01500 ! 01501 ! Compute peak wave frequency 01502 ! 01503 allocate(tempSf(size(combspec%Sf))) 01504 tempSf=0*combspec%Sf 01505 ! find frequency range of 95% wave energy around peak 01506 where (combspec%Sf>0.95d0*maxval(combspec%Sf)) 01507 tempSf=combspec%Sf 01508 end where 01509 ! smoothed peak is weighted mean frequency of 95% range 01510 combspec%fp = sum(tempSf*combspec%f)/sum(tempSf) 01511 end subroutine generate_combined_spectrum 01512 01513 01514 ! -------------------------------------------------------------- 01515 ! -------------- Calculate average water depth ----------------- 01516 ! ------------------ on offshore boundary ---------------------- 01517 pure function calculate_average_water_depth(s) result(h) 01518 01519 use spaceparamsdef, only: spacepars 01520 01521 implicit none 01522 01523 ! input 01524 type(spacepars),intent(in) :: s 01525 ! output 01526 real*8 :: h 01527 ! internal 01528 01529 h = sum((s%zs0(1,:)-s%zb(1,:))*s%dnc(1,:))/sum(s%dnc(1,:)) 01530 01531 end function calculate_average_water_depth 01532 01533 01534 ! -------------------------------------------------------------- 01535 ! ----------- Choose wave train components based on ------------ 01536 ! --------------------- combined spectrum ---------------------- 01537 subroutine generate_wavetrain_components(combspec,wp,par,x0,y0,xe,ye) 01538 01539 use logging_module, only: writelog 01540 use params, only: parameters 01541 use interp, only: linear_interp 01542 use wave_functions_module, only: iteratedispersion 01543 use math_tools, only: random 01544 01545 implicit none 01546 ! input/output 01547 type(spectrum),intent(in) :: combspec 01548 type(waveparamsnew),intent(inout) :: wp 01549 type(parameters),intent(in) :: par 01550 ! internal 01551 integer :: i,ii 01552 integer :: ind1,ind2,dummy,seed,ni 01553 real*8,dimension(:),allocatable :: randnums,pdflocal,cdflocal 01554 real*8 :: L0,L,kmax,fmax 01555 real*8, intent(in) :: x0,y0,xe,ye 01556 real*8 :: dx,dy,L_grid,alfa_grid,alfa_adj,alfa_wave,L_proj,L_wave,nr,L_proj_adj 01557 logical :: cont 01558 01559 01560 ! If we are running non-hydrostatic boundary conditions, we want to remove 01561 ! very high frequency components, as these will not be computed well anyway 01562 if (par%nonhspectrum==1) then 01563 kmax = wdmax/wp%h0 01564 fmax = par%g*kmax*tanh(kmax*wp%h0)/2/par%px 01565 else 01566 ! this is really already taken into account 01567 ! by specifying par%sprdthr 01568 fmax = 2*maxval(combspec%f) 01569 endif 01570 01571 ! Determine frequencies around peak frequency of one-dimensional 01572 ! non-directional variance density spectrum, based on factor sprdthr, which 01573 ! should be included in the determination of the wave boundary conditions 01574 call frange(par,combspec%f,combspec%Sf,fmax,ind1,ind2) 01575 01576 ! Calculate number of wave components to be included in determination of the 01577 ! wave boundary conditions based on the wave record length and width of the 01578 ! wave frequency range 01579 wp%K = ceiling(wp%rtbc*(combspec%f(ind2)-combspec%f(ind1))+1) 01580 ! also include minimum number of components 01581 wp%K = max(wp%K,Kmin) 01582 01583 01584 ! Allocate space in waveparams for all wave train components 01585 allocate(wp%fgen(wp%K)) 01586 allocate(wp%thetagen(wp%K)) 01587 allocate(wp%phigen(wp%K)) 01588 allocate(wp%kgen(wp%K)) 01589 allocate(wp%wgen(wp%K)) 01590 01591 ! Select equidistant wave components between the earlier selected range of 01592 ! frequencies around the peak frequency in the frange subfunction 01593 wp%dfgen = (combspec%f(ind2)-combspec%f(ind1))/(wp%K-1) 01594 do i=1,wp%K 01595 wp%fgen(i)=combspec%f(ind1)+(i-1)*wp%dfgen 01596 enddo 01597 01598 ! This subroutine needs to generate random phase / directions for the individual 01599 ! wave trains. Due to some strange Fortran properties, it is better to select 01600 ! one long vector with random numbers for both the phase and the direction, than 01601 ! one vector for each. 01602 ! Update random seed, if requested 01603 allocate(randnums(2*wp%K)) 01604 if (par%random==1) then 01605 CALL init_seed(seed) 01606 randnums(1) = random(seed) 01607 else 01608 randnums(1) = random(100) 01609 endif 01610 !call random_number(randnums) 01611 do i=2,2*wp%K 01612 randnums(i) = random(0) 01613 enddo 01614 01615 ! Determine a random phase for each wave train component between 0 and 2pi 01616 wp%phigen=randnums(1:wp%K)*2*par%px 01617 01618 ! Determine random directions for each wave train component, based on the CDF of 01619 ! the directional spectrum. For each wave train we will interpolate the directional 01620 ! distribution at that frequency, generate a CDF, and then interpolate the wave 01621 ! train direction from a random number draw and the CDF. 01622 allocate(pdflocal(naint)) 01623 allocate(cdflocal(naint)) 01624 do i=1,wp%K 01625 ! interpolate spectrum at this frequency per angle in the spectrum 01626 do ii=1,naint 01627 call LINEAR_INTERP(combspec%f,combspec%S(:,ii),combspec%nf, wp%fgen(i),pdflocal(ii),dummy) 01628 enddo 01629 ! convert to pdf by ensuring total integral == 1, assuming constant directional bin size 01630 pdflocal = pdflocal/sum(pdflocal) 01631 ! convert to cdf by trapezoidal integration, which is not biased for integration 01632 ! direction. 01633 ! Boundary condition, which may be nonzero: 01634 cdflocal(1) = pdflocal(1) 01635 do ii=2,naint 01636 cdflocal(ii) = cdflocal(ii-1) + (pdflocal(ii)+pdflocal(ii-1))/2 01637 ! Note: this only works if the directional 01638 ! bins are constant in size. Assumed multiplication 01639 ! by one. 01640 enddo 01641 ! interpolate random number draw across the cdf. Note that cdf(1) is not assumed to be zero and 01642 ! there is a posibility of drawing less than cdf(1). Therefore, if the random number is .lt. cdf(1), 01643 ! interpolation should take place across the back of the angle spectrum 01644 ! (i.e., from theta(end)-2pi : theta(1)). 01645 ! Note that we are using the second half of randnums now (K+1:end) 01646 if (randnums(wp%K+i)>=cdflocal(1)) then 01647 call LINEAR_INTERP(cdflocal,combspec%ang,naint,randnums(wp%K+i), wp%thetagen(i),dummy) 01648 else 01649 call LINEAR_INTERP((/0.d0,cdflocal(1)/),(/combspec%ang(naint)-2*par%px,combspec%ang(1)/), & 01650 2,randnums(wp%K+i),wp%thetagen(i),dummy) 01651 if (wp%thetagen(i)<0.d0) then 01652 wp%thetagen(i)=wp%thetagen(i)+2*par%px 01653 endif 01654 endif 01655 enddo 01656 01657 01658 ! determine wave number for each wave train component, using standard dispersion relation 01659 ! solver from wave_functions module. This function returns a negative wave length if the 01660 ! solver did not converge. 01661 do i=1,wp%K 01662 L0 = par%g*(1/wp%fgen(i))**2/2/par%px ! deep water wave length 01663 L = iteratedispersion(L0,L0,par%px,wp%h0) 01664 if (L<0.d0) then 01665 call writelog('lsw','','No dispersion convergence found for wave train ',i, & 01666 ' in boundary condition generation') 01667 L = -L 01668 endif 01669 wp%kgen(i) = 2*par%px/L 01670 enddo 01671 01672 ! Angular frequency 01673 wp%wgen = 2*par%px*wp%fgen 01674 01675 ! In cyclic models, default should adjust wave directions so that an integer number of waves fit in the domain 01676 if(par%cyclicdiradjust==1) then 01677 dx = xe-x0 01678 dy = ye-y0 01679 L_grid = sqrt(dx**2+dy**2) 01680 ! First check: L_grid greater than zero (not a fully circular model) 01681 if (L_grid>1.d-3) then 01682 alfa_grid = atan2(-dx,dy) ! note: not dy,dx 01683 do i=1,wp%K 01684 alfa_wave = wp%thetagen(i)-alfa_grid 01685 L_wave = 2*par%px/wp%kgen(i) 01686 L_proj = L_wave/sin(alfa_wave) 01687 nr = L_grid/L_proj 01688 ni = nint(nr) 01689 ni = min(abs(ni),floor(L_grid/L_wave))*sign(1,ni) 01690 ! cont = .true. 01691 ! do while (cont .and. ni .ne. 0) 01692 ! L_waveadj = L_grid/ni 01693 ! if (abs(L_waveadj)>L_wave) then 01694 ! ni = ni-1*sign(1.d0,nr) 01695 ! else 01696 ! cont = .false. 01697 ! endif 01698 ! enddo 01699 if (ni .ne. 0) then 01700 L_proj_adj = L_grid/ni 01701 alfa_adj = asin(L_wave/L_proj_adj)+alfa_grid 01702 else 01703 alfa_adj = alfa_grid 01704 endif 01705 wp%thetagen(i) = alfa_adj 01706 enddo 01707 else 01708 ! do nothing 01709 endif 01710 endif 01711 01712 01713 ! Free memory 01714 deallocate(pdflocal,cdflocal,randnums) 01715 01716 end subroutine generate_wavetrain_components 01717 01718 ! ----------------------------------------------------------- 01719 ! ----------- Small subroutine to determine ----------------- 01720 ! ------------ representative wave period ------------------- 01721 subroutine tpDcalc(Sf,f,Trep,trepfac,switch) 01722 01723 implicit none 01724 01725 real*8, dimension(:), intent(in) :: Sf, f 01726 real*8, intent(out) :: Trep 01727 real*8, intent(in) :: trepfac 01728 integer, intent(in) :: switch 01729 01730 real*8, dimension(:),allocatable :: temp 01731 01732 allocate(temp(size(Sf))) 01733 temp=0.d0 01734 where (Sf>=trepfac*maxval(Sf)) 01735 temp=1.d0 01736 end where 01737 01738 if (switch == 1) then 01739 Trep=sum(temp*Sf)/sum(temp*Sf*f) ! Tm01 01740 else 01741 Trep = sum(temp*Sf/max(f,0.001d0))/sum(temp*Sf) ! Tm-1,0 01742 endif 01743 01744 deallocate(temp) 01745 01746 end subroutine tpDcalc 01747 01748 01749 ! ----------------------------------------------------------- 01750 ! ---- Small subroutine to determine f-range round peak ----- 01751 ! ----------------------------------------------------------- 01752 subroutine frange(par,f,Sf,fmax,firstp,lastp,findlineout) 01753 01754 use params, only: parameters 01755 01756 implicit none 01757 01758 type(parameters),intent(in) :: par 01759 real*8, dimension(:), intent(in) :: f,Sf 01760 real*8,intent(in) :: fmax 01761 01762 integer, intent(out) :: firstp, lastp 01763 01764 real*8, dimension(:), intent(out),allocatable,optional :: findlineout 01765 real*8, dimension(:),allocatable :: temp, findline 01766 integer :: i = 0 01767 01768 ! find frequency range around peak 01769 allocate(findline(size(Sf))) 01770 findline=0*Sf 01771 where (Sf>par%sprdthr*maxval(Sf)) 01772 findline=1 01773 end where 01774 01775 ! find frequency range below fmax 01776 where(f>fmax) 01777 findline = 0 01778 endwhere 01779 01780 firstp=maxval(maxloc(findline)) ! Picks the first "1" in temp 01781 01782 allocate (temp(size(findline))) 01783 temp=(/(i,i=1,size(findline))/) 01784 lastp=maxval(maxloc(temp*findline)) ! Picks the last "1" in temp 01785 01786 if (present(findlineout)) then 01787 allocate(findlineout(size(Sf))) 01788 findlineout=findline 01789 endif 01790 deallocate(temp, findline) 01791 01792 end subroutine frange 01793 01794 ! ----------------------------------------------------------- 01795 ! --- Small subroutine to reseed random number generator ---- 01796 ! ----------------------------------------------------------- 01797 subroutine init_seed(outseed) 01798 INTEGER :: i, n, clock 01799 INTEGER, DIMENSION(:), ALLOCATABLE :: seed 01800 integer,intent(out) :: outseed 01801 ! RANDOM_SEED does not result in a random seed for each compiler, so we better make sure that we have a pseudo random seed. 01802 ! I am not sure what is a good n 01803 n = 40 01804 i = 1 01805 ! not sure what size means here 01806 ! first call with size 01807 CALL RANDOM_SEED(size = n) 01808 ! define a seed array of size n 01809 ALLOCATE(seed(n)) 01810 ! what time is it 01811 CALL SYSTEM_CLOCK(COUNT=clock) 01812 ! define the seed vector based on a prime, the clock and the set of integers 01813 seed = clock + 37 * (/ (i - 1, i = 1, n) /) 01814 ! if mpi do we need a different seed on each node or the same??? 01815 ! if we do need different seeds on each node 01816 ! seed *= some big prime * rank ? 01817 ! now use the seed 01818 CALL RANDOM_SEED(PUT = seed) 01819 outseed = seed(1) 01820 end subroutine init_seed 01821 01822 01823 ! -------------------------------------------------------------- 01824 ! ---------------- Setup time axis for waves ------------------- 01825 ! -------------------------------------------------------------- 01826 subroutine generate_wave_time_axis(par,wp) 01827 01828 use params, only: parameters 01829 01830 implicit none 01831 ! input/output 01832 type(waveparamsnew),intent(inout) :: wp 01833 type(parameters),intent(in) :: par 01834 ! internal 01835 integer :: ntaper, indend 01836 integer :: i 01837 01838 01839 01840 ! First assume that internal and bc-writing time step is the same 01841 wp%dtin = wp%dtbc 01842 wp%dtchanged = .false. 01843 wp%tslenbc = nint(wp%rtbc/wp%dtbc)+1 01844 01845 ! Check whether the internal frequency is high enough to describe the highest frequency 01846 ! wave train returned from frange (which can be used in the boundary conditions) 01847 if (par%nonhspectrum==0) then 01848 if (wp%dtin>0.5d0/wp%fgen(wp%K)) then 01849 wp%dtin = 0.5d0/wp%fgen(wp%K) 01850 wp%dtchanged = .true. 01851 endif 01852 else 01853 if (wp%dtin>0.1d0/wp%fgen(wp%K)) then 01854 wp%dtin = 0.1d0/wp%fgen(wp%K) 01855 wp%dtchanged = .true. 01856 endif 01857 endif 01858 01859 ! The length of the internal time axis should be even (for Fourier transform) and 01860 ! depends on the internal time step needed and the internal duration (~1/dfgen): 01861 wp%tslen = ceiling(1/wp%dfgen/wp%dtin)+1 01862 if (mod(wp%tslen,2)/=0) then 01863 wp%tslen = wp%tslen +1 01864 end if 01865 01866 ! Now we can make the internal time axis 01867 wp%rtin = wp%tslen * wp%dtin 01868 allocate(wp%tin(wp%tslen)) 01869 do i=1,wp%tslen 01870 wp%tin(i) = (i-1)*wp%dtin 01871 enddo 01872 01873 ! Make a taper function to slowly increase and decrease the boundary condition forcing 01874 ! at the start and the end of the boundary condition file (including any time beyond 01875 ! the external rtbc 01876 allocate(wp%taperf(wp%tslen)) 01877 allocate(wp%taperw(wp%tslen)) 01878 ! fill majority with unity 01879 wp%taperf = 1.d0 01880 wp%taperw = 1.d0 01881 if (par%nonhspectrum==1) then 01882 ! begin taper by building up the wave conditions over 2 wave periods 01883 ntaper = nint((2.0d0*par%Trep)/wp%dtin) 01884 else 01885 ntaper = nint((5.d0*par%Trep)/wp%dtin) 01886 endif 01887 do i=1,min(ntaper,size(wp%taperf)) 01888 wp%taperf(i) = tanh(5.d0*i/ntaper) ! multiplied by five because tanh(5)=~1 01889 wp%taperw(i) = tanh(5.d0*i/ntaper) 01890 enddo 01891 ! We do not want to taperw the end anymore. Instead we pass the wave height at the end of rtbc to 01892 ! the next wave generation iteration. 01893 ! end taper by finding where tin=rtbc, taper before that and set everything to zero after 01894 ! that. 01895 if (wp%tin(wp%tslen)>wp%rtbc) then 01896 indend = minval(minloc(wp%tin,MASK = wp%tin>=wp%rtbc)) 01897 else 01898 indend = wp%tslen 01899 endif 01900 do i=1,min(ntaper,indend) 01901 wp%taperf(indend+1-i) = min(wp%taperf(indend+1-i),tanh(5.d0*i/ntaper)) 01902 enddo 01903 wp%taperf(indend:wp%tslen) = 0.d0 01904 ind_end_taper = indend 01905 end subroutine generate_wave_time_axis 01906 01907 01908 ! -------------------------------------------------------------- 01909 ! -------- Calculate variance at each spectrum location -------- 01910 ! -------------------------------------------------------------- 01911 subroutine generate_wave_train_variance(wp,specinterp) 01912 01913 use interp, only: linear_interp 01914 01915 01916 implicit none 01917 ! input/output 01918 type(waveparamsnew),intent(inout) :: wp 01919 type(spectrum),dimension(nspectra),intent(in):: specinterp 01920 ! internal 01921 integer :: i,ii, dummy 01922 real*8 :: hm0post 01923 01924 ! allocate space for the variance arrays 01925 allocate(wp%vargen(nspectra)) 01926 allocate(wp%vargenq(nspectra)) 01927 01928 ! Determine variance at each spectrum location 01929 do i=1,nspectra 01930 allocate(wp%vargen(i)%Sf(wp%K)) 01931 allocate(wp%vargenq(i)%Sf(wp%K)) 01932 do ii=1,wp%K 01933 ! In order to maintain energy density per frequency, an interpolation 01934 ! is carried out on the input directional variance density spectrum 01935 ! at the frequency and direction locations in fgen. This must be done 01936 ! over the 2D spectrum, not 1D spectrum. 01937 ! call linear_interp_2d(specinterp(i)%f,nfint, & ! input frequency, size 01938 ! specinterp(i)%ang,naint, & ! input angles, size 01939 ! specinterp(i)%S*specinterp(i)%dang, & ! input variance density times grid angle resolution 01940 ! wp%fgen(ii),wp%thetagen(ii), & ! output frquency/angle 01941 ! wp%vargen(i)%Sf(ii), & ! output variance density 01942 ! 'interp',0.d0) ! method and exception return value 01943 call linear_interp(specinterp(i)%f, & 01944 specinterp(i)%Sf, & 01945 nfint, & 01946 wp%fgen(ii), & 01947 wp%vargen(i)%Sf(ii),dummy) 01948 enddo 01949 ! Correct variance to ensure the total variance remains the same as the total variance in the 01950 ! (interpolated) input spectrum 01951 hm0post = 4*sqrt(sum(wp%vargen(i)%Sf)*wp%dfgen) 01952 wp%vargen(i)%Sf = (specinterp(i)%hm0/hm0post)**2*wp%vargen(i)%Sf 01953 ! For the generation of long waves we cannot use wp%vargen%Sf, because it contains an overestimation 01954 ! of energy in the peak frequencies. We can also not use the standard directionally-integrated spectrum 01955 ! because this stores all energy at fgen(K), where it is possible that for this current spectrum, there 01956 ! is no energy at S(f(K),theta(K0)) 01957 ! The current solution is to take the minimum of both methods 01958 do ii=1,wp%K 01959 ! Map Sf input to fgen 01960 call LINEAR_INTERP(specinterp(i)%f,specinterp(i)%Sf,nfint,wp%fgen(ii),wp%vargenq(i)%Sf(ii),dummy) 01961 enddo 01962 wp%vargenq(i)%Sf = min(wp%vargen(i)%Sf,wp%vargenq(i)%Sf) 01963 enddo 01964 01965 end subroutine generate_wave_train_variance 01966 01967 ! -------------------------------------------------------------- 01968 ! ------ Calculate amplitudes at each spectrum location -------- 01969 ! ------------ for every wave train component ------------------ 01970 subroutine generate_wave_train_properties_per_offshore_point(wp,s,nonhspectrum,swkhmin) 01971 01972 use spaceparamsdef, only: spacepars 01973 use interp, only: linear_interp 01974 01975 implicit none 01976 ! input/output 01977 type(waveparamsnew),intent(inout) :: wp 01978 type(spacepars),intent(in) :: s 01979 integer,intent(in) :: nonhspectrum 01980 real*8,intent(in) :: swkhmin 01981 ! internal 01982 integer :: i,ii,dummy 01983 integer,dimension(2) :: interpindex 01984 real*8,dimension(2) :: positions,sf 01985 logical :: interpcase 01986 real*8 :: here,sfnow,sfimp 01987 integer,dimension(size(n_index_loc)) :: temp_index_loc 01988 01989 interpindex = -123 01990 ! allocate space for the amplitude array and representative integration angle 01991 allocate(wp%A(s%ny+1,wp%K)) 01992 ! allocate(wp%danggen(s%ny+1)) 01993 allocate(wp%Sfinterp(s%ny+1,wp%K)) 01994 allocate(wp%Sfinterpq(s%ny+1,wp%K)) 01995 allocate(wp%Hm0interp(s%ny+1)) 01996 01997 ! where necessary, interpolate Sf of each spectrum location 01998 ! to the current grid cell, and use this to calculate A 01999 do i=1,s%ny+1 02000 ! Four possibilities: 02001 ! 1: this exact point has an input spectrum 02002 ! 2: this point lies between two input spectra 02003 ! 3: this point lies before any input spectrum 02004 ! 4: this point lies beyond any input spectrum 02005 if (any(n_index_loc==i)) then ! case 1 02006 interpcase = .false. 02007 do ii=1,nspectra 02008 if(n_index_loc(ii)==i) then 02009 interpindex = ii 02010 endif 02011 enddo 02012 elseif(i<minval(n_index_loc)) then ! case 3 02013 interpcase = .false. 02014 do ii=1,nspectra 02015 if(n_index_loc(ii)==minval(n_index_loc)) then 02016 interpindex = ii 02017 endif 02018 enddo 02019 elseif(i>maxval(n_index_loc)) then ! case 4 02020 interpcase = .false. 02021 do ii=1,nspectra 02022 if(n_index_loc(ii)==maxval(n_index_loc)) then 02023 interpindex = ii 02024 endif 02025 enddo 02026 else ! case 2 02027 interpcase = .true. 02028 temp_index_loc = n_index_loc 02029 where(n_index_loc>i) 02030 temp_index_loc = -huge(0) 02031 endwhere 02032 interpindex(1) = minval(maxloc(temp_index_loc)) 02033 02034 temp_index_loc = n_index_loc 02035 where(n_index_loc<i) 02036 temp_index_loc = huge(0) 02037 endwhere 02038 interpindex(2) = minval(minloc(temp_index_loc)) 02039 endif 02040 02041 ! interpolate Sf 02042 if (interpcase) then 02043 positions(1) = s%ndist(1,n_index_loc(interpindex(1))) 02044 positions(2) = s%ndist(1,n_index_loc(interpindex(2))) 02045 here = s%ndist(1,i) 02046 do ii=1,wp%K 02047 sf(1) = wp%vargen(interpindex(1))%Sf(ii) 02048 sf(2) = wp%vargen(interpindex(2))%Sf(ii) 02049 call LINEAR_INTERP(positions,sf,2,here,wp%Sfinterp(i,ii),dummy) 02050 sf(1) = wp%vargenq(interpindex(1))%Sf(ii) 02051 sf(2) = wp%vargenq(interpindex(2))%Sf(ii) 02052 call LINEAR_INTERP(positions,sf,2,here,wp%Sfinterpq(i,ii),dummy) 02053 enddo 02054 ! Ensure that the total amount of variance has not been reduced/increased 02055 ! during interpolation for short wave energy only. This is not done for 02056 ! Sf for bound long wave generation 02057 sfnow = sum(wp%Sfinterp(i,:)) ! integration over fixed bin df size unnecessary 02058 sf(1) = sum(wp%vargen(interpindex(1))%Sf) 02059 sf(2) = sum(wp%vargen(interpindex(2))%Sf) 02060 call LINEAR_INTERP(positions,sf,2,here,sfimp,dummy) 02061 wp%Sfinterp(i,:)=wp%Sfinterp(i,:)*sfimp/sfnow 02062 else 02063 wp%Sfinterp(i,:) = wp%vargen(interpindex(1))%Sf 02064 wp%Sfinterpq(i,:) = wp%vargenq(interpindex(1))%Sf 02065 endif 02066 02067 wp%A(i,:) = sqrt(2*wp%Sfinterp(i,:)*wp%dfgen) 02068 02069 wp%Hm0interp(i) = 4*sqrt(sum(wp%Sfinterp(i,:))*wp%dfgen) 02070 02071 ! determine if these components will be pahse-resolved, or resolved in the wave energy balance 02072 allocate(wp%PRindex(wp%K)) 02073 if (nonhspectrum==1) then 02074 ! all components phase-resolved 02075 wp%PRindex = 1 02076 else 02077 if(swkhmin>0.d0) then 02078 ! some components resolved, some not 02079 where(wp%kgen*wp%h0 >= swkhmin) 02080 wp%PRindex = 0 02081 elsewhere 02082 wp%PRindex = 1 02083 endwhere 02084 else 02085 ! no components phase-resolved 02086 wp%PRindex = 0 02087 endif 02088 endif 02089 02090 02091 enddo ! i=1,s%ny+1 02092 02093 end subroutine generate_wave_train_properties_per_offshore_point 02094 02095 ! -------------------------------------------------------------- 02096 ! --------- Calculate Fourier componets for each wave ---------- 02097 ! ---------- train component on the offshore boundary ---------- 02098 subroutine generate_wave_train_Fourier(wp,s,par) 02099 02100 use spaceparamsdef, only: spacepars 02101 use math_tools, only: fftkind, flipiv 02102 use params, only: parameters 02103 use logging_module, only: writelog, progress_indicator 02104 use logging_module 02105 02106 implicit none 02107 ! input/output 02108 type(waveparamsnew),intent(inout) :: wp 02109 type(spacepars),intent(in) :: s 02110 type(parameters),intent(in) :: par 02111 ! internal 02112 integer :: i,ii,tempi 02113 complex(fftkind),dimension(:),allocatable :: tempcmplx 02114 02115 ! Determine indices of wave train components in frequency axis and 02116 ! Fourier transform result 02117 allocate(wp%Findex(wp%K)) 02118 tempi = floor(wp%fgen(1)/wp%dfgen) 02119 do i=1,wp%K 02120 wp%Findex(i) = tempi + i 02121 enddo 02122 02123 ! Allocate Fourier coefficients for each y-position at the offshore boundary and 02124 ! each time step 02125 allocate(wp%CompFn(s%ny+1,wp%tslen)) 02126 wp%CompFn=0.d0 02127 allocate(tempcmplx(wp%tslen/2-1)) 02128 call writelog('ls','','Calculating Fourier components') 02129 call progress_indicator(.true.,0.d0,5.d0,2.d0) 02130 do i=1,wp%K 02131 call progress_indicator(.false.,dble(i)/wp%K*100,5.d0,2.d0) 02132 do ii=1,s%ny+1 02133 ! Determine first half of complex Fourier coefficients of wave train 02134 ! components using random phase and amplitudes from sampled spectrum 02135 ! until Nyquist frequency. The amplitudes are represented in a 02136 ! two-sided spectrum, which results in the factor 1/2. 02137 ! Unroll Fourier components along offshore boundary, assuming all wave trains 02138 ! start at x(1,1), y(1,1). 02139 wp%CompFn(ii,wp%Findex(i)) = wp%A(ii,i)/2*exp(compi*wp%phigen(i))* & ! Bas: wp%Findex used in time dimension because t=j*dt in frequency space 02140 exp(-compi*wp%kgen(i)*(dsin(wp%thetagen(i))*(s%yz(1,ii)-s%yz(1,1)) & 02141 +dcos(wp%thetagen(i))*(s%xz(1,ii)-s%xz(1,1))) ) 02142 02143 ! Determine Fourier coefficients beyond Nyquist frequency (equal to 02144 ! coefficients at negative frequency axis) of relevant wave components for 02145 ! first y-coordinate by mirroring 02146 tempcmplx = conjg(wp%CompFn(ii,2:wp%tslen/2)) 02147 call flipiv(tempcmplx,size(tempcmplx)) 02148 wp%CompFn(ii,wp%tslen/2+2:wp%tslen)=tempcmplx 02149 enddo 02150 enddo 02151 02152 ! Free memory 02153 deallocate(tempcmplx) 02154 02155 end subroutine generate_wave_train_Fourier 02156 02157 ! -------------------------------------------------------------- 02158 ! --------- Calculate in which computational wave bin ---------- 02159 ! ------------- each wave train component belongs -------------- 02160 subroutine distribute_wave_train_directions(wp,s,px,nspr,nonhspec) 02161 02162 use spaceparams 02163 use logging_module 02164 02165 implicit none 02166 02167 ! input/output 02168 type(waveparamsnew),intent(inout) :: wp 02169 type(spacepars),intent(in) :: s 02170 real*8,intent(in) :: px 02171 integer,intent(in) :: nspr 02172 logical,intent(in) :: nonhspec 02173 ! internal 02174 integer :: i,ii 02175 real*8,dimension(:),allocatable :: binedges 02176 logical :: toosmall,toolarge 02177 real*8 :: lostvar,keptvar,perclost 02178 integer :: lntheta ! local copies that can be changed without 02179 real*8 :: lthetamin,ldtheta ! damage to the rest of the model 02180 02181 ! Set basic parameters for comparison if using nonh spectrum 02182 if (nonhspec) then 02183 lntheta = 1 02184 lthetamin = -px 02185 ldtheta = 2*px 02186 else 02187 lntheta = s%ntheta 02188 lthetamin = s%thetamin 02189 ldtheta = s%dtheta 02190 endif 02191 02192 ! Calculate the bin edges of all the computational wave bins in the 02193 ! XBeach model (not the input spectrum) 02194 allocate(binedges(lntheta+1)) 02195 do i=1,lntheta+1 02196 binedges(i) = lthetamin+(i-1)*ldtheta 02197 enddo 02198 02199 ! Try to fit as many wave train components as possible between the 02200 ! minimum and maximum bin edges by adding/subtracting 2pi rad. This 02201 ! solves the problem of XBeach not including energy in the computation 02202 ! than should be in there. For instance XBeach does not realize that 02203 ! 90 degrees == -270 degrees == 450 degrees. 02204 ! Note: this does not ensure all energy is included in the wave bins, 02205 ! as wave energy may still fall outside the computational domain. 02206 do i=1,wp%K 02207 if (wp%thetagen(i)>maxval(binedges)) then 02208 toosmall = .false. 02209 toolarge = .true. 02210 elseif (wp%thetagen(i)<minval(binedges)) then 02211 toosmall = .true. 02212 toolarge = .false. 02213 else 02214 toosmall = .false. 02215 toolarge = .false. 02216 endif 02217 ! Continue to reduce/increase wave direction until 02218 ! condition is not met. 02219 do while (toosmall) 02220 wp%thetagen(i) = wp%thetagen(i) + 2*px 02221 toosmall = wp%thetagen(i) < minval(binedges) 02222 enddo 02223 do while (toolarge) 02224 wp%thetagen(i) = wp%thetagen(i) - 2*px 02225 toolarge = wp%thetagen(i) > maxval(binedges) 02226 enddo 02227 enddo 02228 02229 ! distribute the wave train directions among the wave directional bins, 02230 ! store the corresponding bin per wave train in WDindex. 02231 allocate(wp%WDindex(wp%K)) 02232 wp%WDindex = 0 02233 do i=1,wp%K 02234 if (wp%thetagen(i) < minval(binedges)) then 02235 ! remove from computation if wave direction still not within computational 02236 ! bounds 02237 wp%WDindex(i) = 0 02238 elseif (wp%thetagen(i) > maxval(binedges)) then 02239 wp%WDindex(i) = lntheta+1 02240 elseif (wp%thetagen(i) == binedges(lntheta+1) ) then 02241 ! catch upper boundary, and add to upper bin 02242 wp%WDindex(i) = lntheta 02243 else 02244 ! fit this wave train component into one of the lower bins 02245 do ii=1,lntheta 02246 if (wp%thetagen(i)>=binedges(ii) .and. wp%thetagen(i)<binedges(ii+1)) then 02247 wp%WDindex(i) = ii 02248 endif 02249 enddo 02250 endif 02251 enddo 02252 02253 ! If the user has set nspr == 1 then the randomly drawn wave directions 02254 ! should be set to the centres of the wave directional bins. 02255 ! Also move all wave energy falling outside the computational bins, into 02256 ! the computational domain (in the outer wave direction bins) 02257 if (nspr==1) then 02258 if (nonhspec) then 02259 ! We only really want to include all energy between -90 and +90 degrees. 02260 ! Waves outside this direction are sent out of the model 02261 where(wp%WDindex==1) 02262 wp%thetagen = 0.d0 02263 elsewhere 02264 wp%thetagen = -px 02265 endwhere 02266 else 02267 do i=1,wp%K 02268 if (wp%WDindex(i)==0) then 02269 wp%WDindex(i)=1 02270 elseif (wp%WDindex(i)>lntheta) then 02271 wp%WDindex(i)=lntheta 02272 endif 02273 ! reset the direction of this wave train to the centre of the bin 02274 wp%thetagen(i)=s%theta(wp%WDindex(i)) 02275 enddo 02276 endif 02277 endif 02278 02279 ! Check the amount of energy lost to wave trains falling outside the computational 02280 ! domain 02281 lostvar = 0.d0 02282 keptvar = 0.d0 02283 do i=1,wp%K 02284 if (wp%WDindex(i)==0) then 02285 lostvar = lostvar + sum(wp%A(:,i)**2) 02286 else 02287 keptvar = keptvar + sum(wp%A(:,i)**2) 02288 endif 02289 enddo 02290 perclost = 100*(lostvar/(lostvar+keptvar)) 02291 if (perclost>5.0d0) then 02292 call writelog('lsw','(a,f0.1,a)','Large amounts of energy (',perclost, & 02293 '%) fall outside computational domain at the offshore boundary') 02294 call writelog('lsw','','Check specification of input wave angles and wave directional grid') 02295 else 02296 call writelog('ls','(a,f0.1,a)','Wave energy outside computational domain at offshore boundary: ',perclost,'%') 02297 endif 02298 02299 ! Free memory 02300 deallocate(binedges) 02301 02302 end subroutine distribute_wave_train_directions 02303 02304 ! -------------------------------------------------------------- 02305 ! --------- Calculate energy envelope time series from --------- 02306 ! -------- Fourier components, and write to output file -------- 02307 subroutine generate_ebcf(wp,s,par) 02308 02309 use params, only: parameters 02310 use spaceparamsdef, only: spacepars 02311 use math_tools, only: fftkind, fft, flipiv, hilbert 02312 use interp, only: linear_interp 02313 use logging_module, only: writelog, progress_indicator 02314 use filefunctions, only: create_new_fid 02315 02316 implicit none 02317 02318 ! input/output 02319 type(waveparamsnew),intent(inout) :: wp 02320 type(spacepars),intent(in) :: s 02321 type(parameters),intent(in) :: par 02322 ! internal 02323 integer :: itheta,iy,iwc,it,irec 02324 integer :: index,status 02325 integer :: reclen,fid 02326 integer,dimension(:),allocatable :: nwc 02327 real*8,dimension(:,:,:), allocatable :: zeta, Ampzeta, E_tdir, E_interp 02328 real*8,dimension(:,:), allocatable :: eta, Amp 02329 complex(fftkind),dimension(:),allocatable :: Gn, tempcmplx,tempcmplxhalf 02330 integer,dimension(:),allocatable :: tempindex,tempinclude 02331 real*8 :: stdzeta,stdeta,etot,perc,emean 02332 real*8, dimension(:), allocatable :: E_t 02333 02334 if (par%bclwonly==0) then 02335 02336 ! Allocate variables for water level exitation and amplitude with and without 02337 ! directional spreading dependent envelope 02338 allocate(zeta(s%ny+1,wp%tslen,s%ntheta)) 02339 allocate(Ampzeta(s%ny+1,wp%tslen,s%ntheta)) 02340 zeta=0.d0 02341 Ampzeta=0.d0 02342 02343 allocate(eta(s%ny+1,wp%tslen)) 02344 allocate(Amp(s%ny+1,wp%tslen)) 02345 eta=0.d0 02346 Amp=0.d0 02347 02348 ! Calculate wave energy for each y-coordinate along seaside boundary for 02349 ! current computational directional bin 02350 allocate(nwc(s%ntheta)) 02351 allocate(Gn(wp%tslen)) 02352 allocate(tempinclude(wp%K)) 02353 allocate(tempcmplx(wp%tslen)) 02354 allocate(tempcmplxhalf(size(Gn(wp%tslen/2+2:wp%tslen)))) 02355 do itheta=1,s%ntheta 02356 call writelog('ls','(A,I0,A,I0)','Calculating short wave time series for theta bin ',itheta,' of ',s%ntheta) 02357 ! Select wave components that are in the current computational 02358 ! directional bin 02359 tempinclude=0 02360 where (wp%WDindex==itheta .and. wp%PRindex==0) ! only include components in this bin that are not to be phase-resolved 02361 tempinclude=1 02362 end where 02363 02364 ! Determine number of wave components that are in the current 02365 ! computational directional bin 02366 nwc(itheta)=sum(tempinclude) 02367 02368 ! Determine for each wave component in the current computational 02369 ! directional bin its index in the Fourier coefficients array 02370 ! ordered from high to low frequency 02371 allocate(tempindex(nwc(itheta))) 02372 tempindex=0 02373 do iwc=1,nwc(itheta) 02374 ! find highest 02375 index=maxval(maxloc(tempinclude)) 02376 ! reset that one so that the next highest in found in next iteration 02377 tempinclude(index)=0 02378 tempindex(iwc)=wp%Findex(index) 02379 end do 02380 02381 ! Check whether any wave components are in the current computational 02382 ! directional bin 02383 if (nwc(itheta)>0) then 02384 do iy=1,s%ny+1 02385 ! Reset 02386 Gn=0 02387 02388 ! Determine Fourier coefficients of all wave components for current 02389 ! y-coordinate in the current computational directional bin 02390 Gn(tempindex)=wp%CompFn(iy,tempindex) 02391 tempcmplxhalf = conjg(Gn(2:wp%tslen/2)) 02392 call flipiv(tempcmplxhalf,size(tempcmplxhalf)) 02393 Gn(wp%tslen/2+2:wp%tslen)=tempcmplxhalf 02394 02395 ! Inverse Discrete Fourier transformation to transform back to time 02396 ! domain from frequency domain 02397 tempcmplx=Gn 02398 status=0 02399 tempcmplx=fft(tempcmplx,inv=.true.,stat=status) 02400 02401 ! Scale result 02402 tempcmplx=tempcmplx/sqrt(dble(size(tempcmplx))) 02403 02404 ! Superimpose gradual increase and decrease of energy input for 02405 ! current y-coordinate and computational diretional bin on 02406 ! instantaneous water level excitation 02407 ! zeta(iy,:,itheta)=dble(tempcmplx*wp%tslen)*wp%taperw 02408 ! Robert: use final wave elevation from last iteration to startup 02409 ! this boundary condition 02410 zeta(iy,:,itheta)=dble(tempcmplx*wp%tslen) 02411 zeta(iy,:,itheta)=zeta(iy,:,itheta)*wp%taperw+(1-wp%taperw)*lastwaveelevation(iy,itheta) 02412 ! note that taperw is only zero at the start, not the end of the time series, so we 02413 ! can safely copy zeta at the point in time where t=rtbc to lastwaveelevation for use 02414 ! in the generation of the next time series 02415 lastwaveelevation(:,itheta) = zeta(iy,ind_end_taper,itheta) 02416 enddo ! iy=1,ny+1 02417 endif ! nwc>0 02418 deallocate(tempindex) 02419 enddo ! itheta = 1,ntheta 02420 deallocate(tempinclude) 02421 deallocate(Gn) 02422 deallocate(tempcmplxhalf) 02423 ! 02424 ! Calculate energy envelope amplitude 02425 call writelog('ls','(A)','Calculating wave energy envelope at boundary.') 02426 call progress_indicator(.true.,0.d0,5.d0,2.d0) 02427 do iy=1,s%ny+1 02428 ! Integrate instantaneous water level excitation of wave components over directions 02429 eta(iy,:) = sum(zeta(iy,:,:),2) 02430 tempcmplx=eta(iy,:) 02431 ! 02432 ! Hilbert transformation to determine envelope of all total 02433 ! non-directional wave components 02434 call hilbert(tempcmplx,size(tempcmplx)) 02435 ! 02436 ! Determine amplitude of water level envelope by calculating 02437 ! the absolute value of the complex wave envelope descriptions 02438 Amp(iy,:)=abs(tempcmplx) 02439 ! 02440 ! Calculate standard deviation of non-directional 02441 ! instantaneous water level excitation of all 02442 ! wave components to be used as weighing factor 02443 stdeta = sqrt(sum(eta(iy,:)**2)/(size(eta(iy,:))-1)) 02444 do itheta=1,s%ntheta 02445 if (nwc(itheta)>0) then 02446 ! Calculate standard deviations of directional 02447 ! instantaneous water level excitation of all 02448 ! wave components to be used as weighing factor 02449 stdzeta = sqrt(sum(zeta(iy,:,itheta)**2)/(size(zeta(iy,:,itheta))-1)) 02450 ! 02451 ! Calculate amplitude of directional wave envelope 02452 Ampzeta(iy,:,itheta)= Amp(iy,:)*stdzeta/stdeta 02453 else ! nwc==0 02454 ! Current computational directional bin does not contain any wave 02455 ! components, so print message to screen 02456 Ampzeta(iy,:,itheta)=0.d0 02457 endif ! nwc>0 02458 end do ! 1:ntheta 02459 ! Print status message to screen 02460 !call writelog('ls','(A,I0,A,I0,A)','Y-point ',iy,' of ',s%ny+1,' done.') 02461 call progress_indicator(.false.,dble(iy)/(s%ny+1)*100,5.d0,2.d0) 02462 end do ! 1:ny+1 02463 ! 02464 ! free memory 02465 deallocate(tempcmplx) 02466 deallocate(nwc) 02467 ! 02468 else ! only long wave forcing at the boundary 02469 allocate(Ampzeta(s%ny+1,wp%tslen,s%ntheta)) 02470 Ampzeta=0.d0 02471 endif 02472 ! Allocate memory for energy time series 02473 allocate(E_tdir(s%ny+1,wp%tslen,s%ntheta)) 02474 E_tdir=0.0d0 02475 E_tdir=0.5d0*par%rho*par%g*Ampzeta**2 02476 E_tdir=E_tdir/s%dtheta 02477 if (par%wbcEvarreduce<1.d0-1d-10) then 02478 do itheta=1,s%ntheta 02479 do iy=1,s%ny+1 02480 emean = sum(E_tdir(iy,:,itheta))/wp%tslen 02481 E_tdir(iy,:,itheta) = par%wbcEvarreduce*(E_tdir(iy,:,itheta)-emean) + emean 02482 enddo 02483 enddo 02484 endif 02485 02486 ! 02487 ! ! Ensure we scale back to the correct Hm0 02488 ! do iy=1,s%ny+1 02489 ! stdeta = sum(E_tdir(iy,:,:))*s%dtheta ! sum energy 02490 ! stdeta = stdeta/wp%tslen ! mean energy 02491 ! 02492 ! stdzeta = (wp%Hm0interp(iy)/sqrt(2.d0))**2 * par%rhog8 02493 ! 02494 ! E_tdir(iy,:,:) = E_tdir(iy,:,:)*stdzeta/stdeta 02495 ! enddo 02496 02497 ! Print directional energy distribution to screen 02498 if (par%bclwonly==0) then 02499 etot = sum(E_tdir) 02500 do itheta=1,s%ntheta 02501 perc = sum(E_tdir(:,:,itheta))/etot*100 02502 call writelog('ls','(a,i0,a,f0.2,a)','Wave bin ',itheta,' contains ',perc,'% of total energy') 02503 enddo 02504 endif 02505 02506 ! Open file for storage 02507 call writelog('ls','','Writing wave energy to ',trim(wp%Efilename),' ...') 02508 inquire(iolength=reclen) 1.d0 02509 reclen=reclen*(s%ny+1)*(s%ntheta) 02510 fid = create_new_fid() 02511 open(fid,file=trim(wp%Efilename),form='unformatted',access='direct',recl=reclen,status='REPLACE') 02512 02513 ! Write to external file 02514 if (.not. allocated(E_t)) allocate(E_t(wp%tslen)) 02515 if (wp%dtchanged) then 02516 ! Interpolate from internal time axis to output time axis 02517 allocate(E_interp(s%ny+1,wp%tslenbc,s%ntheta)) 02518 do itheta=1,s%ntheta 02519 do iy=1,s%ny+1 02520 E_t=E_tdir(iy,:,itheta) 02521 do it=1,wp%tslenbc 02522 call linear_interp(wp%tin,E_t,wp%tslen,(it-1)*wp%dtbc,E_interp(iy,it,itheta),status) 02523 enddo 02524 enddo 02525 enddo 02526 ! write to file 02527 do irec=1,wp%tslenbc+1 02528 write(fid,rec=irec)E_interp(:,min(irec,wp%tslenbc),:) 02529 end do 02530 deallocate(E_interp) 02531 else 02532 ! no need for interpolation 02533 do irec=1,wp%tslenbc+1 02534 write(fid,rec=irec)E_tdir(:,min(irec,wp%tslenbc),:) 02535 end do 02536 endif 02537 close(fid) 02538 call writelog('sl','','file done') 02539 02540 if (par%bclwonly==0) then 02541 ! Free memory 02542 deallocate(zeta,Ampzeta,E_tdir, Amp, eta) 02543 else 02544 deallocate(Ampzeta,E_tdir) 02545 endif 02546 02547 end subroutine generate_ebcf 02548 02549 02550 ! -------------------------------------------------------------- 02551 ! ------ Calculate time series of short wave at offshore ------- 02552 ! --------------------------- boundary ------------------------- 02553 subroutine generate_swts(wp,s,par) 02554 use params, only: parameters 02555 use logging_module, only: writelog, progress_indicator 02556 use spaceparamsdef, only: spacepars 02557 02558 implicit none 02559 02560 ! input/output 02561 type(waveparamsnew),intent(inout) :: wp 02562 type(spacepars),intent(in) :: s 02563 type(parameters),intent(in) :: par 02564 02565 ! internal 02566 integer :: j,it,ik 02567 real*8 :: u1, u2, z, U, dU 02568 02569 ! allocate memory for time series of data 02570 allocate(wp%zsits(s%ny+1,wp%tslen)) 02571 allocate(wp%uits(s%ny+1,wp%tslen)) 02572 allocate(wp%vits(s%ny+1,wp%tslen)) 02573 allocate(wp%duits(s%ny+1,wp%tslen)) 02574 allocate(wp%dvits(s%ny+1,wp%tslen)) 02575 wp%zsits=0.d0 02576 wp%uits=0.d0 02577 wp%vits=0.d0 02578 wp%duits=0.d0 02579 wp%dvits=0.d0 02580 if (par%bclwonly==0) then 02581 02582 u1 = 0.d0 02583 u2 = 0.d0 02584 U = 0.d0 02585 dU = 0.d0 02586 ! z-level of layer (negative downwards) 02587 z = -1 * (wp%h0 - par%nhlay * wp%h0) 02588 ! total surface elevation 02589 call writelog('ls','','Calculating short wave elevation time series') 02590 call progress_indicator(.true.,0.d0,5.d0,2.d0) 02591 do it=1,wp%tslen 02592 call progress_indicator(.false.,dble(it)/wp%tslen*100,5.d0,2.d0) 02593 do ik=1,wp%K 02594 if (wp%PRindex(ik)==1) then 02595 do j=1,s%ny+1 02596 wp%zsits(j,it)=wp%zsits(j,it)+wp%A(j,ik)*dsin( & 02597 +wp%wgen(ik)*wp%tin(it)& 02598 -wp%kgen(ik)*( dsin(wp%thetagen(ik))*(s%yz(1,j)-s%yz(1,1)) & 02599 +dcos(wp%thetagen(ik))*(s%xz(1,j)-s%xz(1,1))) & 02600 +wp%phigen(ik) & 02601 ) 02602 enddo 02603 endif 02604 enddo 02605 enddo 02606 ! depth-averaged velocity 02607 call writelog('ls','','Calculating short wave velocity time series') 02608 call progress_indicator(.true.,0.d0,5.d0,2.d0) 02609 do it=1,wp%tslen 02610 call progress_indicator(.false.,dble(it)/wp%tslen*100,5.d0,2.d0) 02611 do ik=1,wp%K 02612 if (wp%PRindex(ik)==1) then 02613 do j=1,s%ny+1 02614 02615 if ( par%nonhq3d == 1 .and. par%nhlay>0 ) then 02616 ! Compute layer averaged velocity for layer 1 and 2 based on layer level z. 02617 u1 = wp%wgen(ik) * wp%A(j,ik) / (dsinh(wp%kgen(ik)*wp%h0)*wp%kgen(ik))*(dsinh(wp%kgen(ik)*(z + wp%h0))) * & 02618 dsin(wp%wgen(ik)*wp%tin(it)& 02619 -wp%kgen(ik)*( dsin(wp%thetagen(ik))*(s%yz(1,j)-s%yz(1,1)) & 02620 +dcos(wp%thetagen(ik))*(s%xz(1,j)-s%xz(1,1))) & 02621 +wp%phigen(ik)) 02622 u1 = u1 / (wp%h0+z) 02623 u2 = wp%wgen(ik) * wp%A(j,ik) / (dsinh(wp%kgen(ik)*wp%h0)*wp%kgen(ik))*(dsinh(wp%kgen(ik)*(0 + wp%h0)) - & 02624 dsinh(wp%kgen(ik) * (z + wp%h0))) * & 02625 dsin(wp%wgen(ik)*wp%tin(it) & 02626 -wp%kgen(ik)*( dsin(wp%thetagen(ik))*(s%yz(1,j)-s%yz(1,1)) & 02627 +dcos(wp%thetagen(ik))*(s%xz(1,j)-s%xz(1,1))) & 02628 +wp%phigen(ik)) 02629 u2 = u2/(z*-1) 02630 ! Store U. U = alpha * U1 + (1-alpha) * U2 02631 U = par%nhlay * u1 + (1-par%nhlay) * u2 02632 ! Store dU. Du = U1 - U2 02633 dU = (u1 - u2) 02634 02635 ! Eastward component U: 02636 wp%uits(j,it) = wp%uits(j,it) + dcos(wp%thetagen(ik))*U 02637 ! Northward component V: 02638 wp%vits(j,it) = wp%vits(j,it) + dsin(wp%thetagen(ik))*U 02639 ! Eastward component dU: 02640 wp%duits(j,it) = wp%duits(j,it) + dcos(wp%thetagen(ik))*dU 02641 ! Northward component dV: 02642 wp%dvits(j,it) = wp%dvits(j,it) + dsin(wp%thetagen(ik))*dU 02643 else 02644 ! Depth-average velocity in wave direction: 02645 U = 1.d0/wp%h0*wp%wgen(ik)*wp%A(j,ik) * & 02646 dsin(wp%wgen(ik)*wp%tin(it) & 02647 -wp%kgen(ik)*( dsin(wp%thetagen(ik))*(s%yz(1,j)-s%yz(1,1)) & 02648 +dcos(wp%thetagen(ik))*(s%xz(1,j)-s%xz(1,1))) & 02649 +wp%phigen(ik) & 02650 ) * & 02651 1.d0/wp%kgen(ik) 02652 02653 ! Eastward component: 02654 wp%uits(j,it) = wp%uits(j,it) + dcos(wp%thetagen(ik))*U 02655 ! Northward component: 02656 wp%vits(j,it) = wp%vits(j,it) + dsin(wp%thetagen(ik))*U 02657 endif 02658 enddo 02659 endif 02660 enddo 02661 enddo 02662 ! 02663 ! 02664 ! Apply tapering to time series 02665 do j=1,s%ny+1 02666 wp%uits(j,:)=wp%uits(j,:)*wp%taperf 02667 wp%vits(j,:)=wp%vits(j,:)*wp%taperf 02668 wp%zsits(j,:)=wp%zsits(j,:)*wp%taperf 02669 wp%duits(j,:)=wp%duits(j,:)*wp%taperf 02670 wp%dvits(j,:)=wp%dvits(j,:)*wp%taperf 02671 enddo 02672 else 02673 ! do nothing in this routine 02674 endif 02675 end subroutine generate_swts 02676 02677 02678 ! -------------------------------------------------------------- 02679 ! ----------------------- Bound long wave ---------------------- 02680 ! -------------------------------------------------------------- 02681 subroutine generate_qbcf(wp,s,par) 02682 02683 use params, only: parameters 02684 use spaceparamsdef, only: spacepars 02685 use logging_module, only: writelog, progress_indicator 02686 use math_tools, only: fftkind, fft, flipiv 02687 use interp, only: linear_interp 02688 use filefunctions, only: create_new_fid 02689 02690 implicit none 02691 02692 ! input/output 02693 type(waveparamsnew),intent(inout) :: wp 02694 type(spacepars),intent(in) :: s 02695 type(parameters),intent(in) :: par 02696 ! internal 02697 integer :: j,m,iq,irec,it ! counters 02698 integer :: K ! copy of K 02699 integer :: halflen,reclen,fid,status 02700 logical :: firsttime ! used for output message only 02701 real*8 :: deltaf,qmean ! difference frequency 02702 real*8,dimension(:), allocatable :: term1,term2,term2new,dif,chk1,chk2 02703 real*8,dimension(:,:),allocatable :: Eforc,D,deltheta,KKx,KKy,dphi3,k3,cg3,theta3,Abnd,D_sign 02704 real*8,dimension(:,:,:),allocatable :: q,qinterp 02705 complex(fftkind),dimension(:),allocatable :: Comptemp,Comptemp2,Gn 02706 complex(fftkind),dimension(:,:,:),allocatable:: Ftemp 02707 02708 02709 02710 ! This function has changed with respect to previous versions of XBeach, in that 02711 ! the bound long wave has to be calculated separately at each longshore point, 02712 ! owing to longshore varying incident wave spectra 02713 02714 ! shortcut variable 02715 K = wp%K 02716 02717 ! Print message to screen 02718 call writelog('sl','', 'Calculating primary wave interaction') 02719 02720 ! Allocate two-dimensional variables for all combinations of interacting wave 02721 ! components to be filled triangular 02722 allocate(Eforc(K-1,K)) 02723 allocate(D(K-1,K)) 02724 allocate(deltheta(K-1,K)) 02725 allocate(KKx(K-1,K)) 02726 allocate(KKy(K-1,K)) 02727 allocate(dphi3(K-1,K)) 02728 allocate(k3(K-1,K)) 02729 allocate(cg3(K-1,K)) 02730 allocate(theta3(K-1,K)) 02731 ! Allocate variables for amplitude and Fourier coefficients of bound long wave 02732 allocate(Gn(wp%tslen)) 02733 allocate(Abnd(K-1,K)) 02734 allocate(Ftemp(K-1,K,4)) ! Jaap qx, qy qtot, zeta 02735 ! Storage for output discharge 02736 allocate(q(s%ny+1,wp%tslen,4)) ! qx qy qtot, zeta 02737 ! 02738 ! Initialize variables as zero 02739 Eforc = 0 02740 D = 0 02741 deltheta = 0 02742 KKx = 0 02743 KKy = 0 02744 dphi3 = 0 02745 k3 = 0 02746 cg3 = 0 02747 theta3 = 0 02748 Gn = 0*compi 02749 Abnd = 0 02750 Ftemp = 0*compi 02751 q = 0 02752 02753 ! First time is set true for each time new wave bc are generated 02754 firsttime = .true. 02755 02756 ! upper half of frequency space 02757 halflen = wp%tslen/2 02758 02759 02760 02761 ! Run loop over wave-wave interaction components 02762 call progress_indicator(.true.,0.d0,5.d0,2.d0) 02763 do m=1,K-1 02764 ! call writelog('ls','(a,i0,a,i0)','Wave component ',m,' of ',K-1) 02765 call progress_indicator(.false.,dble(m)/(K-1)*100,5.d0,2.d0) 02766 ! Allocate memory 02767 allocate(term1(K-m),term2(K-m),term2new(K-m),dif(K-m),chk1(K-m),chk2(K-m)) 02768 02769 ! Determine difference frequency 02770 deltaf=m*wp%dfgen 02771 02772 ! Determine difference angles (pi already added) 02773 deltheta(m,1:K-m) = abs(wp%thetagen(m+1:K)-wp%thetagen(1:K-m))+par%px 02774 02775 ! Determine x- and y-components of wave numbers of difference waves 02776 KKy(m,1:K-m)=wp%kgen(m+1:K)*dsin(wp%thetagen(m+1:K))-wp%kgen(1:K-m)*dsin(wp%thetagen(1:K-m)) 02777 KKx(m,1:K-m)=wp%kgen(m+1:K)*dcos(wp%thetagen(m+1:K))-wp%kgen(1:K-m)*dcos(wp%thetagen(1:K-m)) 02778 02779 ! Determine difference wave numbers according to Van Dongeren et al. 2003 02780 ! eq. 19 (+cos due to pi in deltheta) 02781 k3(m,1:K-m) =sqrt(wp%kgen(1:K-m)**2+wp%kgen(m+1:K)**2+ & 02782 2*wp%kgen(1:K-m)*wp%kgen(m+1:K)*dcos(deltheta(m,1:K-m))) 02783 02784 ! Determine group velocity of difference waves 02785 cg3(m,1:K-m)= 2.d0*par%px*deltaf/k3(m,1:K-m) 02786 02787 ! Modification Robert + Jaap: make sure that the bound long wave amplitude does not 02788 ! explode when offshore boundary is too close to shore, 02789 ! by limiting the interaction group velocity 02790 cg3(m,1:K-m) = min(cg3(m,1:K-m),par%nmax*sqrt(par%g/k3(m,1:K-m)*tanh(k3(m,1:K-m)*wp%h0))) 02791 02792 ! Determine difference-interaction coefficient according to Okihiro et al eq. 4a 02793 ! Instead of Herbers 1994 this coefficient is derived for the surface elavtion in stead of the bottom pressure. 02794 term1 = (-wp%wgen(1:K-m))*wp%wgen(m+1:K) 02795 term2 = (-wp%wgen(1:K-m))+wp%wgen(m+1:K) 02796 term2new = cg3(m,1:K-m)*k3(m,1:K-m) 02797 dif = (abs(term2-term2new)) 02798 if (any(dif>0.01*term2) .and. firsttime) then 02799 firsttime = .false. 02800 ! call writelog('lws','','Warning: shallow water so long wave variance is reduced using par%nmax') 02801 endif 02802 chk1 = cosh(wp%kgen(1:K-m)*wp%h0) 02803 chk2 = cosh(wp%kgen(m+1:K)*wp%h0) 02804 02805 D(m,1:K-m) = -par%g*wp%kgen(1:K-m)*wp%kgen(m+1:K)*dcos(deltheta(m,1:K-m))/2.d0/term1+ & 02806 +term2**2/(par%g*2)+par%g*term2/ & 02807 ((par%g*k3(m,1:K-m)*tanh(k3(m,1:K-m)*wp%h0)-(term2new)**2)*term1)* & 02808 (term2*((term1)**2/par%g/par%g - wp%kgen(1:K-m)*wp%kgen(m+1:K)*dcos(deltheta(m,1:K-m))) & 02809 - 0.50d0*((-wp%wgen(1:K-m))*wp%kgen(m+1:K)**2/(chk2**2)+wp%wgen(m+1:K)*wp%kgen(1:K-m)**2/(chk1**2))) 02810 02811 ! Correct for surface elevation input and output instead of bottom pressure 02812 ! so it is consistent with Van Dongeren et al 2003 eq. 18. 02813 !No need to correct for bottom pressure anymore. 02814 !D(m,1:K-m) = D(m,1:K-m)*cosh(k3(m,1:K-m)*wp%h0)/(cosh(wp%kgen(1:K-m)*wp%h0)*cosh(wp%kgen(m+1:K)*wp%h0)) 02815 02816 02817 ! Exclude interactions with components smaller than or equal to current 02818 ! component according to lower limit Herbers 1994 eq. 1 02819 where(wp%fgen<=deltaf) 02820 D(m,:)=0.d0 02821 endwhere 02822 02823 ! Exclude interactions with components that are cut-off by the fcutoff 02824 ! parameter 02825 if (deltaf<=par%fcutoff) D(m,:)=0.d0 02826 02827 ! Determine phase of bound long wave assuming a local equilibrium with 02828 ! forcing of interacting primary waves according to Van Dongeren et al. 02829 ! 2003 eq. 21 (the angle is the imaginary part of the natural log of a 02830 ! complex number as long as the complex number is not zero) 02831 allocate(Comptemp(K-m),Comptemp2(K-m)) 02832 Comptemp=conjg(wp%CompFn(1,wp%Findex(1)+m:wp%Findex(1)+K-1)) 02833 Comptemp2=conjg(wp%CompFn(1,wp%Findex(1):wp%Findex(1)+K-m-1)) 02834 dphi3(m,1:K-m) = par%px+imag(log(Comptemp))-imag(log(Comptemp2)) 02835 ! Menno removed pi, because it is incorporated in the sign of the amplitude 02836 dphi3(m,1:K-m) = imag(log(Comptemp))-imag(log(Comptemp2)) 02837 deallocate (Comptemp,Comptemp2) 02838 ! 02839 ! Determine angle of bound long wave according to Van Dongeren et al. 2003 eq. 22 02840 theta3 = atan2(KKy,KKx) 02841 ! 02842 ! free memory 02843 deallocate(term1,term2,term2new,dif,chk1,chk2) 02844 enddo ! m=1,K-1 02845 ! 02846 ! Output to screen 02847 if (.not. firsttime) then 02848 call writelog('lws','','Warning: shallow water so long wave variance is reduced using par%nmax') 02849 endif 02850 call writelog('sl','', 'Calculating flux at boundary') 02851 ! 02852 ! Allocate temporary arrays for upcoming loop 02853 allocate(Comptemp(halflen-1)) 02854 allocate(Comptemp2(wp%tslen)) 02855 ! 02856 ! Run a loop over the offshore boundary 02857 call progress_indicator(.true.,0.d0,5.d0,2.d0) 02858 do j=1,s%ny+1 02859 ! Determine energy of bound long wave according to Herbers 1994 eq. 1 based 02860 ! on difference-interaction coefficient and energy density spectra of 02861 ! primary waves 02862 ! Robert: E = 2*D**2*S**2*dtheta**2*df can be rewritten as 02863 ! E = 2*D**2*Sf**2*df 02864 Eforc = 0 02865 do m=1,K-1 02866 ! Menno: Use the forced short wave energy to compute the bound long wave energy 02867 ! instead of the corrected energy (Sfinterpq) 02868 if (par%Sfold==1) then 02869 Eforc(m,1:K-m) = 2*D(m,1:K-m)**2*wp%Sfinterpq(j,1:K-m)*wp%Sfinterpq(j,m+1:K)*wp%dfgen 02870 else 02871 Eforc(m,1:K-m) = 2*D(m,1:K-m)**2*wp%Sfinterp(j,1:K-m)*wp%Sfinterp(j,m+1:K)*wp%dfgen 02872 endif 02873 enddo 02874 ! 02875 ! Calculate bound wave amplitude for this offshore grid point 02876 !Abnd = sqrt(2*Eforc*wp%dfgen) 02877 02878 ! Menno: add the sign of the interaction coefficient in the amptlitude. Large dtheta can result in a positive D. 02879 ! The phase of the bound wave is now only determined by phi1+phi2 02880 allocate(D_sign(K-1,K)) 02881 D_sign = 1 02882 ! Menno: put the sign of D in front of D_sign 02883 D_sign = sign(D_sign,D) 02884 ! Multiply amplitude with the sign of D 02885 Abnd = sqrt(2*Eforc*wp%dfgen) * D_sign 02886 deallocate(D_sign) 02887 ! 02888 ! Determine complex description of bound long wave per interaction pair of 02889 ! primary waves for first y-coordinate along seaside boundary 02890 Ftemp(:,:,1) = Abnd/2*exp(-1*compi*dphi3)*cg3*dcos(theta3) ! qx 02891 Ftemp(:,:,2) = Abnd/2*exp(-1*compi*dphi3)*cg3*dsin(theta3) ! qy 02892 Ftemp(:,:,3) = Abnd/2*exp(-1*compi*dphi3)*cg3 ! qtot 02893 Ftemp(:,:,4) = Abnd/2*exp(-1*compi*dphi3) ! eta 02894 ! 02895 ! loop over qx,qy and qtot 02896 do iq=1,4 02897 ! Unroll wave component to correct place along the offshore boundary 02898 Ftemp(:,:,iq) = Ftemp(:,:,iq)* & 02899 exp(-1*compi*(KKy*(s%yz(1,j)-s%yz(1,1))+KKx*(s%xz(1,j)-s%xz(1,1)))) 02900 ! Determine Fourier coefficients 02901 Gn(2:K) = sum(Ftemp(:,:,iq),DIM=2) 02902 Comptemp = conjg(Gn(2:halflen)) 02903 call flipiv(Comptemp,halflen-1) 02904 Gn(halflen+2:wp%tslen) = Comptemp 02905 ! 02906 ! Print status message to screen 02907 if (iq==3) then 02908 !call writelog('ls','(A,I0,A,I0)','Flux ',j,' of ',s%ny+1) 02909 call progress_indicator(.false.,dble(j)/(s%ny+1)*100,5.d0,2.d0) 02910 endif 02911 ! 02912 ! Inverse Discrete Fourier transformation to transform back to time space 02913 ! from frequency space. (Fortran ifft is scaled by sqrt(N) ) 02914 Comptemp2=fft(Gn,inv=.true.) 02915 ! 02916 ! Determine mass flux as function of time and let the flux gradually 02917 ! increase and decrease in and out the wave time record using the earlier 02918 ! specified window. Sqrt(N) is due to scaling of the ifft. 02919 Comptemp2=Comptemp2/sqrt(dble(wp%tslen)) 02920 q(j,:,iq)=dreal(Comptemp2*wp%tslen)*wp%taperf 02921 enddo ! iq=1,4 !Ap Menno 27-06-2018 02922 enddo ! j=1,s%ny+1 02923 ! 02924 ! free memory 02925 deallocate(Comptemp,Comptemp2,Ftemp) 02926 ! 02927 ! 02928 if (par%wbcQvarreduce<1.d0-1d-10) then 02929 do iq=1,4 02930 do j=1,s%ny+1 02931 qmean = sum(q(j,:,iq))/wp%tslen 02932 q(j,:,iq) = par%wbcQvarreduce*(q(j,:,iq)-qmean) + qmean 02933 enddo 02934 enddo 02935 endif 02936 ! 02937 ! 02938 ! 02939 ! Now write data to file 02940 if (par%nonhspectrum==0) then 02941 ! If doing combined wave action balance and swell wave flux with swkhmin>0 then we need to add short wave velocity 02942 ! to time series of q here: 02943 if (par%swkhmin>0.d0) then 02944 do j=1,s%ny+1 02945 q(j,:,1) = q(j,:,1) + wp%uits(j,:)*wp%h0 ! u flux 02946 q(j,:,4) = q(j,:,4) + wp%zsits(j,:) ! eta 02947 enddo 02948 endif 02949 02950 if (par%order==1) then 02951 do j=1,s%ny+1 02952 q(j,:,1) = q(j,:,1) *0 ! u flux 02953 q(j,:,4) = q(j,:,4) *0 ! eta 02954 enddo 02955 endif 02956 ! 02957 ! 02958 ! Open file for storage of bound long wave flux 02959 call writelog('ls','','Writing long wave mass flux to ',trim(wp%qfilename),' ...') 02960 inquire(iolength=reclen) 1.d0 02961 reclen=reclen*((s%ny+1)*4) 02962 fid = create_new_fid() 02963 open(fid,file=trim(wp%qfilename),form='unformatted',access='direct',recl=reclen,status='REPLACE') 02964 ! 02965 ! Write to external file 02966 if (wp%dtchanged) then 02967 ! Interpolate from internal time axis to output time axis 02968 allocate(qinterp(s%ny+1,wp%tslenbc,3)) 02969 do iq=1,3 02970 do it=1,wp%tslenbc 02971 do j=1,s%ny+1 02972 call linear_interp(wp%tin,q(j,:,iq),wp%tslen,(it-1)*wp%dtbc,qinterp(j,it,iq),status) 02973 enddo 02974 enddo 02975 enddo 02976 ! write to file 02977 do irec=1,wp%tslenbc+1 02978 write(fid,rec=irec)qinterp(:,min(irec,wp%tslenbc),:) 02979 end do 02980 deallocate(qinterp) 02981 else 02982 ! no need for interpolation 02983 do irec=1,wp%tslenbc+1 02984 write(fid,rec=irec)q(:,min(irec,wp%tslenbc),:) 02985 end do 02986 endif 02987 close(fid) 02988 call writelog('sl','','file done') 02989 else 02990 do j=1,s%ny+1 02991 ! add to velocity time series 02992 wp%uits(j,:)=wp%uits(j,:)+q(j,:,1)/wp%h0 02993 wp%vits(j,:)=wp%vits(j,:)+q(j,:,2)/wp%h0 02994 ! add to surface elevation time series 02995 wp%zsits(j,:)=wp%zsits(j,:)+q(j,:,4) 02996 enddo 02997 endif ! par%nonhspectrum==1 02998 02999 ! Free memory 03000 deallocate(Eforc,D,deltheta,KKx,KKy,dphi3,k3,cg3,theta3,Gn,Abnd,q) 03001 03002 end subroutine generate_qbcf 03003 03004 03005 ! -------------------------------------------------------------- 03006 ! --------------- Non-hydrostatic wave time -------------------- 03007 ! ---------------- series file generation ---------------------- 03008 subroutine generate_nhtimeseries_file(wp,par) 03009 03010 use params 03011 use filefunctions, only: create_new_fid 03012 use logging_module 03013 03014 implicit none 03015 03016 ! input/output 03017 type(waveparamsnew),intent(inout) :: wp 03018 type(parameters),intent(in) :: par 03019 ! internal 03020 integer :: fid 03021 integer :: it 03022 character(LEN=256) :: rowfmt 03023 03024 03025 call writelog('ls','','Writing short wave time series to ',wp%nhfilename) 03026 03027 fid = create_new_fid() 03028 open(fid,file=trim(wp%nhfilename),status='REPLACE') 03029 write(fid,'(a)')'vector' 03030 write(fid,'(a)')'6' 03031 write(fid,'(a)')'t,U,V,Z,dU,dV' 03032 ! write format descriptor for file to internal string 03033 write(rowfmt,'(A,I4,A)') '(',1+5*(par%ny+1),'(1X,E18.9))' 03034 ! Add one extra point at the start of the time series to deal with interpolation errors 03035 write(fid,rowfmt)par%t-par%dt*par%maxdtfac,wp%uits(:,1),wp%vits(:,1), & 03036 wp%zsits(:,1),wp%duits(:,1),wp%dvits(:,1) 03037 ! write time series to file 03038 do it=1,wp%tslen 03039 write(fid,rowfmt)wp%tin(it)+par%t-par%dt,wp%uits(:,it),wp%vits(:,it),wp%zsits(:,it),wp%duits(:,it),wp%dvits(:,it) 03040 enddo 03041 ! (almost always) add one extra point after the time series to deal with interpolation errors 03042 if (wp%tin(wp%tslen)<wp%rtbc+par%dt*par%maxdtfac) then 03043 write(fid,rowfmt)wp%tin(wp%tslen)+par%t+par%dt*par%maxdtfac,wp%uits(:,wp%tslen),wp%vits(:,wp%tslen), & 03044 wp%zsits(:,wp%tslen),wp%duits(:,wp%tslen),wp%dvits(:,wp%tslen) 03045 endif 03046 close(fid) 03047 03048 end subroutine generate_nhtimeseries_file 03049 03050 03051 subroutine set_stationary_spectrum (s,par,wp,combspec) 03052 use params, only: parameters 03053 use spaceparamsdef, only: spacepars 03054 use interp, only: interp_using_trapez_rule 03055 use filefunctions, only: create_new_fid 03056 use logging_module, only: writelog 03057 type(parameters) :: par 03058 type(spacepars) :: s 03059 type(spectrum) :: combspec 03060 type(waveparamsnew),intent(in) :: wp 03061 real*8 :: xcycle 03062 real*8, dimension(:), allocatable :: angcart,Sdcart 03063 integer :: j 03064 integer :: reclen,fid 03065 03066 allocate(angcart(combspec%nang)) 03067 allocate(Sdcart(combspec%nang)) 03068 03069 xcycle=2*par%px 03070 ! combspec%ang contains nautical directions from 0 to 2 pi; convert to cartesian from -3/2pi to 1/2 pi 03071 ! in reverse order 03072 03073 call interp_using_trapez_rule(combspec%ang,combspec%Sd,combspec%nang,xcycle,s%theta_s,s%ee_s(1,1,:),s%ntheta_s) 03074 do j=1,s%ny+1 03075 s%ee_s(1,j,:)=s%ee_s(1,1,:) 03076 end do 03077 03078 s%ee_s(1,:,:)=s%ee_s(1,:,:)*par%rho*par%g 03079 03080 call writelog('ls','','Writing stationary wave energy directional spread to ',trim(wp%Esfilename),' ...') 03081 inquire(iolength=reclen) 1.d0 03082 reclen=reclen*(s%ny+1)*(s%ntheta_s) 03083 fid = create_new_fid() 03084 open(fid,file=trim(wp%Esfilename),form='unformatted',access='direct',recl=reclen,status='REPLACE') 03085 write(fid,rec=1)s%ee_s(1,:,:) 03086 close(fid) 03087 deallocate(angcart) 03088 deallocate(Sdcart) 03089 03090 end subroutine set_stationary_spectrum 03091 03092 subroutine generate_admin_files(par,wp,rtbc_local,dtbc_local,maindir_local,spectrumendtimeold,spectrumendtime) 03093 03094 use filefunctions, only: create_new_fid 03095 use params, only: parameters 03096 implicit none 03097 03098 type(parameters),intent(in) :: par 03099 type(waveparamsnew),intent(in) :: wp 03100 real*8,intent(in) :: rtbc_local,dtbc_local,maindir_local,spectrumendtimeold,spectrumendtime 03101 integer :: iostat 03102 03103 if (par%nonhspectrum==0) then 03104 if (bccount==1) then 03105 fidelist = create_new_fid() 03106 open(fidelist,file='ebcflist.bcf',form='formatted',status='replace') 03107 else 03108 open(fidelist,file='ebcflist.bcf',form='formatted',position='append') 03109 end if 03110 write(fidelist,'(f12.3,a,f12.3,a,f9.3,a,f9.5,a,f11.5,a)') & 03111 & spectrumendtime,' ',rtbc_local,' ',dtbc_local,' ',par%Trep,' ',maindir_local,' '//trim(wp%Efilename) 03112 close(fidelist) 03113 03114 if (bccount==1) then 03115 fidqlist = create_new_fid() 03116 open(fidqlist,file='qbcflist.bcf',form='formatted',status='replace') 03117 else 03118 open(fidqlist,file='qbcflist.bcf',form='formatted',position='append') 03119 end if 03120 write(fidqlist,'(f12.3,a,f12.3,a,f9.3,a,f9.5,a,f11.5,a)') & 03121 & spectrumendtime,' ',rtbc_local,' ',dtbc_local,' ',par%Trep,' ',maindir_local,' '//trim(wp%qfilename) 03122 close(fidqlist) 03123 03124 if(par%single_dir==1) then 03125 if (bccount==1) then 03126 fideslist = create_new_fid() 03127 open(fideslist,file='esbcflist.bcf',form='formatted',status='replace') 03128 else 03129 open(fideslist,file='esbcflist.bcf',form='formatted',position='append') 03130 end if 03131 write(fideslist,'(f12.3,a,f12.3,a,f9.3,a,f9.5,a,f11.5,a)') & 03132 & spectrumendtime,' ',rtbc_local,' ',dtbc_local,' ',par%Trep,' ',maindir_local,' '//trim(wp%Esfilename) 03133 close(fideslist) 03134 endif 03135 else 03136 if (bccount==1) then 03137 fidnhlist = create_new_fid() 03138 open(fidnhlist,file='nhbcflist.bcf',form='formatted',status='replace',iostat=iostat) 03139 else 03140 open(fidnhlist,file='nhbcflist.bcf',form='formatted',position='append',iostat=iostat) 03141 end if 03142 write(fidnhlist,'(f12.3,a,f12.3,a,f12.3,a)',iostat=iostat) & 03143 & spectrumendtimeold,' ',spectrumendtime,' ',par%Trep,' '//trim(wp%nhfilename) 03144 close(fidnhlist) 03145 end if 03146 end subroutine generate_admin_files 03147 03148 ! Generate time series of bound super harmonics 03149 subroutine generate_secondorder(par,s, wp) 03150 03151 use params, only: parameters 03152 use spaceparamsdef, only: spacepars 03153 use logging_module, only: writelog, progress_indicator 03154 use math_tools, only: fftkind, fft, flipiv 03155 use interp, only: linear_interp 03156 use filefunctions, only: create_new_fid 03157 use spaceparamsdef, only: spacepars 03158 03159 implicit none 03160 03161 ! input/output 03162 type(waveparamsnew),intent(inout) :: wp 03163 type(spacepars),intent(in) :: s 03164 type(parameters),intent(in) :: par 03165 ! internal 03166 integer :: j,m,iq,irec,it,i,count ! counters 03167 integer :: K ! copy of K 03168 integer :: halflen,reclen,fid,status 03169 logical :: firsttime ! used for output message only 03170 real*8 :: deltaf,z 03171 real*8,dimension(:), allocatable :: term1,term2,term2new,dif,chk1,chk2 03172 real*8,dimension(:,:),allocatable :: Eforc,D,deltheta,KKx,KKy,dphi3,k3,w3,c,theta3,Abnd,qx1,qx2,qy1,qy2,D_sign 03173 real*8,dimension(:,:,:),allocatable :: q,qinterp 03174 complex(fftkind),dimension(:),allocatable :: Comptemp,Comptemp2,Gn 03175 complex(fftkind),dimension(:,:,:),allocatable:: Ftemp 03176 integer,dimension(:), allocatable :: ii,jj 03177 03178 03179 03180 03181 ! shortcut variable 03182 K = wp%K 03183 03184 ! Print message to screen 03185 call writelog('sl','', 'Calculating primary wave interaction - super harmonics') 03186 03187 ! Allocate two-dimensional variables for all combinations of interacting wave 03188 ! components to be filled triangular 03189 allocate(Eforc(2*K,2*K)) 03190 allocate(D(2*K,2*K)) 03191 allocate(deltheta(2*K,2*K)) 03192 allocate(KKx(2*K,2*K)) 03193 allocate(KKy(2*K,2*K)) 03194 allocate(dphi3(2*K,2*K)) 03195 allocate(k3(2*K,2*K)) 03196 allocate(w3(2*K,2*K)) 03197 allocate(c(2*K,2*K)) 03198 allocate(theta3(2*K,2*K)) 03199 ! Allocate variables for amplitude and Fourier coefficients of bound wave 03200 allocate(Gn(wp%tslen)) 03201 allocate(Abnd(2*K,2*K)) 03202 if (par%nonhq3d==1 .and. par%nhlay>0) then 03203 allocate(Ftemp(2*K,2*K,6)) ! qx1, qx2, qy1, qy2, qtot, zeta 03204 ! Storage for output discharge 03205 allocate(q(s%ny+1,wp%tslen,6)) ! qx1, qx2, qy1, qy2, qtot, zeta 03206 allocate(qx1(2*K,2*K)) 03207 allocate(qx2(2*K,2*K)) 03208 allocate(qy1(2*K,2*K)) 03209 allocate(qy2(2*K,2*K)) 03210 qx1 = 0 03211 qx2 = 0 03212 qy1 = 0 03213 qy2 = 0 03214 else 03215 allocate(Ftemp(2*K,2*K,4)) ! qx, qy qtot, zeta 03216 ! Storage for output discharge 03217 allocate(q(s%ny+1,wp%tslen,4)) ! qx qy qtot, zeta 03218 endif 03219 03220 ! 03221 ! Initialize variables as zero 03222 Eforc = 0 03223 D = 0 03224 deltheta = 0 03225 KKx = 0 03226 KKy = 0 03227 dphi3 = 0 03228 k3 = 0 03229 w3 = 0 03230 c = 0 03231 theta3 = 0 03232 Gn = 0*compi 03233 Abnd = 0 03234 Ftemp = 0*compi 03235 q = 0 03236 i = 0 03237 count = 0 03238 z = 0 03239 03240 03241 03242 ! upper half of frequency space 03243 halflen = wp%tslen/2 03244 03245 ! Run loop over wave-wave interaction components 03246 call progress_indicator(.true.,0.d0,5.d0,2.d0) 03247 do m=2,2*K 03248 call progress_indicator(.false.,dble(m)/(2*K)*100,5.d0,2.d0) 03249 ! Create indices 03250 if (m .LE. K+1) then 03251 allocate(term1(m-1),term2(m-1),term2new(m-1),dif(m-1),chk1(m-1),chk2(m-1)) 03252 allocate(Comptemp(m-1),Comptemp2(m-1)) 03253 allocate(ii(m-1)) 03254 allocate(jj(m-1)) 03255 ii = 0 03256 jj = 0 03257 ! Indices: ii=1:m-1 03258 ! Indices: jj = flip(ii) 03259 do i=1,m-1 03260 ii(i) = i 03261 jj(m - i) = i 03262 enddo 03263 else 03264 allocate(term1(K+1-(m-K)),term2(K+1-(m-K)),term2new(K+1-(m-K)),dif(K+1-(m-K)),chk1(K+1-(m-K)),chk2(K+1-(m-K))) 03265 allocate(Comptemp(K+1-(m-K)),Comptemp2(K+1-(m-K))) 03266 allocate(ii(K+1-(m-K))) 03267 allocate(jj(K+1-(m-K))) 03268 ii = 0 03269 jj = 0 03270 ! Indices: ii=m-K:K 03271 ! Indices: jj = flip(ii) 03272 do i=1,K+1-(m-K) 03273 ii(i) = m-K + i -1 03274 jj(K+1-(m-K) -i + 1) = m-K + i -1 03275 enddo 03276 endif 03277 ! Determine delta theta: 03278 deltheta(m,ii) = abs(wp%thetagen(ii)-wp%thetagen(jj)) 03279 03280 ! Determine x- and y-components of wave numbers of K3 03281 KKy(m,ii)=wp%kgen(ii)*dsin(wp%thetagen(ii))+wp%kgen(jj)*dsin(wp%thetagen(jj)) 03282 KKx(m,ii)=wp%kgen(ii)*dcos(wp%thetagen(ii))+wp%kgen(jj)*dcos(wp%thetagen(jj)) 03283 03284 ! Determine wave numbers according to Van Dongeren et al. 2003 03285 ! eq. 19 03286 k3(m,ii) =sqrt(wp%kgen(ii)**2+wp%kgen(jj)**2+ & 03287 2*wp%kgen(ii)*wp%kgen(jj)*dcos(deltheta(m,ii))) 03288 ! Determine W3 03289 w3(m,ii) = wp%wgen(ii) + wp%wgen(jj) 03290 ! Determine bound group velocity of waves 03291 c(m,ii) = w3(m,ii)/k3(m,ii) 03292 03293 ! Modification Robert + Jaap: make sure that the bound long wave amplitude does not 03294 ! explode when offshore boundary is too close to shore, 03295 ! by limiting the interaction group velocity 03296 !c(m,ii) = min(c(m,ii),par%nmax*sqrt(par%g/k3(m,ii)*tanh(k3(m,ii)*wp%h0))) 03297 !term2new = cg3(m,1:K-m)*k3(m,1:K-m) 03298 !dif = (abs(term2-term2new)) 03299 !if (any(dif>0.01*term2) .and. firsttime) then 03300 ! firsttime = .false. 03301 ! ! call writelog('lws','','Warning: shallow water so long wave variance is reduced using par%nmax') 03302 !endif 03303 term1 = wp%wgen(ii)*wp%wgen(jj) 03304 term2 = (wp%wgen(ii))+wp%wgen(jj) 03305 chk1 = cosh(wp%kgen(ii)*wp%h0) 03306 chk2 = cosh(wp%kgen(jj)*wp%h0) 03307 03308 ! Determine difference-interaction coefficient according to okihiro 1992 03309 ! eq. 4a 03310 D(m,ii) = -par%g*wp%kgen(ii)*wp%kgen(jj)*dcos(deltheta(m,ii))/2.d0/term1+ & 03311 term2**2/(2*par%g) + & 03312 par%g*term2/ & 03313 ((par%g*k3(m,ii)*tanh(k3(m,ii)*wp%h0)-(term2)**2)*term1)* & 03314 (term2*((term1)**2/par%g/par%g - wp%kgen(ii)*wp%kgen(jj)*dcos(deltheta(m,ii))) & 03315 - 0.50d0*((wp%wgen(ii))*wp%kgen(jj)**2/(chk2**2)+wp%wgen(jj)*wp%kgen(ii)**2/(chk1**2))) 03316 03317 ! Menno: limit the higher components, which can not correcty be resolved. 03318 if (k3(m,1)*wp%h0>5) D(m,:)=0.d0 03319 03320 ! Determine phase of bound long wave assuming a local equilibrium with 03321 ! forcing of interacting primary waves according to Van Dongeren et al. 03322 ! 2003 eq. 21 (the angle is the imaginary part of the natural log of a 03323 ! complex number as long as the complex number is not zero) 03324 Comptemp=conjg(wp%CompFn(1,wp%Findex(1)+ii)) 03325 Comptemp2=conjg(wp%CompFn(1,wp%Findex(1)+jj)) 03326 dphi3(m,ii) = imag(log(Comptemp))+imag(log(Comptemp2)) 03327 deallocate (Comptemp,Comptemp2) 03328 ! 03329 ! Determine angle of bound long wave according to Van Dongeren et al. 2003 eq. 22 03330 theta3 = atan2(KKy,KKx) 03331 ! 03332 ! free memory 03333 deallocate(term1,term2,term2new,dif,chk1,chk2) 03334 deallocate(ii,jj) 03335 enddo ! m=1,K-1 03336 ! 03337 ! Output to screen 03338 !if (.not. firsttime) then 03339 ! call writelog('lws','','Warning: shallow water so long wave variance is reduced using par%nmax') 03340 !endif 03341 call writelog('sl','', 'Calculating flux at boundary') 03342 ! 03343 ! Allocate temporary arrays for upcoming loop 03344 allocate(Comptemp(halflen-1)) 03345 allocate(Comptemp2(wp%tslen)) 03346 ! 03347 ! Run a loop over the offshore boundary 03348 do j=1,s%ny+1 03349 ! Determine energy of bound long wave according to Herbers 1994 eq. 1 based 03350 ! on difference-interaction coefficient and energy density spectra of 03351 ! primary waves 03352 ! Robert: E = 2*D**2*S**2*dtheta**2*df can be rewritten as 03353 ! E = 2*D**2*Sf**2*df 03354 Eforc = 0 03355 do m=2,2*K 03356 if (m .LE. K+1) then 03357 allocate(ii(m-1)) 03358 allocate(jj(m-1)) 03359 ii = 0 03360 jj = 0 03361 do i=1,m-1 03362 ii(i) = i 03363 jj(m -i) = i 03364 enddo 03365 else 03366 allocate(ii(K+1-(m-K))) 03367 allocate(jj(K+1-(m-K))) 03368 ii = 0 03369 jj = 0 03370 do i=1,K+1-(m-K) 03371 ii(i) = m-K + i -1 03372 jj(K+1-(m-K) -i + 1) = m-K + i -1 03373 enddo 03374 endif 03375 ! Use Sfinterp instead of sfinterpq 03376 if (par%Sfold==1) then 03377 Eforc(m,ii) = D(m,ii)**2*wp%Sfinterpq(j,ii)*wp%Sfinterpq(j,jj)*wp%dfgen 03378 else 03379 Eforc(m,ii) = D(m,ii)**2*wp%Sfinterp(j,ii)*wp%Sfinterp(j,jj)*wp%dfgen 03380 endif 03381 03382 ! Compute ratio for nonh. Compute here otherwise divide by zero! 03383 if (par%nonhq3d==1) then 03384 ! z-level of layer 03385 z = -1 * (wp%h0 - par%nhlay * wp%h0) 03386 ! ratio qx1/q 03387 qx1(m,ii) = (dsinh(KKx(m,ii) * (z + wp%h0)))/dsinh(KKx(m,ii)*wp%h0) 03388 ! ratio qx2/q 03389 qx2(m,ii) = (dsinh(KKx(m,ii) * wp%h0) - dsinh(KKx(m,ii)))/dsinh(KKx(m,ii)*wp%h0) 03390 ! ratio qy1/q 03391 qy1(m,ii) = (dsinh(KKy(m,ii) * (z + wp%h0)))/dsinh(KKy(m,ii)*wp%h0) 03392 ! ratio qy2/q 03393 qy2(m,ii) = (dsinh(KKy(m,ii) * wp%h0) - dsinh(KKy(m,ii)))/dsinh(KKy(m,ii)*wp%h0) 03394 endif 03395 deallocate(ii,jj) 03396 enddo 03397 ! 03398 ! Calculate bound wave amplitude for this offshore grid point 03399 ! Menno: add the sign of the interaction coefficient in the amptlitude. Large dtheta can result in a positive D. 03400 ! The phase of the bound wave is now only determined by phi1+phi2 03401 allocate(D_sign(2*K,2*K)) 03402 D_sign = 1 03403 ! Menno: put the sign of D in front of D_sign 03404 D_sign = sign(D_sign,D) 03405 Abnd = sqrt(2*Eforc*wp%dfgen) * D_sign 03406 deallocate(D_sign) 03407 ! 03408 ! Determine complex description of bound long wave per interaction pair of 03409 ! primary waves for first y-coordinate along seaside boundary 03410 if (par%nonhq3d==1) then 03411 count = 6 03412 Ftemp(:,:,1) = Abnd/2*exp(-1*compi*dphi3)*c*dcos(theta3)*qx1 ! qx1 03413 Ftemp(:,:,2) = Abnd/2*exp(-1*compi*dphi3)*c*dcos(theta3)*qx2 ! qx2 03414 Ftemp(:,:,3) = Abnd/2*exp(-1*compi*dphi3)*c*dsin(theta3)*qy1 ! qy1 03415 Ftemp(:,:,4) = Abnd/2*exp(-1*compi*dphi3)*c*dsin(theta3)*qy2 ! qy2 03416 Ftemp(:,:,5) = Abnd/2*exp(-1*compi*dphi3)*c ! qtot 03417 Ftemp(:,:,6) = Abnd/2*exp(-1*compi*dphi3) ! eta 03418 else 03419 count = 4 03420 Ftemp(:,:,1) = Abnd/2*exp(-1*compi*dphi3)*c*dcos(theta3) ! qx 03421 Ftemp(:,:,2) = Abnd/2*exp(-1*compi*dphi3)*c*dsin(theta3) ! qy 03422 Ftemp(:,:,3) = Abnd/2*exp(-1*compi*dphi3)*c ! qtot 03423 Ftemp(:,:,4) = Abnd/2*exp(-1*compi*dphi3) ! eta 03424 endif 03425 ! 03426 ! loop over qx,qy and qtot 03427 do iq=1,count 03428 ! Unroll wave component to correct place along the offshore boundary 03429 Ftemp(:,:,iq) = Ftemp(:,:,iq)* & 03430 exp(-1*compi*(KKy*(s%yz(1,j)-s%yz(1,1))+KKx*(s%xz(1,j)-s%xz(1,1)))) 03431 ! Determine Fourier coefficients 03432 Gn(2*wp%Findex(1):(2*wp%Findex(1)-1)+2*K) = sum(Ftemp(:,:,iq),DIM=2) 03433 Comptemp = conjg(Gn(2:halflen)) 03434 call flipiv(Comptemp,halflen-1) 03435 Gn(halflen+2:wp%tslen) = Comptemp 03436 ! 03437 ! Print status message to screen 03438 if (iq==3) then 03439 call writelog('ls','(A,I0,A,I0)','Flux ',j,' of ',s%ny+1) 03440 endif 03441 ! 03442 ! Inverse Discrete Fourier transformation to transform back to time space 03443 ! from frequency space 03444 Comptemp2=fft(Gn,inv=.true.) 03445 ! 03446 ! Determine mass flux as function of time and let the flux gradually 03447 ! increase and decrease in and out the wave time record using the earlier 03448 ! specified window. Fortran FFT is scaled by squar-roor. 03449 Comptemp2=Comptemp2/sqrt(dble(wp%tslen)) 03450 q(j,:,iq)=dreal(Comptemp2*wp%tslen)*wp%taperf 03451 enddo ! iq=1,3 03452 enddo ! j=1,s%ny+1 03453 03454 ! Now write data to file 03455 ! Menno: I don't know if this is appropriate. Now, nonhspectrum is always 1. 03456 if (par%nonhspectrum==0) then 03457 ! If doing combined wave action balance and swell wave flux with swkhmin>0 then we need to add short wave velocity 03458 ! to time series of q here: 03459 if (par%swkhmin>0.d0) then 03460 do j=1,s%ny+1 03461 q(j,:,1) = q(j,:,1) + wp%uits(j,:)*wp%h0 ! u flux 03462 q(j,:,4) = q(j,:,4) + wp%zsits(j,:) ! eta 03463 enddo 03464 endif 03465 ! 03466 ! 03467 ! Open file for storage of bound long wave flux 03468 call writelog('ls','','Writing long wave mass flux to ',trim(wp%qfilename),' ...') 03469 inquire(iolength=reclen) 1.d0 03470 reclen=reclen*((s%ny+1)*4) 03471 fid = create_new_fid() 03472 open(fid,file=trim(wp%qfilename),form='unformatted',access='direct',recl=reclen,status='REPLACE') 03473 ! 03474 ! Write to external file 03475 if (wp%dtchanged) then 03476 ! Interpolate from internal time axis to output time axis 03477 allocate(qinterp(s%ny+1,wp%tslenbc,3)) 03478 do iq=1,3 03479 do it=1,wp%tslenbc 03480 do j=1,s%ny+1 03481 call linear_interp(wp%tin,q(j,:,iq),wp%tslen,(it-1)*wp%dtbc,qinterp(j,it,iq),status) 03482 enddo 03483 enddo 03484 enddo 03485 ! write to file 03486 do irec=1,wp%tslenbc+1 03487 write(fid,rec=irec)qinterp(:,min(irec,wp%tslenbc),:) 03488 end do 03489 deallocate(qinterp) 03490 else 03491 ! no need for interpolation 03492 do irec=1,wp%tslenbc+1 03493 write(fid,rec=irec)q(:,min(irec,wp%tslenbc),:) 03494 end do 03495 endif 03496 close(fid) 03497 call writelog('sl','','file done') 03498 else 03499 do j=1,s%ny+1 03500 if (par%nonhq3d==1 .and. par%nhlay>0) then 03501 ! add to velocity time series 03502 wp%uits(j,:)=wp%uits(j,:)+ (q(j,:,1)/z + q(j,:,2)/(wp%h0+z))/2.d0 03503 ! add to velocity time series 03504 wp%duits(j,:)=wp%duits(j,:)+ (q(j,:,1)/z - q(j,:,2)/(wp%h0+z)) 03505 ! add to surface elevation time series 03506 wp%zsits(j,:)=wp%zsits(j,:)+q(j,:,6) 03507 if (j==s%ny+1) then 03508 deallocate(Eforc,D,deltheta,KKx,KKy,dphi3,k3,c,theta3,Gn,Abnd,w3,qx1,qx2,qy1,qy2,Ftemp) 03509 endif 03510 03511 else 03512 ! add to velocity time series 03513 wp%uits(j,:)=wp%uits(j,:)+q(j,:,1)/wp%h0 03514 ! add to surface elevation time series 03515 wp%zsits(j,:)=wp%zsits(j,:)+q(j,:,4) 03516 if (j==s%ny+1) then 03517 deallocate(Eforc,D,deltheta,KKx,KKy,dphi3,k3,c,theta3,Gn,Abnd,w3,Ftemp) 03518 endif 03519 endif 03520 enddo 03521 deallocate(q) 03522 endif ! par%nonhspectrum==1 03523 03524 03525 end subroutine generate_secondorder 03526 03527 end module spectral_wave_bc_module