!----- AGPL -------------------------------------------------------------------- ! ! Copyright (C) Stichting Deltares, 2017-2020. ! ! This file is part of Delft3D (D-Flow Flexible Mesh component). ! ! Delft3D is free software: you can redistribute it and/or modify ! it under the terms of the GNU Affero General Public License as ! published by the Free Software Foundation version 3. ! ! Delft3D 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 Affero General Public License for more details. ! ! You should have received a copy of the GNU Affero General Public License ! along with Delft3D. If not, see . ! ! contact: delft3d.support@deltares.nl ! Stichting Deltares ! P.O. Box 177 ! 2600 MH Delft, The Netherlands ! ! All indications and logos of, and references to, "Delft3D", ! "D-Flow Flexible Mesh" and "Deltares" are registered trademarks of Stichting ! Deltares, and remain the property of Stichting Deltares. All rights reserved. ! !------------------------------------------------------------------------------- ! $Id$ ! $HeadURL$ module dfm_merge use netcdf use netcdf_utils use dfm_params implicit none ! TODO: re-use the definitions below from original source: unstruc_netcdf ! The following location codes generalize for 1D/2D/3D models. integer, parameter :: UNC_LOC_CN = 1 !< Data location: corner point. integer, parameter :: UNC_LOC_S = 2 !< Data location: pressure point. integer, parameter :: UNC_LOC_SN = 102 !< TEMP: Data location: netelem (not always identical to flowelem). (UNST-1256) integer, parameter :: UNC_LOC_U = 3 !< Data location: horizontal velocity point. integer, parameter :: UNC_LOC_L = 13 !< Data location: net link. TODO: AvD: phase out integer, parameter :: UNC_LOC_W = 6 !< Data location: vertical velocity point. integer, parameter :: UNC_LOC_SBND=7 !< Data location: boundary waterlevel points contains !> Merges multiple D-Flow FM map files into a single file. !! Typically this routine should be used on output of a parallel run, !! i.e., on files _000x_map.nc function dfm_merge_mapfiles(infiles, nfiles, outfile, force) result(ierr) use m_alloc use string_module use io_netcdf use io_ugrid, only: mdim_face, mdim_node, mdim_edge, mdim_maxfacenodes implicit none character(len=MAXNAMELEN), intent(inout) :: infiles(:) !< Input files names, will be sorted if not sorted already. integer, intent(in) :: nfiles !< Number of input files. character(len=MAXNAMELEN), intent(inout) :: outfile !< Output file name. When empty, the name is derived from the input file names. logical, intent(in) :: force !< Whether to disallow interactive user prompting (for file overwrite) integer :: ierr !< Result status (0 = success) integer, parameter :: int8 = 1 ! also local storage compact in 1 byte integer, parameter :: mapclass_time_buffer_size = 1 integer, dimension(nfiles+1) :: ncids, id_timedim, id_facedim, id_edgedim, id_laydim, id_wdim, id_nodedim, id_sedtotdim, id_sedsusdim, & id_netedgedim, id_netfacedim, id_netfacemaxnodesdim, id_time, id_timestep, id_bnddim !< dim and var ids, maintained for all input files + 1 output file. double precision :: convversion integer :: jaugrid, iconvtype, formatCode, new_ndx integer, dimension(nfiles) :: jaugridi, ioncids logical :: isNetCDF4 integer, allocatable :: dimids(:,:) !< (nfiles+1:NF90_MAX_DIMS) Used for storing any remaining vectormax dimension IDs integer, dimension(nfiles+1), target :: ndx, lnx, ndxg, lnxg, kmx, numk, numl, nump, numkg, numlg, netfacemaxnodes, nt, ndxbnd !< counters, maintained for all input files + 1 output file. integer, dimension(:), pointer :: item_counts !< Generalized count pointer, will point to ndx, lnx, numl, or numk during var data reading + writing. integer:: noutfile !< array index/position of output file ids, by default the last, i.e., nfiles + 1. integer, allocatable, target :: face_domain(:), facebnd_domain(:), edge_domain(:), node_domain(:), netedge_domain(:) !< Global face/edge/node numbers and their domain number. integer, pointer :: item_domain(:) integer, allocatable :: ln(:,:) !< Flow links integer, allocatable :: edgenodes(:,:) !< Net links integer, allocatable :: netedgefaces(:,:) !< ElemLinks integer, allocatable :: netfaceedges(:,:) !< NetElemLinks integer, allocatable :: netfacenodes(:,:) !< Net cell - to - net node mapping integer, allocatable :: netfacenodesl(:,:) !< Net cell - to - net node mapping for local usage (per cell) integer, allocatable :: face_c2g(:), node_c2g(:), netedge_c2g(:), edge_c2g(:) !< Concatenated index - to - global index mapping. integer, allocatable :: node_g2c(:), edge_g2c(:), face_g2c(:), netedge_g2c(:) !< Global index - to - concatenated index mapping. integer, allocatable :: node_faces(:,:) ! faces that surrounds the node integer, allocatable :: nfaceedges(:) ! total number of edges that surround a face double precision, allocatable :: node_x(:), node_y(:), edge_x(:), edge_y(:) !< coordinates double precision :: xx, yy integer :: im,nm,topodim, ikk, ic, iii, k1c, g1, g2, nfaces, iedge, iface, tmpvarDim, jafound integer :: ifacefile, ifacein, ifaceout, ifacec integer :: id_nodex, id_nodey, id_edgex, id_edgey integer :: intmiss = -2147483647 ! integer fillvalue double precision :: dmiss = -999d0, intfillv !netface_g2c(:) integer :: id_facedomain, id_faceglobnr, id_edgefaces, id_netfacenodes, id_edgenodes, id_netedgefaces, id_netfaceedges integer :: ierri integer :: maxlen, nlen, plen, mlen, ii, id, iv, it, itm, ip, ik, is, ie, nvarsel, ntsel, nvars, ndims, nvardims, vartype integer :: netfacemaxnodesg, netnodemaxface=10, ndxc, lnxc, numkc, numlc,ndx_bndc integer :: nfaceglob, nfaceglob0, nfacecount, ifaceglob integer :: nedgeglob, nedgeglob0, nedgecount, iedgeglob integer :: nnodeglob, nnodeglob0, nnodecount integer :: nbndglob, nbndcount integer :: netedgecount, inetedgeglob ! integer :: nnetfaceglob, nnetfaceglob0, nnetfacecount, numpg integer :: nnetedgeglob, nnetedgeglob0, nnetedgecount integer :: nitemglob, nitemglob0, nitemcount, maxitems integer :: nkmxglob integer :: idom, n1, n2, n3, k1, k2 integer :: tmpdimids(NF90_MAX_VAR_DIMS) ! TODO: Consider to change the type of the following i-variables from double precision to integer. double precision, allocatable, target :: itmpvar1D(:) !< array buffer for a single global variable slice, size: (kmx1, max(ndx(noutfile),lnx(noutfile))) integer, allocatable, target :: itmpvar1D_tmp(:) double precision, allocatable, target :: itmpvar2D(:,:) !< array buffer for a single global variable slice, size: (kmx1, max(ndx(noutfile),lnx(noutfile))) double precision, allocatable, target :: itmpvar2D_tmp(:,:) double precision, allocatable, target :: itmpvar2D_tmpmax(:,:) double precision, pointer :: itmpvarptr(:,:,:) ! for class map: store in 1 byte: integer(kind=int8),allocatable,target :: btmpvar1D(:,:) !< array buffer for a single global variable slice, size: (kmx1, max(ndx(noutfile),lnx(noutfile))) integer(kind=int8),allocatable,target :: btmpvar1D_tmp(:,:) integer(kind=int8), pointer :: btmpvarptr(:,:,:,:) ! for others: double precision: double precision, allocatable, target :: tmpvar1D(:) !< array buffer for a single global variable slice, size: (kmx1, max(ndx(noutfile),lnx(noutfile))) double precision, allocatable, target :: tmpvar1D_tmp(:) double precision, allocatable, target :: tmpvar2D(:,:) !< array buffer for a single global variable slice, size: (kmx1, max(ndx(noutfile),lnx(noutfile))) double precision, allocatable, target :: tmpvar2D_tmp(:,:) double precision, allocatable, target :: tmpvar2D_tmpmax(:,:) double precision, allocatable, target :: tmpvar3D(:,:,:) !< array buffer for a single global variable slice, size: (kmx1, max(ndx(noutfile),lnx(noutfile))) double precision, pointer :: tmpvarptr(:,:,:) character(len=4) :: fmtstr character(len=4096) :: tmpstr1, tmpstr2 integer, allocatable :: varids(:,:) !< Variable IDs for the selected variables in the input files. Support having different variables in differnt input files. integer, allocatable :: varids_out(:) !< Variable IDs for the selected variables in the output file. Will typically not be equal to the var IDs in the input files, as we may not be copying *all* vars from input files. character(len=NF90_MAX_NAME), allocatable :: var_names(:) !< Names of the selected variables. integer, allocatable :: var_types(:) !< Data types of the selected variables. integer, allocatable :: var_dimids(:,:) !< Dimension ids for selected variables, should be filled later, starting at the end. integer, allocatable :: var_timdimpos(:) !< Position in var_dimids(1:4,iv) of time dimension (-1 if not timedep) integer, allocatable :: var_spacedimpos(:) !< Position in var_dimids(1:4,iv) of space dimension (-1 if not timedep) integer, allocatable :: var_laydimpos(:) !< Position in var_dimids(1:4,iv) of layer dimension (-1 if not timedep) integer, allocatable :: var_kxdimpos(:) !< Position in var_dimids(1:4,iv) of vectormax dimension (-1 if not timedep) integer, allocatable :: var_seddimpos(:) !< Position in var_dimids(1:4,iv) of sediment dimension (-1 if not timedep) integer, allocatable :: var_ndims(:) !< Actual number of dimensions. integer, allocatable :: var_loctype(:) !< Spatial location type for each var (face/node/etc.) integer, allocatable :: var_wdimpos(:) !< Position in var_dimids(1:4,iv) of layer interface dimension (-1 if not timedep) integer, allocatable :: file_ndims(:) !< Nr. dimensions in every input file integer, allocatable :: dimids_uses(:) !< Nr. vectormax-like dimensions that are used integer :: ivarcandidate, ifirstdim, ilastdim integer, parameter :: MAX_VAR_DIMS = 4 !< Max nr of dimensions for a single var. Support: (vectormax, layers, space, time). integer, dimension(MAX_VAR_DIMS) :: start_idx !< Start index array for calling nf90_get_var(..., start=...) integer, dimension(MAX_VAR_DIMS) :: count_read !< Data size array for calling nf90_get_var(..., count=...) integer, dimension(MAX_VAR_DIMS) :: count_write !< Data size array for calling nf90_put_var(..., count=...) integer :: nofill, ifill_value !< For inquiring fill values integer :: id_npartdim !< Dimension ID for the partitions counter integer :: id_part_face_start, id_part_edge_start, id_part_node_start, id_part_facebnd_start !< Var IDs for storing start index for each partition in the merged global arrays. integer :: id_part_face_count, id_part_edge_count, id_part_node_count, id_part_facebnd_count !< Var IDs for storing count of unique items for each partition in the merged global arrays integer :: Lrst_m, ifile, max_nvars integer :: isBndLink = 0, id_infile integer :: jamerge_cntv = 1 ! merge topology connectivity variables integer :: jaread_sep = 0 ! read the variable seperately character(len=NF90_MAX_NAME) :: varname, dimname character(len=NF90_MAX_NAME) :: facedimname ! UGRID face / FM flow node character(len=NF90_MAX_NAME) :: nodedimname ! UGRID node / FM net node character(len=NF90_MAX_NAME) :: netedgedimname ! UGRID edge / FM net link character(len=NF90_MAX_NAME) :: netfacemaxnodesdimname ! UGRID max face-nodes / FM max net cell nods character(len=NF90_MAX_NAME) :: edgedimname ! (no UGRID) / FM flow link character(len=NF90_MAX_NAME) :: netfacedimname ! (no UGRID) / FM net cell character(len=NF90_MAX_NAME) :: timedimname ! time character(len=NF90_MAX_NAME) :: laydimname ! layer (mids) character(len=NF90_MAX_NAME) :: wdimname ! layer interfaces integer, allocatable :: itimsel(:) double precision, allocatable :: times(:) double precision, allocatable :: timestep(:) logical :: isfound, needshift, exist integer :: size_btmp character(len=1) :: answer character*8 :: cdate character*10 :: ctime character*5 :: czone if (nfiles <= 1) then write (*,'(a)') 'Error: mapmerge: At least two input files required.' ierr = 12 goto 888 else if (verbose_mode) then write (*,'(a,i0,a)') 'Info: mapmerge: Starting merge of ', nfiles, ' files...' end if end if noutfile = nfiles+1 ifile = 1 ! Initially use the first input file as the reference file for merging nvars = 0 max_nvars = 0 ncids = -1 id_timedim = -1 id_facedim = -1 id_edgedim = -1 id_time = -1 id_timestep= -1 id_laydim = -1 id_wdim = -1 id_bnddim = -1 id_sedtotdim = -1 id_sedsusdim = -1 ndx = 0 lnx = 0 kmx = 0 nt = 0 ndxbnd = 0 netfacemaxnodes = 0 !! 0a. Open input files call dfm_order_by_partition(infiles, nfiles) ierr = nf90_noerr isNetCDF4 = .false. do ii=1,nfiles ierri = ionc_open(infiles(ii), NF90_NOWRITE, ioncids(ii), iconvtype, convversion) if (ierri /= nf90_noerr) then write (*,'(a)') 'Error: mapmerge: could not open file `'//trim(infiles(ii))//'''.' ierr = ierri ncids(ii) = -1 else if (iconvtype == IONC_CONV_UGRID .and. convversion >= 1.0) then jaugridi(ii) = 1 else jaugridi(ii) = 0 endif end if ierri = ionc_get_ncid(ioncids(ii), ncids(ii)) ierri = nf90_inquire(ncids(ii), formatNum=formatCode) isNetCDF4 = (isNetCDF4 .or. formatCode == nf90_format_netcdf4 .or. formatCode == nf90_format_netcdf4_classic) end do ! check if all the map files are the same format jaugrid = jaugridi(1) do ii=2,nfiles if (jaugridi(ii) .ne. jaugrid) then write (*,'(a)') 'Error: mapmerge: map files are not the same format.' ierr = 1 ncids(ii) = -1 exit endif enddo if (ierr /= nf90_noerr .and. .not. verbose_mode) then goto 888 else if (jaugrid == 1) then write (*,'(a,i0,a)') 'Info: mapmerge: all map files are of UGRID format.' else write (*,'(a,i0,a)') 'Info: mapmerge: all map files are of old format.' end if end if do ii = 1, nfiles ierr = ionc_get_ncid(ioncids(ii), ncids(ii)) if (ierr /= nf90_noerr) then write (*,'(a)') 'Error: mapmerge: could not get ncID for file: `'//trim(infiles(ii))//'''.' if (.not. verbose_mode) goto 888 end if enddo !! 0b. Open output file if (len_trim(outfile) == 0) then nlen = len_trim(infiles(1)) n3 = index(infiles(1)(1:nlen), '.', .true.) - 1 ! pos of '.nc' if (n3 < 0) then n3 = nlen end if Lrst_m = index(infiles(1), '_rst.nc') if (Lrst_m > 0) then ! If they are _rst files n1 = index(infiles(1)(1:Lrst_m), '_0000_', .true.) outfile = infiles(1)(1:n1) //'merged_'// infiles(1)(n1+6:Lrst_m)//'rst.nc' jamerge_cntv = 0 write (*,'(a)') 'Info: mapmerge: for *_rst.nc files, topology connectivity variables (except for "FlowLink") are not merged.' else n2 = index(infiles(1)(1:n3), '_', .true.) - 1 ! pos of '_map' n1 = index(infiles(1)(1:n2), '_', .true.) - 1 ! pos of '_0000' if (n1 >= 0) then ! mdident_0000_typ.nc --> mdident_merged_typ.nc outfile = infiles(1)(1:n1) // '_merged_'//infiles(1)(n2+2:n3)//'.nc' else if (n2 >= 0) then ! mdident_typ.nc --> mdident_merged_typ.nc outfile = infiles(1)(1:n2) // '_merged_'//infiles(1)(n2+2:n3)//'.nc' else ! mdident.nc --> mdident_merged_map.nc outfile = infiles(1)(1:n3) // '_merged_map.nc' end if endif end if ! Check for possible overwrite of outfile inquire(file=trim(outfile), exist=exist) if (exist .and. .not. force) then write (*, '(a)', advance='no') 'mapmerge: overwrite `'//trim(outfile)//'''? (Y/N) ' answer = '' read (*,'(a)') answer if (.not. strcmpi(answer, 'Y')) then goto 888 end if end if if (isNetCDF4) then ierr = nf90_create(outfile, NF90_HDF5, ncids(noutfile)) else ierr = nf90_create(outfile, ior(NF90_CLOBBER, NF90_64BIT_OFFSET), ncids(noutfile)) endif if (ierr /= nf90_noerr) then write (*,'(a)') 'Error: mapmerge: could not open file for writing: `'//trim(outfile)//'''.' if (.not. verbose_mode) goto 888 end if maxlen = 16 if (nfiles > 0) then maxlen = min(maxlen, maxval(len_trim(infiles(1:nfiles)))) end if !! 1a. Scan for flownode (face), flow link (edge) and time dimensions in input files ! Find the input file 'ifile', which has the most variables. This file will be the reference file where later we scan variables. allocate(file_ndims(nfiles)) do ii = 1, nfiles ierr = nf90_inquire(ncids(ii), nVariables = nvars ) if ( ierr /= nf90_noerr ) then write (*,'(a)') 'Error: mapmerge: no variables found in file `'//trim(infiles(ii))//'''.' if (.not. verbose_mode) goto 888 else ierr = nf90_inquire(ncids(ii), nDimensions = file_ndims(ii) ) if ( ierr /= nf90_noerr ) then write (*,'(a)') 'Error: mapmerge: no dimension found in file `'//trim(infiles(ii))//'''.' if (.not. verbose_mode) goto 888 endif endif if (nvars > max_nvars) then max_nvars = nvars ifile = ii ! Set 'ifile' the one that has the most variables endif enddo nDims = file_ndims(ifile) ! nDims is equal to Nr. dimensions in ifile allocate(dimids(nDims, nfiles+1)) dimids = -999 ! missing do ii=1,nfiles if (ncids(ii) <= 0) then if (verbose_mode) then write (*,'(a)') 'Warning: mapmerge: Skipping scan of file `'//trim(infiles(ii))//'''.' end if cycle end if if (jaugrid == 1) then ! UGRID format ierr = ionc_get_mesh_count(ioncids(ii), nm) im = 1 ! only 2D mesh now, TODO: 1D mesh do im=1,nm ierr = ionc_get_topology_dimension(ioncids(ii), im, topodim) if (topodim == 2) then exit ! We found the correct mesh in #im, no further searching end if end do ! find the mesh topology variables ! face -netelem ierr = ionc_get_dimid(ioncids(ii), im, mdim_face, id) ierr = nf90_inquire_dimension(ncids(ii), id, name = dimname, len = nlen) id_facedim(ii) = id facedimname = dimname ndx(ii) = nlen nump(ii) = nlen ! net node ierr = ionc_get_dimid(ioncids(ii), im, mdim_node, id) ierr = nf90_inquire_dimension(ncids(ii), id, name = dimname, len = nlen) id_nodedim(ii) = id nodedimname = dimname numk(ii) = nlen ! edge ierr = ionc_get_dimid(ioncids(ii), im, mdim_edge, id) ierr = nf90_inquire_dimension(ncids(ii), id, name = dimname, len = nlen) id_netedgedim(ii) = id netedgedimname = dimname ! note: ugrid has no flow link, netlink only (==edge) numl(ii) = nlen ! max face node ierr = ionc_get_dimid(ioncids(ii), im, mdim_maxfacenodes, id) ierr = nf90_inquire_dimension(ncids(ii), id, name = dimname, len = nlen) if (id > 0) then dimids(id, ii) = id end if id_netfacemaxnodesdim(ii) = id netfacemaxnodesdimname = dimname netfacemaxnodes(ii) = nlen ! other dimensions do id = 1, nDims ierr = nf90_inquire_dimension(ncids(ii), id, name = dimname, len = nlen) if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: unable to read dimension information from file `'//trim(infiles(ii))//''' for #', id,'.' if (.not. verbose_mode) goto 888 end if if (id /= id_facedim(ii) .and. id /= id_nodedim(ii) .and. id /= id_netedgedim(ii) .and. id /= id_netfacemaxnodesdim(ii)) then if (trim(dimname) == 'time') then !! Time dimension id_timedim(ii) = id timedimname = dimname nt(ii) = nlen else if (strcmpi(dimname, 'nmesh2d_layer') .or. strcmpi(dimname, 'mesh2d_nLayers')) then id_laydim(ii) = id laydimname = dimname kmx(ii) = nlen else if (strcmpi(dimname, 'nmesh2d_interface') .or. strcmpi(dimname, 'mesh2d_nInterfaces')) then id_wdim(ii) = id wdimname = dimname else ! No special dimension, so probably just some vectormax-type dimension that ! we may need later for some variables, so store it. dimids(id, ii) = id ! Only stored to filter on non-missing values in def_dim loop later ! check if it is a dimension for sediment variables if (strcmpi(dimname, 'nSedTot')) then id_sedtotdim(ii) = id else if (strcmpi(dimname, 'nSedSus')) then id_sedsusdim(ii) = id end if endif endif enddo else ! old format do id=1,file_ndims(ii) ierr = nf90_inquire_dimension(ncids(ii), id, name = dimname, len = nlen) ! NetCDF-F90 allows us to assume that the dim IDs are 1:ndims if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: unable to read dimension information from file `'//trim(infiles(ii))//''' for #', id,'.' if (.not. verbose_mode) goto 888 end if if (trim(dimname) == 'nFlowElem') then !! Flow nodes (face) dimension id_facedim(ii) = id facedimname = dimname ndx(ii) = nlen else if (trim(dimname) == 'nFlowLink') then !! Flow links (edge) dimension id_edgedim(ii) = id edgedimname = dimname lnx(ii) = nlen else if (trim(dimname) == 'laydim') then ! TODO: AvD: also wdim? id_laydim(ii) = id laydimname = dimname kmx(ii) = nlen else if (trim(dimname) == 'wdim') then id_wdim(ii) = id wdimname = dimname !! Now some Net* related dimensions (in addition to Flow*). else if (trim(dimname) == 'nNetElem') then !! Net cells (again face) dimension id_netfacedim(ii) = id netfacedimname = dimname nump(ii) = nlen else if (trim(dimname) == 'nNetElemMaxNode') then ! TODO: AvD: now we detect nNetElemMaxNode, but should be not change to nFlowElemMaxNode, now that facedim is the overall counter and netfacedim is hardly used anymore? dimids(id, ii) = id ! Store this now, because later it is just a vectormax dim, so should be available in dim filter id_netfacemaxnodesdim(ii) = id netfacemaxnodesdimname = dimname netfacemaxnodes(ii) = nlen else if (trim(dimname) == 'nNetNode') then !! Net nodes (node) dimension id_nodedim(ii) = id nodedimname = dimname numk(ii) = nlen else if (trim(dimname) == 'nNetLink') then !! Net links (again edge) dimension id_netedgedim(ii) = id netedgedimname = dimname numl(ii) = nlen else if (trim(dimname) == 'time') then !! Time dimension id_timedim(ii) = id timedimname = dimname nt(ii) = nlen else if (trim(dimname) == 'nFlowElemBnd') then !! Flow nodes (face) boundary points dimension id_bnddim(ii) = id ndxbnd(ii) = nlen ! TODO: dimname needed? if (verbose_mode) then write (*,'(a)') 'Info: mapmerge: find dimension of boundary waterlevel points in file `'//trim(infiles(ii))//'''.' endif else ! No special dimension, so probably just some vectormax-type dimension that ! we may need later for some variables, so store it. dimids(id, ii) = id ! Only stored to filter on non-missing values in def_dim loop later ! check if it is a dimension for sediment variables if (strcmpi(dimname, 'nSedTot')) then id_sedtotdim(ii) = id else if (strcmpi(dimname, 'nSedSus')) then id_sedsusdim(ii) = id end if end if end do ! id end if end do ! ii !! 1b. Scan for variables in the file which has the most dimension (and variables). if (verbose_mode) then write (*,'(a)') 'Info: mapmerge: Scan for variables in file `'//trim(infiles(ifile))//'''.' endif ierr = nf90_inquire(ncids(ifile), nVariables = nvars ) if ( ierr /= nf90_noerr ) then write (*,'(a)') 'Error: mapmerge: no variables found in file `'//trim(infiles(ifile))//'''.' if (.not. verbose_mode) goto 888 endif if (verbose_mode) then write (*,'(a)') '## Selecting variables to include in merge:' end if allocate(varids(nfiles, nvars)); allocate(varids_out(nvars)) allocate(var_names(nvars)) allocate(var_types(nvars)) allocate(var_dimids(MAX_VAR_DIMS,nvars)) allocate(var_timdimpos(nvars)); var_timdimpos = -1 allocate(var_spacedimpos(nvars)); var_spacedimpos = -1 allocate(var_laydimpos(nvars)); var_laydimpos = -1 allocate(var_kxdimpos(nvars)); var_kxdimpos = -1 allocate(var_wdimpos(nvars)); var_wdimpos = -1 allocate(var_seddimpos(nvars)); var_seddimpos = -1 allocate(var_ndims(nvars)); var_ndims = 0 allocate(var_loctype(nvars)); var_loctype = 0 allocate(dimids_uses(nDims)); dimids_uses = 0 nvarsel = 0 do iv = 1,nvars ierr = nf90_inquire_variable(ncids(ifile), iv, name=varname, xtype=vartype, ndims=nvardims, dimids=tmpdimids) if (nvardims == 1) then if (tmpdimids(1) == id_timedim(ifile) .and. trim(varname) == 'time') then id_time(ifile) = iv ! TODO: AvD: do this for all ii files, not only for 'ifile' if (verbose_mode) then write (*,'(a)') 'Found time variable in file `'//trim(infiles(ifile))//''': `'//trim(varname)//'''.' end if cycle ! This was time, continue searching for remaining data variables. elseif (tmpdimids(1) == id_timedim(ifile) .and. trim(varname) == 'timestep') then id_timestep(ifile) = iv ! TODO: AvD: do this for all ii files, not only for 'ifile' cycle end if end if ! It was not time, see if this is a geometry or data variable on flow nodes/links/net nodes: ivarcandidate = nvarsel+1 isfound = .true. ilastdim = MAX_VAR_DIMS ifirstdim = ilastdim+1 ! dummy start: no dims detected yet. do id=nvardims,1,-1 if (tmpdimids(id) == id_timedim(ifile)) then ifirstdim = ifirstdim-1 var_timdimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_facedim(ifile)) then tmpdimids(id) = id_facedim(ifile) ! replace netfacedim by (flow)facedim ifirstdim = ifirstdim-1 var_loctype(ivarcandidate) = UNC_LOC_S var_spacedimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_netfacedim(ifile)) then tmpdimids(id) = id_netfacedim(ifile) ifirstdim = ifirstdim-1 var_loctype(ivarcandidate) = UNC_LOC_SN ! UNST-1256: original UNST_LOC_S caused NetElemNode variable in merged file to be on nFlowElem dimension, whereas input is on nNetElem dimension. var_spacedimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_edgedim(ifile)) then ifirstdim = ifirstdim-1 var_loctype(ivarcandidate) = UNC_LOC_U var_spacedimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_nodedim(ifile)) then ifirstdim = ifirstdim-1 var_loctype(ivarcandidate) = UNC_LOC_CN var_spacedimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_netedgedim(ifile)) then ifirstdim = ifirstdim-1 var_loctype(ivarcandidate) = UNC_LOC_L var_spacedimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_laydim(ifile)) then ! TODO: do we really not need to set the S3D/W3D here? ifirstdim = ifirstdim-1 ! var_loctype(ivarcandidate) = UNC_LOC_S3D var_laydimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_wdim(ifile)) then ifirstdim = ifirstdim-1 !var_loctype(ivarcandidate) = UNC_LOC_W var_wdimpos(ivarcandidate) = ifirstdim else if (tmpdimids(id) == id_bnddim(ifile)) then tmpdimids(id) = id_bnddim(ifile) ifirstdim = ifirstdim-1 var_loctype(ivarcandidate) = UNC_LOC_SBND var_spacedimpos(ivarcandidate) = ifirstdim else if (var_kxdimpos(ivarcandidate) == -1) then ifirstdim = ifirstdim-1 var_kxdimpos(ivarcandidate) = ifirstdim ! count how many times this dimension is used id_infile = tmpdimids(id) dimids_uses(id_infile) = dimids_uses(id_infile) + 1 if (tmpdimids(id) == id_sedtotdim(ifile) .or. tmpdimids(id) == id_sedsusdim(ifile)) then var_seddimpos(ivarcandidate) = ifirstdim end if else if (verbose_mode) then write (*,'(a)') 'Error: mapmerge: detected more than one vectormax dimension for `'//trim(varname)//''':' write (*,'(a,i0,a,i0,a)') ' current: ', id, ', other: ', var_kxdimpos(ivarcandidate), '. Skipping this variable.' end if isfound = .false. exit ! Stop scanning any remaining dimensions for this var. end if end if var_dimids(ifirstdim,ivarcandidate) = tmpdimids(id) ! for debugging only: temp store the dimids from file #1 end do ! id ! We can only merge a variable across multiple domains *if* it is defined on space locations (face/edge/node) ! *or* if it is a special time-related variable. All others impossible to merge. if (var_spacedimpos(ivarcandidate) <= 0 .and. var_timdimpos(ivarcandidate) <= 0 .and. nvardims .ne. 0) then if (nvardims == 1 .and. (var_laydimpos(ivarcandidate) > 0 .or. var_wdimpos(ivarcandidate) > 0) .and. verbose_mode) then write (*,'(a)')'Info: mapmerge: Variable `'//trim(varname)//''' will be copied from one of the input files to the merged file.' else if (verbose_mode) then write (*,'(a)') 'Error: mapmerge: detected that variable `'//trim(varname)//''': is not defined on space' & //'locations, and is not a special time-related variable. Skipping this variable.' end if ! Make decrement -1 to all dimensions of the skipped variable, i.e. they are used one time less ! dimids_uses(id_infile) needs to be decremented -1 here for the kx dim do id=nvardims,1,-1 id_infile = tmpdimids(id) dimids_uses(id_infile) = dimids_uses(id_infile) - 1 enddo var_timdimpos(ivarcandidate) = -1 var_spacedimpos(ivarcandidate) = -1 var_kxdimpos(ivarcandidate) = -1 var_laydimpos(ivarcandidate) = -1 var_wdimpos(ivarcandidate) = -1 isfound = .false. end if end if if (isfound) then nvarsel = nvarsel + 1 ! === ivarcandidate ! NOTE: Use variable ID from file #1 and assume that all input files contain the same variables, and as such the same consecutive variable IDs. varids(ifile, nvarsel) = iv var_names(nvarsel) = varname var_types(nvarsel) = vartype ! NOTE: all dimension positions were already stored, actual dimension ids not, ! because those are only needed for output file, will be done later. var_ndims(nvarsel) = ilastdim-ifirstdim+1 if (verbose_mode) then tmpstr1 = '' nlen=0 do id=ifirstdim,ilastdim ierr = nf90_inquire_dimension(ncids(ifile), var_dimids(id,nvarsel), name=tmpstr2) mlen = len_trim(tmpstr2) tmpstr1 = tmpstr1(1:nlen)//', '//tmpstr2(1:mlen) nlen = nlen + 2 + mlen end do write (*,'(a,i3,a,a)') ' * ', nvarsel, ': ', trim(varname)//'('//tmpstr1(3:nlen)//')' end if ! Set IDs of current variable to other input files, if those files does not have this variable, then set ID to -1 do ii = 1, nfiles if (ii .ne. ifile) then ierr = nf90_inq_varid(ncids(ii), varname, varids(ii, nvarsel)) if (ierr .ne. nf90_noerr) then if (verbose_mode) then write (*,'(a)') 'Info: mapmerge: file `'//trim(infiles(ii))//'''does not have variable `'//trim(varname)//'''.' endif ierr = 0 varids(ii,nvarsel) = -1 endif endif enddo end if end do ! iv if (verbose_mode) then if (nvarsel == 0) then write (*,'(a)') 'Error: mapmerge: no variables found in file `'//infiles(ifile)//'''.' if (.not. verbose_mode) goto 888 end if end if !! 2a. Write top level attributes to file as a copy from input file. ierr = ncu_copy_atts(ncids(ifile), ncids(noutfile), nf90_global, nf90_global) ! We're making history here tmpstr1 = '' ierr = nf90_get_att(ncids(ifile), nf90_global, 'history', tmpstr1) if (ierr /= nf90_noerr) then mlen = 0 else mlen = min(len(tmpstr1), len_trim(tmpstr1)+1) tmpstr1(mlen:mlen) = char(10) ! Prepare for our extra history line below by adding newline now already. end if call get_command (tmpstr2, nlen, ierr) if (ierr < 0) then ! command did not fit into string, abbreviate it. tmpstr2(len(tmpstr2)-2:len(tmpstr2)) = '...' end if call date_and_time(cdate, ctime, czone) ierr = nf90_put_att(ncids(noutfile), nf90_global, 'history', & tmpstr1(1:mlen)// & cdate(1:4)//'-'//cdate(5:6)//'-'//cdate(7:8)//'T'//ctime(1:2)//':'//ctime(3:4)//':'//ctime(5:6)//czone(1:5)//': '// & tmpstr2(1:nlen)) ! Don't set 'date_created', hopefully it was in the original input files, and then we copy it and leave it at that source value. ierr = nf90_put_att(ncids(noutfile), nf90_global, 'date_modified', cdate(1:4)//'-'//cdate(5:6)//'-'//cdate(7:8)//'T'//ctime(1:2)//':'//ctime(3:4)//':'//ctime(5:6)//czone(1:5)) !! 2b. Define time dim&var with attributes in outputfile as a copy from input file. ierr = nf90_def_dim(ncids(noutfile), trim(timedimname), nf90_unlimited, id_timedim(noutfile)) ierr = nf90_def_var(ncids(noutfile), 'time', nf90_double, (/ id_timedim(noutfile) /), id_time (noutfile)) ierr = ncu_copy_atts(ncids(ifile), ncids(noutfile), id_time(ifile), id_time(noutfile)) if (isNetCDF4) then ierr = ncu_copy_chunking_deflate(ncids(ifile), ncids(noutfile), id_time(ifile), id_time(noutfile)) endif ierr = nf90_def_var(ncids(noutfile), 'timestep', nf90_double, (/ id_timedim(noutfile) /), id_timestep(noutfile)) ierr = ncu_copy_atts(ncids(ifile), ncids(noutfile), id_timestep(ifile), id_timestep(noutfile)) if (isNetCDF4) then ierr = ncu_copy_chunking_deflate(ncids(ifile), ncids(noutfile), id_timestep(ifile), id_timestep(noutfile)) if (id_timestep(ifile) < 0) then ! avoid very large chuncksize on Linux for timestep ierr = nf90_def_var_chunking(ncids(noutfile), id_timestep(noutfile), nf90_chunked, [512]) if (ierr /= 0) write(*,*) 'nf90_def_var_chunking failed for var timestep' endif endif !! 3. Construct merged flow geometry (using proper cellmasks, global numbers, etc. !! 3a. Count dimensions (removing partition overlap) for merged flow nodes (faces) and flow links (edges). nfacecount = 0 !< running total of ndx(ii) while iterating the files nedgecount = 0 !< running total of lnx(ii) while iterating the files nnodecount = 0 !< running total of numk(ii) while iterating the files !nnetfacecount = 0 !< running total of nump(ii) while iterating the files nnetedgecount = 0 !< running total of numl(ii) while iterating the files nfaceglob = 0 !< total number of flow nodes (faces) without duplicates nedgeglob = 0 !< total number of flow links (edges) without duplicates nnodeglob = 0 !< total number of net nodes (nodes) without duplicates !nnetfaceglob = 0 !< total number of net cells (faces) without duplicates nnetedgeglob = 0 !< total number of net links (edges) without duplicates nbndglob = 0 !< total number of boundary waterlevel points without duplicates nbndcount = 0 !< total number of boundary waterlevel points ndxc = sum(ndx(1:nfiles)) call realloc(face_domain, ndxc, keepExisting=.false., fill=-1) call realloc(face_c2g, ndxc, keepExisting=.false.) call realloc(face_g2c, ndxc, keepExisting=.false.) lnxc = sum(lnx(1:nfiles)) call realloc(edge_domain, lnxc, keepExisting=.false., fill=-1) call realloc(ln, (/ 2, lnxc /), keepExisting=.false.) call realloc(edge_g2c, lnxc, keepExisting=.false.) call realloc(edge_c2g, lnxc, keepExisting=.false., fill=-1) netfacemaxnodesg = maxval(netfacemaxnodes(1:nfiles)) call realloc(netfacenodes, (/ netfacemaxnodesg, sum(nump(1:nfiles)) /), keepExisting=.false., fill=-1) call realloc(netfaceedges, (/ netfacemaxnodesg, sum(nump(1:nfiles)) /), keepExisting=.false., fill=-1) call realloc(nfaceedges, sum(nump(1:nfiles)), keepExisting=.false., fill=0) numkc = sum(numk(1:nfiles)) call realloc(node_domain, numkc, keepExisting=.false., fill=huge(1)) call realloc(node_g2c, numkc, keepExisting=.false.) call realloc(node_c2g, numkc, keepExisting=.false., fill=-1) call realloc(node_x, numkc, keepExisting=.false.) call realloc(node_y, numkc, keepExisting=.false.) call realloc(node_faces, (/ netnodemaxface+1, numkc/), keepExisting=.false., fill= -1) ! Prepare node_faces array: the last value for each nodes is a counter of how many faces are surrounding this node node_faces(netnodemaxface+1, 1: numkc) = 0 numlc = sum(numl(1:nfiles)) call realloc(netedge_domain, numlc, keepExisting=.false., fill=-1) call realloc(edgenodes, (/ 2, numlc /), keepExisting=.false.) call realloc(netedge_g2c, numlc, keepExisting=.false.) ndx_bndc = sum(ndxbnd(1:nfiles)) call realloc(facebnd_domain, ndx_bndc, keepExisting=.false., fill =-1) call realloc(netedge_c2g, numlc, keepExisting=.false., fill=-1) call realloc(netedgefaces, (/2, numlc/), keepExisting=.false., fill=-1) call realloc(edge_x, numlc, keepExisting=.false.) call realloc(edge_y, numlc, keepExisting=.false.) if (verbose_mode) then write (*,'(a)') '## Scanning input files for dimensions...' write (fmtstr, '(a,i0)') 'a', maxlen write (*,'('//trim(fmtstr)//',a3,4(a13),a8)') 'File', & ' : ', ' flow nodes', ' | flow links', ' | net nodes', ' | net links', ' | times' end if do ii=1,nfiles !! 3a.1: handle flow nodes (faces) nfaceglob0 = nfaceglob face_domain(nfacecount+1:nfacecount+ndx(ii)) = ii-1 ! Just set default domain if FlowElemDomain not present. if (jaugrid == 0) then ierr = nf90_inq_varid(ncids(ii), 'FlowElemDomain', id_facedomain) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_flowelem_domain', id_facedomain) endif if (ierr == nf90_noerr) then ierr = nf90_get_var(ncids(ii), id_facedomain, face_domain(nfacecount+1:nfacecount+ndx(ii)), count=(/ ndx(ii) /)) if (ierr /= nf90_noerr) then write (*,'(a)') 'Error: mapmerge: could not retrieve FlowElemDomain from `'//trim(infiles(ii))//'''. ' if (.not. verbose_mode) goto 888 end if else ! no problem if FlowElemDomain is missing: just include all elems (default ii-1 was set above). end if if (ierr == nf90_noerr) then if (jaugrid == 0) then ierr = nf90_inq_varid(ncids(ii), 'FlowElemGlobalNr', id_faceglobnr) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_flowelem_globalnr', id_faceglobnr) ! TODO: UNST-1794: generalize to multiple meshes in the UGRID file, a bit like the following: ! ierr = ionc_inq_varid(ncids(ii), meshid, 'flowelem_globalnr', id_faceglobnr) end if end if if (ierr == nf90_noerr) then ierr = nf90_get_var(ncids(ii), id_faceglobnr, face_c2g(nfacecount+1:nfacecount+ndx(ii)), count=(/ ndx(ii) /)) if (ierr /= nf90_noerr) then write (*,'(a)') 'Error: mapmerge: could not retrieve FlowElemGlobalNr from `'//trim(infiles(ii))//'''. ' if (.not. verbose_mode) goto 888 end if else ! no problem if FlowElemGlobalNr is missing: just include all elems: global nr is equal to 'concat-index'. do ip=1,ndx(ii) face_c2g(nfacecount+ip) = nfacecount+ip end do end if ! Count the actual unique flow nodes (to get rid of partition overlap) do ip=1,ndx(ii) if (face_domain(nfacecount+ip) == ii-1) then nfaceglob = nfaceglob+1 ifaceglob = face_c2g(nfacecount+ip) face_g2c(ifaceglob) = nfacecount + ip end if end do ndxg(ii) = nfaceglob-nfaceglob0 !! 3a.2: handle flow links (edges) if (jaugrid==0) then ! TODO: zhao: why is this if different from the one for faces and nodes? nedgeglob0 = nedgeglob ierr = nf90_inq_varid(ncids(ii), 'FlowLink', id_edgefaces) if (ierr == nf90_noerr) then ierr = nf90_get_var(ncids(ii), id_edgefaces, ln(:,nedgecount+1:nedgecount+lnx(ii)), count=(/ 2,lnx(ii) /)) else write (*,'(a)') 'Warning: mapmerge: could not retrieve FlowLink from `'//trim(infiles(ii))//'''. ' if (.not. verbose_mode) goto 888 end if ! Count the actual unique flow links (to get rid of partition overlap, and also of duplicate boundary links) do ip=1,lnx(ii) ! For each link, take 2nd point (such that boundary links will be uniquely ! owned by the domain who owns the internal flow node of that link.) n1 = ln(1,nedgecount+ip) ! Could be a boundary point n2 = ln(2,nedgecount+ip) if (n1 > ndx(ii)) then idom = ii - 1 ! Boundary mirrornodes are always owned by the domain itself. isBndLink = 1 ! Mark the boundary link else idom = face_domain(nfacecount+n1) end if idom = min(idom, face_domain(nfacecount+n2)) if (isBndLink == 1 .and. face_domain(nfacecount+n2) .ne. ii-1) then ! If this boundary link connects an interior flownode which does not belong to the current subdomain idom = - 999 endif edge_domain(nedgecount+ip) = idom if (idom == ii-1) then nedgeglob = nedgeglob+1 edge_g2c(nedgeglob) = nedgecount + ip edge_c2g(nedgecount+ip) = nedgeglob end if isBndLink = 0 end do lnxg(ii) = nedgeglob-nedgeglob0 end if !! 3a.3: handle net nodes (nodes) nnodeglob0 = nnodeglob if (jaugrid==0) then ierr = nf90_inq_varid(ncids(ii), 'NetElemNode', id_netfacenodes) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_face_nodes', id_netfacenodes) endif if (ierr == nf90_noerr) then ierr = ncu_inq_var_fill(ncids(ii), id_netfacenodes, nofill, ifill_value) call realloc (netfacenodesl, (/ netfacemaxnodes(ii), nump(ii) /), keepExisting = .false.) ierr = nf90_get_var(ncids(ii), id_netfacenodes, netfacenodesl(:,:), count=(/ netfacemaxnodes(ii), nump(ii) /)) do ip=1,nump(ii) netfacenodes(1:netfacemaxnodes(ii),nfacecount+ip) = netfacenodesl(1:netfacemaxnodes(ii),ip) ! generate node_faces: the faces that surround a node do ik =1, netfacemaxnodes(ii) ! for its every node k1 = netfacenodesl(ik,ip) if (k1 .ne. -1 .and. k1 .ne. ifill_value) then nfaces = node_faces(netnodemaxface+1, nnodecount+k1) + 1 node_faces(netnodemaxface+1, nnodecount+k1) = nfaces ! update the counter node_faces(nfaces,nnodecount+k1) = nfacecount+ip end if end do end do ! read coordinates of net nodes if (jaugrid==0) then ierr = nf90_inq_varid(ncids(ii), 'NetNode_x', id_nodex) ierr = nf90_inq_varid(ncids(ii), 'NetNode_y', id_nodey) ierr = nf90_get_var(ncids(ii), id_nodex, node_x(nnodecount+1:nnodecount+numk(ii))) ierr = nf90_get_var(ncids(ii), id_nodey, node_y(nnodecount+1:nnodecount+numk(ii))) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_node_x', id_nodex) ierr = nf90_inq_varid(ncids(ii), 'mesh2d_node_y', id_nodey) ierr = nf90_get_var(ncids(ii), id_nodex, node_x(nnodecount+1:nnodecount+numk(ii))) ierr = nf90_get_var(ncids(ii), id_nodey, node_y(nnodecount+1:nnodecount+numk(ii))) end if if (ierr /= nf90_noerr) then jamerge_cntv = 0 write (*,'(a)') 'Warning: mapmerge: could not retrieve coordinates of net nodes from `'//trim(infiles(ii))//'''. ' end if else if (jaugrid==0) then write (*,'(a)') 'Warning: mapmerge: could not retrieve NetElemNode from `'//trim(infiles(ii))//'''. ' else write (*,'(a)') 'Warning: mapmerge: could not retrieve mesh2d_face_nodes from `'//trim(infiles(ii))//'''. ' end if if (.not. verbose_mode) goto 888 end if ! Identify the node domain based on the already processed net cells do ip=1,nump(ii) do ik=1,netfacemaxnodes(ii) k1 = netfacenodes(ik, nfacecount+ip) if (k1 == -1 .or. k1 == ifill_value) then exit end if ! Owner of the node will be the lowest domain number of any of the cells surrounding that node. node_domain(nnodecount+k1) = min(node_domain(nnodecount+k1), face_domain(nfacecount+ip)) end do end do !numpg(ii) = nnetfaceglob-nnetfaceglob0 ! Count the actual unique nodes (to get rid of partition overlap) do ip=1,numk(ii) idom = node_domain(nnodecount+ip) if (idom == ii-1) then ! Second check is to identify orphan nodes (in case no 1D netcells were available on file) nnodeglob = nnodeglob+1 ! TODO: orphan nodes are not handled correctly yet for 1D channel strings (duplicates?) node_g2c(nnodeglob) = nnodecount + ip node_c2g(nnodecount + ip) = nnodeglob end if end do numkg(ii) = nnodeglob-nnodeglob0 !! 3a.4: handle net edges (also edges) nnetedgeglob0 = nnetedgeglob if (jaugrid==0) then ierr = nf90_inq_varid(ncids(ii), 'NetLink', id_edgenodes) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_edge_nodes', id_edgenodes) endif if (ierr == nf90_noerr) then ierr = nf90_get_var(ncids(ii), id_edgenodes, edgenodes(:,nnetedgecount+1:nnetedgecount+numl(ii)), count=(/ 2, numl(ii) /)) else if (jaugrid==0) then write (*,'(a)') 'Warning: mapmerge: could not retrieve NetLink from `'//trim(infiles(ii))//'''. ' else write (*,'(a)') 'Warning: mapmerge: could not retrieve mesh2d_edge_nodes from `'//trim(infiles(ii))//'''. ' end if if (.not. verbose_mode) goto 888 end if if (jaugrid==0) then ierr = nf90_inq_varid(ncids(ii), 'ElemLink', id_netedgefaces) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_edge_faces', id_netedgefaces) end if if (ierr == nf90_noerr) then ierr = nf90_get_var(ncids(ii), id_netedgefaces, netedgefaces(:,nnetedgecount+1:nnetedgecount+numl(ii)), count=(/ 2, numl(ii) /)) else if (jaugrid==0) then write (*,'(a)') 'Warning: mapmerge: could not retrieve ElemLink from `'//trim(infiles(ii))//'''. ' else write (*,'(a)') 'Warning: mapmerge: could not retrieve mesh2d_edge_faces from `'//trim(infiles(ii))//'''. ' end if if (.not. verbose_mode) goto 888 end if if (jaugrid==0) then ierr = nf90_inq_varid(ncids(ii), 'NetElemLink', id_netfaceedges) if (ierr == nf90_noerr) then ierr = nf90_get_var(ncids(ii), id_netfaceedges, netfaceedges(1:netfacemaxnodes(ii),nfacecount+1:nfacecount+nump(ii)), count=(/ netfacemaxnodes(ii), nump(ii) /)) if (ierr /= nf90_noerr) then jamerge_cntv = 0 write (*,'(a)') 'Warning: mapmerge: could not retrieve NetElemLink from `'//trim(infiles(ii))//'''. ' end if else write (*,'(a)') 'Warning: mapmerge: could not retrieve NetElemLink from `'//trim(infiles(ii))//'''. ' if (.not. verbose_mode) goto 888 end if else ! UGRID map file does not contain _face_edges, build it here: netfaceedges do iedge = 1, numl(ii) do ikk = 1, 2 ic = netedgefaces(ikk,iedge+nnetedgecount) if (ic > 0) then iface = ic + nfacecount nfaceedges(iface) = nfaceedges(iface) +1 netfaceedges(nfaceedges(iface), iface) = iedge end if end do end do end if if (jaugrid ==0) then ! read coordinates of NetLinks ierr = nf90_inq_varid(ncids(ii), 'NetLink_xu', id_edgex) ierr = nf90_inq_varid(ncids(ii), 'NetLink_yu', id_edgey) ierr = nf90_get_var(ncids(ii), id_edgex, edge_x(nnetedgecount+1:nnetedgecount+numl(ii))) ierr = nf90_get_var(ncids(ii), id_edgey, edge_y(nnetedgecount+1:nnetedgecount+numl(ii))) else ierr = nf90_inq_varid(ncids(ii), 'mesh2d_edge_x', id_edgex) ierr = nf90_inq_varid(ncids(ii), 'mesh2d_edge_y', id_edgey) ierr = nf90_get_var(ncids(ii), id_edgex, edge_x(nnetedgecount+1:nnetedgecount+numl(ii))) ierr = nf90_get_var(ncids(ii), id_edgey, edge_y(nnetedgecount+1:nnetedgecount+numl(ii))) end if if (ierr /= nf90_noerr) then jamerge_cntv = 0 write (*,'(a)') 'Warning: mapmerge: could not retrieve coordinates of net edges from `'//trim(infiles(ii))//'''. ' end if ! Identify the net link node domain based on the already processed net nodes do ip=1,numl(ii) k1 = edgenodes(1,nnetedgecount+ip) k2 = edgenodes(2,nnetedgecount+ip) ! NOTE: AvD: by taking the MAX below, I believe this also guarantees that one and ! the same domain owns both the net link and the associated flow link. idom = max(node_domain(nnodecount+k1), node_domain(nnodecount+k2)) netedge_domain(nnetedgecount+ip) = idom if (idom == ii-1) then nnetedgeglob = nnetedgeglob+1 netedge_g2c(nnetedgeglob) = nnetedgecount + ip netedge_c2g(nnetedgecount + ip) = nnetedgeglob end if end do numlg(ii) = nnetedgeglob-nnetedgeglob0 !! 3a.5: handle boundary waterlevel points if (ndxbnd(ii) > 0) then facebnd_domain(nbndcount+1:nbndcount+ndxbnd(ii)) = ii-1 nbndglob = nbndglob + ndxbnd(ii) else if (verbose_mode) then write (*,'(a)') 'Info: mapmerge: no waterlevel boundary in `'//trim(infiles(ii))//'''. ' endif ! Intentional: all of these need to be done at very last instant: nedgecount = nedgecount + lnx(ii) nfacecount = nfacecount + ndx(ii) nnodecount = nnodecount + numk(ii) nnetedgecount = nnetedgecount + numl(ii) !nnetfacecount = nnetfacecount + nump(ii) nbndcount = nbndcount + ndxbnd(ii) if (verbose_mode) then nlen = len_trim(infiles(ii)) plen = 3*max(0,sign(1,nlen-maxlen-1)) write (fmtstr, '(a,i0)') 'a', maxlen write (*,'('//trim(fmtstr)//',a3,i7,3(i13),i8)') repeat('.', plen)//infiles(ii)(max(1,nlen-maxlen+1)+plen:nlen)//repeat(' ', max(0,maxlen-nlen-plen)), & ' : ', ndx(ii), lnx(ii), numk(ii), numl(ii), nt(ii) write (*,'('//trim(fmtstr)//',a3,4(i13))') repeat(' ', maxlen-7)//'Removed', & ' : ', (ndxg(ii)-ndx(ii)), (lnxg(ii)-lnx(ii)), (numkg(ii)-numk(ii)), (numlg(ii)-numl(ii)) end if end do ! ii if (jamerge_cntv == 1) then !! fulfill node_c2g, because in domain that is larger than 0000, there are nodes which are in this domain but ! their domain numbers are another domain. do ii = 2, nfiles nfacecount = sum(nump(1:ii-1)) nnodecount = sum(numk(1:ii-1)) do ik=1,numk(ii) if (node_c2g(nnodecount+ik)==-1) then jafound = 0 do ikk = 1, node_faces(netnodemaxface+1, nnodecount+ik) ic = node_faces(ikk,nnodecount+ik) ! index of one surrounding cell if (ic .ne. -1) then if (face_domain(ic) .ne. ii-1 .and. face_domain(ic) == node_domain(nnodecount+ik)) then ! cell ic is not in current domain, and is in the same domain with node ik ifaceglob = face_c2g(ic) ifacec = face_g2c(ifaceglob) ! and this is now from the OTHER domain. ! in which domain (iii) does iface belong do iii = 1, nfiles if (ifacec > sum(nump(1:iii-1)) .and. ifacec <= sum(nump(1:iii))) then ifacefile = iii exit end if end do ! Loop on all the nodes of face ifacec, to find the node which has the same coordinates with node ik from file ii do im=1,netfacemaxnodes(ifacefile) k1 = netfacenodes(im, ifacec) if (k1 .ne. -1 .and. k1 .ne. ifill_value) then k1c = k1 + sum(numk(1:ifacefile-1)) ! concatinated index of nodes xx = node_x(k1c) yy = node_y(k1c) if (abs(node_x(ik+nnodecount)-xx)<1d-10 .and. abs(node_y(ik+nnodecount)-yy)<1d-10) then node_c2g(ik+nnodecount) = node_c2g(k1c) jafound = 1 exit end if end if end do end if end if end do if (jafound == 0 .and. verbose_mode) then write (*,'(a,i0,a)') 'Warning: mapmerge: node_c2g: could not find global number for node # ', ik ,' of file `'//trim(infiles(ii))//'''. ' end if end if end do end do !! fulfill netedge_c2g, because in domain larger than 0000, there are netedges which are in this domain ! but their domain numbers are another domain. do ii = 2, nfiles netedgecount = sum(numl(1:ii-1)) nfacecount = sum(nump(1:ii-1)) do ip = 1, numl(ii) k1 = netedgefaces(1, netedgecount+ip) ! flowelem that edge L connects k2 = netedgefaces(2, netedgecount+ip) if (k1.ne.0 .and. k2.ne. 0) then ! When edge L is not on the boundary g1 = face_domain(nfacecount+k1) ! domain number of this flowelem g2 = face_domain(nfacecount+k2) if (g1 .ne. g2) then ! if edge L lies on the boundary of two different domains if (g1==ii-1 .and. g1>g2) then ! decide which flowelem is in the current domain, which is not ifacein = k1 ifaceout= k2 else if (g2==ii-1 .and. g2>g1) then ifacein = k2 ifaceout= k1 else cycle end if ifaceglob = face_c2g(ifaceout+nfacecount) ! for the flowelem which is in another domain with smaller domain number ifacec = face_g2c(ifaceglob) ! and this is now from the OTHER domain. ! in which domain (iii) does iface belong do iii = 1, nfiles if (ifacec>sum(nump(1:iii-1)) .and. ifacec < sum(nump(1:iii))) then ifacefile = iii exit end if end do ! Loop on all netedges of face ifacec do im = 1, netfacemaxnodes(ifacefile) k1 = netfaceedges(im, ifacec) if (k1 .ne. -1 .and. k1 .ne. ifill_value) then if (k1 .ne. -1) then k1c= k1 + sum(numl(1:ifacefile-1))! concatinated index of the edge xx = edge_x(k1c) yy = edge_y(k1c) if (abs(edge_x(ip+netedgecount)-xx)<1d-10 .and. abs(edge_y(ip+netedgecount)-yy)<1d-10) then netedge_c2g(ip+netedgecount) = netedge_c2g(k1c) exit end if end if end if end do end if end if end do end do end if nkmxglob = kmx(1) do ii = 2,nfiles if (kmx(ii) .ne. nkmxglob) then write (*,'(a,i0,a,i0,a)') 'Error: mapmerge: Numbers of layers are different in files: ', kmx(ii), 'layers in file`'//trim(infiles(ii))//''' and', & nkmxglob, 'layers in other files.' if (.not. verbose_mode) goto 888 endif enddo ndx (noutfile) = nfaceglob lnx (noutfile) = nedgeglob numk(noutfile) = nnodeglob numl(noutfile) = nnetedgeglob kmx (noutfile) = nkmxglob ndxbnd(noutfile) = nbndglob if (verbose_mode) then nlen = len_trim(outfile) plen = 3*max(0,sign(1,nlen-maxlen-1)) write (fmtstr, '(a,i0)') 'a', maxlen write (*,'('//trim(fmtstr)//',a3,4(i13))') repeat(' ', maxlen-7)//'Net sum', & ' : ', sum(ndxg(1:nfiles)), sum(lnxg(1:nfiles)), sum(numkg(1:nfiles)), sum(numlg(1:nfiles)) write (*,'('//trim(fmtstr)//',a3,4(i13))') repeat(' ', maxlen-11)//'Check count', & ' : ', ndx(noutfile), lnx(noutfile), numk(noutfile), numl(noutfile) end if !! 3b. dimensions for merged flow nodes (faces) and flow links (edges), same for net nodes + links. if (jaugrid==0) then ierr = nf90_def_dim(ncids(noutfile), trim(netfacedimname), ndx(noutfile), id_netfacedim(noutfile)) ! UNST-1256: temp fix, pending proper UGRID support in UNST-1134. ierr = nf90_def_dim(ncids(noutfile), trim(facedimname), ndx(noutfile), id_facedim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), trim(edgedimname), lnx(noutfile), id_edgedim(noutfile)) if (kmx(noutfile) > 0) then ierr = nf90_def_dim(ncids(noutfile), trim(laydimname), kmx(noutfile), id_laydim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), trim(wdimname), kmx(noutfile)+1, id_wdim(noutfile)) end if ierr = nf90_def_dim(ncids(noutfile), trim(nodedimname), numk(noutfile), id_nodedim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), trim(netedgedimname), numl(noutfile), id_netedgedim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), 'nFlowElemBnd', ndxbnd(noutfile), id_bnddim(noutfile))! TODO: Add if ndxbnd > 0 else ierr = nf90_def_dim(ncids(noutfile), trim(facedimname), ndx(noutfile), id_facedim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), trim(netedgedimname), numl(noutfile), id_netedgedim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), trim(nodedimname), numk(noutfile), id_nodedim(noutfile)) if (kmx(noutfile) > 0) then ierr = nf90_def_dim(ncids(noutfile), trim(laydimname), kmx(noutfile), id_laydim(noutfile)) ierr = nf90_def_dim(ncids(noutfile), trim(wdimname), kmx(noutfile)+1, id_wdim(noutfile)) end if endif !! 4. Define all variables (grid + data), including any remaining dimensions !! 4a. Simply copy all remaining dimensions (probably vectormax-like) to output file. do id=1,ndims if (dimids(id,ifile) > 0) then ! For now, just copy the vectormax dimids (if any) from file #1 to output file. Assume same length in all files. ierr = nf90_inquire_dimension(ncids(ifile), dimids(id,ifile), name = dimname, len = nlen) if (dimids_uses(id) <= 0) then write (*,'(a)') 'Info: mapmerge: Dimension `'//trim(dimname)//''' is not merged because no merged variable uses it. ' cycle endif ! When one file has only triangular mesh and one file has only rectangular mesh, then a variable, e.g. 'NetElemNode' ! has dimension 3 and 4, respectively. Then this variable in the target merged map should have dimension nlen=4, ! which is the maximum (UNST-1842). if (dimname == 'nNetElemMaxNode' .or. dimname == 'max_nmesh2d_face_nodes' .or. dimname == 'mesh2d_nMax_face_nodes' .or. dimname=='nFlowElemContourPts') then nlen = maxval(netfacemaxnodes) end if if (ierr == nf90_noerr) then ierr = nf90_def_dim(ncids(noutfile), trim(dimname), nlen, dimids(id, noutfile)) end if if (ierr /= nf90_noerr) then write (*,'(a,i0,a,i0)') 'Error: mapmerge: Could not copy dimension #', dimids(id,ifile), & ' from `'//trim(infiles(ifile))//''' into output file `'//trim(outfile)//'''. Error: ', ierr if (.not. verbose_mode) goto 888 end if end if end do !! 4b. Define all variables, based on previously detected dimension information. ! var_dimids = -1 ! NOTE: can't do this, as we still need the vectormax dimensions that are stored in here. do iv=1,nvarsel ! ierr = nf90_inquire_var(ncids(1), varids(iv) ! ndims = 2 !dimids(1) = id_facedim(noutfile) !dimids(2) = id_timedim(noutfile) ip = var_timdimpos(iv) if (ip /= -1) then var_dimids(ip,iv) = id_timedim(noutfile) end if ip = var_spacedimpos(iv) if (ip /= -1) then if (var_loctype(iv) == UNC_LOC_S) then var_dimids(ip,iv) = id_facedim(noutfile) else if (var_loctype(iv) == UNC_LOC_SN) then var_dimids(ip,iv) = id_netfacedim(noutfile) else if (var_loctype(iv) == UNC_LOC_U) then var_dimids(ip,iv) = id_edgedim(noutfile) else if (var_loctype(iv) == UNC_LOC_CN) then var_dimids(ip,iv) = id_nodedim(noutfile) else if (var_loctype(iv) == UNC_LOC_L) then var_dimids(ip,iv) = id_netedgedim(noutfile) else if (var_loctype(iv) == UNC_LOC_SBND) then var_dimids(ip,iv) = id_bnddim(noutfile) else write (*,'(a,i0,a)') 'Error: mapmerge: Unknown location type ', var_loctype(iv), ' for `'//trim(var_names(iv))//'''.' end if end if ip = var_laydimpos(iv) if (ip /= -1) then var_dimids(ip,iv) = id_laydim(noutfile) end if ip = var_wdimpos(iv) if (ip /= -1) then var_dimids(ip,iv) = id_wdim(noutfile) end if ip = var_kxdimpos(iv) if (ip /= -1) then var_dimids(ip,iv) = dimids(var_dimids(ip,iv), noutfile) ! this is necessary because in outfile dim IDs will be in different order, even if *all* dim ids have been copied ! Dim ID for this var in outfile === based on *pos* in dimids(:) for *this* noutfile. end if ierr = nf90_def_var(ncids(noutfile), var_names(iv), var_types(iv), var_dimids(4-var_ndims(iv)+1:4,iv), varids_out(iv)) if (ierr /= nf90_noerr) then write (*,'(a)') 'Error: mapmerge: could not create output variable `'//trim(var_names(iv))//'''. ' write (*,'(a)') 'Details : '//trim(nf90_strerror(ierr)) if (.not. verbose_mode) goto 888 varids_out(iv) = -1 cycle end if ierr = ncu_copy_atts(ncids(ifile), ncids(noutfile), varids(ifile, iv), varids_out(iv)) if (isNetCDF4) then new_ndx = min(ndx(noutfile), 2000) ! ! must be equal to mapclass_chunksize_ndx ierr = ncu_copy_chunking_deflate(ncids(ifile), ncids(noutfile), varids(ifile, iv), varids_out(iv), new_ndx) endif ! For Variable 'FlowLink', the flowelem might be outside the domain, and there might be no info. about such flowelem ! in mapfiles, so they are denoted by _FillValue. if (var_names(iv) .eq. 'FlowLink') then ierr = nf90_put_att(ncids(noutfile), varids_out(iv), '_FillValue', intmiss) end if end do ierr = nf90_put_att(ncids(noutfile), nf90_global, 'NumPartitionsInFile', nfiles) ierr = nf90_def_dim(ncids(noutfile), 'nPartitions', nfiles, id_npartdim) ierr = nf90_def_var(ncids(noutfile), 'partitions_face_start', nf90_int, (/ id_npartdim /), id_part_face_start) ierr = nf90_def_var(ncids(noutfile), 'partitions_edge_start', nf90_int, (/ id_npartdim /), id_part_edge_start) ierr = nf90_def_var(ncids(noutfile), 'partitions_node_start', nf90_int, (/ id_npartdim /), id_part_node_start) ierr = nf90_put_att(ncids(noutfile), id_part_face_start, 'long_name', 'start index in global data arrays for face data for all partititions') ierr = nf90_put_att(ncids(noutfile), id_part_edge_start, 'long_name', 'start index in global data arrays for edge data for all partititions') ierr = nf90_put_att(ncids(noutfile), id_part_node_start, 'long_name', 'start index in global data arrays for node data for all partititions') ! NOTE: AvD: intentionally not adding netedge here, as I want to phase it out. ierr = nf90_def_var(ncids(noutfile), 'partitions_face_count', nf90_int, (/ id_npartdim /), id_part_face_count) ierr = nf90_def_var(ncids(noutfile), 'partitions_edge_count', nf90_int, (/ id_npartdim /), id_part_edge_count) ierr = nf90_def_var(ncids(noutfile), 'partitions_node_count', nf90_int, (/ id_npartdim /), id_part_node_count) ierr = nf90_put_att(ncids(noutfile), id_part_face_count, 'long_name', 'per-partition count in global data arrays for face data') ierr = nf90_put_att(ncids(noutfile), id_part_edge_count, 'long_name', 'per-partition count in global data arrays for edge data') ierr = nf90_put_att(ncids(noutfile), id_part_node_count, 'long_name', 'per-partition count in global data arrays for node data') if (nbndglob>0) then ierr = nf90_def_var(ncids(noutfile), 'partitions_facebnd_count', nf90_int, (/ id_npartdim /), id_part_facebnd_count) ierr = nf90_put_att(ncids(noutfile), id_part_facebnd_count, 'long_name', 'per-partition count in global data arrays for boundary points data') ierr = nf90_def_var(ncids(noutfile), 'partitions_facebnd_start', nf90_int, (/ id_npartdim /), id_part_facebnd_start) ierr = nf90_put_att(ncids(noutfile), id_part_facebnd_start, 'long_name', 'start index in global data arrays for boundary points for all partititions') endif ! 4. Write new flow geom to output file ! 5. Write all timedep fields from all files into output file ierr = nf90_enddef(ncids(noutfile)) ! For now: do all times. ntsel = nt(ifile) nt(noutfile) = ntsel allocate(itimsel(ntsel)) itimsel = (/ (it, it=1,ntsel) /) allocate(times(ntsel), timestep(ntsel)) do it=1,ntsel ierr = nf90_get_var(ncids(ifile), id_time(ifile), times(it), start = (/ itimsel(it) /)) ! count=1 ierr = nf90_get_var(ncids(ifile), id_timestep(ifile), timestep(it), start = (/ itimsel(it) /)) ! count=1 end do ierr = nf90_put_var(ncids(noutfile), id_time(noutfile), times, count = (/ ntsel /)) ierr = nf90_put_var(ncids(noutfile), id_timestep(noutfile), timestep, count = (/ ntsel /)) if (verbose_mode) then write (*,'(a)') '## Writing merged variables to output file...' end if ! 1D tmp array: take largest of all topological position counts: maxitems = max(nedgecount, nfacecount, nnodecount, nnetedgecount, nbndcount, kmx(noutfile)+1) call realloc( tmpvar1D, maxitems, keepExisting=.false.) call realloc(itmpvar1D, maxitems, keepExisting=.false.) call realloc(itmpvar1D_tmp,maxitems, keepExisting=.false.) size_btmp = max(ndx(size(ndx)), sum(ndx(1:size(ndx)-1))) call realloc(btmpvar1D, [size_btmp, mapclass_time_buffer_size], keepExisting=.false.) call realloc(btmpvar1D_tmp, [ndx(size(ndx)), mapclass_time_buffer_size], keepExisting=.false.) call realloc(tmpvar1D_tmp, maxitems, keepExisting=.false.) ! 2D/3D done in loop below do iv=1,nvarsel if (verbose_mode) then write (tmpstr1, '(a,i0,a,i0,a)') 'Var #', iv, ' of ', nvarsel, ': ' end if ! Skip merging the connectivity variables when coordinates of netnodes or netedges are not read from the files if (jamerge_cntv == 0 .and. (var_names(iv) .eq. 'NetLink' .or. var_names(iv) .eq. 'NetElemNode' .or. & var_names(iv) .eq. 'NetElemLink' .or. var_names(iv) .eq. 'ElemLink' .or. & var_names(iv) .eq. 'mesh2d_edge_nodes' .or. var_names(iv) .eq. 'mesh2d_face_nodes' .or. var_names(iv) .eq. 'mesh2d_edge_faces')) then write (*,'(a)') 'Warning: mapmerge: Skipping merging topology variable: `'//trim(var_names(iv))//''', because coordinates of netnodes or netedges can not be read from the file.' cycle end if if (var_ndims(iv) == 0) then ! For instance, 'Mesh2D' cycle else if (var_spacedimpos(iv) == -1 .and. var_timdimpos(iv) == -1 .and. var_kxdimpos(iv) /= -1) then ! Some unknown non-space and non-time dimension: impossible to merge in a generic way. Skip it. write (*,'(a)') 'Warning: mapmerge: cannot merge vars with non-space/time dimensions: `'//trim(var_names(iv))//'''. Skipping.' cycle ! TODO: AvD: read + write timestep variable and similar here. end if is = MAX_VAR_DIMS-var_ndims(iv)+1 ie = MAX_VAR_DIMS start_idx (is:ie) = 1 ! For all relevant dimensions for this var: start indices to be set below count_read (is:ie) = 1 ! For all relevant dimensions for this var: read counts to be set below count_write(is:ie) = 1 ! For all relevant dimensions for this var: write counts to be set below ! 5a.1 Optional kx/vectormax is the same for all files, for all times: if (var_kxdimpos(iv) /= -1) then id = var_dimids(var_kxdimpos(iv), iv) ! Dim ID for this kx dim in outfile ierr = nf90_inquire_dimension(ncids(noutfile), id, name = dimname, len = nlen) if (ierr /= nf90_noerr) then write (*,'(a,i0,a,a,a)') 'Error: mapmerge: Could not inquire vectormax dimension #', id, ' for variable `', trim(var_names(iv)), '''.' cycle ! iv end if count_read (var_kxdimpos(iv)) = nlen count_write(var_kxdimpos(iv)) = nlen end if ! 5a.2 Optional kmx/layer dim is assumed to be the same for all files, for all times: if (var_laydimpos(iv) /= -1) then count_read (var_laydimpos(iv)) = kmx(noutfile) count_write(var_laydimpos(iv)) = kmx(noutfile) ! TODO: AvD: UNST-993: support w-position quantities: difference between kmx and kmx1 !! ! TODO: AvD: future work: what if kmx varies between input files? end if if (var_wdimpos(iv) /= -1) then count_read (var_wdimpos(iv)) = kmx(noutfile) +1 count_write(var_wdimpos(iv)) = kmx(noutfile) +1 end if ! 5a.3 Prepare for space dimension, by pointers to proper face/edge/node/netedge variables if ( var_spacedimpos(iv) /= -1) then select case (var_loctype(iv)) case (UNC_LOC_S) item_counts => ndx item_domain => face_domain case (UNC_LOC_SN) item_counts => ndx item_domain => face_domain case (UNC_LOC_U) item_counts => lnx item_domain => edge_domain case (UNC_LOC_CN) item_counts => numk item_domain => node_domain case (UNC_LOC_L) item_counts => numl item_domain => netedge_domain case (UNC_LOC_SBND) item_counts => ndxbnd item_domain => facebnd_domain case default ! TODO: Handle non-space dependent vars if (var_ndims(iv) > 0) then write (*,'(a)') 'Warning: mapmerge: cannot write space-independent vars: `'//trim(var_names(iv))//'''. Skipping.' else if (verbose_mode) then call progress(tmpstr1, 100) ! generate the progress bar. end if end if cycle end select count_write(var_spacedimpos(iv)) = item_counts(noutfile) endif ! NOTE: AvD: below we assume that order is kx, kmx, ndx, nt, so not as generic anymore as the var_*dimpos analysis would allow. ! Allocate the proper memory space for nf90_get_var without risk of stack overflows in the netcdf lib if (var_types(iv) /= nf90_double .and. var_types(iv) /= nf90_int .and. var_types(iv) /= nf90_short .and. var_types(iv) /= nf90_byte) then write (*,'(a,i0,a)') 'Error: mapmerge: encountered unsupported data type ', var_types(iv), ' for variable `'//trim(var_names(iv))//'''.' if (.not. verbose_mode) goto 888 end if intfillv = dble(intmiss) if ((var_kxdimpos(iv) == -1 .and. var_laydimpos(iv) == -1 .and. var_wdimpos(iv) == -1) & ! 1D array with no layers and no vectormax (possibly time-dep) .or. (var_ndims(iv) == 1 .and. (var_laydimpos(iv) > 0 .or. var_wdimpos(iv) > 0)) )then ! 1D array of vertical coordinates ! Already allocated at max(lnx, ndx, numk, numl), no risk of stack overflow if (var_types(iv) == nf90_double) then tmpvarptr(1:1,1:1,1:maxitems) => tmpvar1D(:) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then itmpvarptr(1:1,1:1,1:maxitems) => itmpvar1D(:) else if (var_types(iv) == nf90_byte) then btmpvarptr(1:1,1:1,1:maxitems,1:mapclass_time_buffer_size) => btmpvar1D(:,1:mapclass_time_buffer_size) end if tmpvarDim = 1 else if (var_kxdimpos(iv) /= -1) then if (var_laydimpos(iv) /= -1) then ! Both a vectormax AND a laydim call realloc(tmpvar3D, (/ count_read(var_kxdimpos(iv)), count_read(var_laydimpos(iv)), maxitems /), keepExisting=.false.) ! use maxitems instead of items_count(noutfile) to try and have as few reallocs as possible. tmpvarptr => tmpvar3D tmpvarDim = 3 else ! Only a vectormax dim if (var_types(iv) == nf90_double) then call realloc( tmpvar2D, (/ count_read(var_kxdimpos(iv)), maxitems /), keepExisting=.false., fill=dmiss) tmpvarptr(1:count_read(var_kxdimpos(iv)),1:1,1:maxitems) => tmpvar2D(:,:) call realloc( tmpvar2D_tmp, (/ count_read(var_kxdimpos(iv)), maxitems /), keepExisting=.false., fill=dmiss) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then call realloc(itmpvar2D, (/ count_read(var_kxdimpos(iv)), maxitems /), keepExisting=.false., fill=intfillv) itmpvarptr(1:count_read(var_kxdimpos(iv)),1:1,1:maxitems) => itmpvar2D(:,:) call realloc(itmpvar2D_tmp, (/ count_read(var_kxdimpos(iv)), maxitems /), keepExisting=.false., fill=intfillv) end if tmpvarDim = 2 end if else if (var_laydimpos(iv) /= -1) then ! Only a laydim if (var_types(iv) == nf90_double) then call realloc(tmpvar2D, (/ kmx(noutfile), maxitems /), keepExisting=.false., fill=dmiss) tmpvarptr(1:1,1:kmx(noutfile),1:maxitems) => tmpvar2D(:,:) call realloc(tmpvar2D_tmp, (/ kmx(noutfile), maxitems /), keepExisting=.false., fill=dmiss) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then call realloc(itmpvar2D, (/ kmx(noutfile), maxitems /), keepExisting=.false., fill=intfillv) itmpvarptr(1:1,1:kmx(noutfile),1:maxitems) => itmpvar2D(:,:) call realloc(itmpvar2D_tmp, (/ kmx(noutfile), maxitems /), keepExisting=.false., fill=intfillv) end if tmpvarDim = 2 else if (var_wdimpos(iv) /= -1) then if (var_types(iv) == nf90_double) then call realloc(tmpvar2D, (/ kmx(noutfile)+1, maxitems /), keepExisting=.false., fill=dmiss) tmpvarptr(1:1,1:kmx(noutfile)+1,1:maxitems) => tmpvar2D(:,:) call realloc(tmpvar2D_tmp, (/ kmx(noutfile)+1, maxitems /), keepExisting=.false., fill=dmiss) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then call realloc(itmpvar2D, (/ kmx(noutfile)+1, maxitems /), keepExisting=.false., fill=intfillv) itmpvarptr(1:1,1:kmx(noutfile)+1,1:maxitems) => itmpvar2D(:,:) call realloc(itmpvar2D_tmp, (/ kmx(noutfile)+1, maxitems /), keepExisting=.false., fill=intfillv) end if tmpvarDim = 2 end if !! 1D array of vertical coordinates are COPIED from file "ifile" to the merged file if (var_ndims(iv) == 1 .and. (var_laydimpos(iv) > 0 .or. var_wdimpos(iv) > 0)) then nlen = count_read(ie) if (var_types(iv) == nf90_double) then ierr = nf90_get_var(ncids(ifile), varids(ifile,iv), tmpvar1D(1:nlen), count=count_read(is:ie)) if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: cannot read variable ', var_names(iv), ' from file `'//trim(infiles(ifile))//'''.' if (.not. verbose_mode) goto 888 end if ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar1D, count = count_write(is:ie), start = start_idx(is:ie)) if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: cannot write variable ', var_names(iv), ' to the merged file.' if (.not. verbose_mode) goto 888 end if else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ierr = nf90_get_var(ncids(ifile), varids(ifile,iv), itmpvar1D(1:nlen), count=count_read(is:ie)) if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: cannot read variable ', var_names(iv), ' from file `'//trim(infiles(ifile))//'''.' if (.not. verbose_mode) goto 888 end if ierr = nf90_put_var(ncids(noutfile), varids_out(iv), itmpvar1D, count = count_write(is:ie), start = start_idx(is:ie)) if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: cannot write variable ', var_names(iv), ' to the merged file.' if (.not. verbose_mode) goto 888 end if end if else ! the following is merging do it=1,ntsel itm = mod(it-1, mapclass_time_buffer_size)+1 if (verbose_mode) then call progress(tmpstr1, ceiling((it-1)*100.0/ntsel)) ! generate the progress bar. end if ! 5a.4 Time dimension: Which timestep to read from input files if (var_timdimpos(iv) /= -1) then start_idx(var_timdimpos(iv)) = itimsel(it) end if nitemglob = 0 nitemcount = 0 do ii=1,nfiles nitemglob0 = nitemglob if (var_spacedimpos(iv) /= -1) then if (item_counts(ii) == 0) then cycle else count_read(var_spacedimpos(iv)) = item_counts(ii) ! How many flow face/edges/nodes to read from file #ii endif end if ! Do the actual reading ! When one file has only triangular mesh and one file has only rectangular mesh, then a variable, e.g. 'NetElemNode' ! in the target merged map has vectormax dimension nlen=4. To read such a variable from each file, the vectormax dimension ! should be consistent in the current file. If this dimension is smaller than the maximal nlen, then a seperate array ! "itmpvar2D_tmpmax" will be defined by the current vectormax dimension. We first read values into this new array and then ! put them into array "itmpvar2D" (UNST-1842). if (var_kxdimpos(iv) /= -1 .and. (dimname == 'nNetElemMaxNode' .or. dimname == 'max_nmesh2d_face_nodes' .or. dimname == 'mesh2d_nMax_face_nodes' .or. dimname=='nFlowElemContourPts')) then count_read(is) = netfacemaxnodes(ii) if (netfacemaxnodes(ii) < nlen) then jaread_sep = 1 end if end if if (var_kxdimpos(iv) == -1 .and. var_laydimpos(iv) == -1 .and. var_wdimpos(iv) == -1) then ! 1D array with no layers and no vectormax (possibly time-dep) if (var_types(iv) == nf90_double) then ierr = nf90_get_var(ncids(ii), varids(ii,iv), tmpvar1D( nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ierr = nf90_get_var(ncids(ii), varids(ii,iv), itmpvar1D( nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) else if (var_types(iv) == nf90_byte) then ierr = nf90_get_var(ncids(ii), varids(ii,iv), btmpvar1D( nitemglob0+1:,itm:itm), count=count_read(is:ie), start=start_idx(is:ie)) end if else if (var_kxdimpos(iv) /= -1 .neqv. var_laydimpos(iv) /= -1) then ! Either a vectormax OR a laydim if (var_types(iv) == nf90_double) then if (jaread_sep == 1) then call realloc(tmpvar2D_tmpmax, (/ count_read(is), count_read(ie) /), keepExisting=.false., fill=dmiss) ierr = nf90_get_var(ncids(ii), varids(ii,iv), tmpvar2D_tmpmax, count=count_read(is:ie), start=start_idx(is:ie)) tmpvar2D(1:netfacemaxnodes(ii),nitemglob0+1:nitemglob0+count_read(ie)) = tmpvar2D_tmpmax(1:count_read(is),1:count_read(ie)) jaread_sep = 0 else if (var_seddimpos(iv) /= -1) then ! Reading a sediment variable needs to specify the "map" argument in nf90_get_var, because its dimensions are in a different order than other vectormax variables ierr = nf90_get_var(ncids(ii), varids(ii,iv), tmpvar2D( :,nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie), map = (/ count_read (var_kxdimpos(iv)), 1, count_read (var_kxdimpos(iv))*item_counts(ii) /)) else ierr = nf90_get_var(ncids(ii), varids(ii,iv), tmpvar2D( :,nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) end if end if else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then if (jaread_sep == 1) then call realloc(itmpvar2D_tmpmax, (/ count_read(is), count_read(ie) /), keepExisting=.false., fill=intfillv) ierr = nf90_get_var(ncids(ii), varids(ii,iv), itmpvar2D_tmpmax, count=count_read(is:ie), start=start_idx(is:ie)) itmpvar2D(1:netfacemaxnodes(ii),nitemglob0+1:nitemglob0+count_read(ie)) = itmpvar2D_tmpmax(1:count_read(is),1:count_read(ie)) jaread_sep = 0 else ierr = nf90_get_var(ncids(ii), varids(ii,iv), itmpvar2D( :,nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) end if end if else if (var_kxdimpos(iv) /= -1 .neqv. var_wdimpos(iv) /= -1) then ! Either a vectormax OR a wdim if (var_types(iv) == nf90_double) then ierr = nf90_get_var(ncids(ii), varids(ii,iv), tmpvar2D( :,nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ierr = nf90_get_var(ncids(ii), varids(ii,iv), itmpvar2D( :,nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) end if else ! Both a vectormax AND a laydim if (var_types(iv) == nf90_double) then ierr = nf90_get_var(ncids(ii), varids(ii,iv), tmpvar3D(:,:,nitemglob0+1:), count=count_read(is:ie), start=start_idx(is:ie)) end if end if if (ierr /= nf90_noerr) then write (*,'(a,i0,a)') 'Error: mapmerge: could not read `'//trim(var_names(iv))//''' from file `'//trim(infiles(ii))//''' (it=', itimsel(it), ').' if (.not. verbose_mode) goto 888 end if ! Now shift all items (in space) that really belong to *current* domain ii to the left, ! such that global item (edge/node) nrs form one increasing range in tmpvar. ! Faces related variables in the merged file are numbered by 'FlowElemGlobalNr' ! Conectivity arrays are taken care seperately. if (var_names(iv) .eq. 'NetLink' .or. var_names(iv) .eq. 'mesh2d_edge_nodes') then nnetedgecount = sum(numl(1:ii-1)) nnodecount = sum(numk(1:ii-1)) do ip=1,item_counts(ii) if (item_domain(nnetedgecount+ip) == ii-1) then ! only for the NetLink which belongs to the current domain inetedgeglob = netedge_c2g(nnetedgecount+ip) if (inetedgeglob > 0) then nitemglob = nitemglob + 1 ! for later checking nitemglob itmpvar2D(:,nitemglob) = itmpvar2D(:,nitemglob0+ip) do ikk=1,2 g1 = itmpvar2D(ikk, nitemglob) if (g1 > 0) then g1 = g1 +nnodecount itmpvar2D(ikk,nitemglob) = node_c2g(g1) ! mapping the nodes to global nodes end if end do end if end if end do else if (var_names(iv) .eq. 'NetElemNode' .or. var_names(iv) .eq. 'mesh2d_face_nodes') then nfacecount = sum(nump(1:ii-1)) nnodecount = sum(numk(1:ii-1)) do ip=1, item_counts(ii) if (item_domain(nfacecount+ip) == ii-1) then ifaceglob = face_c2g(nfacecount+ip) if (ifaceglob > 0) then nitemglob = nitemglob + 1 itmpvar2D_tmp(:,nitemglob) = itmpvar2D(:,nitemglob0+ip) do ikk = 1, netfacemaxnodes(ii) g1 = itmpvar2D_tmp(ikk,nitemglob) if (g1 > 0) then g1 = g1 + nnodecount itmpvar2D_tmp(ikk,nitemglob) = node_c2g(g1) end if end do end if end if end do if (ii==nfiles) then ! When finished, copy to itmpvar2D for writting itmpvar2D(:,1:nitemglob) = itmpvar2D_tmp(:,1:nitemglob) end if else if (var_names(iv) .eq. 'NetElemLink') then nfacecount = sum(nump(1:ii-1)) nnetedgecount = sum(numl(1:ii-1)) do ip =1, item_counts(ii) if (item_domain(nfacecount+ip) == ii-1) then ifaceglob = face_c2g(nfacecount+ip) if (ifaceglob > 0) then nitemglob = nitemglob + 1 itmpvar2D_tmp(:,nitemglob) = itmpvar2D(:,nitemglob0+ip) do ikk = 1, netfacemaxnodes(ii) g1 = itmpvar2D_tmp(ikk,nitemglob) if (g1 > 0) then g1 = g1 + nnetedgecount itmpvar2D_tmp(ikk,nitemglob) = netedge_c2g(g1) end if end do end if end if end do if (ii==nfiles) then itmpvar2D(:,1:nitemglob) = itmpvar2D_tmp(:,1:nitemglob) end if else if (var_names(iv) .eq. 'ElemLink' .or. var_names(iv) .eq. 'mesh2d_edge_faces') then nnetedgecount = sum(numl(1:ii-1)) nfacecount = sum(nump(1:ii-1)) do ip=1,item_counts(ii) if (item_domain(nnetedgecount+ip) == ii-1) then inetedgeglob = netedge_c2g(nnetedgecount+ip) if (inetedgeglob > 0) then nitemglob = nitemglob + 1 itmpvar2D(:,nitemglob) = itmpvar2D(:,nitemglob0+ip) do ikk=1,2 g1 = itmpvar2D(ikk, nitemglob) if (g1 > 0) then g1 = g1 +nfacecount itmpvar2D(ikk,nitemglob) = face_c2g(g1) else if (g1 == 0) then itmpvar2D(ikk,nitemglob) = 0 end if end do end if end if end do else if (var_names(iv) .eq. 'FlowLink') then nedgecount = sum(lnx(1:ii-1)) nfacecount = sum(nump(1:ii-1)) do ip=1,item_counts(ii) if (item_domain(nedgecount+ip) == ii-1) then iedgeglob = edge_c2g(nedgecount+ip) if (iedgeglob > 0) then nitemglob = nitemglob + 1 itmpvar2D_tmp(:,nitemglob) = itmpvar2D(:,nitemglob0+ip) do ikk=1,2 g1 = itmpvar2D_tmp(ikk, nitemglob) if (g1 > 0 .and. g1 <= nump(ii)) then g1 = g1 +nfacecount itmpvar2D_tmp(ikk,nitemglob) = face_c2g(g1) else if (g1 > nump(ii)) then itmpvar2D_tmp(ikk,nitemglob) = intmiss end if end do end if end if end do if (ii==nfiles) then itmpvar2D(:,1:nitemglob) = itmpvar2D_tmp(:,1:nitemglob) end if !else if (var_loctype(iv) == UNC_LOC_S) then ! variables that locate on faces, temporaly disabled ! if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ! nfacecount = sum(nump(1:ii-1)) ! do ip=1, item_counts(ii) ! if (item_domain(nfacecount+ip) == ii-1) then ! ifaceglob = face_c2g(nfacecount+ip) ! if (ifaceglob > 0) then ! nitemglob = nitemglob + 1 ! itmpvar1D_tmp(ifaceglob) = itmpvar1D(nitemglob0+ip) ! end if ! end if ! end do ! if (ii==nfiles) then ! itmpvar1D(1:nitemglob) = itmpvar1D_tmp(1:nitemglob) ! end if ! else if (var_types(iv) == nf90_byte) then ! nfacecount = sum(nump(1:ii-1)) ! do ip=1, item_counts(ii) ! if (item_domain(nfacecount+ip) == ii-1) then ! ifaceglob = face_c2g(nfacecount+ip) ! if (ifaceglob > 0) then ! nitemglob = nitemglob + 1 ! btmpvar1D_tmp(ifaceglob,itm:itm) = btmpvar1D(nitemglob0+ip,itm:itm) ! end if ! end if ! end do ! if (ii==nfiles) then ! btmpvar1D(1:nitemglob,itm:itm) = btmpvar1D_tmp(1:nitemglob,itm:itm) ! end if ! else ! nfacecount = sum(nump(1:ii-1)) ! if (tmpvarDim == 1) then ! do ip=1, item_counts(ii) ! if (item_domain(nfacecount+ip) == ii-1) then ! ifaceglob = face_c2g(nfacecount+ip) ! if (ifaceglob > 0) then ! nitemglob = nitemglob + 1 ! tmpvar1D_tmp(ifaceglob) = tmpvar1D(nitemglob0+ip) ! end if ! end if ! end do ! if (ii==nfiles) then ! tmpvar1D(1:nitemglob) = tmpvar1D_tmp(1:nitemglob) ! end if ! else if (tmpvarDim == 2) then ! do ip=1, item_counts(ii) ! if (item_domain(nfacecount+ip) == ii-1) then ! ifaceglob = face_c2g(nfacecount+ip) ! if (ifaceglob > 0) then ! nitemglob = nitemglob + 1 ! tmpvar2D_tmp(:,ifaceglob)=tmpvar2D(:,nitemglob0+ip) ! end if ! end if ! end do ! if (ii==nfiles) then ! tmpvar2D(:,1:nitemglob) = tmpvar2D_tmp(:,1:nitemglob) ! end if ! end if ! end if else needshift = .false. ! The get_var above started at the right place, so no shifting needed yet. if (var_types(iv) == nf90_double) then ! TODO: AvD: try to remove this ugly code-copy for just different types do ip=1,item_counts(ii) if (item_domain(nitemcount+ip) == ii-1) then nitemglob = nitemglob+1 if (needshift) then tmpvarptr(:,:,nitemglob) = tmpvarptr(:,:,nitemglob0+ip) end if else needshift = .true. ! From now on, all points from this var/file need one or more shifts to the left. end if end do else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then do ip=1,item_counts(ii) if (item_domain(nitemcount+ip) == ii-1) then nitemglob = nitemglob+1 if (needshift) then itmpvarptr(:,:,nitemglob) = itmpvarptr(:,:,nitemglob0+ip) end if else needshift = .true. ! From now on, all points from this var/file need one or more shifts to the left. end if end do else if (var_types(iv) == nf90_byte) then do ip=1,item_counts(ii) if (item_domain(nitemcount+ip) == ii-1) then nitemglob = nitemglob+1 if (needshift) then btmpvarptr(:,:,nitemglob,itm:itm) = btmpvarptr(:,:,nitemglob0+ip,itm:itm) end if else needshift = .true. ! From now on, all points from this var/file need one or more shifts to the left. end if end do end if nitemcount = nitemcount + item_counts(ii) end if end do ! ii if (item_counts(noutfile) /= nitemglob) then write (*,'(a,i0,a,i0,a)') 'Error: mapmerge: accumulated ', nitemglob, ' items, but expected ', item_counts(noutfile), ', for `'//var_names(iv)//'''.' if (.not. verbose_mode) goto 888 end if !! tmpvar is now filled with 1 var, 1 time, across all domains, without overlap, so write it now: if (var_kxdimpos(iv) == -1 .and. var_laydimpos(iv) == -1 .and. var_wdimpos(iv) == -1 .and. var_seddimpos(iv) == -1) then ! 1D array with no layers and no vectormax (possibly time-dep) if (var_types(iv) == nf90_double) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar1D, count = count_write(is:ie), start = start_idx(is:ie)) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), itmpvar1D, count = count_write(is:ie), start = start_idx(is:ie)) else if (var_types(iv) == nf90_byte) then if (itm == mapclass_time_buffer_size) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), btmpvar1D, count = [count_write(is), mapclass_time_buffer_size*count_write(ie)], start = [start_idx(is), start_idx(ie) + 1 - mapclass_time_buffer_size]) endif end if else if (var_kxdimpos(iv) /= -1 .neqv. var_laydimpos(iv) /= -1) then ! Either a vectormax OR a laydim if (var_types(iv) == nf90_double) then if (var_seddimpos(iv) /= -1) then ! if it is a sediment variable ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar2D, count = count_write(is:ie), start = start_idx(is:ie), map = (/ count_write (var_kxdimpos(iv)), 1, count_write (var_kxdimpos(iv))*item_counts(ii) /)) else ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar2D, count = count_write(is:ie), start = start_idx(is:ie)) end if else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), itmpvar2D, count = count_write(is:ie), start = start_idx(is:ie)) end if else if (var_kxdimpos(iv) /= -1 .neqv. var_wdimpos(iv) /= -1) then ! Either a vectormax OR a laydim if (var_types(iv) == nf90_double) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar2D, count = count_write(is:ie), start = start_idx(is:ie)) else if (var_types(iv) == nf90_int .or. var_types(iv) == nf90_short) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), itmpvar2D, count = count_write(is:ie), start = start_idx(is:ie)) end if else ! Both a vectormax AND a laydim if (var_types(iv) == nf90_double) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar3D, count = count_write(is:ie), start = start_idx(is:ie)) end if end if !if (kmx(noutfile) > 0) then ! ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar, count = (/ kmx(noutfile), ndx(noutfile), 1 /), start = (/ 1, 1, it /)) !else ! ierr = nf90_put_var(ncids(noutfile), varids_out(iv), tmpvar, count = (/ ndx(noutfile), 1 /), start = (/ 1, it /)) !end if !if (ierr /= nf90_noerr) then ! write (*,'(a,i0,a)') 'Error: mapmerge: could not write merged variable `'//trim(var_names(iv))//''' into output file `'//trim(outfile)//''' (itime=', it, ').' ! if (.not. verbose_mode) goto 888 !end if ! Check: if this was time-independent variable, first iteration-step was enough for reading+writing. if (var_timdimpos(iv) == -1) then exit ! it end if end do ! it end if ! if writing with time buffer, write remaining time steps: if (var_types(iv) == nf90_byte) then if (itm /= mapclass_time_buffer_size) then ierr = nf90_put_var(ncids(noutfile), varids_out(iv), btmpvar1D(:,1:itm), count = [count_write(is), itm*count_write(ie)], start = [start_idx(is), start_idx(ie) + 1 - itm]) endif endif if (verbose_mode) then call progress(tmpstr1, 100) ! generate the progress bar. end if end do ! iv ! 6. Write some useful meta info on all merged domains into the output file nfacecount = 0 nedgecount = 0 nnodecount = 0 do ii=1,nfiles ierr = nf90_put_var(ncids(noutfile), id_part_face_start, nfacecount+1, start=(/ ii /)) ierr = nf90_put_var(ncids(noutfile), id_part_edge_start, nedgecount+1, start=(/ ii /)) ierr = nf90_put_var(ncids(noutfile), id_part_node_start, nnodecount+1, start=(/ ii /)) nfacecount = nfacecount + ndxg (ii) nedgecount = nedgecount + lnxg (ii) nnodecount = nnodecount + numkg(ii) end do ierr = nf90_put_var(ncids(noutfile), id_part_face_count, ndxg (1:nfiles)) ierr = nf90_put_var(ncids(noutfile), id_part_edge_count, lnxg (1:nfiles)) ierr = nf90_put_var(ncids(noutfile), id_part_node_count, numkg(1:nfiles)) if (nbndglob>0) then nbndcount = 0 do ii=1,nfiles ierr = nf90_put_var(ncids(noutfile), id_part_facebnd_start, nbndcount+1, start=(/ ii /)) nbndcount = nbndcount + ndxbnd(ii) enddo ierr = nf90_put_var(ncids(noutfile), id_part_facebnd_count, ndxbnd(1:nfiles)) endif ! Success: ierr = 0 !! Cleanup: 888 continue do ii=1,nfiles ierr = nf90_close(ncids(ii)) end do ierr = nf90_close(ncids(noutfile)) if (allocated(varids)) deallocate(varids) if (allocated(varids_out)) deallocate(varids_out) if (allocated(var_names)) deallocate(var_names) if (allocated(var_types)) deallocate(var_types) if (allocated(var_dimids)) deallocate(var_dimids) if (allocated(var_timdimpos)) deallocate(var_timdimpos) if (allocated(var_spacedimpos)) deallocate(var_spacedimpos) if (allocated(var_laydimpos)) deallocate(var_laydimpos) if (allocated(var_kxdimpos)) deallocate(var_kxdimpos) if (allocated(var_ndims)) deallocate(var_ndims) if (allocated(var_loctype)) deallocate(var_loctype) if (allocated(itmpvar1D)) deallocate(itmpvar1D) if (allocated(itmpvar2D)) deallocate(itmpvar2D) if (allocated(itmpvar2D_tmp)) deallocate(itmpvar2D_tmp) if (allocated(itmpvar2D_tmpmax))deallocate(itmpvar2D_tmpmax) if (allocated(tmpvar1D)) deallocate(tmpvar1D) if (allocated(tmpvar1D_tmp)) deallocate(tmpvar1D_tmp) if (allocated(tmpvar2D)) deallocate(tmpvar2D) if (allocated(tmpvar2D_tmp)) deallocate(tmpvar2D_tmp) if (allocated(tmpvar2D_tmpmax)) deallocate(tmpvar2D_tmpmax) if (allocated(tmpvar3D)) deallocate(tmpvar3D) if (allocated(btmpvar1D)) deallocate(btmpvar1D) if (allocated(btmpvar1D_tmp)) deallocate(btmpvar1D_tmp) end function dfm_merge_mapfiles !> Orders a filename list by increasing partition number. !! Sorting is done in place. subroutine dfm_order_by_partition(files, nfiles) use m_alloc implicit none character(len=MAXNAMELEN), intent(inout) :: files(:) !< List files names, will be replaced by the sorted list. integer, intent(in) :: nfiles !< Number of input files. integer, allocatable :: idom(:) character(len=MAXNAMELEN), allocatable :: filesorig(:) integer :: ii, ierr, nlen allocate(idom(nfiles)) allocate(filesorig(nfiles)) do ii=1,nfiles filesorig(ii) = files(ii) nlen = len_trim(files(ii)) ! modelname_xxxx_map.nc' ! 0 76 3 0 if (files(ii)(max(1,nlen-6):nlen) == '_map.nc') then read (files(ii)(max(1,nlen-10):nlen-7), '(i4.4)', iostat=ierr) idom(ii) else ierr = 1 end if if (ierr /= 0) then idom(ii) = ii-1 end if end do do ii=1,nfiles if (idom(ii)+1 > nfiles) then write (*,'(a,i0,a,i0,a)') 'Error: mapmerge: found domain number ', idom(ii), ', which exceeds the number of files: ', nfiles, '.' exit ! We can't really recover from this. end if files(idom(ii)+1) = filesorig(ii) end do deallocate(idom, filesorig) end subroutine dfm_order_by_partition subroutine progress(prefix, j) implicit none character(len=*),intent(in) :: prefix integer(kind=4),intent(in) :: j integer(kind=4) :: k character(len=27) ::bar bar = "???% | |" write(unit=bar(1:3),fmt="(i3)") j do k=1, ceiling(j/5.0) bar(6+k:6+k)="*" enddo ! print the progress bar. if (j==100) then write(unit=6,fmt="(a1,a16,a27)") char(13), prefix, bar else write(unit=6,fmt="(a1,a16,a27)", advance='no') char(13), prefix, bar end if flush(6) return end subroutine progress end module dfm_merge