!> @file w3smcomd.F90 !> @brief SMC grid interpolation and regridding functionality !> !> @author Chris Bunney !> @date 21-Jul-2021 !/ !> @brief Service module for support of SMC regridding and interpolation !> !> @details !> For SMC grids, four types of output are possible: !> 1. Flat grid (seapoint) output of SMC cells with associated !> cell size variables (cx and cy). Requires extra effort to !> plot as grid is not regular. !> !> 2. Regular, uniformly gridded output to a specified output grid. !> This is achieved by area averaging of the SMC cells. The output !> grid will be aligned with the SMC grid cell edges which may !> result in the actual output grid being slightly different to !> the original user request. A land/sea mask is created by !> keeping track of the cell coverage and setting cells with !> total coverage of <50% UNDEF. !> !> 3. [Experimental] Arbitrary regualar grid re-gridding !> using indices and weights generated from WW3_SMCINT*. !> Uses the local gradient within the grid cell and distance !> between cell centres to interpolate. !> !> 4. [Experimental] As type 3, but with no interpolation - !> value from surrounding SMC cell only. !> !> * The ww3_smcint program is experimental and not yet included !> in the official WW3 distribution; it is currently part of the UK !> Met Office's suite of internal tools. !> !> @remark Note - directional fields are expected to be passed to !> routines with units of radians (cartesian convention). !> They will be OUTPUT in units of degrees (nautical convention). !> !> @author Chris Bunney !> @date 21-Jul-2021 !> !> ### Change log !> Date | Ver | Comments !> ------------|------|--------- !> 18-Jan-2016 | 4.18 | Initial version !> 28-Sep-2016 | 4.18 | Bug fix EXO/EYO calcs for whole domain output !> 05-Oct-2016 | 4.18 | Bug fix regular grid indicies for type 2 output !> 29-Sep-2017 | 4.18 | Revise calculation of indicies to ensure selected cells fall inside user selected areas. !> 18-Apr-2018 | 4.18 | Added Type 3 and 4 SMC output !> 20-Jun-2018 | 4.18 | Directional fields output as nautical convention (deg) !> 27-Jun-2018 | 6.05 | Ported to V6 !> 06-Jan-2021 | 7.12 | Use ARCTC option for SMC grid. !> 20-Jul-2021 | 7.12 | Fix bug where edge cells in design grid may not be matched due where SMC cell > base grid size. !> 21-Jul-2021 | 7.12 | Elevated some grid variables to DOUBLE PRECISION, fixed EXO/EYO bug !> MODULE W3SMCOMD !/ !/ +-----------------------------------+ !/ | WAVEWATCH III NOAA/NCEP | !/ | Chris Bunney, UKMO | !/ | FORTRAN 90 | !/ | Last update : 21-Jul-2021 | !/ +-----------------------------------+ !/ !/ Copyright 2009-2012 National Weather Service (NWS), !/ National Oceanic and Atmospheric Administration. All rights !/ reserved. WAVEWATCH III is a trademark of the NWS. !/ No unauthorized use without permission. !/ USE W3GDATMD USE CONSTANTS USE W3ODATMD, ONLY: UNDEF PUBLIC ! Output grid definition DOUBLE PRECISION :: SXO !< Output grid longitude origin DOUBLE PRECISION :: SYO !< Output grid latitude origin DOUBLE PRECISION :: EXO !< Output grid final longitude DOUBLE PRECISION :: EYO !< Output grid final latitude DOUBLE PRECISION :: DXO !< Output grid cell longitude size DOUBLE PRECISION :: DYO !< Output grid cell latitude size INTEGER :: NXO !< Output grid number of longitude cells INTEGER :: NYO !< Output grid number of latitude cells ! Variables for SMC regridding (type 2 output): !> Type of SMC output: 1=seapoint grid of SMC cells; 2=regridding to regular grid; !> 3=interpolation to arbtrary grid; 4=nearest neighbour interpolation to !> arbitrary grid. INTEGER :: SMCOTYPE !> Output grid cell scaling factor; should be an integer power of 2. INTEGER :: CELFAC INTEGER, ALLOCATABLE :: XIDX(:) !< X-indices of SMC cells in regular grid INTEGER, ALLOCATABLE :: YIDX(:) !< Y-Indices of SMC cells in regular grid INTEGER, ALLOCATABLE :: XSPAN(:) !< Number of longitude cells SMC cell spans INTEGER, ALLOCATABLE :: YSPAN(:) !< Number of longitude cells SMC cell spans REAL, ALLOCATABLE :: WTS(:) !< Regridding weights REAL, ALLOCATABLE :: COV(:,:) !< Wet fraction (coverage) of cell INTEGER, ALLOCATABLE :: MAPSMC(:,:) !< Regridded MAPSTA LOGICAL, ALLOCATABLE :: SMCMASK(:) !< Mask for type 1 output (flat array) INTEGER, ALLOCATABLE :: SMCIDX(:) !< Indices of SMC cells within output grid domain !> SMC grid definition INTEGER, ALLOCATABLE :: SMCCX(:) !< Longitude cell size factors INTEGER, ALLOCATABLE :: SMCCY(:) !< Latitude cell size factors REAL :: DLAT !< Base longitude cell size REAL :: DLON !< Base latitude cell size INTEGER :: CFAC !< SMC scaling factor (number of levels) REAL :: NOVAL !< Fill value for seapoints with no value ! Variables for SMC nearest neighbour interpolation (type 3/4 output) INTEGER, ALLOCATABLE :: NNIDX(:,:) !< Nearest neighbour SMC point to regular grid REAL, ALLOCATABLE :: XDIST(:,:) !< Lng. distance to nearest neighbour REAL, ALLOCATABLE :: YDIST(:,:) !< Lat. distance to nearest neighbour INTEGER :: NDSMC !< ww3_smcint file unit number ! Counters: INTEGER :: SMCNOUT !< Number of SMC output cells INTEGER :: NSMC !< Number of SMC cells used in regridding CONTAINS !-------------------------------------------------------------------------- !> @brief Generate SMC interpolation/output information !> !> @details !> This subroutine generates index or mask values for extraction !> of SMC data to either a flat grid or regular lat/lon grid, !> depending on the type of SMC output grid selected: !> !> Type 1: Generates a mask for extracting only points from !> the user requested region. !> !> Type 2: Calculate interpolation indices and weights for !> regridding the irregular SMC grid onto a regular, !> uniformly spaced lat/lon grid. !> !> @author Chris Bunney !> @date 22-Oct-2015 !> SUBROUTINE SMC_INTERP() IMPLICIT NONE ! Locals REAL :: CX0, CY0 ! SW corner of origin of grid REAL :: S0CHK, XSNAP, YSNAP INTEGER :: ISEA, mx, my, ixx, iyy, J REAL :: lat, lon J = 1 NSMC = 0 ! Determine smallest cell size factor: cfac = 2**(NRLv - 1) ! Get smallest SMC grid cells step size: dlat = SY / cfac dlon = SX / cfac ! SW Corner of grid origin cell: CX0 = X0 - SX / 2. CY0 = Y0 - SY / 2. ! Grid cell size to snap design grid to. Will be regular grid ! resolution for cellsize <= cfac, or cellsize for cellsize > cfac XSNAP = SX YSNAP = SY IF(CELFAC .gt. CFAC) XSNAP = CELFAC * dlon IF(CELFAC .gt. CFAC) YSNAP = CELFAC * dlat ! Get start lat,lon (must be aligned with SMC grid edges). Use ! regular grid origins if SXO or SYO is -999.9 (use full grid): IF(ABS(SXO + 999.9) .LT. 1E-4) THEN SXO = CX0 ELSE S0CHK = CX0 + FLOOR((SXO - CX0) / XSNAP) * XSNAP ! Ensure first grid value falls within specified range IF (S0CHK .LT. SXO) THEN SXO = S0CHK + XSNAP ELSE SXO = S0CHK ENDIF ENDIF IF(ABS(SYO + 999.9) .LT. 1E-4) THEN SYO = CY0 ELSE S0CHK = CY0 + FLOOR((SYO - CY0) / YSNAP) * YSNAP ! Ensure first grid value falls within specified range IF (S0CHK .LT. SYO) THEN SYO = S0CHK + YSNAP ELSE SYO = S0CHK ENDIF ENDIF ! Use regular grid extents for last lat/lon if user ! specifies -999.9 for EXO/EYO (use full grid): IF(ABS(EXO + 999.9) .LT. 1E-4) THEN EXO = CX0 + SX * NX ! TRHC of last cell ENDIF IF(ABS(EYO + 999.9) .LT. 1E-4) THEN EYO = CY0 + SY * NY ! TRHC of last cell ENDIF ! Ouput grid cell dx/dy will be integer factor of smallest ! SMC grid cell size: DXO = dlon * celfac DYO = dlat * celfac ! Determine number of cells in grid: NXO = NINT((EXO - SXO) / DXO) NYO = NINT((EYO - SYO) / DYO) IF(SMCOTYPE .EQ. 2) THEN ! Initialise all indices to "missing": XIDX(:) = -1 YIDX(:) = -1 ENDIF ! Loop over cell array and calculate regidding factors: DO ISEA=1, NSEA ! ! For grids with Arctic region: make sure we don't double count ! ! the overlapping boundary cells. Also, don't process the arctic ! ! cell (which is always the last cell). ! ! Note: NARC contains ALL the boundary cells (global + arctic). ! ! whereas NGLO contains only the global boundary cells. ! IF(ISEA .GT. NGLO-NBAC .AND. ISEA .LT. NSEA-NARC+1) CYCLE IF( ARCTC .AND. & ISEA .GT. NGLO-NBAC .AND. ISEA .LT. NSEA-NARC+1) CYCLE ! Get grid cell size: mx = IJKCel(3,ISEA) my = IJKCel(4,ISEA) ! Determine cell lat/lon (bottom left corner of cell) lon = CX0 + IJKCel(1,ISEA) * dlon lat = CY0 + IJKCel(2,ISEA) * dlat ! For output type 1 (seapoint array), just check whether ! cell centre is within specified domain range, and update ! output mask accordingly: IF( SMCOTYPE .EQ. 1 ) THEN ! Ensure longitude ranges are aligned lon = lon + 0.5 * mx * dlon lat = lat + 0.5 * my * dlat IF(lon .LT. SXO) lon = lon + 360.0 IF(lon .GT. EXO) lon = lon - 360.0 ! Now check if it is within range of requested domain: IF(lon .GE. SXO .AND. lon .LE. EXO .AND. & lat .GE. SYO .AND. lat .LE. EYO ) THEN SMCMASK(ISEA) = .TRUE. SMCIDX(J) = ISEA J = J + 1 ENDIF CYCLE ENDIF ! SMCOTYPE == 1 ! For output type 2 (area averaged regular grid), determine ! SMC grid cell location and coverage in output grid: ! Align lons IF(lon .LT. SXO) THEN lon = lon + 360. ENDIF IF(lon .GT. EXO) THEN lon = lon - 360. ENDIF ! Find first SW cell in design grid: ! We add on 1/2 of the smallest SMC cell dlon/dlat values to ensure ! source grid cell ends up in the correct target grid cell (after ! integer trunction): ixx = FLOOR((lon + 0.5*dlon - SXO) / DXO) + 1 iyy = FLOOR((lat + 0.5*dlat - SYO) / DYO) + 1 ! If we fall outside the left/bottom edge of the design grid, ! check for cases where the SMC cell has a lon or lat ! scaling factor > cfac (design grid is assumed to align ! its origin with cells of size cfac). For such cells, ! keep moving the left/bottom edge up by cfac until ! the SW corner (possibly) matches a design grid cell. IF(ixx .LE. 0 .AND. ixx + mx / celfac .GT. 0) THEN DO WHILE(mx .GT. cfac) mx = mx - cfac lon = lon + dlon * cfac ixx = FLOOR((lon + 0.5*dlon - SXO) / DXO) + 1 IF(ixx .GT. 0) EXIT ! Found cell lon-edge in design grid ENDDO ENDIF IF(iyy .LE. 0 .AND. iyy + my / celfac .GT. 0) THEN DO WHILE(my .GT. cfac) my = my - cfac lat = lat + dlat * cfac iyy = FLOOR((lat + 0.5*dlat - SYO) / DYO) + 1 IF(iyy .GT. 0) EXIT ! Found cell lat-edge in design grid ENDDO ENDIF ! If SMC cell definitely out of design grid domain, then cycle. IF(ixx .LE. 0 .OR. ixx .GT. NXO .OR. & iyy .LE. 0 .OR. iyy .GT. NYO) THEN xidx(ISEA) = -1 yidx(ISEA) = -1 CYCLE ENDIF XIDX(ISEA) = ixx YIDX(ISEA) = iyy NSMC = NSMC + 1 SMCIDX(NSMC) = ISEA ! find out how many cells it covers in the x/y directions: XSPAN(ISEA) = MAX(1, INT(mx / CELFAC)) YSPAN(ISEA) = MAX(1, INT(my / CELFAC)) ! Do a bit of error checking (non fatal - just produced warning): IF(XSPAN(ISEA) .GT. 1) THEN IF(ABS((sxo+(ixx-1)*dxo) - lon) .GT. dxo/100.0) THEN PRINT*, 'Potential problem with SMC grid cell span:' PRINT*, xspan(ISEA), FLOAT(mx) / celfac PRINT*, lon,lat PRINT*, sxo+(ixx-1)*dxo,syo+iyy*dyo,dxo,dyo PRINT*, "diff:", (sxo+(ixx-1)*dxo) - lon ENDIF ENDIF ! calc cell weight in relation to output grid: WTS(ISEA) = MIN(1., DBLE(MIN(CELFAC, mx) * MIN(CELFAC, my)) / & (CELFAC**2)) ENDDO ! Reset SXO and SYO to be the cell-centre (currently cell SW edge): SXO = SXO + 0.5 * DXO SYO = SYO + 0.5 * DYO END SUBROUTINE SMC_INTERP !-------------------------------------------------------------------------- !> @brief Regrid SMC data onto a regular grid !> !> @details Regrids scalar data from the SMC grid onto a regular grid. !> Uses pre-calculated grid indices and weights generated from the !> smc_interp() subroutine. !> !> @remark If source field is directional data, use the w3s2xy_smcrg_dir() !> subroutine instead. !> !> @param[in] S Source field, on SMC grid. !> @param[out] XY Storage for regridded field; must be 2D array with !> dimensions of (NXO,NYO). !> !> @author Chris Bunney !> @date 02-Jul-2013 !> SUBROUTINE W3S2XY_SMCRG(S, XY) IMPLICIT NONE ! Input parameters: REAL, INTENT(IN) :: S(:) REAL, INTENT(OUT) :: XY(NXO,NYO) ! Local parameters INTEGER :: I, J, IX, IY, ISEA, ISMC ! Initialise coverage and output arrays: COV(:,:) = 0.0 XY(:,:) = 0.0 DO ISMC=1,NSMC ISEA = SMCIDX(ISMC) IF(S(ISEA) .EQ. UNDEF) CYCLE ! MDI ! Loop over number of spanned cells: DO I=0, XSPAN(ISEA) - 1 DO J=0, YSPAN(ISEA) - 1 IX = XIDX(ISEA) + I IY = YIDX(ISEA) + J ! Spans outside of grid? IF(IX .GT. NXO .OR. IY .GT. NYO) CYCLE ! Interpolate: XY(IX, IY) = XY(IX, IY) + S(ISEA) * WTS(ISEA) ! Keep track of how much of cell is (wet) covered: COV(IX, IY) = COV(IX, IY) + WTS(ISEA) ENDDO ENDDO ENDDO ! Create coastline by masking out areas with < 50% coverage: DO IX=1,NXO DO IY=1,NYO IF(MAPSMC(IX,IY) .EQ. 0) THEN ! Make land point XY(IX,IY) = UNDEF ELSE IF(COV(IX,IY) .LT. 0.5) THEN ! More than half of cell has UNDEF values - set to NOVAL: XY(IX,IY) = NOVAL ELSE IF(COV(IX,IY) .LT. 1.0) THEN ! If coverage < 1.0, scale values back to full cell coverage. ! Without this step, points around coast could end up with lower ! waveheights due to weights not summing to 1.0: XY(IX,IY) = XY(IX,IY) * ( 1.0 / COV(IX,IY) ) ENDIF ENDDO ENDDO RETURN END SUBROUTINE W3S2XY_SMCRG !-------------------------------------------------------------------------- !> @brief Regrid directional SMC data onto a regular grid !> !> @details Regrids directioanl scalar data from the SMC grid onto !> a regular grid. Uses pre-calculated grid indices and weights !> generated from the smc_interp() subroutine. !> !> @remark Functionality as per w3s2xy_smc(), but decomposes the field !> into u/v components first to ensure proper area averaging of !> directional data (handles cyclic transition between 359 -> 0 degrees). !> !> @param[in] S Directional source field, on SMC grid. !> @param[out] XY Storage for regridded field; must be 2D array with !> dimensions of (NXO,NYO). !> !> @author Chris Bunney !> @date 02-Jul-2013 !> SUBROUTINE W3S2XY_SMCRG_DIR(S, XY) IMPLICIT NONE ! Input parameters: REAL, INTENT(IN) :: S(:) REAL, INTENT(OUT) :: XY(NXO,NYO) ! Local parameters INTEGER :: I, J, IX, IY, ISEA, ISMC REAL, ALLOCATABLE :: AUX1(:,:), AUX2(:,:) REAL :: COSS, SINS ! Initialise coverage and output arrays: ALLOCATE(AUX1(NXO,NYO),AUX2(NXO,NYO)) COV(:,:) = 0.0 XY(:,:) = 0.0 AUX1(:,:) = 0.0 AUX2(:,:) = 0.0 DO ISMC=1,NSMC ISEA = SMCIDX(ISMC) IF(S(ISEA) .EQ. UNDEF) CYCLE ! MDI COSS = COS(S(ISEA)) SINS = SIN(S(ISEA)) ! Loop over number of spanned cells: DO I=0, XSPAN(ISEA) - 1 DO J=0, YSPAN(ISEA) - 1 IX = XIDX(ISEA) + I IY = YIDX(ISEA) + J ! Spans outside of grid? IF(IX .GT. NXO .OR. IY .GT. NYO) CYCLE ! Interpolate: !XY(IX, IY) = XY(IX, IY) + S(ISEA) * WTS(ISEA) AUX1(IX, IY) = AUX1(IX, IY) + COSS * WTS(ISEA) AUX2(IX, IY) = AUX2(IX, IY) + SINS * WTS(ISEA) ! Keep track of how much of cell is (wet) covered: COV(IX, IY) = COV(IX, IY) + WTS(ISEA) ENDDO ENDDO ENDDO ! Create coastline by masking out areas with < 50% coverage: DO IX=1,NXO DO IY=1,NYO IF(MAPSMC(IX,IY) .EQ. 0) THEN ! Make land point XY(IX,IY) = UNDEF ELSE IF(COV(IX,IY) .LT. 0.5) THEN ! More than half of cell has UNDEF values - set to NOVAL XY(IX,IY) = NOVAL ELSE IF(COV(IX,IY) .LT. 1.0) THEN ! If coverage < 1.0, scale values back to full cell coverage. ! Without this step, points around coast could end up with lower ! waveheights due to weights not summing to 1.0: XY(IX,IY) = ATAN2(AUX2(IX,IY), AUX1(IX,IY)) XY(IX,IY) = MOD(630. - RADE * XY(IX,IY), 360. ) ELSE XY(IX,IY) = ATAN2(AUX2(IX,IY), AUX1(IX,IY)) XY(IX,IY) = MOD(630. - RADE * XY(IX,IY), 360. ) ENDIF ENDDO ENDDO RETURN END SUBROUTINE W3S2XY_SMCRG_DIR !-------------------------------------------------------------------------- !> @brief Calculates a new MAPSTA using SMC grid cell averaging. !> !> @author Chris Bunney !> @date 02-Jul-2013 !> SUBROUTINE MAPSTA_SMC() IMPLICIT NONE ! Local parameters INTEGER :: I, J, IX, IY, IMX, IMY, ISEA ! Initialise coverage and output arrays: COV(:,:) = 0.0 MAPSMC(:,:) = 0 DO ISEA=1,NSEA IMX = MAPSF(ISEA,1) IMY = MAPSF(ISEA,2) IF(XIDX(ISEA) .EQ. -1) CYCLE ! Out of grid ! Loop over number of spanned cells: DO I=0, XSPAN(ISEA) - 1 DO J=0, YSPAN(ISEA) - 1 IX = XIDX(ISEA) + I IY = YIDX(ISEA) + J ! Spans outside of grid? IF(IX .GT. NXO .OR. IY .GT. NYO) CYCLE ! MAPSTA values: 0=Excluded, (+-)1=Sea, (+-2)=Input boundary ! We will just keep track of sea and non-sea points: IF(MAPSTA(IMY, IMX) .NE. 0) THEN ! Keep track of how much of cell is (wet) covered: COV(IX, IY) = COV(IX, IY) + WTS(ISEA) ENDIF ENDDO ENDDO ENDDO ! Create coastline by masking out areas with < 50% coverage: DO IX=1,NXO DO IY=1,NYO IF(COV(IX,IY) .LT. 0.5) THEN MAPSMC(IX, IY) = 0 ELSE MAPSMC(IX, IY) = 1 ENDIF ENDDO ENDDO RETURN END SUBROUTINE MAPSTA_SMC !-------------------------------------------------------------------------- !> @brief Read interpolation information from smcint.ww3 !> !> @details Reads the interpolation indices and distance weights from the !> smcint.ww3 file generated by ww3_smcint program. !> !> @author Chris Bunney !> @date 18-Apr-2018 !> SUBROUTINE READ_SMCINT() USE W3SERVMD, ONLY: EXTCDE IMPLICIT NONE ! Locals INTEGER :: IERR, I, J REAL :: PLATO, PLONO ! Not used yet....future version might allow ! output to a rotated pole grid... NDSMC = 50 OPEN(NDSMC, file='smcint.ww3', status='old', form='unformatted', convert=file_endian, iostat=ierr) IF(ierr .NE. 0) THEN WRITE(*,*) "ERROR! Failed to open smcint.ww3 for reading" CALL EXTCDE(1) ENDIF ! Header READ(NDSMC) NXO, NYO, SXO, SYO, DXO, DYO, PLONO, PLATO ALLOCATE(NNIDX(NXO,NYO), XDIST(NXO,NYO), YDIST(NXO,NYO)) ! Indices and weights: READ(NDSMC)((NNIDX(I,J), XDIST(I,J), YDIST(I,J),I=1,NXO),J=1,NYO) CLOSE(NDSMC) END SUBROUTINE READ_SMCINT !-------------------------------------------------------------------------- !> @brief Calculates weights for SMC to arbitrary grid intepolation. !> !> @details !> Calculates the interpolation indices and weights for regridding !> an SMC grid to an arbitrary regular grid. Calculated index is that of !> the SMC cell that contains output cell centre. Weights are the distance !> in metres between the output and SMC cell centres. !> !> A future version may allow for output grids to be on a !> rotated pole. !> !> @author Chris Bunney !> @date 18-Apr-2018 !> SUBROUTINE CALC_INTERP() USE W3GDATMD, ONLY: CLATS USE CONSTANTS, ONLY : DERA, RADIUS #ifdef W3_RTD USE W3SERVMD, ONLY: W3LLTOEQ USE W3GDATMD, ONLY: POLON, POLAT #endif IMPLICIT NONE INTEGER :: IERR, I, J, ISEA, N, CFAC REAL :: mlon(NSEA), mlat(NSEA), olon(nxo,nyo), olat(nxo,nyo), & ang(nxo,nyo), lon, lat #ifdef W3_RTD REAL :: tmplon(nxo,nyo), tmplat(nxo,nyo) #endif ! Determine smallest cell size factor: cfac = 2**(NRLv - 1) ! Get smallest SMC grid cells step size: dlat = SY / cfac dlon = SX / cfac ALLOCATE(xdist(nx,ny), ydist(ny,nx)) ! Model lat/lons: DO ISEA = 1,NSEA mlon(isea) = (X0-0.5*SX) + (IJKCel(1,ISEA) + 0.5 * IJKCel(3,ISEA)) * dlon mlat(isea) = (Y0-0.5*SY) + (IJKCel(2,ISEA) + 0.5 * IJKCel(4,ISEA)) * dlat ENDDO ! Generate output grid cell centres: DO I=1,NXO DO J=1,NYO olon(i,J) = SXO + (I-1) * DXO olat(i,J) = SYO + (J-1) * DYO ENDDO ENDDO #ifdef W3_RTD tmplat = olat tmplon = olon PRINT*,'Rotating coordinates' CALL W3LLTOEQ ( tmplat, tmplon, olat, olon, & ang, POLAT, POLON, NXO*NYO ) PRINT*,'Rotating coordinates complete' #endif ! Cycle over output grid points and find containing SMC cell: ! NOTE : BRUTE FORCE! NNIDX(:,:) = -1 DO I=1,NXO PRINT*,I,' of ',NXO DO J=1,NYO lon = olon(i,j) lat = olat(i,j) IF(lon .LT. X0 - SX / 2) lon = lon + 360.0 IF(lon .GT. (X0 + (NX-1) * SX) + 0.5 * SX) lon = lon - 360.0 DO ISEA=1,NSEA IF(mlon(ISEA) - 0.5 * IJKCel(3,ISEA) * dlon .LE. lon .AND. & mlon(ISEA) + 0.5 * IJKCel(3,ISEA) * dlon .GE. lon .AND. & mlat(ISEA) - 0.5 * IJKCel(4,ISEA) * dlat .LE. lat .AND. & mlat(ISEA) + 0.5 * IJKCel(4,ISEA) * dlat .GE. lat ) THEN ! Match! NNIDX(I,J) = ISEA xdist(I,J) = (lon - mlon(ISEA)) * DERA * RADIUS * CLats(ISEA) ydist(I,J) = (lat - mlat(ISEA)) * DERA * RADIUS EXIT ENDIF ENDDO ENDDO ENDDO END SUBROUTINE CALC_INTERP !-------------------------------------------------------------------------- !> @brief Fill regular grid using nearest SMC point data !> !> @details Directional fields (DIRN=True) will be assumed to be in radians !> and will be converted to degrees in nautical convention. !> !> @param[in] S Input array on SMC grid !> @param[out] XY Output array to store interpolated 2D field !> @param[in] DIRN Set to .TRUE. if S is a directional field !> !> @author Chris Bunney !> @date 18-Apr-2018 !> SUBROUTINE W3S2XY_SMCNN(S, XY, DIRN) IMPLICIT NONE ! Input parameters: REAL, INTENT(IN) :: S(:) ! Inupt array REAL, INTENT(OUT) :: XY(NXO,NYO) ! Output data LOGICAL, INTENT(IN) :: DIRN ! Directional field? ! Local parameters INTEGER :: I, J, IX, IY, ISEA, ISMC DO IX = 1,NXO DO IY = 1,NYO ISEA = NNIDX(IX,IY) ! Nearest neighbour SMC point IF(ISEA .EQ. -1) THEN ! Land XY(IX,IY) = UNDEF ELSE IF(S(ISEA) .EQ. UNDEF) THEN ! Set undefined sea points to NOVAL XY(IX,IY) = NOVAL ELSE XY(IX,IY) = S(ISEA) IF(DIRN) THEN ! Convert direction fields to degrees nautical XY(IX,IY) = MOD(630. - RADE * XY(IX,IY), 360.0) ENDIF ENDIF ENDIF ENDDO ENDDO END SUBROUTINE W3S2XY_SMCNN !-------------------------------------------------------------------------- !> @brief Nearest neighbour interpolation !> !> @details Fill regular grid using nearest SMC point data and interpolate !> output value based on local gradient and distance between grid !> cell centres. !> !> Directional fields (DIRN=True) will be assumed to be in radians !> and will be converted to degrees in nautical convention. !> !> @param[in] S Input array on SMC grid !> @param[out] XY Output array to store interpolated 2D field !> @param[in] DIRN Set to .TRUE. if S is a directional field !> !> @author Chris Bunney !> @date 18-Apr-2018 !> SUBROUTINE W3S2XY_SMCNN_INT(S, XY, DIRN) USE W3PSMCMD, ONLY: SMCGradn IMPLICIT NONE ! Input parameters: REAL, INTENT(IN) :: S(:) ! Input array REAL, INTENT(OUT) :: XY(NXO,NYO) ! Output array LOGICAL, INTENT(IN) :: DIRN ! Directional field? ! Locals INTEGER :: I, J, IX, IY, ISEA, ISMC REAL :: CVQ(-9:NSEA) REAL :: GrdX(NSEA), GrdY(NSEA) ! Calculate local gradients: CVQ(1:NSEA) = S ! Need to copy S into array with bounds starting at -9 CALL SMCGradn(CVQ, GrdX, GrdY, 0) ! Interpolate: DO IX = 1,NXO DO IY = 1,NYO ISEA = NNIDX(IX,IY) ! Nearest neighbour SMC point IF(ISEA .EQ. -1) THEN XY(IX,IY) = UNDEF ELSE ! Interpolate using local gradient and distance from cell centre: XY(IX,IY) = S(ISEA) + grdx(isea) * xdist(ix,iy) + grdy(isea) * ydist(ix,iy) IF(DIRN) THEN ! Convert direction fields to degrees nautical XY(IX,IY) = MOD(630. - RADE * XY(IX,IY), 360.0) ENDIF ENDIF ENDDO ENDDO END SUBROUTINE W3S2XY_SMCNN_INT !-------------------------------------------------------------------------- !-------------------------------------------------------------------------- !> @brief Entry point for SMC version of W3S2XY. !> !> @details Dispatches to regridding subroutine based on SMCOTYPE. !> Optional DIR logical specifies whether field is a directional !> value; in which case it will be decomposed into u/v components !> prior to any interpolation. !> !> @param[in] S Input array on SMC grid !> @param[out] XY Output array to store interpolated 2D field !> @param[in] DIR (Optional) Set to .TRUE. if S is a directional field !> !> @author Chris Bunney !> @date 18-Apr-2018 !> SUBROUTINE W3S2XY_SMC(S, XY, DIR) IMPLICIT NONE REAL, INTENT(IN) :: S(:) REAL, INTENT(OUT) :: XY(NXO,NYO) LOGICAL, OPTIONAL :: DIR LOGICAL :: DIRN INTEGER :: ISEA IF(PRESENT(DIR)) THEN DIRN = DIR ELSE DIRN = .false. ENDIF IF(SMCOTYPE .EQ. 1) THEN ! Flat sea point array XY(:,1) = PACK(S, SMCMASK) IF(DIRN) THEN ! Convert to nautical convention in degrees DO ISEA=1,NXO IF(XY(ISEA,1) .NE. UNDEF) THEN XY(ISEA,1) = MOD(630. - RADE * XY(ISEA,1), 360.) ENDIF ENDDO ENDIF ELSEIF(SMCOTYPE .EQ. 2) THEN ! Regular gridded SMC cells IF(DIRN) THEN CALL W3S2XY_SMCRG_DIR(S, XY) ELSE CALL W3S2XY_SMCRG(S, XY) ENDIF ELSEIF(SMCOTYPE .EQ. 3) THEN ! Regridded to arbitrary regular grid with interpolation CALL W3S2XY_SMCNN_INT(S, XY, DIRN) ELSEIF(SMCOTYPE .EQ. 4) THEN ! Regridded to arbitrary regular grid - no interpolation CALL W3S2XY_SMCNN(S, XY, DIRN) ELSE WRITE(*,*) "Uknonwn SMC type!", SMCOTYPE ! Unknown SMC type! STOP ENDIF END SUBROUTINE W3S2XY_SMC !-------------------------------------------------------------------------- END MODULE W3SMCOMD