module swn_outnc ! ! --|-----------------------------------------------------------|-- ! | BMT ARGOSS | ! | Voorsterweg 28, 8316 PT Vollenhove | ! | http://www.bmtargoss.com | ! | | ! | | ! | Programmer: A.Th.C. Hulst | ! --|-----------------------------------------------------------|-- ! ! ! SWAN (Simulating WAves Nearshore); a third generation wave model ! Copyright (C) 1993-2020 Delft University of Technology ! ! This program is free software; you can redistribute it and/or ! modify it under the terms of the GNU General Public License as ! published by the Free Software Foundation; either version 2 of ! the License, or (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! A copy of the GNU General Public License is available at ! http://www.gnu.org/copyleft/gpl.html#SEC3 ! or by writing to the Free Software Foundation, Inc., ! 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! ! ! Authors ! ! 0.01: A.Th.C.Hulst ! ! Updates ! ! 0.01, Feb 2012: New Module ! ! Purpose ! ! Write SWAN output into netcdf files ! ! Method ! ! MODULE construct ! ! Modules used ! ! NETCDF : Standard netcdf library ! AGIONCMD : Set of utilities for writing (metocean) data to netcdf ! NCTABLEMD : description table for metocean parameters use agioncmd use nctablemd, only : pi use M_PARALL use NETCDF use nctablemd, only: nctable, nctable_record, get_nctable_record use OUTP_DATA, only: ORQDAT, MAX_OUTP_REQ, LCOMPGRD, OUTP_FILES, NREOQ use OCPCOMM2, only: PROJNR, PROJID, VERTXT use OCPCOMM4 use SWCOMM1, only: CHTIME, OVEXCV use SWCOMM2, only: XOFFS, YOFFS, OPTG, EXCFLD use SWCOMM3, only: NSTATC, NSTATM, ALPC, MDC, MSC, MCGRD, MXC, MYC, DNORTH, & PI2, ICUR, BNAUT use SWCOMM4 use TIMECOMM, only: TFINC, TIMCO, TINIC, DT ! implicit none private public :: swn_outnc_openblockfile, swn_outnc_close_on_end, swn_outnc_colspc, swn_outnc_spec, & swn_outnc_appendblock, record_index, stnames, stnames_init, ncoffset ! ! Module parameters ! ! oqi(1) : saved alias for nref ! oqi(2) : index of request ! oqi(3) : number of output variables ! oqi(4) : idla of output request ! oqr(1) : tbegin ! oqr(2) : delta ! ! Module variables character(len=40) :: STNAMES(171,2) = '' logical :: stnames_initialized = .false., & skip_range_error = .true. type(recordaxe_type), save :: recordaxe(MAX_OUTP_REQ) integer,save :: ncoffset(MAX_OUTP_REQ) = 0 logical,save :: read_header(MAX_OUTP_REQ) = .true. integer,save :: nrefcp(MAX_OUTP_REQ) = -1 !PUN logical :: PUNSWAN = .true. !NPUN logical :: PUNSWAN = .false. type spcaux_type real, dimension(:), allocatable :: hs, depth, ux, uy, wndx, wndy, & xc, yc, xp, yp, f, theta integer, dimension(:), allocatable :: ips real, dimension(:,:), allocatable :: E logical :: is2d = .false., relative = .false. integer :: mip = 0, ndir = 0, nfreq = 0 end type spcaux_type ! ! Source text ! contains subroutine swn_outnc_spec(RTYPE, OQI, OQR, MIP, VOQR, VOQ, AC2, & SPCSIG, SPCDIR, DEP2, KGRPNT, CROSS, IONOD) ! ! ! --|-----------------------------------------------------------|-- ! | BMT ARGOSS | ! | Voorsterweg 28, 8316 PT Vollenhove | ! | http://www.bmtargoss.com | ! | | ! | | ! | Programmer: A.Th.C. Hulst | ! --|-----------------------------------------------------------|-- ! ! ! SWAN (Simulating WAves Nearshore); a third generation wave model ! Copyright (C) 1993-2020 Delft University of Technology ! ! This program is free software; you can redistribute it and/or ! modify it under the terms of the GNU General Public License as ! published by the Free Software Foundation; either version 2 of ! the License, or (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! A copy of the GNU General Public License is available at ! http://www.gnu.org/copyleft/gpl.html#SEC3 ! or by writing to the Free Software Foundation, Inc., ! 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! ! ! 0. Authors ! ! ! 1. Updates ! ! ! 2. Purpose ! ! Write action density spectrum to netcdf file when serial OR ! binary file when PARLL or PUNSWAN ! ! 3. Method ! ! --- ! ! 4. Argument variables ! ! IO..name....type...size.......description ! i RTYPE char * type of output request: 'SPEC' for 2-D spectral ! 'SPE1' for 1-D freq. spectrum ! i IONOD int MIP array indicating in which subdomain output points ! are located ! i SPCDIR real MDC (*,1); spectral directions (radians) ! (*,2); cosine of spectral directions ! (*,3); sine of spectral directions ! (*,4); cosine^2 of spectral directions ! (*,5); cosine*sine of spectral directions ! (*,6); sine^2 of spectral directions ! i SPCSIG real MSC Relative frequencies in computational domain in sigma-space ! i VOQR int * Adminstrative list tracks which column in VOQ has which ! variable ! i CROSS bool 4,MIP ! i KGRPNT int MXC,MYC ! i MIP int 1 number of output points ! i VOQ real * Array with "integrated" parameters ! i AC2 real MDC,MSC, 2D spectra ! MCGRD ! i DEP2 real MCGRD ! real, intent( in) :: SPCDIR(MDC,6), SPCSIG(MSC) logical, intent( in) :: CROSS(1:4,1:MIP) character (len=*), intent( in) :: RTYPE integer, intent( in) :: VOQR(*), & KGRPNT(MXC,MYC), IONOD(*), & MIP integer, intent(inout) :: OQI(4) real*8, intent( in) :: OQR(2) real, intent( in) :: VOQ(MIP,*), AC2(MDC,MSC,MCGRD), & DEP2(MCGRD) ! ! 5. Local variables ! integer :: irq integer, save :: IENT=0 type(spcaux_type) :: spcaux ! 8. Subroutines used ! ! swn_outnc_spcaux ! swn_outnc_openspecfile ! swn_outnc_appendspc ! nccheck ! nf90_close ! swn_outnc_deallocate_spcaux ! logical STPNOW ! ! 9. Subroutines calling ! ! SWOUTP (SWAN/OUTP) ! ! 10. Error messages ! ! --- ! ! 11. Remarks ! ! ! 12. Structure. ! ! a. compute spectra and auxilary data (writes binary file if parll or punswan) ! if serial ! b. open / create netcdf file ! c. write spectra to netcdf ! d. close ncfile ! ! 13. Source text ! if (LTRACE) call STRACE (IENT,'swn_outnc_spec') irq = OQI(2) call swn_outnc_spcaux(RTYPE, OQI, MIP, VOQR, VOQ, AC2, & SPCSIG, SPCDIR, DEP2, KGRPNT, CROSS, IONOD, & spcaux) if (STPNOW()) return ! ! b. open / create netcdf file (generic output file) ! if ( .not. (PARLL .or. PUNSWAN) ) then ! opening / creating the netcdf file. Sets module variable recordaxe(irq) call swn_outnc_openspecfile(OUTP_FILES(irq), spcaux, OQI, OQR) if (STPNOW()) return call swn_outnc_appendspc(OQI, spcaux) if (STPNOW()) return call nccheck(nf90_close(oqi(1) + ncoffset(irq) )) ncoffset(irq) = 0 end if call swn_outnc_deallocate_spcaux( spcaux ) end subroutine swn_outnc_spec subroutine swn_outnc_colspc ( RTYPE, OQI, OQR, MIP, KGRPGL ) use SwanGriddata, only: xcugrdgl, ycugrdgl, nverts !PUN USE OCPCOMM2, ONLY: LENFNM, DIRCH2 !PUN USE SIZES, ONLY: GLOBALDIR, LOCALDIR ! ! ! --|-----------------------------------------------------------|-- ! | BMT ARGOSS | ! | Voorsterweg 28, 8316 PT Vollenhove | ! | http://www.bmtargoss.com | ! | | ! | | ! | Programmer: A.Th.C. Hulst | ! --|-----------------------------------------------------------|-- ! ! ! SWAN (Simulating WAves Nearshore); a third generation wave model ! Copyright (C) 1993-2020 Delft University of Technology ! ! This program is free software; you can redistribute it and/or ! modify it under the terms of the GNU General Public License as ! published by the Free Software Foundation; either version 2 of ! the License, or (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! A copy of the GNU General Public License is available at ! http://www.gnu.org/copyleft/gpl.html#SEC3 ! or by writing to the Free Software Foundation, Inc., ! 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! ! ! 0. Authors ! 41.70 A.Th.C. Hulst ! ! 1. Updates ! 41.70 Added support for multiple compute commands and multiple specout requests ! ! 2. Purpose ! ! Collect spectra from binary files and store in netcdf file ! ! 3. Method ! ! --- ! ! 4. Argument variables character (len=*), intent( in) :: RTYPE * 4 integer, intent(inout) :: OQI(4) real*8, intent( in) :: OQR(2) integer, intent( in) :: MIP, KGRPGL(MXCGL,MYCGL) ! ! IO..name....type...size.......description ! i RTYPE char * type of output request: 'SPEC' for 2-D spectral ! 'SPE1' for 1-D freq. spectrum ! i XP, YP real MIP coordinates of all output points ! i MIP int 1 number of output points ! ! 5. Local variables ! logical :: lopen, file_exists, & EQREAL, STPNOW integer :: ip, ierr, otype, xpctime2, & i, tmip, binnr, irq, & tip, ips(MIP), iproc, nref, ilpos integer, save :: IENT=0 character*80 :: binfile, ncfile, basefile character*256 :: errmsg type(spcaux_type) :: spcaux, lspcaux real :: xref, yref ! ! 9. Subroutines calling ! ! SWCOLOUT ! ! 10. Error messages ! ! ! 11. Remarks ! ! ! 12. Structure ! ! ! 13. Source text ! if (LTRACE) call STRACE (IENT,'swn_outnc_colspc') ierr = 0 ! ! a. Prepare spectra/auxilary data struct ! irq = OQI(2) spcaux%mip = MIP allocate(spcaux%hs(MIP), spcaux%depth(MIP), spcaux%ux(MIP), spcaux%uy(MIP), & spcaux%wndx(MIP), spcaux%wndy(MIP), spcaux%xc(MIP), spcaux%yc(MIP)) if ( LCOMPGRD .and. PUNSWAN) then allocate(spcaux%xp(nverts), spcaux%yp(nverts)) else allocate(spcaux%xp(MIP), spcaux%yp(MIP)) end if spcaux%hs = OVEXCV(10) spcaux%depth = OVEXCV(4) spcaux%ux = OVEXCV(5) spcaux%uy = OVEXCV(5) spcaux%wndx = OVEXCV(26) spcaux%wndy = OVEXCV(26) if (RTYPE(3:3) .eq. 'R') spcaux%relative = .true. if (RTYPE(4:4) .eq. 'C') then allocate(spcaux%E(MSC * MDC, MIP)) spcaux%is2d = .true. else allocate(spcaux%E(3 * MSC, MIP)) spcaux%is2d = .false. end if spcaux%E = 0 spcaux%nfreq = MSC spcaux%ndir = MDC allocate( spcaux%f(MSC), spcaux%theta(MDC)) ! ! open and read binary files ! binfile = OUTP_FILES(irq) ! nref is unitnr set while writing the binary file. nref = HIOPEN + IRQ basefile = outp_files(irq) ilpos = index(basefile, '-0') if (ilpos.ne.0) basefile = basefile(1:ilpos-1) !PUN ilpos = len(trim(LOCALDIR))+2 !PUN basefile = basefile(ilpos:LENFNM) !PUN ilpos = index(LOCALDIR, '0')-1 do iproc = 1, NPROC binnr = HIOPEN+NREOQ+(NREF-HIOPEN-1)*NPROC+iproc !PUN write(binfile, '(A, I4.4, A, A)') LOCALDIR(1:ilpos), iproc - 1, DIRCH2, trim(basefile) !NPUN write(binfile, '(A,"-",I3.3)') trim(basefile), iproc inquire(unit=binnr, opened=lopen) if ( .not. lopen) then open(file=binfile, unit=binnr, form='unformatted', IOSTAT=ierr) if (ierr /= 0) then write(errmsg, '("Opening ",A, " on ",I3, " failed with IOSTAT ",I3)') trim(binfile), binnr, ierr call MSGERR(4, errmsg) return end if end if inquire(unit=binnr, opened=lopen) if ( .not. lopen ) then write(errmsg,'("unit ",I3," is NOT open")'), binnr call MSGERR(4, errmsg) return end if ! In the final collocation the spatial, spectral and directional grids are unknown. ! They are exchanged via the binary file of the master process. if ( read_header(irq) .and. iproc == 1 ) then read_header(irq) = .false. read(binnr, IOSTAT=ierr) spcaux%xp if ( ierr /= 0 ) then write(errmsg,'("Reading grid data from ", A, " returned IOSTAT ", I3.3)') trim(binfile), ierr call MSGERR(4, errmsg) return end if read(binnr) spcaux%yp if ( PUNSWAN .and. LCOMPGRD ) then deallocate(spcaux%xp, spcaux%yp) allocate(spcaux%xp(MIP), spcaux%yp(MIP)) spcaux%xp = xcugrdgl spcaux%yp = ycugrdgl end if read(binnr) spcaux%f read(binnr) spcaux%theta end if read(binnr, IOSTAT=ierr) xpctime2, tmip if ( ierr /= 0 ) then write(errmsg,'("Reading time and tmip from ", A, "(",I3,") , time ", A, ", returned IOSTAT ", I3)') & trim(binfile), binnr, CHTIME, ierr call MSGERR(4, errmsg) return end if if ( tmip == 0 ) cycle read(binnr) ips(1:tmip) do tip = 1, tmip ip = ips(tip) read(binnr, iostat = ierr) xref, yref, spcaux%E(:, ip), spcaux%depth(ip), & spcaux%ux(ip), spcaux%uy(ip), spcaux%hs(ip), spcaux%wndx(ip), spcaux%wndy(ip) if ( ierr /= 0 ) then write(errmsg,'("Reading IP ",I3.3, ", TIP ", I3.3, " in ", A, " returned IOSTAT ", I3.3)') & ip, tip, binfile, ierr call MSGERR(4, errmsg) return end if end do ! points end do !nproc ! ! MPI with a cold start shows some interesting initial values ! remove them ! where(spcaux%E < epsilon(1.)) spcaux%E = 0. ! ! b. netcdf IO ! ncfile = outp_files(irq) ilpos = index(ncfile, '-0') if (ilpos.ne.0) ncfile = ncfile(1:ilpos-1) !PUN ilpos = len(trim(LOCALDIR))+2 !PUN ncfile = ncfile(ilpos:LENFNM) !PUN ncfile = trim(GLOBALDIR)//DIRCH2//trim(ncfile) if ( ncoffset(irq) == 0 ) then call swn_outnc_openspecfile(ncfile, spcaux, OQI, OQR) if (STPNOW()) return end if call swn_outnc_appendspc(OQI, spcaux, xpctime2) if (STPNOW()) return call swn_outnc_deallocate_spcaux( spcaux ) if ( NSTATM == 0 .or. TIMCO >= TFINC ) then ! The end time is reached in each computation block. See ! SWCOLOUT structure. call close_ncfile(OQI(1) + ncoffset(irq) ) ncoffset(irq) = 0 ! The intermediate binary files are closed and deleted in ! swanparll.ftn -> SWCOLOUT else call nccheck( NF90_SYNC( OQI(1) + ncoffset(irq) )) end if end subroutine swn_outnc_colspc subroutine swn_outnc_spcaux(RTYPE, OQI, MIP, VOQR, VOQ, AC2, & SPCSIG, SPCDIR, DEP2, KGRPNT, CROSS, IONOD, & lspcaux) USE OCPCOMM2 USE TIMECOMM, only: TINIC, TFINC, TIMCO !PUN USE SIZES, ONLY: MYPROC ! ! ! --|-----------------------------------------------------------|-- ! | BMT ARGOSS | ! | Voorsterweg 28, 8316 PT Vollenhove | ! | http://www.bmtargoss.com | ! | | ! | | ! | Programmer: A.Th.C. Hulst | ! --|-----------------------------------------------------------|-- ! ! ! SWAN (Simulating WAves Nearshore); a third generation wave model ! Copyright (C) 1993-2020 Delft University of Technology ! ! This program is free software; you can redistribute it and/or ! modify it under the terms of the GNU General Public License as ! published by the Free Software Foundation; either version 2 of ! the License, or (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! A copy of the GNU General Public License is available at ! http://www.gnu.org/copyleft/gpl.html#SEC3 ! or by writing to the Free Software Foundation, Inc., ! 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! ! ! 0. Authors ! 41.70 Sander Hulst ! ! 1. Updates ! 41.70 Do not close binary files. Breaks on multiple compute commands ! ! 2. Purpose ! ! Write action density spectrum to netcdf file ! ! 3. Method ! ! --- ! ! 4. Argument variables ! ! IO..name....type...size.......description ! i RTYPE char * type of output request: 'SPEC' for 2-D spectral ! 'SPE1' for 1-D freq. spectrum ! i IONOD int MIP array indicating in which subdomain output points ! are located ! i SPCDIR real MDC (*,1); spectral directions (radians) ! (*,2); cosine of spectral directions ! (*,3); sine of spectral directions ! (*,4); cosine^2 of spectral directions ! (*,5); cosine*sine of spectral directions ! (*,6); sine^2 of spectral directions ! i SPCSIG real MSC Relative frequencies in computational domain in sigma-space ! i VOQR int * Adminstrative list tracks which column in VOQ has which ! variable ! i CROSS bool 4,MIP ! i KGRPNT int MXC,MYC ! i MIP int 1 number of output points ! i VOQ real * Array with "integrated" parameters ! i AC2 real MDC,MSC, 2D spectra ! MCGRD ! i DEP2 real MCGRD ! real, intent( in) :: SPCDIR(MDC,6), SPCSIG(MSC) logical, intent( in) :: CROSS(1:4,1:MIP) character (len=*), intent( in) :: RTYPE integer, intent( in) :: OQI(4), VOQR(*), & KGRPNT(MXC,MYC), IONOD(*), & MIP real, intent( in) :: VOQ(MIP,*), AC2(MDC,MSC,MCGRD), & DEP2(MCGRD) type(spcaux_type), intent( out) :: lspcaux ! ! 5. Local variables ! logical :: EQREAL, lopen, do_open_files, write_header integer :: ip, ierr, otype, xpctmp(2), xpctime, & pnr, ri, i, tmip, irq, iproc, & binnr, xi, yi, npnts integer, allocatable :: ips(:) integer, save :: IENT=0 character*80 :: outfile character*256 :: errmsg ! 8. Subroutines used ! ! SWCMSP ! logical STPNOW ! ! 9. Subroutines calling ! ! SWOUTP (SWAN/OUTP) ! ! 10. Error messages ! ! --- ! ! 11. Remarks ! ! - PUNSWAN support was added later on. In punswan, PARLL is always false but the ! IAMMASTER is set. For most output purposes, PARLL should be considered ! true. Hence a logical PUNSWAN is set at the top if this module. In the structure ! below PARLL means if PARLL .or. PUNSWAN. ! ! 12. Structure. ! a. decide wether relative and 2D spectra are required ! b. collect depth, current, xc, yc, f, theta ! c if PARLL open unformatted binary ! d. set output type (1D or 2D, relative or absolute) and allocate Energy array ! e. if PARLL, write time and number of output points in this subgrid to ! unformatted binary file ! for each point... ! f.1. compute relative / absolute 1D or 2D energy ! f.2. if PARLL spectra and auxillary data to unformatted binary file ! ! 13. Source text ! !NPUN iproc = INODE !PUN iproc = MYPROC if (LTRACE) call STRACE (IENT,'swn_outnc_spcaux') lspcaux%mip = MIP allocate(lspcaux%hs(MIP), lspcaux%depth(MIP), lspcaux%ux(MIP), lspcaux%uy(MIP), & lspcaux%wndx(MIP), lspcaux%wndy(MIP), lspcaux%xc(MIP), lspcaux%yc(MIP), & lspcaux%xp(MIP), lspcaux%yp(MIP), ips(MIP)) ! ! a. decide wether relative and 2D spectra are required ! irq = OQI(2) ierr = 0 if (RTYPE(3:3) .eq. 'R') lspcaux%relative = .true. if (RTYPE(4:4) .eq. 'C') lspcaux%is2d = .true. ! ! b. collect auxillary data / set default (task e. set hs and wind values) ! lspcaux%depth = VOQ(:,VOQR(4)) lspcaux%hs = 0 lspcaux%wndx = 0 lspcaux%wndy = 0 if (ICUR > 0) then lspcaux%ux = VOQ(:,VOQR(5)) lspcaux%uy = VOQ(:,VOQR(5)+1) else lspcaux%ux = 0. lspcaux%uy = 0. end if lspcaux%xc = OVEXCV(1) lspcaux%yc = OVEXCV(2) lspcaux%xp = OVEXCV(1) lspcaux%yp = OVEXCV(2) do ip = 1, MIP if (OPTG.ne.5) then if (.not.EQREAL(VOQ(ip,VOQR(24)),OVEXCV(1) )) then lspcaux%xc(ip) = VOQ(ip,VOQR(24)) lspcaux%yc(ip) = VOQ(ip,VOQR(25)) end if else if (.not.EQREAL(VOQ(ip,1),OVEXCV(1))) then lspcaux%xc(ip) = VOQ(ip,1) - XOFFS lspcaux%yc(ip) = VOQ(ip,2) - YOFFS end if end if lspcaux%xp(ip) = VOQ(ip,1) lspcaux%yp(ip) = VOQ(ip,2) end do lspcaux%nfreq = MSC lspcaux%ndir = MDC allocate( lspcaux%f(MSC), lspcaux%theta(MDC)) lspcaux%f = SPCSIG/PI2 if ( BNAUT ) then ! meteorological convention lspcaux%theta = PI + DNORTH*PI/180 - SPCDIR(:,1) else lspcaux%theta = SPCDIR(:,1) end if ! ! c if PARLL or PUNSWAN open unformatted binary if not already opened ! if ( PARLL .or. PUNSWAN) then if ( oqi(1) == 0 ) then call FOR(oqi(1), OUTP_FILES(irq), 'UU', ierr) if ( ierr > 0 ) then write(errmsg,'("File ", A, " open returned IOSTAT ", I3)'), trim(OUTP_FILES(irq)), ierr call MSGERR(4, errmsg) return end if write_header = .true. else write_header = .false. end if end if ! d. set output type (1D or 2D, relative or absolute) and allocate Energy array if ( lspcaux%is2d ) then allocate(lspcaux%E(MSC * MDC, MIP)) if ( lspcaux%relative ) then otype = 2 else otype = -2 end if else allocate(lspcaux%E(3 * MSC, MIP)) if ( lspcaux%relative ) then otype = 1 else otype = -1 end if end if lspcaux%E = 0 ! e. write time and number of output points in this subgrid to unformatted binary file if ( PARLL .or. PUNSWAN) then tmip = 0 ! if part of this subgrid and an active point do ip = 1, MIP if ( IONOD(ip).eq.IPROC ) then tmip = tmip + 1 ips(tmip) = ip end if end do if ( NSTATM > 0 ) then READ (chtime, '(I8,1X,I6)') (xpctmp(i), i=1,2) xpctime = seconds_since_epoch(xpctmp(1), xpctmp(2)) else xpctime = 0 end if ! In the final collocation the spatial, spectral and directional grids are unknown. ! They are exchanged via the binary file of the master on the first timestep. ! ! note the statement below and its reading counterpart imply that the master is ! always IPROC 1 if ( write_header .and. IAMMASTER ) then write(OQI(1)) lspcaux%xp write(OQI(1)) lspcaux%yp write(OQI(1)) lspcaux%f write(OQI(1)) lspcaux%theta end if write(OQI(1), IOSTAT=ierr) xpctime, tmip if (ierr /= 0) then write(errmsg, '(A, "-> IOSTAT=", I3, " time=", I10, " TMIP=", I4, " OQI(1)=", I3)') & trim(outfile), ierr, xpctime, tmip, oqi(1) call MSGERR(4, errmsg) return end if if ( tmip /= 0 ) write(OQI(1)) ips end if ! for each point, compute spectra and collect remaining auxilary do ip = 1, MIP if ( (.not. PARLL .and. .not. PUNSWAN) .or. & !NPUN IONOD(ip).eq.INODE ) then !PUN IONOD(ip).eq.MYPROC ) then ! if depth > 0 and not a dummy value if ( lspcaux%depth(ip) > epsilon(1.) .and. abs(lspcaux%depth(ip) - OVEXCV(4)) > epsilon(1.) ) then call SWCMSP (otype, lspcaux%xc(ip), lspcaux%yc(ip), AC2, lspcaux%E(:, ip), SPCSIG, & lspcaux%depth(ip) , dep2, lspcaux%ux(ip), lspcaux%uy(ip), & SPCDIR(1,2), SPCDIR(1,3), 1., KGRPNT, & CROSS(1,IP), ierr) ! when ierr > 0 then energy == 0. That's fine. lspcaux%hs(ip) = VOQ(ip,VOQR(10)) lspcaux%wndx(ip) = VOQ(ip,VOQR(26)) lspcaux%wndy(ip) = VOQ(ip,VOQR(26)+1) end if if ( PARLL .or. PUNSWAN ) then ! write to binary write(OQI(1)) lspcaux%xp(ip), lspcaux%yp(ip), lspcaux%E(:, ip), lspcaux%depth(ip), & lspcaux%ux(ip), lspcaux%uy(ip), lspcaux%hs(ip), lspcaux%wndx(ip), & lspcaux%wndy(ip) end if end if end do deallocate(ips) end subroutine swn_outnc_spcaux subroutine swn_outnc_spcaux_on_wetnodes( lkgrpnt, lspcaux, spcaux ) ! lkgrpnt is KGRPNT when called from shared memory mode or KGRPGL ! from colspc in the case of distributed memory. In both cases, it covers ! the full grid integer, intent( in) :: lkgrpnt(MXCGL,MYCGL) type(spcaux_type), intent( in) :: lspcaux type(spcaux_type), intent( out) :: spcaux integer :: ip, indx, npnts, ix, iy integer, save :: IENT=0 character*256 :: errmsg if (LTRACE) call STRACE(IENT,'swn_outnc_spcaux_on_wetnodes') npnts = MCGRDGL - 1 allocate(spcaux%hs(npnts), spcaux%depth(npnts), spcaux%ux(npnts), spcaux%uy(npnts), & spcaux%wndx(npnts), spcaux%wndy(npnts), spcaux%xp(npnts), spcaux%yp(npnts), & spcaux%xc(npnts), spcaux%yc(npnts), spcaux%ips(npnts), & spcaux%E(size(lspcaux%E,1), npnts) ) do ix = 1, MXCGL do iy = 1, MYCGL ip = (iy-1)*MXCGL + ix if ( lkgrpnt(ix,iy) /= 1 ) then indx = lkgrpnt(ix,iy) - 1 spcaux%ips(indx) = ip spcaux%hs(indx) = lspcaux%hs(ip) spcaux%depth(indx) = lspcaux%depth(ip) spcaux%ux(indx) = lspcaux%ux(ip) spcaux%uy(indx) = lspcaux%uy(ip) spcaux%wndx(indx) = lspcaux%wndx(ip) spcaux%wndy(indx) = lspcaux%wndy(ip) spcaux%xc(indx) = lspcaux%xc(ip) spcaux%yc(indx) = lspcaux%yc(ip) spcaux%xp(indx) = lspcaux%xp(ip) spcaux%yp(indx) = lspcaux%yp(ip) spcaux%E(:,indx) = lspcaux%E(:,ip) end if end do end do if ( indx /= npnts ) then write(errmsg, '("Inconsistent number of wet nodes:", i5, i5)') indx, npnts call MSGERR(4, errmsg) return end if spcaux%is2d = lspcaux%is2d spcaux%relative = lspcaux%relative spcaux%nfreq = lspcaux%nfreq spcaux%ndir = lspcaux%ndir allocate( spcaux%f(spcaux%nfreq), spcaux%theta(spcaux%ndir)) spcaux%f = lspcaux%f spcaux%theta = lspcaux%theta spcaux%mip = npnts end subroutine swn_outnc_spcaux_on_wetnodes subroutine swn_outnc_deallocate_spcaux( lspcaux ) type(spcaux_type), intent(inout) :: lspcaux integer, save :: IENT=0 if (LTRACE) call STRACE (IENT,'swn_outnc_deallocate_spcaux') ! yesyes, derived type fields are deallocated when out of scope ! but sometimes variables stay in scope if (allocated(lspcaux%hs)) deallocate(lspcaux%hs) if (allocated(lspcaux%depth)) deallocate(lspcaux%depth) if (allocated(lspcaux%ux)) deallocate(lspcaux%ux) if (allocated(lspcaux%uy)) deallocate(lspcaux%uy) if (allocated(lspcaux%wndx)) deallocate(lspcaux%wndx) if (allocated(lspcaux%wndy)) deallocate(lspcaux%wndy) if (allocated(lspcaux%xc)) deallocate(lspcaux%xc) if (allocated(lspcaux%yc)) deallocate(lspcaux%yc) if (allocated(lspcaux%xp)) deallocate(lspcaux%xp) if (allocated(lspcaux%yp)) deallocate(lspcaux%yp) if (allocated(lspcaux%f)) deallocate(lspcaux%f) if (allocated(lspcaux%theta)) deallocate(lspcaux%theta) if (allocated(lspcaux%E)) deallocate(lspcaux%E) if (allocated(lspcaux%ips)) deallocate(lspcaux%ips) lspcaux%mip = 0 lspcaux%ndir = 0 lspcaux%nfreq = 0 end subroutine swn_outnc_deallocate_spcaux subroutine swn_outnc_appendspc(oqi, spcaux, xpctime2) USE OCPCOMM2 ! ! ! --|-----------------------------------------------------------|-- ! | BMT ARGOSS | ! | Voorsterweg 28, 8316 PT Vollenhove | ! | http://www.bmtargoss.com | ! | | ! | | ! | Programmer: A.Th.C. Hulst | ! --|-----------------------------------------------------------|-- ! ! ! SWAN (Simulating WAves Nearshore); a third generation wave model ! Copyright (C) 1993-2020 Delft University of Technology ! ! This program is free software; you can redistribute it and/or ! modify it under the terms of the GNU General Public License as ! published by the Free Software Foundation; either version 2 of ! the License, or (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! A copy of the GNU General Public License is available at ! http://www.gnu.org/copyleft/gpl.html#SEC3 ! or by writing to the Free Software Foundation, Inc., ! 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! ! ! 0. Authors ! ! ! 1. Updates ! ! ! 2. Purpose ! ! Append spectra and auxilary variables to netcdf file. ! ! 3. Method ! ! --- ! ! 4. Argument variables ! ! IO..name....type...size.......description ! integer, intent( in) :: oqi(4) type(spcaux_type), target, intent(inout) :: spcaux integer, optional, intent( in) :: xpctime2 ! ! 5. Local variables ! integer :: ierr, xpctmp(2), xpctime, pnr, ri, irq, i, & ncid character*256 :: errmsg integer, save :: IENT=0 logical :: spc_as_map, wetnode_list, noaux type(spcaux_type), target :: lspcaux type(spcaux_type), pointer :: pspcaux ! 8. Subroutines used ! ! SWCMSP ! logical STPNOW ! ! 9. Subroutines calling ! ! SWOUTP (SWAN/OUTP) ! ! 10. Error messages ! ! --- ! ! 11. Remarks ! Auxillary data is retrieved from the VOQ array. Please modify SWORDC when ! VOQ need to be extended ! ! 12. Structure ! a. Determine the index in the record axe of ncfile ! b. add spectra to ncfile ! c. add auxilary variables (depth, current and Hs) to ncfile ! ! 13. Source text ! if (LTRACE) call STRACE (IENT,'swn_outnc_appendspc') irq = oqi(2) ncid = oqi(1) + ncoffset(irq) call swn_outnc_spcflags(oqi(4), spc_as_map, wetnode_list, spcaux, noaux=noaux) ! ! Determine the index in the record axe ! if ( recordaxe(irq)%nstatm ) then READ (chtime, '(I8,1X,I6)') (xpctmp(i), i=1,2) xpctime = seconds_since_epoch(xpctmp(1), xpctmp(2)) ! If provided, check the time against a reference time if ( present(xpctime2) ) then if ( xpctime2 /= xpctime ) then write(errmsg, '(I12, " does not equal ", I12)') xpctime2, xpctime call MSGERR(4, errmsg) return end if end if call record_index(ncid, recordaxe(irq), xpctime, ri, .false., '') else if ( string_is_int(PROJNR) ) then read( PROJNR, '(i16)' ) pnr if ( pnr == 0 ) then call MSGERR(3, 'The projnr number may not be zero') return end if else pnr = 1 end if call record_index(ncid, recordaxe(irq), pnr, ri, .false., '' ) end if if ( wetnode_list ) then ! remove dry points. In memory there's a copy of spcaux with dry points and ! lspcaux without them. If this remains problematic than we need to do something clever ! with KGRPGL at write time call swn_outnc_spcaux_on_wetnodes(KGRPGL, spcaux, lspcaux ) if (STPNOW()) return ! pointer to wetnode list pspcaux => lspcaux ! deallocate list with dry points call swn_outnc_deallocate_spcaux( spcaux ) else pspcaux => spcaux end if ! ! add spectra ! where(pspcaux%E < epsilon(1.)) pspcaux%E = 0. if ( pspcaux%is2d ) then ! factor 2*PI to account for transition from rad/s to Hz pspcaux%E = pspcaux%E * 2 * pi call agnc_add_spcdata_density(ncid, ri, pspcaux%E, spc_as_map) else call agnc_add_spcdata(ncid, ri, pspcaux%E(1:MSC,:), & pspcaux%E(MSC+1:2*MSC,:), & pspcaux%E(2*MSC+1:3*MSC,:), & spc_as_map) end if ! ! add auxilary variables (depth, current and Hs). ! ! hs is provided so that user can check his integration routines. His / hers ! hs should match the one computed by SWAN. if ( .not.noaux ) then if ( spc_as_map ) then call agnc_add_mapdata(ncid, 'depth', ri, pspcaux%depth, OVEXCV( 4), skip_range_error) call agnc_add_mapdata(ncid, 'xcur', ri, pspcaux%ux, OVEXCV( 5), skip_range_error) call agnc_add_mapdata(ncid, 'ycur', ri, pspcaux%uy, OVEXCV( 5), skip_range_error) call agnc_add_mapdata(ncid, 'hs', ri, pspcaux%hs, OVEXCV(10), skip_range_error) call agnc_add_mapdata(ncid, 'xwnd', ri, pspcaux%wndx, OVEXCV(26), skip_range_error) call agnc_add_mapdata(ncid, 'ywnd', ri, pspcaux%wndy, OVEXCV(26), skip_range_error) else call agnc_add_pntdata(ncid, 'depth', ri, pspcaux%depth, OVEXCV( 4), skip_range_error) call agnc_add_pntdata(ncid, 'xcur', ri, pspcaux%ux, OVEXCV( 5), skip_range_error) call agnc_add_pntdata(ncid, 'ycur', ri, pspcaux%uy, OVEXCV( 5), skip_range_error) call agnc_add_pntdata(ncid, 'hs', ri, pspcaux%hs, OVEXCV(10), skip_range_error) call agnc_add_pntdata(ncid, 'xwnd', ri, pspcaux%wndx, OVEXCV(26), skip_range_error) call agnc_add_pntdata(ncid, 'ywnd', ri, pspcaux%wndy, OVEXCV(26), skip_range_error) end if end if ! garbage collect nullify(pspcaux) if ( wetnode_list ) then call swn_outnc_deallocate_spcaux( lspcaux ) else call swn_outnc_deallocate_spcaux( spcaux ) end if end subroutine swn_outnc_appendspc subroutine swn_outnc_openspecfile(ncfile, spcaux, oqi, oqr) ! ! Structure: ! ! 1) input arguments ! 2) local variables ! if file does not exist ! 3) Define new record axe (run or time) ! If spectra requested on COMPGRID ! if not rotated ! 4.a) define mspcgrid with 1D coordinate axes ! else ! 4.b) define mspcgrid with 2D coordinate axes ! end ! 4.c) create ncfile ! else ! 5.a) define spcgrid ! if WETCGRID ! 5.b) filter pointlist ! end if ! 5.b) create ncfile ! end if ! 6.a) add global attributes ! 6.b) create variables ! 6.c) close define mode and reopen write ! 6.d) fill dimension variables and close ! end if file does not exist ! 7) open file in write mode ! ! 1) input arguments ! character*80, intent( in) :: ncfile type(spcaux_type), target, intent( in) :: spcaux integer, intent( in) :: oqi(4) real*8, intent( in) :: oqr(2) ! ! 2) local variables ! logical :: file_exists, nc_debug, & Escale, monthly, spc_as_map, & wetnode_list, STPNOW, noaux integer :: ncid, xpctmp(2), i, irq type(spcgrid_type) :: spcgrid type(mapgrid_type) :: mapgrid type(pntgrid_type) :: pntgrid type(spcaux_type), target :: lspcaux type(spcaux_type), pointer :: pspcaux integer, save :: IENT=0 file_exists = .false. nc_debug = .false. if (LTRACE) call STRACE (IENT,'swn_outnc_openspecfile') call swn_outnc_spcflags(oqi(4), spc_as_map, wetnode_list, spcaux, Escale, noaux, monthly) irq = OQI(2) if ( .not. stnames_initialized ) call stnames_init() inquire( FILE=ncfile, EXIST=file_exists ) ! ! if file does not exists ! if ( .not. file_exists) then ! ! 3) Define new record axe (run or time) and spectral grid ! allocate(recordaxe(irq)%content(1)) recordaxe(irq)%ncontent = 1 if ( NSTATM > 0 ) then READ (chtime, '(I8,1X,I6)') (xpctmp(i), i=1,2) ! should be seconds since epoch. recordaxe(irq)%content(1) = seconds_since_epoch(xpctmp(1), xpctmp(2)) if ( NSTATC == 0 ) then recordaxe(irq)%delta = oqr(2) else recordaxe(irq)%delta = maxval((/oqr(2), DT/)) endif recordaxe(irq)%nstatm = .true. else ! stationary, record axe is "run" if ( string_is_int(PROJNR) ) then read( PROJNR, '(i16)' ) recordaxe(irq)%content(1) else recordaxe(irq)%content(1) = 1 end if recordaxe(irq)%delta = 1 recordaxe(irq)%nstatm = .false. end if allocate (spcgrid%frequency(spcaux%nfreq)) spcgrid%nfreq = spcaux%nfreq spcgrid%frequency = spcaux%f if ( spcaux%is2d ) then spcgrid%ndir = spcaux%ndir allocate (spcgrid%direction(spcaux%ndir)) spcgrid%direction = spcaux%theta else spcgrid%ndir = 0 end if spcgrid%relative = spcaux%relative ! ! If spectra requested on COMPGRID ! if ( spc_as_map .and. OPTG /= 5 ) then ! COMPGRID is a special case. By specifying this spectra are 4D ! written to the netcdf instead of only the wet points ! ! if not rotated ! ! XGRDGL, YGRDGL, MXCGL and MYCGL from M_PARALL if ( abs(ALPC) < epsilon(1.) .and. OPTG == 1 ) then ! ! 4.a) define mapgrid with 1D coordinate axes ! allocate (mapgrid%longitude(MXCGL,1)) allocate (mapgrid%latitude (1,MYCGL)) if ( PARLL ) then mapgrid%longitude(:,1) = xgrdgl(:,1) + XOFFS mapgrid%latitude(1,:) = ygrdgl(1,:) + YOFFS else mapgrid%longitude(:,1) = xgrdgl(:,1) mapgrid%latitude(1,:) = ygrdgl(1,:) end if mapgrid%mdc = .false. mapgrid%alpc = 0. else ! ! 4.b) define mapgrid with 2D coordinate axes ! allocate (mapgrid%longitude(MXCGL, MYCGL)) allocate (mapgrid%latitude (MXCGL, MYCGL)) if ( PARLL ) then mapgrid%longitude = XGRDGL + XOFFS mapgrid%latitude = YGRDGL + YOFFS else mapgrid%longitude = XGRDGL mapgrid%latitude = YGRDGL end if mapgrid%mdc = .true. mapgrid%alpc = ALPC end if ! curvilinear grids may contain the EXCEPTION value of the bottom ! Replace with the hardcoded float _FillValue where ( abs(mapgrid%longitude - EXCFLD(1)) < epsilon(1.) ) & mapgrid%longitude = NF90_FILL_FLOAT where ( abs(mapgrid%latitude - EXCFLD(1)) < epsilon(1.) ) & mapgrid%latitude = NF90_FILL_FLOAT if ( KSPHER == 0 ) mapgrid%lunit = 'meter' mapgrid%nx = MXCGL mapgrid%ny = MYCGL ! ! 4.c) create ncfile ! ! Definition mode call create_ncfile(ncfile, ncid, recordaxe(irq), spcgrid=spcgrid, mapgrid=mapgrid, Escale=Escale, nautical=BNAUT) else ! ! 5.a) define pntgrid, eg, spectra along a point list ! if ( wetnode_list ) then ! remove dry points call swn_outnc_spcaux_on_wetnodes(KGRPGL, spcaux, lspcaux ) if (STPNOW()) return pspcaux => lspcaux else pspcaux => spcaux end if allocate (pntgrid%longitude(pspcaux%mip)) allocate (pntgrid%latitude(pspcaux%mip)) pntgrid%npoints = pspcaux%mip pntgrid%longitude = pspcaux%xp pntgrid%latitude = pspcaux%yp if ( wetnode_list ) then allocate(pntgrid%ips(pspcaux%mip)) pntgrid%ips = pspcaux%ips pntgrid%xdimlen = MXCGL pntgrid%ydimlen = MYCGL end if nullify(pspcaux) if ( wetnode_list ) then call swn_outnc_deallocate_spcaux( lspcaux ) end if if ( KSPHER == 0 ) pntgrid%lunit = 'meter' ! ! 5.b) create point list ncfile ! ! Definition mode call create_ncfile(ncfile, ncid, recordaxe(irq), spcgrid=spcgrid, pntgrid=pntgrid, Escale=Escale, nautical=BNAUT) end if ! ! 6.a) add global attributes ! call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'project', PROJID) ) call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'model', VERTXT) ) if ( NSTATM > 0 ) then call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'run', PROJNR) ) end if ! ! 6.b) create variables ! if ( .not.noaux ) call create_auxvariables(ncid, spc_as_map) ! 6.c) close define mode and reopen write ! ! write mode, e.g, fill dimension variables. Note that record dimension ! variable is updated by open_ncfile call close_ncfile(ncid) call open_ncfile( ncfile, "write", ncid, recordaxe(irq), monthly) ! ! 6.d) fill dimension variables and close ! call agnc_set_spcgrid (ncid, spcgrid) if ( spc_as_map .and. OPTG /= 5 ) then call agnc_set_mapgrid (ncid, mapgrid) else call agnc_set_pntgrid (ncid, pntgrid) end if ! close the freshly created file so all dimension variable data is written call close_ncfile(ncid) end if ! ! 7) open file in write mode ! call open_ncfile( ncfile, "write", ncid, recordaxe(irq)) ! if only only record found, set delta to delta specified in command file (or 1 on stationary) if ( recordaxe(irq)%ncontent == 1 ) then if ( NSTATM > 0 ) then if ( NSTATC == 0 ) then recordaxe(irq)%delta = oqr(2) else recordaxe(irq)%delta = maxval((/oqr(2), DT/)) endif else recordaxe(irq)%delta = 1 end if end if ncoffset(irq) = ncid - oqi(1) end subroutine swn_outnc_openspecfile subroutine swn_outnc_spcflags(oqi, spc_as_map, wetnode_list, spcaux, Escale, noaux, monthly) integer, intent( in) :: oqi type(spcaux_type), intent( in) :: spcaux logical, intent( out) :: spc_as_map, wetnode_list logical, optional, intent( out) :: Escale, monthly, noaux integer :: iocode iocode = oqi wetnode_list = .false. spc_as_map = .false. noaux = .false. if ( iocode >= 16 ) then noaux = .true. iocode = iocode - 16 end if ! MDGRID is the special keyword to store spectra (including dry points) on the ! full geographic grid instead of along a pointlist ! On the other hand, when COMPGRID is requested on any grid but unstructured, ! store the point indices in the computational grid in the netcdf file if ( spcaux%mip == MCGRDGL-1 .or. spcaux%mip == (MXCGL * MYCGL) ) then if (iocode >= 8 ) then spc_as_map = .true. iocode = iocode - 8 elseif ( iocode >= 4 .and. OPTG /= 5 ) then ! request to remove dry points from COMPGRID pointlist, e.g., compress wetnode_list = .true. end if end if ! catch conflicting MDGRID + COMPRESS iocode = mod(iocode, 4) ! Enery scaling is of creating a new variable that stores a scale per timestep and point ! the energy itself is then stored as int16 instead of float if ( present( EScale) ) then Escale = .false. if ( iocode > 1) Escale = .true. end if ! By setting the keyword MONTH a netcdf file is created that has a time axis ! starting at the first of the month. if ( present( monthly ) ) then monthly = .false. if ( mod(iocode,2) == 1 .and. NSTATM == 1) monthly = .true. end if end subroutine swn_outnc_spcflags subroutine swn_outnc_appendblock(myk, mxk, ivtype, nref, irq, data, excv, col) ! note that ivtyp is an array and ivtype is ivtyp(JVAR) ! col is used for writing vector data. integer, intent( in) :: irq, ivtype, nref, col, myk, mxk real, intent( in) :: data(mxk * myk), excv integer :: ri, pnr, xpctmp(2), i, & xpctime, ilpos, ncid integer, save :: IENT=0 character(len=40) :: name if (LTRACE) call STRACE (IENT,'swn_outnc_appendblock') if ( ivtype == 40 ) return ncid = nrefcp(irq) + ncoffset(irq) if ( recordaxe(irq)%nstatm ) then READ (chtime, '(I8,1X,I6)') (xpctmp(i), i=1,2) xpctime = seconds_since_epoch(xpctmp(1), xpctmp(2)) call record_index(ncid, recordaxe(irq), xpctime, ri, .false., '') else if ( string_is_int(PROJNR) ) then read( PROJNR, '(i16)' ) pnr if ( pnr == 0 ) then call MSGERR(3, 'The projnr number may not be zero') return end if else pnr = 1 end if call record_index(ncid, recordaxe(irq), pnr, ri, .false., '') end if if (myk == 1) then call agnc_add_pntdata(ncid, STNAMES(ivtype,col), ri, data, excv, skip_range_error) else name = STNAMES(ivtype,col) if (name == ' ' .and. ivtype == 1) name = 'x' if (name == ' ' .and. ivtype == 2) name = 'y' call agnc_add_mapdata(ncid, name, ri, data, excv, skip_range_error) end if end subroutine swn_outnc_appendblock subroutine swn_outnc_close_on_end(nref, irq) integer, intent(inout) :: nref integer, optional, intent( in) :: irq integer :: ncid integer, save :: IENT=0 ncid = nref if ( present(irq) ) ncid = nrefcp(irq) + ncoffset(irq) ! Decided to sync data to file even in non stationary mode because ! otherwise data is buffered during the entire computation, ! increasing the chance on file corruption if (LTRACE) call STRACE (IENT,'swn_outnc_close_on_end') if ( nstatm == 0 .or. TIMCO >= TFINC ) then ncoffset(irq) = 0 call close_ncfile(ncid) else call nccheck( NF90_SYNC(ncid) ) end if end subroutine swn_outnc_close_on_end subroutine record_index(ncid, recordaxe, xpctime, ri, reading, filename) integer, intent( in) :: ncid, xpctime type(recordaxe_type), intent(inout) :: recordaxe integer, intent( out) :: ri logical, intent( in) :: reading !< ncfile used for reading character(len=*), intent( in) :: filename ! for use in error message integer, allocatable, dimension(:) :: tmp_time integer, save :: IENT=0 if (LTRACE) call STRACE (IENT,'record_index') ri = timeindex(recordaxe%content, xpctime) if ( ri < 0 ) then if (reading) then call MSGERR(2, "first time not available in input netcdf " // filename) ri = 1 else call MSGERR(4, 'agioncmd cannot prepend data, only overwrite '// & 'and append. Consider setting option in order '// & 'to generate monthly files. You''ll need to rerun '// & 'the first period too though') call close_ncfile(ncid) endif else if ( ri == 0 ) then if (reading) then call msgerr(2, "last time not available in input netcdf " // filename) else ! record needs to be appended allocate(tmp_time(recordaxe%ncontent)) tmp_time = recordaxe%content deallocate(recordaxe%content) allocate(recordaxe%content(recordaxe%ncontent+1)) recordaxe%content(1:recordaxe%ncontent) = tmp_time recordaxe%content(recordaxe%ncontent+1) = xpctime recordaxe%ncontent = recordaxe%ncontent+1 ! ! Update netcdf file with new time axe ! call agnc_set_recordaxe (ncid, recordaxe) endif ri = recordaxe%ncontent end if end subroutine record_index subroutine swn_outnc_openblockfile(ncfile, myk, mxk, ovlnam, & oqi, oqr, ivtyp,irq, xgrdgl, ygrdgl) character*80, intent( in) :: ncfile integer, intent( in) :: myk, mxk, irq integer, dimension(:), intent( in) :: oqi, ivtyp real*8, dimension(:), intent( in) :: oqr character*40, intent( in) :: ovlnam(:) real, optional, intent( in) :: xgrdgl(mxk, myk), & ygrdgl(mxk, myk) ! local variables logical :: file_exists = .false., & nc_debug = .false., & monthly = .false. integer :: ncid, xpctmp(2), i type(mapgrid_type) :: mapgrid type(pntgrid_type) :: pntgrid integer, save :: IENT=0 if (LTRACE) call STRACE (IENT,'swn_outnc_openblockfile') if ( .not. stnames_initialized ) call stnames_init() ! By setting the idla to 5 a netcdf file is created that has a time axis ! that starts at the first of the month. Monthly is only valid on non-stationary ! run that has time axis. Declaring a monthly file is required when computing ! hindcasts in several chunks in time. if ( oqi(4) == 5 .and. nstatm == 1 ) monthly = .true. inquire( FILE=ncfile, EXIST=file_exists ) if ( .not. file_exists) then allocate(recordaxe(irq)%content(1)) recordaxe(irq)%ncontent = 1 if ( nstatm > 0 ) then READ (chtime, '(I8,1X,I6)') (xpctmp(i), i=1,2) ! should be seconds since epoch. This is probably not it. recordaxe(irq)%content(1) = seconds_since_epoch(xpctmp(1), xpctmp(2)) if ( NSTATC == 0 ) then recordaxe(irq)%delta = oqr(2) else recordaxe(irq)%delta = maxval((/oqr(2), DT/)) endif recordaxe(irq)%nstatm = .true. else ! stationary, record axe is "run" if ( string_is_int(PROJNR) ) then read( PROJNR, '(i16)' ) recordaxe(irq)%content(1) else recordaxe(irq)%content(1) = 1 end if recordaxe(irq)%delta = 1 recordaxe(irq)%nstatm = .false. end if if (myk == 1) then ! unstructured mesh is a pointlist. TABLE output also uses this section pntgrid%npoints = mxk allocate (pntgrid%longitude(mxk)) allocate (pntgrid%latitude(mxk)) if (present(xgrdgl) .and. present(ygrdgl)) then pntgrid%latitude = ygrdgl(:,1) pntgrid%longitude = xgrdgl(:,1) else pntgrid%latitude = -999.0 pntgrid%longitude = -999.0 endif if ( KSPHER == 0 ) pntgrid%lunit = 'meter' ! Definition mode call create_ncfile(ncfile, ncid, recordaxe(irq), pntgrid=pntgrid, nautical=BNAUT) call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'project', PROJID) ) call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'run', PROJNR) ) call create_variables(ncid, ivtyp, ovlnam, .false.) ! write mode, e.g, fill dimension variables. Note that record dimension ! variable is updated by open_ncfile call close_ncfile(ncid) call open_ncfile( ncfile, "write", ncid, recordaxe(irq), monthly) call agnc_set_pntgrid (ncid, pntgrid) else ! Regular mesh. Depending on the angle of the computational grid, ! longitude and latitude can be a vector or a matrix ! curvilinear grids always have multi-dimension coordinate fields ! Definition mode if ( abs(ALPC) < epsilon(1.) .and. OPTG == 1 ) then allocate (mapgrid%longitude(mxk,1)) allocate (mapgrid%latitude (1,myk)) if (present(xgrdgl) .and. present(ygrdgl)) then if ( PARLL ) then mapgrid%longitude(:,1) = xgrdgl(:,1) + XOFFS mapgrid%latitude(1,:) = ygrdgl(1,:) + YOFFS else mapgrid%longitude(:,1) = xgrdgl(:,1) mapgrid%latitude(1,:) = ygrdgl(1,:) end if else mapgrid%longitude(:,1) = -999.0 mapgrid%latitude(1,:) = -999.0 end if mapgrid%mdc = .false. else allocate (mapgrid%longitude(mxk, myk)) allocate (mapgrid%latitude (mxk, myk)) if (present(xgrdgl) .and. present(ygrdgl)) then if ( PARLL ) then mapgrid%longitude = xgrdgl + XOFFS mapgrid%latitude = ygrdgl + YOFFS else mapgrid%longitude = xgrdgl mapgrid%latitude = ygrdgl end if else mapgrid%longitude = -999.0 mapgrid%latitude = -999.0 end if mapgrid%mdc = .true. end if ! curvilinear grids may contain the EXCEPTION value of the bottom ! Replace with the hardcoded float _FillValue if (present(xgrdgl) .and. present(ygrdgl)) then where ( abs(mapgrid%longitude - EXCFLD(1)) < epsilon(1.) ) & mapgrid%longitude = NF90_FILL_FLOAT where ( abs(mapgrid%latitude - EXCFLD(1)) < epsilon(1.) ) & mapgrid%latitude = NF90_FILL_FLOAT endif mapgrid%nx = mxk mapgrid%ny = myk mapgrid%alpc = ALPC if ( KSPHER == 0 ) mapgrid%lunit = 'meter' call create_ncfile(ncfile, ncid, recordaxe(irq), mapgrid=mapgrid, nautical=BNAUT) call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'project', PROJID) ) call nccheck ( nf90_put_att( ncid, NF90_GLOBAL, 'run', PROJNR) ) call create_variables(ncid, ivtyp, ovlnam, .true.) ! write mode, e.g, fill dimension variables. Note that record dimension ! variable is updated by open_ncfile call close_ncfile(ncid) call open_ncfile( ncfile, "write", ncid, recordaxe(irq), monthly) call agnc_set_mapgrid (ncid, mapgrid) end if ! close the freshly created file so all buffers are emptied call close_ncfile(ncid) end if call open_ncfile( ncfile, "write", ncid, recordaxe(irq)) ! set the offset to translate between swan's HIOPEN + IRQ and ! the netcdf 65xxx range ncoffset(irq) = ncid - oqi(1) nrefcp(irq) = oqi(1) ! if only only record found, set delta to delta specified in command file (or 1 on stationary) if ( recordaxe(irq)%ncontent == 1 ) then if ( NSTATM > 0 ) then if ( NSTATC == 0 ) then recordaxe(irq)%delta = oqr(2) else recordaxe(irq)%delta = maxval((/oqr(2), DT/)) endif else recordaxe(irq)%delta = 1 end if end if end subroutine swn_outnc_openblockfile subroutine create_auxvariables(ncid, spc_as_map) integer, intent( in) :: ncid logical, intent( in) :: spc_as_map integer :: ivtyp(4) character*40 :: dummy(4) ivtyp = (/4, 5, 10, 26/) call create_variables(ncid, ivtyp, dummy, spc_as_map) end subroutine create_auxvariables subroutine create_variables(ncid, ivtyp, ovlnam, spc_as_map) integer, intent( in) :: ncid integer,dimension(:), intent( in) :: ivtyp character*40, intent( in) :: ovlnam(:) logical, intent( in) :: spc_as_map integer :: i character*40 :: stname integer, save :: IENT=0 if (LTRACE) call STRACE (IENT,'create_variables') do i=1, size(ivtyp) stname = STNAMES(ivtyp(i),1) if ( stname /= '' ) then if ( spc_as_map ) then call agnc_define_mapvariable( ncid, stname) else call agnc_define_pntvariable( ncid, stname) end if ! if vector, write second variable stname = STNAMES(ivtyp(i),2) if ( stname /= '' ) then if ( spc_as_map ) then call agnc_define_mapvariable( ncid, stname) else call agnc_define_pntvariable( ncid, stname) end if end if else ! See list at the end of the source file if ( ivtyp(i) > 3) call MSGERR(1, "Field '"//trim(ovlnam(ivtyp(i)))//"' not supported (yet).") end if end do end subroutine create_variables subroutine stnames_init() STNAMES( 4, 1) = 'depth' STNAMES( 5, 1) = 'xcur' STNAMES( 5, 2) = 'ycur' STNAMES( 6, 1) = 'ubot' STNAMES( 10, 1) = 'hs' STNAMES( 11, 1) = 'tm01' STNAMES( 12, 1) = 'tp' STNAMES( 13, 1) = 'theta0' STNAMES( 14, 1) = 'thetap' STNAMES( 16, 1) = 'spread' STNAMES( 17, 1) = 'L' STNAMES( 26, 1) = 'xwnd' STNAMES( 26, 2) = 'ywnd' STNAMES( 28, 1) = 'rtm01' STNAMES( 30, 1) = 'dhs' STNAMES( 31, 1) = 'dtm' STNAMES( 32, 1) = 'tm02' STNAMES( 34, 1) = 'urms' STNAMES( 35, 1) = 'ustar' STNAMES( 38, 1) = 'cdrag' STNAMES( 44, 1) = 'hswe' STNAMES( 47, 1) = 'tmm10' STNAMES( 48, 1) = 'rtmm10' STNAMES( 51, 1) = 'ssh' STNAMES( 52, 1) = 'botl' STNAMES( 53, 1) = 'tps' STNAMES(100, 1) = 'phs0' STNAMES(101, 1) = 'phs1' STNAMES(102, 1) = 'phs2' STNAMES(103, 1) = 'phs3' STNAMES(104, 1) = 'phs4' STNAMES(105, 1) = 'phs5' STNAMES(110, 1) = 'ptp0' STNAMES(111, 1) = 'ptp1' STNAMES(112, 1) = 'ptp2' STNAMES(113, 1) = 'ptp3' STNAMES(114, 1) = 'ptp4' STNAMES(115, 1) = 'ptp5' STNAMES(130, 1) = 'pdir0' STNAMES(131, 1) = 'pdir1' STNAMES(132, 1) = 'pdir2' STNAMES(133, 1) = 'pdir3' STNAMES(134, 1) = 'pdir4' STNAMES(135, 1) = 'pdir5' stnames_initialized = .true. end subroutine stnames_init function string_is_int(str) result (is_int) character(len=*), intent( in) :: str logical :: is_int character(len=10) :: nums = "0123456789" is_int = .true. if ( verify(str, nums) > 0 ) is_int = .false. end function string_is_int end module swn_outnc ! ( 3) [ 'distance along output curve'] ! ( 7) [ 'Energy dissipation'] ! ( 8) [ 'Fraction breaking waves'] ! ( 9) [ 'Energy leak over spectral boundaries'] ! (15) [ 'direction of the energy transport'] ! (18) [ 'Wave steepness'] ! (19) [ 'Wave energy transport'] ! (20) [ 'Wave driven force per unit surface'] ! (21) [ 'spectral action density'] ! (22) [ 'spectral energy density'] ! (23) [ 'auxiliary variable'] ! (24) [ 'X computational grid coordinate'] ! (25) [ 'Y computational grid coordinate'] ! (27) [ 'Bottom friction coefficient'] ! (29) [ 'energy density integrated over direction'] ! (33) [ 'Frequency spectral width (Kappa)'] ! (35) [ 'Friction velocity'] ! (40) [ 'Date-time'] ! (41) [ 'Time in seconds from reference time'] ! (36) ['Zero velocity thickness of boundary layer'] ! (37) [ ' '] ! (39) [ 'Setup due to waves'] ! (49) [ 'Diffraction parameter'] ! (50) [ 'Bottom wave period'] ! (54) [ 'Bottom friction dissipation'] ! (55) [ 'Surf breaking dissipation'] ! (56) [ 'Whitecapping dissipation'] ! (57) [ 'Vegetation dissipation'] ! (58) [ 'Peakedness'] ! (59) [ 'Benjamin-Feir index'] ! (60) [ 'Energy generation'] ! (61) [ 'Wind source term'] ! (62) [ 'Energy redistribution'] ! (63) [ 'Total absolute 4-wave interaction'] ! (64) [ 'Total absolute 3-wave interaction'] ! (65) [ 'Energy propagation'] ! (66) [ 'xy-propagation'] ! (67) [ 'theta-propagation'] ! (68) [ 'sigma-propagation'] ! (69) [ 'Radiation stress'] ! (70) [ 'Plants per m2'] ! (71) [ 'Peak wave length']