← back to index

src/initialization/MOM_grid_initialize.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> Initializes horizontal grid
6module MOM_grid_initialize
7
8use MOM_checksums, only : hchksum, Bchksum, uvchksum, hchksum_pair, Bchksum_pair
9use MOM_domains, only : pass_var, pass_vector, pe_here, root_PE, broadcast
10use MOM_domains, only : AGRID, BGRID_NE, CGRID_NE, To_All, Scalar_Pair
11use MOM_domains, only : To_North, To_South, To_East, To_West
12use MOM_domains, only : MOM_domain_type, clone_MOM_domain, deallocate_MOM_domain
13use MOM_dyn_horgrid, only : dyn_horgrid_type, set_derived_dyn_horgrid
14use MOM_error_handler, only : MOM_error, MOM_mesg, FATAL, is_root_pe
15use MOM_error_handler, only : callTree_enter, callTree_leave
16use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
17use MOM_io, only : MOM_read_data, slasher, file_exists, stdout
18use MOM_io, only : CORNER, NORTH_FACE, EAST_FACE
19use MOM_unit_scaling, only : unit_scale_type
20
21implicit none ; private
22
23public set_grid_metrics, initialize_masks, Adcroft_reciprocal
24
25! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
26! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
27! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
28! vary with the Boussinesq approximation, the Boussinesq variant is given first.
29
30!> Global positioning system (aka container for information to describe the grid)
31type, private :: GPS ; private
32 real :: len_lon !< The longitudinal or x-direction length of the domain [degrees_E] or [km] or [m].
33 real :: len_lat !< The latitudinal or y-direction length of the domain [degrees_N] or [km] or [m].
34 real :: west_lon !< The western longitude of the domain or the equivalent
35 !! starting value for the x-axis [degrees_E] or [km] or [m].
36 real :: south_lat !< The southern latitude of the domain or the equivalent
37 !! starting value for the y-axis [degrees_N] or [km] or [m].
38 real :: Rad_Earth_L !< The radius of the Earth in rescaled units [L ~> m]
39 real :: Lat_enhance_factor !< The amount by which the meridional resolution
40 !! is enhanced within LAT_EQ_ENHANCE of the equator [nondim]
41 real :: Lat_eq_enhance !< The latitude range to the north and south of the equator
42 !! over which the resolution is enhanced [degrees_N]
43 logical :: isotropic !< If true, an isotropic grid on a sphere (also known as a Mercator grid)
44 !! is used. With an isotropic grid, the meridional extent of the domain
45 !! (LENLAT), the zonal extent (LENLON), and the number of grid points in each
46 !! direction are _not_ independent. In MOM the meridional extent is determined
47 !! to fit the zonal extent and the number of grid points, while grid is
48 !! perfectly isotropic.
49 logical :: equator_reference !< If true, the grid is defined to have the equator at the
50 !! nearest q- or h- grid point to (-LOWLAT*NJGLOBAL/LENLAT).
51 integer :: niglobal !< The number of i-points in the global grid computational domain
52 integer :: njglobal !< The number of j-points in the global grid computational domain
53end type GPS
54
55contains
56
57!> set_grid_metrics is used to set the primary values in the model's horizontal
58!! grid. The bathymetry, land-sea mask and any restricted channel widths are
59!! not known yet, so these are set later.
601subroutine set_grid_metrics(G, param_file, US)
61 type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type
62 type(param_file_type), intent(in) :: param_file !< Parameter file structure
63 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
64
65 ! Local variables
66 ! This include declares and sets the variable "version".
67# include "version_variable.h"
68 logical :: debug
69 character(len=256) :: config
70
711 call callTree_enter("set_grid_metrics(), MOM_grid_initialize.F90")
721 call log_version(param_file, "MOM_grid_init", version, "")
73 call get_param(param_file, "MOM_grid_init", "GRID_CONFIG", config, &
74 "A character string that determines the method for "//&
75 "defining the horizontal grid. Current options are: \n"//&
76 " \t mosaic - read the grid from a mosaic (supergrid) \n"//&
77 " \t file set by GRID_FILE.\n"//&
78 " \t cartesian - use a (flat) Cartesian grid.\n"//&
79 " \t spherical - use a simple spherical grid.\n"//&
80 " \t mercator - use a Mercator spherical grid.", &
811 fail_if_missing=.true.)
82 call get_param(param_file, "MOM_grid_init", "DEBUG", debug, &
83 "If true, write out verbose debugging data.", &
841 default=.false., debuggingParam=.true.)
85
86 ! These are defaults that may be changed in the next select block.
871 G%x_axis_units = "degrees_east" ; G%y_axis_units = "degrees_north"
881 G%x_ax_unit_short = "degrees_E" ; G%y_ax_unit_short = "degrees_N"
891 G%grid_unit_to_L = 0.0
901 G%Rad_Earth_L = -1.0*US%m_to_L ; G%len_lat = 0.0 ; G%len_lon = 0.0
912 select case (trim(config))
920 case ("mosaic"); call set_grid_metrics_from_mosaic(G, param_file, US)
930 case ("cartesian"); call set_grid_metrics_cartesian(G, param_file, US)
941 case ("spherical"); call set_grid_metrics_spherical(G, param_file, US)
950 case ("mercator"); call set_grid_metrics_mercator(G, param_file, US)
96 case ("file"); call MOM_error(FATAL, "MOM_grid_init: set_grid_metrics "//&
97 'GRID_CONFIG "file" is no longer a supported option. Use a '//&
980 'mosaic file ("mosaic") or one of the analytic forms instead.')
99 case default ; call MOM_error(FATAL, "MOM_grid_init: set_grid_metrics "//&
1002 "Unrecognized grid configuration "//trim(config))
101 end select
1021 if (G%Rad_Earth_L <= 0.0) then
103 ! The grid metrics were set with an option that does not explicitly initialize Rad_Earth.
104 call get_param(param_file, "MOM_grid_init", "RAD_EARTH", G%Rad_Earth_L, &
1050 "The radius of the Earth.", units="m", default=6.378e6, scale=US%m_to_L)
106 endif
107
108 ! Calculate derived metrics (i.e. reciprocals and products)
1091 call callTree_enter("set_derived_metrics(), MOM_grid_initialize.F90")
1101 call set_derived_dyn_horgrid(G, US)
1111 call callTree_leave("set_derived_metrics()")
112
1131 if (debug) call grid_metrics_chksum('MOM_grid_init/set_grid_metrics', G, US)
114
1151 call callTree_leave("set_grid_metrics()")
1161end subroutine set_grid_metrics
117
118! ------------------------------------------------------------------------------
119
120!> grid_metrics_chksum performs a set of checksums on metrics on the grid for
121!! debugging.
1220subroutine grid_metrics_chksum(parent, G, US)
123 character(len=*), intent(in) :: parent !< A string identifying the caller
124 type(dyn_horgrid_type), intent(in) :: G !< The dynamic horizontal grid type
125 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
126
127 integer :: halo
128
1290 halo = min(G%ied-G%iec, G%jed-G%jec, 1)
130
131 call hchksum_pair(trim(parent)//': d[xy]T', G%dxT, G%dyT, G%HI, &
1320 haloshift=halo, unscale=US%L_to_m, scalar_pair=.true.)
133
1340 call uvchksum(trim(parent)//': dxC[uv]', G%dxCu, G%dyCv, G%HI, haloshift=halo, unscale=US%L_to_m)
135
1360 call uvchksum(trim(parent)//': dxC[uv]', G%dyCu, G%dxCv, G%HI, haloshift=halo, unscale=US%L_to_m)
137
1380 call Bchksum_pair(trim(parent)//': dxB[uv]', G%dxBu, G%dyBu, G%HI, haloshift=halo, unscale=US%L_to_m)
139
140 call hchksum_pair(trim(parent)//': Id[xy]T', G%IdxT, G%IdyT, G%HI, &
1410 haloshift=halo, unscale=US%m_to_L, scalar_pair=.true.)
142
1430 call uvchksum(trim(parent)//': Id[xy]C[uv]', G%IdxCu, G%IdyCv, G%HI, haloshift=halo, unscale=US%m_to_L)
144
1450 call uvchksum(trim(parent)//': Id[xy]C[uv]', G%IdyCu, G%IdxCv, G%HI, haloshift=halo, unscale=US%m_to_L)
146
1470 call Bchksum_pair(trim(parent)//': Id[xy]B[uv]', G%IdxBu, G%IdyBu, G%HI, haloshift=halo, unscale=US%m_to_L)
148
1490 call hchksum(G%areaT, trim(parent)//': areaT',G%HI, haloshift=halo, unscale=US%L_to_m**2)
1500 call Bchksum(G%areaBu, trim(parent)//': areaBu',G%HI, haloshift=halo, unscale=US%L_to_m**2)
151
1520 call hchksum(G%IareaT, trim(parent)//': IareaT',G%HI, haloshift=halo, unscale=US%m_to_L**2)
1530 call Bchksum(G%IareaBu, trim(parent)//': IareaBu',G%HI, haloshift=halo, unscale=US%m_to_L**2)
154
1550 call hchksum(G%geoLonT,trim(parent)//': geoLonT',G%HI, haloshift=halo)
1560 call hchksum(G%geoLatT,trim(parent)//': geoLatT',G%HI, haloshift=halo)
157
1580 call Bchksum(G%geoLonBu, trim(parent)//': geoLonBu',G%HI, haloshift=halo)
1590 call Bchksum(G%geoLatBu, trim(parent)//': geoLatBu',G%HI, haloshift=halo)
160
1610 call uvchksum(trim(parent)//': geoLonC[uv]', G%geoLonCu, G%geoLonCv, G%HI, haloshift=halo)
162
1630 call uvchksum(trim(parent)//': geoLatC[uv]', G%geoLatCu, G%geoLatCv, G%HI, haloshift=halo)
164
1650end subroutine grid_metrics_chksum
166
167! ------------------------------------------------------------------------------
168
169!> Sets the grid metrics from a mosaic file.
1700subroutine set_grid_metrics_from_mosaic(G, param_file, US)
171 type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type
172 type(param_file_type), intent(in) :: param_file !< Parameter file structure
173 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
174
175 ! Local variables
176 ! These are symmetric arrays, corresponding to the data in the mosaic file
1770 real, dimension(2*G%isd-2:2*G%ied+1,2*G%jsd-2:2*G%jed+1) :: tmpT ! Areas [L2 ~> m2]
1780 real, dimension(2*G%isd-3:2*G%ied+1,2*G%jsd-2:2*G%jed+1) :: tmpU ! East face supergrid spacing [L ~> m]
1790 real, dimension(2*G%isd-2:2*G%ied+1,2*G%jsd-3:2*G%jed+1) :: tmpV ! North face supergrid spacing [L ~> m]
1800 real, dimension(2*G%isd-3:2*G%ied+1,2*G%jsd-3:2*G%jed+1) :: tmpZ ! Corner latitudes [degrees_N] or
181 ! longitudes [degrees_E]
1820 real, dimension(:,:), allocatable :: tmpGlbl ! A global array of axis labels [degrees_N] or [km] or [m]
183 character(len=200) :: filename, grid_file, inputdir
184 character(len=64) :: mdl = "MOM_grid_init set_grid_metrics_from_mosaic"
185 type(MOM_domain_type), pointer :: SGdom => NULL() ! Supergrid domain
186 logical :: lon_bug ! If true use an older buggy answer in the tripolar longitude.
187 integer :: i, j, i2, j2, ni, nj
188 integer :: start(4), nread(4)
189
1900 call callTree_enter("set_grid_metrics_from_mosaic(), MOM_grid_initialize.F90")
191
192 call get_param(param_file, mdl, "GRID_FILE", grid_file, &
193 "Name of the file from which to read horizontal grid data.", &
1940 fail_if_missing=.true.)
195 call get_param(param_file, mdl, "USE_TRIPOLAR_GEOLONB_BUG", lon_bug, &
196 "If true, use older code that incorrectly sets the longitude "//&
197 "in some points along the tripolar fold to be off by 360 degrees.", &
1980 default=.false.)
1990 call get_param(param_file, mdl, "INPUTDIR", inputdir, default=".")
2000 inputdir = slasher(inputdir)
2010 filename = trim(adjustl(inputdir)) // trim(adjustl(grid_file))
2020 call log_param(param_file, mdl, "INPUTDIR/GRID_FILE", filename)
2030 if (.not.file_exists(filename)) &
204 call MOM_error(FATAL," set_grid_metrics_from_mosaic: Unable to open "//&
2050 trim(filename))
206
207 !<MISSING CODE TO READ REFINEMENT LEVEL>
208
209 call clone_MOM_domain(G%domain, SGdom, symmetric=.true., domain_name="MOM_MOSAIC", &
2100 refine=2, extra_halo=1)
211
212 ! Read X from the supergrid
2130 tmpZ(:,:) = 999.
2140 call MOM_read_data(filename, 'x', tmpZ, SGdom, position=CORNER)
215
2160 if (lon_bug) then
2170 call pass_var(tmpZ, SGdom, position=CORNER)
218 else
2190 call pass_var(tmpZ, SGdom, position=CORNER, inner_halo=0)
220 endif
2210 call extrapolate_metric(tmpZ, 2*(G%jsc-G%jsd)+2, missing=999.)
2220 do j=G%jsd,G%jed ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*j
2230 G%geoLonT(i,j) = tmpZ(i2-1,j2-1)
224 enddo ; enddo
2250 do J=G%JsdB,G%JedB ; do I=G%IsdB,G%IedB ; i2 = 2*I ; j2 = 2*J
2260 G%geoLonBu(I,J) = tmpZ(i2,j2)
227 enddo ; enddo
2280 do j=G%jsd,G%jed ; do I=G%IsdB,G%IedB ; i2 = 2*i ; j2 = 2*j
2290 G%geoLonCu(I,j) = tmpZ(i2,j2-1)
230 enddo ; enddo
2310 do J=G%JsdB,G%JedB ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*J
2320 G%geoLonCv(i,J) = tmpZ(i2-1,j2)
233 enddo ; enddo
234 ! For some reason, this messes up the solution...
235 ! call pass_var(G%geoLonBu, G%domain, position=CORNER)
236
237 ! Read Y from the supergrid
2380 tmpZ(:,:) = 999.
2390 call MOM_read_data(filename, 'y', tmpZ, SGdom, position=CORNER)
240
2410 call pass_var(tmpZ, SGdom, position=CORNER)
2420 call extrapolate_metric(tmpZ, 2*(G%jsc-G%jsd)+2, missing=999.)
2430 do j=G%jsd,G%jed ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*j
2440 G%geoLatT(i,j) = tmpZ(i2-1,j2-1)
245 enddo ; enddo
2460 do J=G%JsdB,G%JedB ; do I=G%IsdB,G%IedB ; i2 = 2*I ; j2 = 2*J
2470 G%geoLatBu(I,J) = tmpZ(i2,j2)
248 enddo ; enddo
2490 do j=G%jsd,G%jed ; do I=G%IsdB,G%IedB ; i2 = 2*i ; j2 = 2*j
2500 G%geoLatCu(I,j) = tmpZ(i2,j2-1)
251 enddo ; enddo
2520 do J=G%JsdB,G%JedB ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*J
2530 G%geoLatCv(i,J) = tmpZ(i2-1,j2)
254 enddo ; enddo
255
256 ! This routine could be modified to support the use of a mosaic using Cartesian grid coordinates,
257 ! in which case the values of G%x_axis_units, G%y_axis_units and G%grid_unit_to_L would need to be
258 ! reset appropriately here, but this option has not yet been implemented, and the grid coordinates
259 ! are assumed to be degrees of longitude and latitude.
260
261 ! Read DX,DY from the supergrid
2620 tmpU(:,:) = 0. ; tmpV(:,:) = 0.
2630 call MOM_read_data(filename, 'dx', tmpV, SGdom, position=NORTH_FACE, scale=US%m_to_L)
2640 call MOM_read_data(filename, 'dy', tmpU, SGdom, position=EAST_FACE, scale=US%m_to_L)
2650 call pass_vector(tmpU, tmpV, SGdom, To_All+Scalar_Pair, CGRID_NE)
2660 call extrapolate_metric(tmpV, 2*(G%jsc-G%jsd)+2, missing=0.)
2670 call extrapolate_metric(tmpU, 2*(G%jsc-G%jsd)+2, missing=0.)
268
2690 do j=G%jsd,G%jed ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*j
2700 G%dxT(i,j) = tmpV(i2-1,j2-1) + tmpV(i2,j2-1)
2710 G%dyT(i,j) = tmpU(i2-1,j2-1) + tmpU(i2-1,j2)
272 enddo ; enddo
273
2740 do j=G%jsd,G%jed ; do I=G%IsdB,G%IedB ; i2 = 2*i ; j2 = 2*j
2750 G%dxCu(I,j) = tmpV(i2,j2-1) + tmpV(i2+1,j2-1)
2760 G%dyCu(I,j) = tmpU(i2,j2-1) + tmpU(i2,j2)
277 enddo ; enddo
278
2790 do J=G%JsdB,G%JedB ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*j
2800 G%dxCv(i,J) = tmpV(i2-1,j2) + tmpV(i2,j2)
2810 G%dyCv(i,J) = tmpU(i2-1,j2) + tmpU(i2-1,j2+1)
282 enddo ; enddo
283
2840 do J=G%JsdB,G%JedB ; do I=G%IsdB,G%IedB ; i2 = 2*i ; j2 = 2*j
2850 G%dxBu(I,J) = tmpV(i2,j2) + tmpV(i2+1,j2)
2860 G%dyBu(I,J) = tmpU(i2,j2) + tmpU(i2,j2+1)
287 enddo ; enddo
288
289 ! Read AREA from the supergrid
2900 tmpT(:,:) = 0.
2910 call MOM_read_data(filename, 'area', tmpT, SGdom, scale=US%m_to_L**2)
2920 call pass_var(tmpT, SGdom)
2930 call extrapolate_metric(tmpT, 2*(G%jsc-G%jsd)+2, missing=0.)
294
2950 do j=G%jsd,G%jed ; do i=G%isd,G%ied ; i2 = 2*i ; j2 = 2*j
296 G%areaT(i,j) = (tmpT(i2-1,j2-1) + tmpT(i2,j2)) + &
2970 (tmpT(i2-1,j2) + tmpT(i2,j2-1))
298 enddo ; enddo
2990 do J=G%JsdB,G%JedB ; do I=G%IsdB,G%IedB ; i2 = 2*i ; j2 = 2*j
300 G%areaBu(I,J) = (tmpT(i2,j2) + tmpT(i2+1,j2+1)) + &
3010 (tmpT(i2,j2+1) + tmpT(i2+1,j2))
302 enddo ; enddo
303
3040 ni = SGdom%niglobal
3050 nj = SGdom%njglobal
3060 call deallocate_MOM_domain(SGdom)
307
3080 call pass_vector(G%dyCu, G%dxCv, G%Domain, To_All+Scalar_Pair, CGRID_NE)
3090 call pass_vector(G%dxCu, G%dyCv, G%Domain, To_All+Scalar_Pair, CGRID_NE)
3100 call pass_vector(G%dxBu, G%dyBu, G%Domain, To_All+Scalar_Pair, BGRID_NE)
3110 call pass_var(G%areaT, G%Domain)
3120 call pass_var(G%areaBu, G%Domain, position=CORNER)
313
314 ! Construct axes for diagnostic output (only necessary because "ferret" uses
315 ! broken convention for interpretting netCDF files).
3160 start(:) = 1 ; nread(:) = 1
3170 start(2) = 2 ; nread(1) = ni+1 ; nread(2) = 2
3180 allocate( tmpGlbl(ni+1,2) )
3190 if (is_root_PE()) &
320 call MOM_read_data(filename, "x", tmpGlbl, start, nread, &
3210 no_domain=.TRUE., turns=G%HI%turns)
3220 call broadcast(tmpGlbl, 2*(ni+1), root_PE())
323
324 ! I don't know why the second axis is 1 or 2 here. -RWH
3250 do i=G%isg,G%ieg
3260 G%gridLonT(i) = tmpGlbl(2*(i-G%isg)+2,2)
327 enddo
328 ! Note that the dynamic grid always uses symmetric memory for the global
329 ! arrays G%gridLatB and G%gridLonB.
3300 do I=G%isg-1,G%ieg
3310 G%gridLonB(I) = tmpGlbl(2*(I-G%isg)+3,1)
332 enddo
3330 deallocate( tmpGlbl )
334
3350 allocate( tmpGlbl(1, nj+1) )
3360 start(:) = 1 ; nread(:) = 1
3370 start(1) = int(ni/4)+1 ; nread(2) = nj+1
3380 if (is_root_PE()) &
339 call MOM_read_data(filename, "y", tmpGlbl, start, nread, &
3400 no_domain=.TRUE., turns=G%HI%turns)
3410 call broadcast(tmpGlbl, nj+1, root_PE())
342
3430 do j=G%jsg,G%jeg
3440 G%gridLatT(j) = tmpGlbl(1,2*(j-G%jsg)+2)
345 enddo
3460 do J=G%jsg-1,G%jeg
3470 G%gridLatB(J) = tmpGlbl(1,2*(j-G%jsg)+3)
348 enddo
3490 deallocate( tmpGlbl )
350
3510 call callTree_leave("set_grid_metrics_from_mosaic()")
3520end subroutine set_grid_metrics_from_mosaic
353
354
355! ------------------------------------------------------------------------------
356
357!> Calculate the values of the metric terms for a Cartesian grid that
358!! might be used and save them in arrays.
359!!
360!! Within this subroutine, the x- and y- grid spacings and their
361!! inverses and the cell areas centered on h, q, u, and v points are
362!! calculated, as are the geographic locations of each of these 4
363!! sets of points.
3640subroutine set_grid_metrics_cartesian(G, param_file, US)
365 type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type
366 type(param_file_type), intent(in) :: param_file !< Parameter file structure
367 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
368 ! Local variables
369 integer :: i, j, isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB, I1off, J1off
370 integer :: niglobal, njglobal
3710 real :: grid_latT(G%jsd:G%jed), grid_latB(G%JsdB:G%JedB) ! Axis labels [degrees_N] or [km] or [m]
3720 real :: grid_lonT(G%isd:G%ied), grid_lonB(G%IsdB:G%IedB) ! Axis labels [degrees_E] or [km] or [m]
373 real :: dx_everywhere, dy_everywhere ! Grid spacings [L ~> m].
374 real :: I_dx, I_dy ! Inverse grid spacings [L-1 ~> m-1].
375 real :: PI ! The ratio of the circumference of a circle to its diameter [nondim]
376 character(len=80) :: units_temp
377 character(len=48) :: mdl = "MOM_grid_init set_grid_metrics_cartesian"
378
3790 niglobal = G%Domain%niglobal ; njglobal = G%Domain%njglobal
3800 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
3810 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
3820 I1off = G%idg_offset ; J1off = G%jdg_offset
383
3840 call callTree_enter("set_grid_metrics_cartesian(), MOM_grid_initialize.F90")
385
3860 PI = 4.0*atan(1.0)
387
388 call get_param(param_file, mdl, "AXIS_UNITS", units_temp, &
389 "The units for the Cartesian axes. Valid entries are: \n"//&
390 " \t degrees - degrees of latitude and longitude \n"//&
391 " \t m or meter(s) - meters \n"//&
3920 " \t k or km or kilometer(s) - kilometers", default="degrees")
3930 if (trim(units_temp) == "k") units_temp = "km"
394
395 call get_param(param_file, mdl, "SOUTHLAT", G%south_lat, &
396 "The southern latitude of the domain or the equivalent "//&
397 "starting value for the y-axis.", units=units_temp, &
3980 fail_if_missing=.true.)
399 call get_param(param_file, mdl, "LENLAT", G%len_lat, &
400 "The latitudinal or y-direction length of the domain.", &
4010 units=units_temp, fail_if_missing=.true.)
402 call get_param(param_file, mdl, "WESTLON", G%west_lon, &
403 "The western longitude of the domain or the equivalent "//&
404 "starting value for the x-axis.", units=units_temp, &
4050 default=0.0)
406 call get_param(param_file, mdl, "LENLON", G%len_lon, &
407 "The longitudinal or x-direction length of the domain.", &
4080 units=units_temp, fail_if_missing=.true.)
409 call get_param(param_file, mdl, "RAD_EARTH", G%Rad_Earth_L, &
4100 "The radius of the Earth.", units="m", default=6.378e6, scale=US%m_to_L)
411
4120 if (units_temp(1:1) == 'k') then
4130 G%x_axis_units = "kilometers" ; G%y_axis_units = "kilometers"
4140 G%x_ax_unit_short = "km" ; G%y_ax_unit_short = "km"
4150 elseif (units_temp(1:1) == 'm') then
4160 G%x_axis_units = "meters" ; G%y_axis_units = "meters"
4170 G%x_ax_unit_short = "m" ; G%y_ax_unit_short = "m"
418 endif
4190 call log_param(param_file, mdl, "explicit AXIS_UNITS", G%x_axis_units)
420
421 ! Note that the dynamic grid always uses symmetric memory for the global
422 ! arrays G%gridLatB and G%gridLonB.
4230 do J=G%jsg-1,G%jeg
4240 G%gridLatB(j) = G%south_lat + G%len_lat*REAL(J-(G%jsg-1))/REAL(njglobal)
425 enddo
4260 do j=G%jsg,G%jeg
4270 G%gridLatT(j) = G%south_lat + G%len_lat*(REAL(j-G%jsg)+0.5)/REAL(njglobal)
428 enddo
4290 do I=G%isg-1,G%ieg
4300 G%gridLonB(i) = G%west_lon + G%len_lon*REAL(I-(G%isg-1))/REAL(niglobal)
431 enddo
4320 do i=G%isg,G%ieg
4330 G%gridLonT(i) = G%west_lon + G%len_lon*(REAL(i-G%isg)+0.5)/REAL(niglobal)
434 enddo
435
4360 do J=JsdB,JedB
4370 grid_latB(J) = G%south_lat + G%len_lat*REAL(J+J1off-(G%jsg-1))/REAL(njglobal)
438 enddo
4390 do j=jsd,jed
4400 grid_latT(J) = G%south_lat + G%len_lat*(REAL(j+J1off-G%jsg)+0.5)/REAL(njglobal)
441 enddo
4420 do I=IsdB,IedB
4430 grid_lonB(I) = G%west_lon + G%len_lon*REAL(i+I1off-(G%isg-1))/REAL(niglobal)
444 enddo
4450 do i=isd,ied
4460 grid_lonT(i) = G%west_lon + G%len_lon*(REAL(i+I1off-G%isg)+0.5)/REAL(niglobal)
447 enddo
448
4490 if (units_temp(1:1) == 'k') then ! Axes are measured in km.
4500 G%grid_unit_to_L = 1000.0*US%m_to_L
4510 dx_everywhere = 1000.0*US%m_to_L * G%len_lon / (REAL(niglobal))
4520 dy_everywhere = 1000.0*US%m_to_L * G%len_lat / (REAL(njglobal))
4530 elseif (units_temp(1:1) == 'm') then ! Axes are measured in m.
4540 G%grid_unit_to_L = US%m_to_L
4550 dx_everywhere = US%m_to_L*G%len_lon / (REAL(niglobal))
4560 dy_everywhere = US%m_to_L*G%len_lat / (REAL(njglobal))
457 else ! Axes are measured in degrees of latitude and longitude.
4580 dx_everywhere = G%Rad_Earth_L * G%len_lon * PI / (180.0 * niglobal)
4590 dy_everywhere = G%Rad_Earth_L * G%len_lat * PI / (180.0 * njglobal)
460 endif
461
4620 I_dx = 1.0 / dx_everywhere ; I_dy = 1.0 / dy_everywhere
463
4640 do J=JsdB,JedB ; do I=IsdB,IedB
4650 G%geoLonBu(I,J) = grid_lonB(I) ; G%geoLatBu(I,J) = grid_latB(J)
466
4670 G%dxBu(I,J) = dx_everywhere ; G%IdxBu(I,J) = I_dx
4680 G%dyBu(I,J) = dy_everywhere ; G%IdyBu(I,J) = I_dy
4690 G%areaBu(I,J) = dx_everywhere * dy_everywhere ; G%IareaBu(I,J) = I_dx * I_dy
470 enddo ; enddo
471
4720 do j=jsd,jed ; do i=isd,ied
4730 G%geoLonT(i,j) = grid_lonT(i) ; G%geoLatT(i,j) = grid_LatT(j)
4740 G%dxT(i,j) = dx_everywhere ; G%IdxT(i,j) = I_dx
4750 G%dyT(i,j) = dy_everywhere ; G%IdyT(i,j) = I_dy
4760 G%areaT(i,j) = dx_everywhere * dy_everywhere ; G%IareaT(i,j) = I_dx * I_dy
477 enddo ; enddo
478
4790 do j=jsd,jed ; do I=IsdB,IedB
4800 G%geoLonCu(I,j) = grid_lonB(I) ; G%geoLatCu(I,j) = grid_LatT(j)
481
4820 G%dxCu(I,j) = dx_everywhere ; G%IdxCu(I,j) = I_dx
4830 G%dyCu(I,j) = dy_everywhere ; G%IdyCu(I,j) = I_dy
484 enddo ; enddo
485
4860 do J=JsdB,JedB ; do i=isd,ied
4870 G%geoLonCv(i,J) = grid_lonT(i) ; G%geoLatCv(i,J) = grid_latB(J)
488
4890 G%dxCv(i,J) = dx_everywhere ; G%IdxCv(i,J) = I_dx
4900 G%dyCv(i,J) = dy_everywhere ; G%IdyCv(i,J) = I_dy
491 enddo ; enddo
492
4930 call callTree_leave("set_grid_metrics_cartesian()")
4940end subroutine set_grid_metrics_cartesian
495
496! ------------------------------------------------------------------------------
497
498!> Calculate the values of the metric terms that might be used
499!! and save them in arrays.
500!!
501!! Within this subroutine, the x- and y- grid spacings and their
502!! inverses and the cell areas centered on h, q, u, and v points are
503!! calculated, as are the geographic locations of each of these 4
504!! sets of points.
5051subroutine set_grid_metrics_spherical(G, param_file, US)
506 type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type
507 type(param_file_type), intent(in) :: param_file !< Parameter file structure
508 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
509 ! Local variables
510 real :: PI ! PI = 3.1415926... as 4*atan(1) [nondim]
511 real :: PI_180 ! The conversion factor from degrees to radians [radians degree-1]
512 integer :: i, j, isd, ied, jsd, jed
513 integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, IsdB, IedB, JsdB, JedB
514 integer :: i_offset, j_offset
5152 real :: grid_latT(G%jsd:G%jed), grid_latB(G%JsdB:G%JedB) ! Axis labels [degrees_N]
5162 real :: grid_lonT(G%isd:G%ied), grid_lonB(G%IsdB:G%IedB) ! Axis labels [degrees_E]
517 real :: dLon ! The change in longitude between successive grid points [degrees_E]
518 real :: dLat ! The change in latitude between successive grid points [degrees_N]
519 real :: dL_di ! dLon rescaled from degrees to radians [radians]
520 real :: latitude ! The latitude of a grid point [degrees_N]
521 character(len=48) :: mdl = "MOM_grid_init set_grid_metrics_spherical"
522
5231 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
5241 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
5251 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
5261 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
5271 i_offset = G%idg_offset ; j_offset = G%jdg_offset
528
5291 call callTree_enter("set_grid_metrics_spherical(), MOM_grid_initialize.F90")
530
531! Calculate the values of the metric terms that might be used
532! and save them in arrays.
5331 PI = 4.0*atan(1.0) ; PI_180 = atan(1.0)/45.
534
535 call get_param(param_file, mdl, "SOUTHLAT", G%south_lat, &
536 "The southern latitude of the domain.", units="degrees_N", &
5371 fail_if_missing=.true.)
538 call get_param(param_file, mdl, "LENLAT", G%len_lat, &
539 "The latitudinal length of the domain.", units="degrees_N", &
5401 fail_if_missing=.true.)
541 call get_param(param_file, mdl, "WESTLON", G%west_lon, &
542 "The western longitude of the domain.", units="degrees_E", &
5431 default=0.0)
544 call get_param(param_file, mdl, "LENLON", G%len_lon, &
545 "The longitudinal length of the domain.", units="degrees_E", &
5461 fail_if_missing=.true.)
547 call get_param(param_file, mdl, "RAD_EARTH", G%Rad_Earth_L, &
5481 "The radius of the Earth.", units="m", default=6.378e6, scale=US%m_to_L)
549
5501 dLon = G%len_lon/G%Domain%niglobal
5511 dLat = G%len_lat/G%Domain%njglobal
552
553 ! Note that the dynamic grid always uses symmetric memory for the global
554 ! arrays G%gridLatB and G%gridLonB.
55562 do j=G%jsg-1,G%jeg
55661 latitude = G%south_lat + dLat*(REAL(J-(G%jsg-1)))
55762 G%gridLatB(J) = MIN(MAX(latitude,-90.),90.)
558 enddo
55961 do j=G%jsg,G%jeg
56060 latitude = G%south_lat + dLat*(REAL(j-G%jsg)+0.5)
56161 G%gridLatT(j) = MIN(MAX(latitude,-90.),90.)
562 enddo
563122 do i=G%isg-1,G%ieg
564122 G%gridLonB(I) = G%west_lon + dLon*(REAL(I-(G%isg-1)))
565 enddo
566121 do i=G%isg,G%ieg
567121 G%gridLonT(i) = G%west_lon + dLon*(REAL(i-G%isg)+0.5)
568 enddo
569
57070 do J=JsdB,JedB
57169 latitude = G%south_lat + dLat* REAL(J+J_offset-(G%jsg-1))
57270 grid_LatB(J) = MIN(MAX(latitude,-90.),90.)
573 enddo
57469 do j=jsd,jed
57568 latitude = G%south_lat + dLat*(REAL(j+J_offset-G%jsg)+0.5)
57669 grid_LatT(j) = MIN(MAX(latitude,-90.),90.)
577 enddo
578130 do I=IsdB,IedB
579130 grid_LonB(I) = G%west_lon + dLon*REAL(I+I_offset-(G%isg-1))
580 enddo
581129 do i=isd,ied
582129 grid_LonT(i) = G%west_lon + dLon*(REAL(i+I_offset-G%isg)+0.5)
583 enddo
584
5851 dL_di = (G%len_lon * 4.0*atan(1.0)) / (180.0 * G%Domain%niglobal)
5868971 do J=JsdB,JedB ; do I=IsdB,IedB
5878901 G%geoLonBu(I,J) = grid_lonB(I)
5888901 G%geoLatBu(I,J) = grid_latB(J)
589
590 ! The following line is needed to reproduce the solution from
591 ! set_grid_metrics_mercator when used to generate a simple spherical grid.
5928901 G%dxBu(I,J) = G%Rad_Earth_L * COS( G%geoLatBu(I,J)*PI_180 ) * dL_di
593! G%dxBu(I,J) = G%Rad_Earth_L * dLon*PI_180 * COS( G%geoLatBu(I,J)*PI_180 )
5948901 G%dyBu(I,J) = G%Rad_Earth_L * dLat*PI_180
5958970 G%areaBu(I,J) = G%dxBu(I,J) * G%dyBu(I,J)
596 enddo ; enddo
597
5988902 do J=JsdB,JedB ; do i=isd,ied
5998832 G%geoLonCv(i,J) = grid_LonT(i)
6008832 G%geoLatCv(i,J) = grid_latB(J)
601
602 ! The following line is needed to reproduce the solution from
603 ! set_grid_metrics_mercator when used to generate a simple spherical grid.
6048832 G%dxCv(i,J) = G%Rad_Earth_L * COS( G%geoLatCv(i,J)*PI_180 ) * dL_di
605! G%dxCv(i,J) = G%Rad_Earth_L * (dLon*PI_180) * COS( G%geoLatCv(i,J)*PI_180 )
6068901 G%dyCv(i,J) = G%Rad_Earth_L * dLat*PI_180
607 enddo ; enddo
608
6098841 do j=jsd,jed ; do I=IsdB,IedB
6108772 G%geoLonCu(I,j) = grid_lonB(I)
6118772 G%geoLatCu(I,j) = grid_LatT(j)
612
613 ! The following line is needed to reproduce the solution from
614 ! set_grid_metrics_mercator when used to generate a simple spherical grid.
6158772 G%dxCu(I,j) = G%Rad_Earth_L * COS( G%geoLatCu(I,j)*PI_180 ) * dL_di
616! G%dxCu(I,j) = G%Rad_Earth_L * dLon*PI_180 * COS( latitude )
6178840 G%dyCu(I,j) = G%Rad_Earth_L * dLat*PI_180
618 enddo ; enddo
619
6208773 do j=jsd,jed ; do i=isd,ied
6218704 G%geoLonT(i,j) = grid_LonT(i)
6228704 G%geoLatT(i,j) = grid_LatT(j)
623
624 ! The following line is needed to reproduce the solution from
625 ! set_grid_metrics_mercator when used to generate a simple spherical grid.
6268704 G%dxT(i,j) = G%Rad_Earth_L * COS( G%geoLatT(i,j)*PI_180 ) * dL_di
627! G%dxT(i,j) = G%Rad_Earth_L * dLon*PI_180 * COS( latitude )
6288704 G%dyT(i,j) = G%Rad_Earth_L * dLat*PI_180
629
630! latitude = G%geoLatCv(i,J)*PI_180 ! In radians
631! dL_di = G%geoLatCv(i,max(jsd,J-1))*PI_180 ! In radians
632! G%areaT(i,j) = Rad_Earth_L**2*dLon*dLat*ABS(SIN(latitude)-SIN(dL_di))
6338772 G%areaT(i,j) = G%dxT(i,j) * G%dyT(i,j)
634 enddo ; enddo
635
6361 call callTree_leave("set_grid_metrics_spherical()")
6371end subroutine set_grid_metrics_spherical
638
639!> Calculate the values of the metric terms that might be used
640!! and save them in arrays.
641!!
642!! Within this subroutine, the x- and y- grid spacings and their
643!! inverses and the cell areas centered on h, q, u, and v points are
644!! calculated, as are the geographic locations of each of these 4
645!! sets of points.
6460subroutine set_grid_metrics_mercator(G, param_file, US)
647 type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type
648 type(param_file_type), intent(in) :: param_file !< Parameter file structure
649 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
650 ! Local variables
651 integer :: i, j, isd, ied, jsd, jed
652 integer :: I_off, J_off
653 type(GPS) :: GP
654 character(len=48) :: mdl = "MOM_grid_init set_grid_metrics_mercator"
655 real :: PI, PI_2 ! PI = 3.1415926... as 4*atan(1), PI_2 = (PI) /2.0 [nondim]
656 real :: y_q, y_h ! Latitudes of a point [radians]
657 real :: id ! The i-grid space positions whose longitude is being sought [gridpoints]
658 real :: jd ! The j-grid space positions whose latitude is being sought [gridpoints]
659 real :: x_q, x_h ! Longitudes of a point [radians]
660 real, dimension(G%isd:G%ied,G%jsd:G%jed) :: &
6610 xh, yh ! Latitude and longitude of h points in radians [radians]
662 real, dimension(G%IsdB:G%IedB,G%jsd:G%jed) :: &
6630 xu, yu ! Latitude and longitude of u points in radians [radians]
664 real, dimension(G%isd:G%ied,G%JsdB:G%JedB) :: &
6650 xv, yv ! Latitude and longitude of v points in radians [radians]
666 real, dimension(G%IsdB:G%IedB,G%JsdB:G%JedB) :: &
6670 xq, yq ! Latitude and longitude of q points in radians [radians]
668 real :: fnRef ! fnRef is the value of Int_dj_dy or
669 ! Int_dj_dy at a latitude or longitude that is
670 ! being set to be at grid index jRef or iRef [gridpoints]
671 real :: jRef, iRef ! The grid index at which fnRef is evaluated [gridpoints]
672 integer :: itt1, itt2
673 logical, parameter :: simple_area = .true.
674 integer :: is, ie, js, je, Isq, Ieq, Jsq, Jeq, IsdB, IedB, JsdB, JedB
675
676 ! All of the metric terms should be defined over the domain from
677 ! isd to ied. Outside of the physical domain, both the metrics
678 ! and their inverses may be set to zero.
6790 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec
6800 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
6810 Isq = G%IscB ; Ieq = G%IecB ; Jsq = G%JscB ; Jeq = G%JecB
6820 IsdB = G%IsdB ; IedB = G%IedB ; JsdB = G%JsdB ; JedB = G%JedB
6830 I_off = G%idg_offset ; J_off = G%jdg_offset
684
6850 GP%niglobal = G%Domain%niglobal
6860 GP%njglobal = G%Domain%njglobal
687
6880 call callTree_enter("set_grid_metrics_mercator(), MOM_grid_initialize.F90")
689
690 ! Calculate the values of the metric terms that might be used
691 ! and save them in arrays.
6920 PI = 4.0*atan(1.0) ; PI_2 = 0.5*PI
693
694 call get_param(param_file, mdl, "SOUTHLAT", GP%south_lat, &
695 "The southern latitude of the domain.", units="degrees_N", &
6960 fail_if_missing=.true.)
697 call get_param(param_file, mdl, "LENLAT", GP%len_lat, &
698 "The latitudinal length of the domain.", units="degrees_N", &
6990 fail_if_missing=.true.)
700 call get_param(param_file, mdl, "WESTLON", GP%west_lon, &
701 "The western longitude of the domain.", units="degrees_E", &
7020 default=0.0)
703 call get_param(param_file, mdl, "LENLON", GP%len_lon, &
704 "The longitudinal length of the domain.", units="degrees_E", &
7050 fail_if_missing=.true.)
706 call get_param(param_file, mdl, "RAD_EARTH", GP%Rad_Earth_L, &
7070 "The radius of the Earth.", units="m", default=6.378e6, scale=US%m_to_L)
7080 G%south_lat = GP%south_lat ; G%len_lat = GP%len_lat
7090 G%west_lon = GP%west_lon ; G%len_lon = GP%len_lon
7100 G%Rad_Earth_L = GP%Rad_Earth_L
711
712 call get_param(param_file, mdl, "ISOTROPIC", GP%isotropic, &
713 "If true, an isotropic grid on a sphere (also known as "//&
714 "a Mercator grid) is used. With an isotropic grid, the "//&
715 "meridional extent of the domain (LENLAT), the zonal "//&
716 "extent (LENLON), and the number of grid points in each "//&
717 "direction are _not_ independent. In MOM the meridional "//&
718 "extent is determined to fit the zonal extent and the "//&
719 "number of grid points, while grid is perfectly isotropic.", &
7200 default=.false.)
721 call get_param(param_file, mdl, "EQUATOR_REFERENCE", GP%equator_reference, &
722 "If true, the grid is defined to have the equator at the "//&
723 "nearest q- or h- grid point to (-LOWLAT*NJGLOBAL/LENLAT).", &
7240 default=.true.)
725 call get_param(param_file, mdl, "LAT_ENHANCE_FACTOR", GP%Lat_enhance_factor, &
726 "The amount by which the meridional resolution is "//&
727 "enhanced within LAT_EQ_ENHANCE of the equator.", &
7280 units="nondim", default=1.0)
729 call get_param(param_file, mdl, "LAT_EQ_ENHANCE", GP%Lat_eq_enhance, &
730 "The latitude range to the north and south of the equator "//&
731 "over which the resolution is enhanced.", units="degrees_N", &
7320 default=0.0)
733
734 ! With an isotropic grid, the north-south extent of the domain,
735 ! the east-west extent, and the number of grid points in each
736 ! direction are _not_ independent. Here the north-south extent
737 ! will be determined to fit the east-west extent and the number of
738 ! grid points. The grid is perfectly isotropic.
7390 if (GP%equator_reference) then
740 ! With the following expression, the equator will always be placed
741 ! on either h or q points, in a position consistent with the ratio
742 ! GP%south_lat to GP%len_lat.
7430 jRef = (G%jsg-1) + 0.5*FLOOR(GP%njglobal*((-1.0*GP%south_lat*2.0)/GP%len_lat)+0.5)
7440 fnRef = Int_dj_dy(0.0, GP)
745 else
746 ! The following line sets the reference latitude GP%south_lat at j=js-1 (or -2?)
7470 jRef = (G%jsg-1)
7480 fnRef = Int_dj_dy((GP%south_lat*PI/180.0), GP)
749 endif
750
751 ! These calculations no longer depend on the order in which they
752 ! are performed because they all use the same (poor) starting guess and
753 ! iterate to convergence.
754 ! Note that the dynamic grid always uses symmetric memory for the global
755 ! arrays G%gridLatB and G%gridLonB.
7560 do J=G%jsg-1,G%jeg
7570 jd = fnRef + (J - jRef)
7580 y_q = find_root(Int_dj_dy, dy_dj, GP, jd, 0.0, -1.0*PI_2, PI_2, itt2)
7590 G%gridLatB(J) = y_q*180.0/PI
760 ! if (is_root_pe()) &
761 ! write(stdout, '("J, y_q = ",I0,", ",ES14.4," itts = ",I0)') j, y_q, itt2
762 enddo
7630 do j=G%jsg,G%jeg
7640 jd = fnRef + (j - jRef) - 0.5
7650 y_h = find_root(Int_dj_dy, dy_dj, GP, jd, 0.0, -1.0*PI_2, PI_2, itt1)
7660 G%gridLatT(j) = y_h*180.0/PI
767 ! if (is_root_pe()) &
768 ! write(stdout, '("j, y_h = ",I0,", ",ES14.4," itts = ",I0)') j, y_h, itt1
769 enddo
7700 do J=JsdB+J_off,JedB+J_off
7710 jd = fnRef + (J - jRef)
7720 y_q = find_root(Int_dj_dy, dy_dj, GP, jd, 0.0, -1.0*PI_2, PI_2, itt2)
7730 do I=IsdB,IedB ; yq(I,J-J_off) = y_q ; enddo
7740 do i=isd,ied ; yv(i,J-J_off) = y_q ; enddo
775 enddo
7760 do j=jsd+J_off,jed+J_off
7770 jd = fnRef + (j - jRef) - 0.5
7780 y_h = find_root(Int_dj_dy, dy_dj, GP, jd, 0.0, -1.0*PI_2, PI_2, itt1)
7790 if ((j >= jsd+J_off) .and. (j <= jed+J_off)) then
7800 do i=isd,ied ; yh(i,j-J_off) = y_h ; enddo
7810 do I=IsdB,IedB ; yu(I,j-J_off) = y_h ; enddo
782 endif
783 enddo
784
785 ! Determine the longitudes of the various points.
786
787 ! These two lines place the western edge of the domain at GP%west_lon.
7880 iRef = (G%isg-1) + GP%niglobal
7890 fnRef = Int_di_dx(((GP%west_lon+GP%len_lon)*PI/180.0), GP)
790
791 ! These calculations no longer depend on the order in which they
792 ! are performed because they all use the same (poor) starting guess and
793 ! iterate to convergence.
7940 do I=G%isg-1,G%ieg
7950 id = fnRef + (I - iRef)
7960 x_q = find_root(Int_di_dx, dx_di, GP, id, 0.0, -4.0*PI, 4.0*PI, itt2)
7970 G%gridLonB(I) = x_q*180.0/PI
798 enddo
7990 do i=G%isg,G%ieg
8000 id = fnRef + (i - iRef) - 0.5
8010 x_h = find_root(Int_di_dx, dx_di, GP, id, 0.0, -4.0*PI, 4.0*PI, itt1)
8020 G%gridLonT(i) = x_h*180.0/PI
803 enddo
8040 do I=IsdB+I_off,IedB+I_off
8050 id = fnRef + (I - iRef)
8060 x_q = find_root(Int_di_dx, dx_di, GP, id, 0.0, -4.0*PI, 4.0*PI, itt2)
8070 do J=JsdB,JedB ; xq(I-I_off,J) = x_q ; enddo
8080 do j=jsd,jed ; xu(I-I_off,j) = x_q ; enddo
809 enddo
8100 do i=isd+I_off,ied+I_off
8110 id = fnRef + (i - iRef) - 0.5
8120 x_h = find_root(Int_di_dx, dx_di, GP, id, 0.0, -4.0*PI, 4.0*PI, itt1)
8130 do j=jsd,jed ; xh(i-I_off,j) = x_h ; enddo
8140 do J=JsdB,JedB ; xv(i-I_off,J) = x_h ; enddo
815 enddo
816
8170 do J=JsdB,JedB ; do I=IsdB,IedB
8180 G%geoLonBu(I,J) = xq(I,J)*180.0/PI
8190 G%geoLatBu(I,J) = yq(I,J)*180.0/PI
8200 G%dxBu(I,J) = ds_di(xq(I,J), yq(I,J), GP)
8210 G%dyBu(I,J) = ds_dj(xq(I,J), yq(I,J), GP)
822
8230 G%areaBu(I,J) = G%dxBu(I,J) * G%dyBu(I,J)
8240 G%IareaBu(I,J) = 1.0 / (G%areaBu(I,J))
825 enddo ; enddo
826
8270 do j=jsd,jed ; do i=isd,ied
8280 G%geoLonT(i,j) = xh(i,j)*180.0/PI
8290 G%geoLatT(i,j) = yh(i,j)*180.0/PI
8300 G%dxT(i,j) = ds_di(xh(i,j), yh(i,j), GP)
8310 G%dyT(i,j) = ds_dj(xh(i,j), yh(i,j), GP)
832
8330 G%areaT(i,j) = G%dxT(i,j)*G%dyT(i,j)
8340 G%IareaT(i,j) = 1.0 / (G%areaT(i,j))
835 enddo ; enddo
836
8370 do j=jsd,jed ; do I=IsdB,IedB
8380 G%geoLonCu(I,j) = xu(I,j)*180.0/PI
8390 G%geoLatCu(I,j) = yu(I,j)*180.0/PI
8400 G%dxCu(I,j) = ds_di(xu(I,j), yu(I,j), GP)
8410 G%dyCu(I,j) = ds_dj(xu(I,j), yu(I,j), GP)
842 enddo ; enddo
843
8440 do J=JsdB,JedB ; do i=isd,ied
8450 G%geoLonCv(i,J) = xv(i,J)*180.0/PI
8460 G%geoLatCv(i,J) = yv(i,J)*180.0/PI
8470 G%dxCv(i,J) = ds_di(xv(i,J), yv(i,J), GP)
8480 G%dyCv(i,J) = ds_dj(xv(i,J), yv(i,J), GP)
849 enddo ; enddo
850
851 if (.not.simple_area) then
852 do j=JsdB+1,jed ; do i=IsdB+1,ied
853 G%areaT(I,J) = GP%Rad_Earth_L**2 * &
854 (dL(xq(I-1,J-1),xq(I-1,J),yq(I-1,J-1),yq(I-1,J)) + &
855 (dL(xq(I-1,J),xq(I,J),yq(I-1,J),yq(I,J)) + &
856 (dL(xq(I,J),xq(I,J-1),yq(I,J),yq(I,J-1)) + &
857 dL(xq(I,J-1),xq(I-1,J-1),yq(I,J-1),yq(I-1,J-1)))))
858 enddo ; enddo
859 if ((IsdB == isd) .or. (JsdB == jsq)) then
860 ! Fill in row and column 1 to calculate the area in the southernmost
861 ! and westernmost land cells when we are not using symmetric memory.
862 ! The pass_var call updates these values if they are not land cells.
863 G%areaT(isd+1,jsd) = G%areaT(isd+1,jsd+1)
864 do j=jsd,jed ; G%areaT(isd,j) = G%areaT(isd+1,j) ; enddo
865 do i=isd,ied ; G%areaT(i,jsd) = G%areaT(i,jsd+1) ; enddo
866 ! Now replace the data in the halos, if value values exist.
867 call pass_var(G%areaT,G%Domain)
868 endif
869 do j=jsd,jed ; do i=isd,ied
870 G%IareaT(i,j) = 1.0 / (G%areaT(i,j))
871 enddo ; enddo
872 endif
873
8740 call callTree_leave("set_grid_metrics_mercator()")
8750end subroutine set_grid_metrics_mercator
876
877
878!> This function returns the grid spacing in the logical x direction in [L ~> m].
8790function ds_di(x, y, GP)
880 real, intent(in) :: x !< The longitude in question [radians]
881 real, intent(in) :: y !< The latitude in question [radians]
882 type(GPS), intent(in) :: GP !< A structure of grid parameters
883
884 real :: ds_di ! The returned grid spacing [L ~> m]
885
8860 ds_di = GP%Rad_Earth_L * cos(y) * dx_di(x,GP)
887 ! In general, this might be...
888 ! ds_di = GP%Rad_Earth_L * sqrt( cos(y)*cos(y) * dx_di(x,y,GP)*dx_di(x,y,GP) + &
889 ! dy_di(x,y,GP)*dy_di(x,y,GP))
8900end function ds_di
891
892!> This function returns the grid spacing in the logical y direction in [L ~> m].
8930function ds_dj(x, y, GP)
894 real, intent(in) :: x !< The longitude in question [radians]
895 real, intent(in) :: y !< The latitude in question [radians]
896 type(GPS), intent(in) :: GP !< A structure of grid parameters
897
898 real :: ds_dj ! The returned grid spacing [L ~> m]
899
9000 ds_dj = GP%Rad_Earth_L * dy_dj(y,GP)
901 ! In general, this might be...
902 ! ds_dj = GP%Rad_Earth_L * sqrt( cos(y)*cos(y) * dx_dj(x,y,GP)*dx_dj(x,y,GP) + &
903 ! dy_dj(x,y,GP)*dy_dj(x,y,GP))
9040end function ds_dj
905
906!> This function returns the contribution from the line integral along one of the four sides of a
907!! cell face to the area of a cell, in [radians2], assuming that the sides follow a linear path in
908!! latitude and longitude (i.e., on a Mercator grid).
9090function dL(x1, x2, y1, y2)
910 real, intent(in) :: x1 !< Segment starting longitude [radians]
911 real, intent(in) :: x2 !< Segment ending longitude [radians]
912 real, intent(in) :: y1 !< Segment starting latitude [radians]
913 real, intent(in) :: y2 !< Segment ending latitude [radians]
914 ! Local variables
915 real :: dL ! A contribution to the spanned area the surface of the sphere [radian2]
916 real :: r ! A contribution from the range of latitudes, including trigonometric factors [radians]
917 real :: dy ! The spanned range of latitudes [radians]
918
9190 dy = y2 - y1
920
9210 if (ABS(dy) > 2.5e-8) then
9220 r = ((1.0 - cos(dy))*cos(y1) + sin(dy)*sin(y1)) / dy
923 else
9240 r = (0.5*dy*cos(y1) + sin(y1))
925 endif
9260 dL = r * (x2 - x1)
927
9280end function dL
929
930!> This subroutine finds and returns the value of y at which the monotonically increasing
931!! function fn takes the value fnval, also returning in ittmax the number of iterations of
932!! Newton's method that were used to polish the root.
9330function find_root( fn, dy_df, GP, fnval, y1, ymin, ymax, ittmax)
934 real :: find_root !< The value of y where fn(y) = fnval that will be returned [radians]
935 real, external :: fn !< The external function whose root is being sought [gridpoints]
936 real, external :: dy_df !< The inverse of the derivative of that function [radian gridpoint-1]
937 type(GPS), intent(in) :: GP !< A structure of grid parameters
938 real, intent(in) :: fnval !< The value of fn being sought [gridpoints]
939 real, intent(in) :: y1 !< A first guess for y [radians]
940 real, intent(in) :: ymin !< The minimum permitted value of y [radians]
941 real, intent(in) :: ymax !< The maximum permitted value of y [radians]
942 integer, intent(out) :: ittmax !< The number of iterations used to polish the root
943 ! Local variables
944 real :: y, y_next ! Successive guesses at the root position [radians]
945 real :: ybot, ytop ! Brackets bounding the root [radians]
946 real :: fnbot, fntop ! Values of fn at the bounding values of y [gridpoints]
947 real :: dy_dfn ! The inverse of the local derivative of fn with y [radian gridpoint-1]
948 real :: dy ! The jump to the next guess of y [radians]
949 real :: fny ! The difference between fn(y) and the target value [gridpoints]
950 integer :: itt
951 character(len=256) :: warnmesg
952
953! Bracket the root. Do not use the bounding values because the value at the
954! function at the bounds could be infinite, as is the case for the Mercator
955! grid recursion relation. (I.e., this is a search on an open interval.)
9560 ybot = y1
9570 fnbot = fn(ybot,GP) - fnval ; itt = 0
9580 do while (fnbot > 0.0)
9590 if ((ybot - 2.0*dy_df(ybot,GP)) < (0.5*(ybot+ymin))) then
960 ! Go twice as far as the secant method would normally go.
9610 ybot = ybot - 2.0*dy_df(ybot,GP)
962 else ! But stay within the open interval!
9630 ybot = 0.5*(ybot+ymin) ; itt = itt + 1
964 endif
9650 fnbot = fn(ybot,GP) - fnval
966
9670 if ((itt > 50) .and. (fnbot > 0.0)) then
968 write(warnmesg, '("PE ",I0," unable to find bottom bound for grid function. &
969 &x = ",ES10.4,", xmax = ",ES10.4,", fn = ",ES10.4,", dfn_dx = ",ES10.4,&
970 &", seeking fn = ",ES10.4," - fn = ",ES10.4,".")') &
9710 pe_here(),ybot,ymin,fn(ybot,GP),dy_df(ybot,GP),fnval, fnbot
9720 call MOM_error(FATAL,warnmesg)
973 endif
974 enddo
975
9760 ytop = y1
9770 fntop = fn(ytop,GP) - fnval ; itt = 0
9780 do while (fntop < 0.0)
9790 if ((ytop + 2.0*dy_df(ytop,GP)) < (0.5*(ytop+ymax))) then
980 ! Go twice as far as the secant method would normally go.
9810 ytop = ytop + 2.0*dy_df(ytop,GP)
982 else ! But stay within the open interval!
9830 ytop = 0.5*(ytop+ymax) ; itt = itt + 1
984 endif
9850 fntop = fn(ytop,GP) - fnval
986
9870 if ((itt > 50) .and. (fntop < 0.0)) then
988 write(warnmesg, '("PE ",I0," unable to find top bound for grid function. &
989 &x = ",ES10.4,", xmax = ",ES10.4,", fn = ",ES10.4,", dfn_dx = ",ES10.4, &
990 &", seeking fn = ",ES10.4," - fn = ",ES10.4,".")') &
9910 pe_here(),ytop,ymax,fn(ytop,GP),dy_df(ytop,GP),fnval,fntop
9920 call MOM_error(FATAL,warnmesg)
993 endif
994 enddo
995
996 ! Find the root using a bracketed variant of Newton's method, starting
997 ! with a false-positon method first guess.
9980 if ((fntop < 0.0) .or. (fnbot > 0.0) .or. (ytop < ybot)) then
999 write(warnmesg, '("PE ",I0," find_root failed to bracket function. y = ",&
10000 &2ES10.4,", fn = ",2ES10.4,".")') pe_here(),ybot,ytop,fnbot,fntop
10010 call MOM_error(FATAL, warnmesg)
1002 endif
1003
10040 if (fntop == 0.0) then ; y = ytop ; fny = fntop
10050 elseif (fnbot == 0.0) then ; y = ybot ; fny = fnbot
1006 else
10070 y = (ybot*fntop - ytop*fnbot) / (fntop - fnbot)
10080 fny = fn(y,GP) - fnval
10090 if (fny < 0.0) then ; fnbot = fny ; ybot = y
10100 else ; fntop = fny ; ytop = y ; endif
1011 endif
1012
10130 do itt=1,50
10140 dy_dfn = dy_df(y,GP)
1015
10160 dy = -1.0* fny * dy_dfn
10170 y_next = y + dy
10180 if ((y_next >= ytop) .or. (y_next <= ybot)) then
1019 ! The Newton's method estimate has escaped bracketing, so use the
1020 ! false-position method instead. The complicated test is to properly
1021 ! handle the case where the iteration is down to roundoff level differences.
10220 y_next = y
10230 if (abs(fntop - fnbot) > EPSILON(y) * (abs(fntop) + abs(fnbot))) &
10240 y_next = (ybot*fntop - ytop*fnbot) / (fntop - fnbot)
1025 endif
1026
10270 dy = y_next - y
10280 if (ABS(dy) < (2.0*EPSILON(y)*(ABS(y) + ABS(y_next)) + 1.0e-20)) then
10290 y = y_next ; exit
1030 endif
10310 y = y_next
1032
10330 fny = fn(y,GP) - fnval
10340 if (fny > 0.0) then ; ytop = y ; fntop = fny
10350 elseif (fny < 0.0) then ; ybot = y ; fnbot = fny
10360 else ; exit ; endif
1037
1038 enddo
10390 if (ABS(y) < 1e-12) y = 0.0
1040
10410 ittmax = itt
10420 find_root = y
10430end function find_root
1044
1045!> This function calculates and returns the value of dx/di in [radian gridpoint-1],
1046!! where x is the longitude in Radians, and i is the integral east-west grid index.
10470function dx_di(x, GP)
1048 real, intent(in) :: x !< The longitude in question [radians]
1049 type(GPS), intent(in) :: GP !< A structure of grid parameters
1050 real :: dx_di ! The derivative of zonal position with the grid index [radian gridpoint-1]
1051
10520 dx_di = (GP%len_lon * 4.0*atan(1.0)) / (180.0 * GP%niglobal)
1053
10540end function dx_di
1055
1056!> This function calculates and returns the integral of the inverse
1057!! of dx/di to the point x, in radians [gridpoints]
10580function Int_di_dx(x, GP)
1059 real, intent(in) :: x !< The longitude in question [radians]
1060 type(GPS), intent(in) :: GP !< A structure of grid parameters
1061 real :: Int_di_dx ! A position in the global i-index space [gridpoints]
1062
10630 Int_di_dx = x * ((180.0 * GP%niglobal) / (GP%len_lon * 4.0*atan(1.0)))
1064
10650end function Int_di_dx
1066
1067!> This subroutine calculates and returns the value of dy/dj in [radian gridpoint-1],
1068!! where y is the latitude in Radians, and j is the integral north-south grid index.
10690function dy_dj(y, GP)
1070 real, intent(in) :: y !< The latitude in question [radians]
1071 type(GPS), intent(in) :: GP !< A structure of grid parameters
1072 real :: dy_dj ! The derivative of meridional position with the grid index [radian gridpoint-1]
1073 ! Local variables
1074 real :: PI ! 3.1415926... calculated as 4*atan(1) [nondim]
1075 real :: C0 ! The constant that converts the nominal y-spacing in
1076 ! gridpoints to the nominal spacing in Radians [radian gridpoint-1]
1077 real :: y_eq_enhance ! The latitude in radians within which the resolution
1078 ! is enhanced [radians]
10790 PI = 4.0*atan(1.0)
10800 if (GP%isotropic) then
10810 C0 = (GP%len_lon * PI) / (180.0 * GP%niglobal)
10820 y_eq_enhance = PI*abs(GP%lat_eq_enhance)/180.0
10830 if (ABS(y) < y_eq_enhance) then
1084 dy_dj = C0 * (cos(y) / (1.0 + 0.5*cos(y) * (GP%lat_enhance_factor - 1.0) * &
10850 (1.0+cos(PI*y/y_eq_enhance)) ))
1086 else
10870 dy_dj = C0 * cos(y)
1088 endif
1089 else
10900 C0 = (GP%len_lat * PI) / (180.0 * GP%njglobal)
10910 dy_dj = C0
1092 endif
1093
10940end function dy_dj
1095
1096!> This subroutine calculates and returns the integral of the inverse
1097!! of dy/dj to the point y in radians [gridpoints]
10980function Int_dj_dy(y, GP)
1099 real, intent(in) :: y !< The latitude in question [radians]
1100 type(GPS), intent(in) :: GP !< A structure of grid parameters
1101 real :: Int_dj_dy ! The grid position of latitude y [gridpoints]
1102 ! Local variables
1103 real :: I_C0 ! The inverse of the constant that converts the
1104 ! nominal spacing in gridpoints to the nominal
1105 ! spacing in Radians [gridpoint radian-1]
1106 real :: PI ! 3.1415926... calculated as 4*atan(1) [nondim]
1107 real :: y_eq_enhance ! The latitude in radians from from the equator within which the meridional
1108 ! grid spacing is enhanced by a factor of GP%lat_enhance_factor [radians]
1109 real :: r ! The y grid position in the global index space [gridpoints]
1110
11110 PI = 4.0*atan(1.0)
11120 if (GP%isotropic) then
11130 I_C0 = (180.0 * GP%niglobal) / (GP%len_lon * PI)
11140 y_eq_enhance = PI*ABS(GP%lat_eq_enhance)/180.0
1115
11160 if (y >= 0.0) then
11170 r = I_C0 * log((1.0 + sin(y))/cos(y))
1118 else
11190 r = -1.0 * I_C0 * log((1.0 - sin(y))/cos(y))
1120 endif
1121
11220 if (y >= y_eq_enhance) then
11230 r = r + I_C0*0.5*(GP%lat_enhance_factor - 1.0)*y_eq_enhance
11240 elseif (y <= -y_eq_enhance) then
11250 r = r - I_C0*0.5*(GP%lat_enhance_factor - 1.0)*y_eq_enhance
1126 else
1127 r = r + I_C0*0.5*(GP%lat_enhance_factor - 1.0) * &
11280 (y + (y_eq_enhance/PI)*sin(PI*y/y_eq_enhance))
1129 endif
1130 else
11310 I_C0 = (180.0 * GP%njglobal) / (GP%len_lat * PI)
11320 r = I_C0 * y
1133 endif
1134
11350 Int_dj_dy = r
11360end function Int_dj_dy
1137
1138!> Extrapolates missing metric data into all the halo regions.
11390subroutine extrapolate_metric(var, jh, missing)
1140 real, dimension(:,:), intent(inout) :: var !< The array in which to fill in halos in arbitrary units [A]
1141 integer, intent(in) :: jh !< The size of the halos to be filled
1142 real, optional, intent(in) :: missing !< The missing data fill value, 0 by default [A]
1143 ! Local variables
1144 real :: badval ! A bad data value [A]
1145 integer :: i, j
1146
11470 badval = 0.0 ; if (present(missing)) badval = missing
1148
1149 ! Fill in southern halo by extrapolating from the computational domain
11500 do j=lbound(var,2)+jh,lbound(var,2),-1 ; do i=lbound(var,1),ubound(var,1)
11510 if (var(i,j)==badval) var(i,j) = 2.0*var(i,j+1)-var(i,j+2)
1152 enddo ; enddo
1153
1154 ! Fill in northern halo by extrapolating from the computational domain
11550 do j=ubound(var,2)-jh,ubound(var,2) ; do i=lbound(var,1),ubound(var,1)
11560 if (var(i,j)==badval) var(i,j) = 2.0*var(i,j-1)-var(i,j-2)
1157 enddo ; enddo
1158
1159 ! Fill in western halo by extrapolating from the computational domain
11600 do j=lbound(var,2),ubound(var,2) ; do i=lbound(var,1)+jh,lbound(var,1),-1
11610 if (var(i,j)==badval) var(i,j) = 2.0*var(i+1,j)-var(i+2,j)
1162 enddo ; enddo
1163
1164 ! Fill in eastern halo by extrapolating from the computational domain
11650 do j=lbound(var,2),ubound(var,2) ; do i=ubound(var,1)-jh,ubound(var,1)
11660 if (var(i,j)==badval) var(i,j) = 2.0*var(i-1,j)-var(i-2,j)
1167 enddo ; enddo
1168
11690end subroutine extrapolate_metric
1170
1171!> This function implements Adcroft's rule for reciprocals, namely that
1172!! Adcroft_Inv(x) = 1/x for |x|>0 or 0 for x=0.
117317604function Adcroft_reciprocal(val) result(I_val)
1174 real, intent(in) :: val !< The value being inverted in arbitrary units [A]
1175 real :: I_val !< The Adcroft reciprocal of val [A-1]
1176
117717604 I_val = 0.0
117817604 if (val /= 0.0) I_val = 1.0/val
117917604end function Adcroft_reciprocal
1180
1181!> Initializes the grid masks and any metrics that come with masks already applied.
1182!!
1183!! Initialize_masks sets mask2dT, mask2dCu, mask2dCv, and mask2dBu to mask out
1184!! flow over any points which are shallower than Dmask and permit an
1185!! appropriate treatment of the boundary conditions. mask2dCu and mask2dCv
1186!! are 0.0 at any points adjacent to a land point. mask2dBu is 0.0 at
1187!! any land or boundary point. For points in the ocean interior or at open boundary
1188!! condition points, mask2dCu, mask2dCv, and mask2dBu are all 1.0.
11890subroutine initialize_masks(G, PF, US, OBC_dir_u, OBC_dir_v, open_corner_OBCs, maskT)
1190 type(dyn_horgrid_type), intent(inout) :: G !< The dynamic horizontal grid type
1191 type(param_file_type), intent(in) :: PF !< Parameter file structure
1192 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
1193 integer, dimension(G%IsdB:G%IedB,G%jsd:G%jed), &
1194 optional, intent(in) :: OBC_dir_u !< Trinary values that indicate whether there
1195 !! is an open boundary condition at zonal velocity
1196 !! faces and their orientation, with 0 for no OBC,
1197 !! a positive value for an Eastern OBC and
1198 !! a negative value for a Western OBC.
1199 integer, dimension(G%isd:G%ied,G%JsdB:G%JedB), &
1200 optional, intent(in) :: OBC_dir_v !< Trinary values that indicate whether there
1201 !! is an open boundary condition at zonal velocity
1202 !! faces and their orientation, with 0 for no OBC,
1203 !! a positive value for a Northern OBC and
1204 !! a negative value for a Southern OBC.
1205 logical, optional, intent(in) :: open_corner_OBCs !< If present and true, the bay-like corner
1206 !! between two orthogonal open boundary segments is open,
1207 !! otherwise it is closed.
1208 real, dimension(G%isd:G%ied,G%jsd:G%jed), &
1209 optional, intent(in) :: maskT !< If present, this array is used to set the
1210 !! the mask at tracer points instead of using the
1211 !! bathymetry to determine the masks [nondim]
1212
1213 ! Local variables
1214 real :: Dmask ! The depth for masking in the same units as G%bathyT [Z ~> m].
1215 real :: min_depth ! The minimum ocean depth in the same units as G%bathyT [Z ~> m].
1216 real :: mask_depth ! The depth shallower than which to mask a point as land [Z ~> m].
1217 logical :: open_corners ! If true, the bay-like corner between two orthogonal open boundary segments is open
1218 character(len=40) :: mdl = "MOM_grid_init initialize_masks"
1219 integer :: i, j
1220
12211 call callTree_enter("initialize_masks(), MOM_grid_initialize.F90")
1222
1223 call get_param(PF, mdl, "MINIMUM_DEPTH", min_depth, &
1224 "If MASKING_DEPTH is unspecified, then anything shallower than "//&
1225 "MINIMUM_DEPTH is assumed to be land and all fluxes are masked out. "//&
1226 "If MASKING_DEPTH is specified, then all depths shallower than "//&
1227 "MINIMUM_DEPTH but deeper than MASKING_DEPTH are rounded to MINIMUM_DEPTH.", &
12281 units="m", default=0.0, scale=US%m_to_Z)
1229 call get_param(PF, mdl, "MASKING_DEPTH", mask_depth, &
1230 "The depth below which to mask points as land points, for which all "//&
1231 "fluxes are zeroed out. MASKING_DEPTH is ignored if it has the special "//&
1232 "default value.", &
12331 units="m", default=-9999.0, scale=US%m_to_Z, do_not_log=present(maskT))
1234
12351 Dmask = mask_depth
12361 if (mask_depth == -9999.0*US%m_to_Z) Dmask = min_depth
1237
12381 open_corners = .false. ; if (present(open_corner_OBCs)) open_corners = open_corner_OBCs
1239
124035484 G%mask2dT(:,:) = 0.0 ; G%mask2dCu(:,:) = 0.0 ; G%mask2dCv(:,:) = 0.0 ; G%mask2dBu(:,:) = 0.0
1241
1242 ! Construct the h-point or T-point mask
12431 if (present(maskT)) then
12440 do j=G%jsd,G%jed ; do i=G%isd,G%ied
12450 G%mask2dT(i,j) = max(min(maskT(i,j), 1.0), 0.0)
1246 enddo ; enddo
1247 else
12488773 do j=G%jsd,G%jed ; do i=G%isd,G%ied
12498772 if (G%bathyT(i,j) <= Dmask) then
12503534 G%mask2dT(i,j) = 0.0
1251 else
12525170 G%mask2dT(i,j) = 1.0
1253 endif
1254 enddo ; enddo
1255 endif
1256
12571 call pass_var(G%mask2dT, G%Domain)
1258
12598705 do j=G%jsd,G%jed ; do I=G%isd,G%ied-1
12608704 G%mask2dCu(I,j) = G%mask2dT(i,j) * G%mask2dT(i+1,j)
1261 enddo ; enddo
1262
12631 if (present(OBC_dir_u)) then
12640 do j=G%jsd,G%jed ; do I=G%isd,G%ied-1
12650 if (OBC_dir_u(I,j) > 0) G%mask2dCu(I,j) = G%mask2dT(i,j)
12660 if (OBC_dir_u(I,j) < 0) G%mask2dCu(I,j) = G%mask2dT(i+1,j)
1267 enddo ; enddo
1268 endif
1269
12708705 do j=G%jsd,G%jed ; do I=G%isd,G%ied-1
1271 ! This mask may be revised later after the open boundary positions are specified.
12728704 G%OBCmaskCu(I,j) = G%mask2dCu(I,j)
1273 enddo ; enddo
1274
12758644 do J=G%jsd,G%jed-1 ; do i=G%isd,G%ied
12768643 G%mask2dCv(i,J) = G%mask2dT(i,j) * G%mask2dT(i,j+1)
1277 enddo ; enddo
1278
12791 if (present(OBC_dir_v)) then
12800 do J=G%jsd,G%jed-1 ; do i=G%isd,G%ied
12810 if (OBC_dir_v(i,J) > 0) G%mask2dCv(i,J) = G%mask2dT(i,j)
12820 if (OBC_dir_v(i,J) < 0) G%mask2dCv(i,J) = G%mask2dT(i,j+1)
1283 enddo ; enddo
1284 endif
1285
12868644 do J=G%jsd,G%jed-1 ; do i=G%isd,G%ied
1287 ! This mask may be revised later after the open boundary positions are specified.
12888643 G%OBCmaskCv(i,J) = G%mask2dCv(i,J)
1289 enddo ; enddo
1290
1291 ! The mask at the vertex can be determined from the masks at the faces.
1292 ! This works at interior ocean points or at convex OBC points.
12938577 do J=G%jsd,G%jed-1 ; do I=G%isd,G%ied-1
12948576 G%mask2dBu(I,J) = (G%mask2dCu(I,j) * G%mask2dCu(I,j+1)) * (G%mask2dCv(i,J) * G%mask2dCv(i+1,J))
1295 enddo ; enddo
1296
1297 ! This block resets masks at the vertices when there are OBCs. The right logic is that if there
1298 ! are 2 or more unmasked OBCs, this point should be open, but to recreate the previous answers,
12991 if (present(OBC_dir_u)) then
13000 do J=G%jsd,G%jed-1 ; do I=G%isd,G%ied-1
1301 ! These are conditions to set open vertex points on a straight north-south coastline
13020 if ((G%mask2dCu(I,j) * OBC_dir_u(I,j)) * (G%mask2dCu(I,j+1) * OBC_dir_u(I,j+1)) > 0.) &
13030 G%mask2dBu(I,J) = 1.0
1304 enddo ; enddo
1305 endif
13061 if (present(OBC_dir_v)) then
13070 do J=G%jsd,G%jed-1 ; do I=G%isd,G%ied-1
1308 ! These are conditions to set open vertex points on a straight east-west coastline
13090 if ((G%mask2dCv(i,J) * OBC_dir_v(i,J)) * (G%mask2dCv(i+1,J) * OBC_dir_v(i+1,J)) > 0.) &
13100 G%mask2dBu(I,J) = 1.0
1311 enddo ; enddo
1312 endif
13131 if (open_corners .and. present(OBC_dir_u) .and. present(OBC_dir_v)) then
13140 do J=G%jsd,G%jed-1 ; do I=G%isd,G%ied-1
1315 ! These are the 4 conditions to set an open point in a concave (bay-like) corner
13160 if ((G%mask2dCu(I,j+1) * OBC_dir_u(I,j+1) < 0.) .and. (G%mask2dCv(i+1,J) * OBC_dir_v(i+1,J) < 0.)) &
13170 G%mask2dBu(I,J) = 1.0 ! Southwestern corner
13180 if ((G%mask2dCu(I,j+1) * OBC_dir_u(I,j+1) > 0.) .and. (G%mask2dCv(i,J) * OBC_dir_v(i,J) < 0.)) &
13190 G%mask2dBu(I,J) = 1.0 ! Southeastern corner
13200 if ((G%mask2dCu(I,j) * OBC_dir_u(I,j) < 0.) .and. (G%mask2dCv(i+1,J) * OBC_dir_v(i+1,J) > 0.)) &
13210 G%mask2dBu(I,J) = 1.0 ! Northwestern corner
13220 if ((G%mask2dCu(I,j) * OBC_dir_u(I,j) > 0.) .and. (G%mask2dCv(i,J) * OBC_dir_v(i,J) > 0.)) &
13230 G%mask2dBu(I,J) = 1.0 ! Northeastern corner
1324 enddo ; enddo
1325 endif
1326
13271 call pass_var(G%mask2dBu, G%Domain, position=CORNER)
13281 call pass_vector(G%mask2dCu, G%mask2dCv, G%Domain, To_All+Scalar_Pair, CGRID_NE)
1329
13308841 do j=G%jsd,G%jed ; do I=G%IsdB,G%IedB
1331 ! This open face length may be revised later.
13328772 G%dy_Cu(I,j) = G%mask2dCu(I,j) * G%dyCu(I,j)
13338772 G%IdxCu_OBCmask(I,j) = G%OBCmaskCu(I,j) * G%IdxCu(I,j)
13348772 G%areaCu(I,j) = G%dxCu(I,j) * G%dy_Cu(I,j)
13358840 G%IareaCu(I,j) = G%mask2dCu(I,j) * Adcroft_reciprocal(G%areaCu(I,j))
1336 enddo ; enddo
1337
13388902 do J=G%JsdB,G%JedB ; do i=G%isd,G%ied
1339 ! This open face length may be revised later.
13408832 G%dx_Cv(i,J) = G%mask2dCv(i,J) * G%dxCv(i,J)
13418832 G%IdyCv_OBCmask(i,J) = G%OBCmaskCv(i,J) * G%IdyCv(i,J)
13428832 G%areaCv(i,J) = G%dyCv(i,J) * G%dx_Cv(i,J)
13438901 G%IareaCv(i,J) = G%mask2dCv(i,J) * Adcroft_reciprocal(G%areaCv(i,J))
1344 enddo ; enddo
1345
13461 call callTree_leave("initialize_masks()")
13471end subroutine initialize_masks
1348
1349!> \namespace mom_grid_initialize
1350!!
1351!! The metric terms have the form Dzp, IDzp, or DXDYp, where z can
1352!! be X or Y, and p can be q, u, v, or h. z describes the direction
1353!! of the metric, while p describes the location. IDzp is the
1354!! inverse of Dzp, while DXDYp is the product of DXp and DYp except
1355!! that areaT is calculated analytically from the latitudes and
1356!! longitudes of the surrounding q points.
1357!!
1358!! On a sphere, a variety of grids can be implemented by defining
1359!! analytic expressions for dx_di, dy_dj (where x and y are latitude
1360!! and longitude, and i and j are grid indices) and the expressions
1361!! for the integrals of their inverses in the four subroutines
1362!! dy_dj, Int_dj_dy, dx_di, and Int_di_dx.
1363!!
1364!! initialize_masks sets up land masks based on the depth field.
1365!! The one argument is the minimum ocean depth. Depths that are
1366!! less than this are interpreted as land points.
1367
1368end module MOM_grid_initialize