SOCA
soca_geom_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-2021 UCAR
2 !
3 ! This software is licensed under the terms of the Apache Licence Version 2.0
4 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5 !
6 
7 !> Geometry module
9 
10 ! jedi modules
11 use atlas_module, only: atlas_functionspace_pointcloud, atlas_fieldset, &
12  atlas_field, atlas_real, atlas_integer, atlas_geometry, atlas_indexkdtree
13 use fckit_configuration_module, only: fckit_configuration
14 use fckit_mpi_module, only: fckit_mpi_comm
15 use kinds, only: kind_real
16 
17 ! mom6 / fms modules
18 use fms_io_mod, only : fms_io_init, fms_io_exit, &
19  register_restart_field, restart_file_type, &
20  restore_state, free_restart_type, save_restart
21 use mom_diag_remap, only : diag_remap_ctrl, diag_remap_init, diag_remap_configure_axes, &
22  diag_remap_end, diag_remap_update
23 use mom_domains, only : mom_domain_type
24 use mom_eos, only : eos_type
25 use mpp_domains_mod, only : mpp_get_compute_domain, mpp_get_data_domain, &
26  mpp_get_global_domain, mpp_update_domains
27 ! soca modules
31 
32 
33 implicit none
34 private
35 
36 
37 ! ------------------------------------------------------------------------------
38 ! ------------------------------------------------------------------------------
39 
40 
41 ! ------------------------------------------------------------------------------
42 !> Geometry data structure
43 type, public :: soca_geom
44  type(mom_domain_type), pointer :: domain !< Ocean model domain
45  integer :: nzo, nzo_zstar
46 
47  !> \name local domain indices
48  !! \{
49  integer :: isc, iec, jsc, jec
50  !> \}
51 
52  !> \name data domain indices
53  !! \{
54  integer :: isd, ied, jsd, jed
55  !> \}
56 
57  !> \name global domain indices
58  !! \{
59  integer :: isg, ieg, jsg, jeg
60  !> \}
61 
62  !> \name local compute domain indices
63  !! \{
64  integer :: iscl, iecl, jscl, jecl
65  !> \}
66 
67  !> \name local data domain indices
68  !! \{
69  integer :: isdl, iedl, jsdl, jedl
70  !> \}
71 
72  !> \name grid latitude/longitude
73  !! \{
74  real(kind=kind_real), allocatable, dimension(:) :: lonh !< cell center nominal longitude
75  real(kind=kind_real), allocatable, dimension(:) :: lath !< cell center nominal latitude
76  real(kind=kind_real), allocatable, dimension(:) :: lonq !< cell corner nominal longitude
77  real(kind=kind_real), allocatable, dimension(:) :: latq !< cell corner nominal latitude
78  real(kind=kind_real), allocatable, dimension(:,:) :: lon !< Tracer grid longitude
79  real(kind=kind_real), allocatable, dimension(:,:) :: lat !< Tracer grid latitude
80  real(kind=kind_real), allocatable, dimension(:,:) :: lonu !< U grid longitude
81  real(kind=kind_real), allocatable, dimension(:,:) :: latu !< U grid latitude
82  real(kind=kind_real), allocatable, dimension(:,:) :: lonv !< V grid longitude
83  real(kind=kind_real), allocatable, dimension(:,:) :: latv !< V grid latitude
84  !> \}
85 
86  !> \name ocean/land masks
87  !! \{
88 
89  !> mask for tracer grid. 0 = land 1 = ocean
90  real(kind=kind_real), allocatable, dimension(:,:) :: mask2d
91  !> mask for U grid. 0 = land 1 = ocean
92  real(kind=kind_real), allocatable, dimension(:,:) :: mask2du
93  !> mask for V grid. 0 = land 1 = ocean
94  real(kind=kind_real), allocatable, dimension(:,:) :: mask2dv
95  !> \}
96 
97  !> \name other grid properties
98  !! \{
99  real(kind=kind_real), allocatable, dimension(:,:) :: sin_rot !< sine of rotation between logical grid north
100  real(kind=kind_real), allocatable, dimension(:,:) :: cos_rot !< cosine of rotation between logical grid north
101  real(kind=kind_real), allocatable, dimension(:,:) :: cell_area !< cell area (m^2)
102  real(kind=kind_real), allocatable, dimension(:,:) :: rossby_radius !< rossby radius (m) at the gridpoint
103  real(kind=kind_real), allocatable, dimension(:,:) :: distance_from_coast !< distance to closest land grid point (m)
104  real(kind=kind_real), allocatable, dimension(:,:,:) :: h !< layer thickness (m)
105  real(kind=kind_real), allocatable, dimension(:,:,:) :: h_zstar
106  !> \}
107 
108  !> instance of the metadata that is read in from a config file upon initialization
109  type(soca_fields_metadata) :: fields_metadata
110 
111  logical, private :: save_local_domain = .false. !< If true, save the local geometry for each pe.
112  character(len=:), allocatable :: geom_grid_file !< filename of geometry
113  type(fckit_mpi_comm) :: f_comm !< MPI communicator
114  type(atlas_functionspace_pointcloud) :: afunctionspace !< atlas stuff
115 
116 
117  contains
118 
119 
120  !> \copybrief soca_geom_init \see soca_geom_init
121  procedure :: init => soca_geom_init
122 
123  !> \copybrief soca_geom_end \see soca_geom_end
124  procedure :: end => soca_geom_end
125 
126  !> \copybrief soca_geom_set_atlas_lonlat \see soca_geom_set_atlas_lonlat
127  procedure :: set_atlas_lonlat => soca_geom_set_atlas_lonlat
128 
129  !> \copybrief soca_geom_fill_atlas_fieldset \see soca_geom_fill_atlas_fieldset
130  procedure :: fill_atlas_fieldset => soca_geom_fill_atlas_fieldset
131 
132  !> \copybrief soca_geom_clone \see soca_geom_clone
133  procedure :: clone => soca_geom_clone
134 
135  !> \copybrief soca_geom_gridgen \see soca_geom_gridgen
136  procedure :: gridgen => soca_geom_gridgen
137 
138  !> \copybrief soca_geom_thickness2depth \see soca_geom_thickness2depth
139  procedure :: thickness2depth => soca_geom_thickness2depth
140 
141  !> \copybrief soca_geom_struct2atlas \see soca_geom_struct2atlas
142  procedure :: struct2atlas => soca_geom_struct2atlas
143 
144  !> \copybrief soca_geom_atlas2struct \ see soca_geom_atlas2struct
145  procedure :: atlas2struct => soca_geom_atlas2struct
146 
147  !> \copybrief soca_geom_write \see soca_geom_write
148  procedure :: write => soca_geom_write
149 
150 end type soca_geom
151 
152 ! ------------------------------------------------------------------------------
153 contains
154 ! ------------------------------------------------------------------------------
155 
156 ! ------------------------------------------------------------------------------
157 !> Setup geometry object
158 !!
159 !! \related soca_geom_mod::soca_geom
160 subroutine soca_geom_init(self, f_conf, f_comm)
161  class(soca_geom), intent(out) :: self
162  type(fckit_configuration), intent(in) :: f_conf
163  type(fckit_mpi_comm), intent(in) :: f_comm !< MPI communicator for this geometry
164 
165  character(len=:), allocatable :: str
166  logical :: full_init = .false.
167 
168  ! MPI communicator
169  self%f_comm = f_comm
170 
171  ! Domain decomposition
172  call soca_geomdomain_init(self%Domain, self%nzo, f_comm)
173 
174  ! User-defined grid filename
175  if ( .not. f_conf%get("geom_grid_file", self%geom_grid_file) ) &
176  self%geom_grid_file = "soca_gridspec.nc" ! default if not found
177 
178  ! Allocate geometry arrays
179  call soca_geom_allocate(self)
180 
181  ! Check if a full initialization is required, default to false
182  if ( .not. f_conf%get("full_init", full_init) ) full_init = .false.
183 
184  ! Read the geometry from file by default,
185  ! skip this step if a full init is required
186  if ( .not. full_init) call soca_geom_read(self)
187 
188  ! Fill halo
189  call mpp_update_domains(self%lon, self%Domain%mpp_domain)
190  call mpp_update_domains(self%lat, self%Domain%mpp_domain)
191  call mpp_update_domains(self%lonu, self%Domain%mpp_domain)
192  call mpp_update_domains(self%latu, self%Domain%mpp_domain)
193  call mpp_update_domains(self%lonv, self%Domain%mpp_domain)
194  call mpp_update_domains(self%latv, self%Domain%mpp_domain)
195  call mpp_update_domains(self%sin_rot, self%Domain%mpp_domain)
196  call mpp_update_domains(self%cos_rot, self%Domain%mpp_domain)
197  call mpp_update_domains(self%mask2d, self%Domain%mpp_domain)
198  call mpp_update_domains(self%mask2du, self%Domain%mpp_domain)
199  call mpp_update_domains(self%mask2dv, self%Domain%mpp_domain)
200  call mpp_update_domains(self%cell_area, self%Domain%mpp_domain)
201  call mpp_update_domains(self%rossby_radius, self%Domain%mpp_domain)
202  call mpp_update_domains(self%distance_from_coast, self%Domain%mpp_domain)
203 
204  ! Set output option for local geometry
205  if ( .not. f_conf%get("save_local_domain", self%save_local_domain) ) &
206  self%save_local_domain = .false.
207 
208  ! process the fields metadata file
209  call f_conf%get_or_die("fields metadata", str)
210  call self%fields_metadata%create(str)
211 
212 end subroutine soca_geom_init
213 
214 
215 ! ------------------------------------------------------------------------------
216 !> Geometry destructor
217 !!
218 !! \related soca_geom_mod::soca_geom
219 subroutine soca_geom_end(self)
220  class(soca_geom), intent(out) :: self
221 
222  if (allocated(self%lonh)) deallocate(self%lonh)
223  if (allocated(self%lath)) deallocate(self%lath)
224  if (allocated(self%lonq)) deallocate(self%lonq)
225  if (allocated(self%latq)) deallocate(self%latq)
226  if (allocated(self%lon)) deallocate(self%lon)
227  if (allocated(self%lat)) deallocate(self%lat)
228  if (allocated(self%lonu)) deallocate(self%lonu)
229  if (allocated(self%latu)) deallocate(self%latu)
230  if (allocated(self%lonv)) deallocate(self%lonv)
231  if (allocated(self%latv)) deallocate(self%latv)
232  if (allocated(self%sin_rot)) deallocate(self%sin_rot)
233  if (allocated(self%cos_rot)) deallocate(self%cos_rot)
234  if (allocated(self%mask2d)) deallocate(self%mask2d)
235  if (allocated(self%mask2du)) deallocate(self%mask2du)
236  if (allocated(self%mask2dv)) deallocate(self%mask2dv)
237  if (allocated(self%cell_area)) deallocate(self%cell_area)
238  if (allocated(self%rossby_radius)) deallocate(self%rossby_radius)
239  if (allocated(self%distance_from_coast)) deallocate(self%distance_from_coast)
240  if (allocated(self%h)) deallocate(self%h)
241  if (allocated(self%h_zstar)) deallocate(self%h_zstar)
242  nullify(self%Domain)
243  call self%afunctionspace%final()
244 
245 end subroutine soca_geom_end
246 
247 
248 ! --------------------------------------------------------------------------------------------------
249 !> Set ATLAS lonlat fieldset
250 !!
251 !! \related soca_geom_mod::soca_geom
252 subroutine soca_geom_set_atlas_lonlat(self, afieldset)
253  class(soca_geom), intent(inout) :: self
254  type(atlas_fieldset), intent(inout) :: afieldset
255 
256  real(kind_real), pointer :: real_ptr(:,:)
257  type(atlas_field) :: afield
258 
259  ! Create lon/lat field
260  afield = atlas_field(name="lonlat", kind=atlas_real(kind_real), shape=(/2,(self%iec-self%isc+1)*(self%jec-self%jsc+1)/))
261  call afield%data(real_ptr)
262  real_ptr(1,:) = reshape(self%lon(self%isc:self%iec,self%jsc:self%jec),(/(self%iec-self%isc+1)*(self%jec-self%jsc+1)/))
263  real_ptr(2,:) = reshape(self%lat(self%isc:self%iec,self%jsc:self%jec),(/(self%iec-self%isc+1)*(self%jec-self%jsc+1)/))
264  call afieldset%add(afield)
265 
266 end subroutine soca_geom_set_atlas_lonlat
267 
268 
269 ! --------------------------------------------------------------------------------------------------
270 !> Fill ATLAS fieldset
271 !!
272 !! \related soca_geom_mod::soca_geom
273 subroutine soca_geom_fill_atlas_fieldset(self, afieldset)
274  class(soca_geom), intent(inout) :: self
275  type(atlas_fieldset), intent(inout) :: afieldset
276 
277  integer :: i, jz, n
278  integer, pointer :: int_ptr_2(:,:)
279  real(kind=kind_real), pointer :: real_ptr_1(:), real_ptr_2(:,:)
280  type(atlas_field) :: afield
281 
282  ! Add area
283  afield = self%afunctionspace%create_field(name='area', kind=atlas_real(kind_real), levels=0)
284  call afield%data(real_ptr_1)
285  real_ptr_1 = reshape(self%cell_area(self%isc:self%iec,self%jsc:self%jec),(/(self%iec-self%isc+1)*(self%jec-self%jsc+1)/))
286  call afieldset%add(afield)
287  call afield%final()
288 
289  ! Add vertical unit
290  afield = self%afunctionspace%create_field(name='vunit', kind=atlas_real(kind_real), levels=self%nzo)
291  call afield%data(real_ptr_2)
292  do jz=1,self%nzo
293  real_ptr_2(jz,:) = real(jz, kind_real)
294  end do
295  call afieldset%add(afield)
296  call afield%final()
297 
298  ! Add geographical mask
299  afield = self%afunctionspace%create_field(name='gmask', kind=atlas_integer(kind(0)), levels=self%nzo)
300  call afield%data(int_ptr_2)
301  do jz=1,self%nzo
302  int_ptr_2(jz,:) = int(reshape(self%mask2d(self%isc:self%iec,self%jsc:self%jec), &
303  & (/(self%iec-self%isc+1)*(self%jec-self%jsc+1)/)))
304  end do
305  call afieldset%add(afield)
306  call afield%final()
307 
308 end subroutine soca_geom_fill_atlas_fieldset
309 
310 
311 ! ------------------------------------------------------------------------------
312 !> Clone, self = other
313 !!
314 !! \related soca_geom_mod::soca_geom
315 subroutine soca_geom_clone(self, other)
316  class(soca_geom), intent(inout) :: self
317  class(soca_geom), intent(in) :: other
318 
319  ! Clone communicator
320  self%f_comm = other%f_comm
321 
322  ! Clone fms domain and vertical levels
323  self%Domain => other%Domain
324  self%nzo = other%nzo
325 
326  !
327  self%geom_grid_file = other%geom_grid_file
328 
329  ! Allocate and clone geometry
330  call soca_geom_allocate(self)
331  self%lonh = other%lonh
332  self%lath = other%lath
333  self%lonq = other%lonq
334  self%latq = other%latq
335  self%lon = other%lon
336  self%lat = other%lat
337  self%lonu = other%lonu
338  self%latu = other%latu
339  self%lonv = other%lonv
340  self%latv = other%latv
341  self%sin_rot = other%sin_rot
342  self%cos_rot = other%cos_rot
343  self%mask2d = other%mask2d
344  self%mask2du = other%mask2du
345  self%mask2dv = other%mask2dv
346  self%cell_area = other%cell_area
347  self%rossby_radius = other%rossby_radius
348  self%distance_from_coast = other%distance_from_coast
349  self%h = other%h
350  call self%fields_metadata%clone(other%fields_metadata)
351 end subroutine soca_geom_clone
352 
353 
354 ! ------------------------------------------------------------------------------
355 !> Generate the grid with the help of mom6, and save it to a file for use later.
356 !!
357 !! \related soca_geom_mod::soca_geom
358 subroutine soca_geom_gridgen(self)
359  class(soca_geom), intent(inout) :: self
360 
361  ! allocate variables for regridding to zstar coord
362  type(soca_mom6_config) :: mom6_config
363  type(diag_remap_ctrl) :: remap_ctrl
364  type(eos_type), pointer :: eqn_of_state
365  integer :: k
366  real(kind=kind_real), allocatable :: tracer(:,:,:)
367  logical :: answers_2018 = .false.
368 
369  ! Generate grid
370  call soca_mom6_init(mom6_config, partial_init=.true.)
371  self%lonh = mom6_config%grid%gridlont
372  self%lath = mom6_config%grid%gridlatt
373  self%lonq = mom6_config%grid%gridlonb
374  self%latq = mom6_config%grid%gridlatb
375  self%lon = mom6_config%grid%GeoLonT
376  self%lat = mom6_config%grid%GeoLatT
377  self%lonu = mom6_config%grid%geoLonCu
378  self%latu = mom6_config%grid%geoLatCu
379  self%lonv = mom6_config%grid%geoLonCv
380  self%latv = mom6_config%grid%geoLatCv
381 
382  self%sin_rot = mom6_config%grid%sin_rot
383  self%cos_rot = mom6_config%grid%cos_rot
384 
385  self%mask2d = mom6_config%grid%mask2dT
386  self%mask2du = mom6_config%grid%mask2dCu
387  self%mask2dv = mom6_config%grid%mask2dCv
388  self%cell_area = mom6_config%grid%areaT
389  self%h = mom6_config%MOM_CSp%h
390 
391  ! Setup intermediate zstar coordinate
392  allocate(tracer(self%isd:self%ied, self%jsd:self%jed, self%nzo))
393  tracer = 0.d0 ! dummy tracer
394  call diag_remap_init(remap_ctrl, coord_tuple='ZSTAR, ZSTAR, ZSTAR', answers_2018=answers_2018)
395  call diag_remap_configure_axes(remap_ctrl, mom6_config%GV, mom6_config%scaling, mom6_config%param_file)
396  self%nzo_zstar = remap_ctrl%nz
397  if (allocated(self%h_zstar)) deallocate(self%h_zstar)
398  allocate(self%h_zstar(self%isd:self%ied, self%jsd:self%jed, 1:remap_ctrl%nz))
399 
400  ! Compute intermediate vertical coordinate self%h_zstar
401  call diag_remap_update(remap_ctrl, &
402  mom6_config%grid, &
403  mom6_config%GV, &
404  mom6_config%scaling, &
405  self%h, tracer, tracer, eqn_of_state, self%h_zstar)
406  call diag_remap_end(remap_ctrl)
407 
408  ! Get Rossby Radius
409  call soca_geom_rossby_radius(self)
410 
411  call soca_geom_distance_from_coast(self)
412 
413  ! Output to file
414  call soca_geom_write(self)
415 
416 end subroutine soca_geom_gridgen
417 
418 
419 ! ------------------------------------------------------------------------------
420 !> Allocate memory and point to mom6 data structure
421 !!
422 !! \related soca_geom_mod::soca_geom
423 subroutine soca_geom_allocate(self)
424  class(soca_geom), intent(inout) :: self
425 
426  integer :: nzo
427  integer :: isd, ied, jsd, jed
428 
429  ! Get domain shape (number of levels, indices of data and compute domain)
430  call soca_geom_get_domain_indices(self, "compute", self%isc, self%iec, self%jsc, self%jec)
431  call soca_geom_get_domain_indices(self, "data", isd, ied, jsd, jed)
432  self%isd = isd ; self%ied = ied ; self%jsd = jsd; self%jed = jed
433  call soca_geom_get_domain_indices(self, "global", self%isg, self%ieg, self%jsg, self%jeg)
434  call soca_geom_get_domain_indices(self, "compute", self%iscl, self%iecl, self%jscl, self%jecl, local=.true.)
435  call soca_geom_get_domain_indices(self, "data", self%isdl, self%iedl, self%jsdl, self%jedl, local=.true.)
436  nzo = self%nzo
437 
438  ! Allocate arrays on compute domain
439  allocate(self%lonh(self%isg:self%ieg)); self%lonh = 0.0_kind_real
440  allocate(self%lath(self%jsg:self%jeg)); self%lath = 0.0_kind_real
441  allocate(self%lonq(self%isg:self%ieg)); self%lonq = 0.0_kind_real
442  allocate(self%latq(self%jsg:self%jeg)); self%latq = 0.0_kind_real
443  allocate(self%lon(isd:ied,jsd:jed)); self%lon = 0.0_kind_real
444  allocate(self%lat(isd:ied,jsd:jed)); self%lat = 0.0_kind_real
445  allocate(self%lonu(isd:ied,jsd:jed)); self%lonu = 0.0_kind_real
446  allocate(self%latu(isd:ied,jsd:jed)); self%latu = 0.0_kind_real
447  allocate(self%lonv(isd:ied,jsd:jed)); self%lonv = 0.0_kind_real
448  allocate(self%latv(isd:ied,jsd:jed)); self%latv = 0.0_kind_real
449 
450  allocate(self%sin_rot(isd:ied,jsd:jed)); self%sin_rot = 0.0_kind_real
451  allocate(self%cos_rot(isd:ied,jsd:jed)); self%cos_rot = 0.0_kind_real
452 
453  allocate(self%mask2d(isd:ied,jsd:jed)); self%mask2d = 0.0_kind_real
454  allocate(self%mask2du(isd:ied,jsd:jed)); self%mask2du = 0.0_kind_real
455  allocate(self%mask2dv(isd:ied,jsd:jed)); self%mask2dv = 0.0_kind_real
456 
457  allocate(self%cell_area(isd:ied,jsd:jed)); self%cell_area = 0.0_kind_real
458  allocate(self%rossby_radius(isd:ied,jsd:jed)); self%rossby_radius = 0.0_kind_real
459  allocate(self%distance_from_coast(isd:ied,jsd:jed)); self%distance_from_coast = 0.0_kind_real
460  allocate(self%h(isd:ied,jsd:jed,1:nzo)); self%h = 0.0_kind_real
461 
462 end subroutine soca_geom_allocate
463 
464 
465 ! ------------------------------------------------------------------------------
466 !> Calcuate distance from coast for the ocean points
467 !!
468 !! \related soca_geom_mod::soca_geom
469 subroutine soca_geom_distance_from_coast(self)
470  class(soca_geom), intent(inout) :: self
471 
472  type(atlas_indexkdtree) :: kd
473  type(atlas_geometry) :: ageometry
474  integer :: i, j, idx(1)
475  integer :: num_land_l, num_land
476  integer, allocatable :: rcvcnt(:), displs(:)
477  real(kind=kind_real), allocatable :: land_lon(:), land_lat(:)
478  real(kind=kind_real), allocatable :: land_lon_l(:), land_lat_l(:)
479  real(kind=kind_real) :: closest_lon, closest_lat
480 
481 
482  ! collect lat/lon of all land points on all procs
483  ! (use the tracer grid and mask for this)
484  ! --------------------------------------------------
485  allocate(rcvcnt(self%f_comm%size()))
486  allocate(displs(self%f_comm%size()))
487 
488  num_land_l = count(self%mask2d(self%isc:self%iec, self%jsc:self%jec)==0.0)
489  call self%f_comm%allgather(num_land_l, rcvcnt)
490  num_land = sum(rcvcnt)
491 
492  displs(1) = 0
493  do j = 2, self%f_comm%size()
494  displs(j) = displs(j-1) + rcvcnt(j-1)
495  enddo
496 
497  allocate(land_lon_l(num_land_l))
498  allocate(land_lat_l(num_land_l))
499  land_lon_l = pack(self%lon(self%isc:self%iec, self%jsc:self%jec), &
500  mask=self%mask2d(self%isc:self%iec,self%jsc:self%jec)==0.0)
501  land_lat_l = pack(self%lat(self%isc:self%iec, self%jsc:self%jec), &
502  mask=self%mask2d(self%isc:self%iec,self%jsc:self%jec)==0.0)
503  allocate(land_lon(num_land))
504  allocate(land_lat(num_land))
505 
506  call self%f_comm%allgather(land_lon_l, land_lon, num_land_l, rcvcnt, displs)
507  call self%f_comm%allgather(land_lat_l, land_lat, num_land_l, rcvcnt, displs)
508 
509 
510  ! pass land points to the kd tree
511  !---------------------------------------
512  ageometry = atlas_geometry("Earth") !< TODO: remove this hardcoded value so
513  ! we can do DA on Europa at some point.
514  ! (Next AOP??)
515  kd = atlas_indexkdtree(ageometry)
516  call kd%reserve(num_land)
517  call kd%build(num_land, land_lon, land_lat)
518 
519 
520  ! for each point in local domain, lookup distance to nearest land point
521  ! ---------------------------------------
522  do i = self%isc, self%iec
523  do j = self%jsc, self%jec
524  call kd%closestPoints( self%lon(i,j), self%lat(i,j), 1, idx )
525  self%distance_from_coast(i,j) = ageometry%distance( &
526  self%lon(i,j), self%lat(i,j), land_lon(idx(1)), land_lat(idx(1)))
527  enddo
528  enddo
529 
530  ! cleanup
531  call kd%final()
532 
533 end subroutine
534 
535 
536 ! ------------------------------------------------------------------------------
537 !> Read and store Rossby Radius of deformation
538 !!
539 !! Input data is interpolated to the current grid.
540 !!
541 !! \related soca_geom_mod::soca_geom
542 subroutine soca_geom_rossby_radius(self)
543  class(soca_geom), intent(inout) :: self
544 
545  integer :: unit, i, n
546  real(kind=kind_real) :: dum
547  real(kind=kind_real), allocatable :: lon(:),lat(:),rr(:)
548  integer :: isc, iec, jsc, jec
549  integer :: io
550 
551  ! read in the file
552  unit = 20
553  open(unit=unit,file="rossrad.dat",status="old",action="read")
554  n = 0
555  do
556  read(unit,*,iostat=io)
557  if (io/=0) exit
558  n = n+1
559  end do
560  rewind(unit)
561  allocate(lon(n),lat(n),rr(n))
562  do i = 1, n
563  read(unit,*) lat(i),lon(i),dum,rr(i)
564  end do
565  close(unit)
566 
567  ! convert to meters
568  rr = rr * 1e3
569 
570  ! remap
571  isc = self%isc ; iec = self%iec ; jsc = self%jsc ; jec = self%jec
572  call soca_remap_idw(lon, lat, rr, self%lon(isc:iec,jsc:jec), &
573  self%lat(isc:iec,jsc:jec), self%rossby_radius(isc:iec,jsc:jec) )
574 
575 end subroutine soca_geom_rossby_radius
576 
577 
578 ! ------------------------------------------------------------------------------
579 !> Write geometry to file
580 !!
581 !! \related soca_geom_mod::soca_geom
582 subroutine soca_geom_write(self)
583  class(soca_geom), intent(in) :: self
584 
585  character(len=256) :: geom_output_pe
586  integer :: pe
587  character(len=8) :: fmt = '(I5.5)'
588  character(len=1024) :: strpe
589  integer :: ns
590  integer :: idr_geom
591  type(restart_file_type) :: geom_restart
592 
593  ! Save global domain
594  call fms_io_init()
595  idr_geom = register_restart_field(geom_restart, &
596  &self%geom_grid_file, &
597  &'lonh', &
598  &self%lonh(:), &
599  domain=self%Domain%mpp_domain)
600  idr_geom = register_restart_field(geom_restart, &
601  &self%geom_grid_file, &
602  &'lath', &
603  &self%lath(:), &
604  domain=self%Domain%mpp_domain)
605  idr_geom = register_restart_field(geom_restart, &
606  &self%geom_grid_file, &
607  &'lonq', &
608  &self%lonq(:), &
609  domain=self%Domain%mpp_domain)
610  idr_geom = register_restart_field(geom_restart, &
611  &self%geom_grid_file, &
612  &'latq', &
613  &self%latq(:), &
614  domain=self%Domain%mpp_domain)
615  idr_geom = register_restart_field(geom_restart, &
616  &self%geom_grid_file, &
617  &'lon', &
618  &self%lon(:,:), &
619  domain=self%Domain%mpp_domain)
620  idr_geom = register_restart_field(geom_restart, &
621  &self%geom_grid_file, &
622  &'lat', &
623  &self%lat(:,:), &
624  domain=self%Domain%mpp_domain)
625  idr_geom = register_restart_field(geom_restart, &
626  &self%geom_grid_file, &
627  &'lonu', &
628  &self%lonu(:,:), &
629  domain=self%Domain%mpp_domain)
630  idr_geom = register_restart_field(geom_restart, &
631  &self%geom_grid_file, &
632  &'latu', &
633  &self%latu(:,:), &
634  domain=self%Domain%mpp_domain)
635  idr_geom = register_restart_field(geom_restart, &
636  &self%geom_grid_file, &
637  &'lonv', &
638  &self%lonv(:,:), &
639  domain=self%Domain%mpp_domain)
640  idr_geom = register_restart_field(geom_restart, &
641  &self%geom_grid_file, &
642  &'latv', &
643  &self%latv(:,:), &
644  domain=self%Domain%mpp_domain)
645  idr_geom = register_restart_field(geom_restart, &
646  &self%geom_grid_file, &
647  &'sin_rot', &
648  &self%sin_rot(:,:), &
649  domain=self%Domain%mpp_domain)
650  idr_geom = register_restart_field(geom_restart, &
651  &self%geom_grid_file, &
652  &'cos_rot', &
653  &self%cos_rot(:,:), &
654  domain=self%Domain%mpp_domain)
655  idr_geom = register_restart_field(geom_restart, &
656  &self%geom_grid_file, &
657  &'area', &
658  &self%cell_area(:,:), &
659  domain=self%Domain%mpp_domain)
660  idr_geom = register_restart_field(geom_restart, &
661  &self%geom_grid_file, &
662  &'rossby_radius', &
663  &self%rossby_radius(:,:), &
664  domain=self%Domain%mpp_domain)
665  idr_geom = register_restart_field(geom_restart, &
666  &self%geom_grid_file, &
667  &'mask2d', &
668  &self%mask2d(:,:), &
669  domain=self%Domain%mpp_domain)
670  idr_geom = register_restart_field(geom_restart, &
671  &self%geom_grid_file, &
672  &'mask2du', &
673  &self%mask2du(:,:), &
674  domain=self%Domain%mpp_domain)
675  idr_geom = register_restart_field(geom_restart, &
676  &self%geom_grid_file, &
677  &'mask2dv', &
678  &self%mask2dv(:,:), &
679  domain=self%Domain%mpp_domain)
680  idr_geom = register_restart_field(geom_restart, &
681  &self%geom_grid_file, &
682  &'h', &
683  &self%h(:,:,:), &
684  domain=self%Domain%mpp_domain)
685  idr_geom = register_restart_field(geom_restart, &
686  &self%geom_grid_file, &
687  &'nzo_zstar', &
688  &self%nzo_zstar, &
689  domain=self%Domain%mpp_domain)
690  idr_geom = register_restart_field(geom_restart, &
691  &self%geom_grid_file, &
692  &'h_zstar', &
693  &self%h_zstar(:,:,:), &
694  domain=self%Domain%mpp_domain)
695  idr_geom = register_restart_field(geom_restart, &
696  &self%geom_grid_file, &
697  &'distance_from_coast', &
698  &self%distance_from_coast(:,:), &
699  domain=self%Domain%mpp_domain)
700 
701  call save_restart(geom_restart, directory='')
702  call free_restart_type(geom_restart)
703  call fms_io_exit()
704 
705  if (self%save_local_domain) then
706  ! Save local compute grid
707  pe = self%f_comm%rank()
708 
709  write (strpe,fmt) pe
710  geom_output_pe='geom_output_'//trim(strpe)//'.nc'
711 
712  ns = (self%iec - self%isc + 1) * (self%jec - self%jsc + 1 )
713  call write2pe(reshape(self%mask2d(self%isc:self%iec,self%jsc:self%jec),(/ns/)),'mask',geom_output_pe,.false.)
714  call write2pe(reshape(self%lon(self%isc:self%iec,self%jsc:self%jec),(/ns/)),'lon',geom_output_pe,.true.)
715  call write2pe(reshape(self%lat(self%isc:self%iec,self%jsc:self%jec),(/ns/)),'lat',geom_output_pe,.true.)
716  end if
717 
718 end subroutine soca_geom_write
719 
720 
721 ! ------------------------------------------------------------------------------
722 !> Read geometry from file
723 !!
724 !! \related soca_geom_mod::soca_geom
725 subroutine soca_geom_read(self)
726  class(soca_geom), intent(inout) :: self
727 
728  integer :: idr_geom
729  type(restart_file_type) :: geom_restart
730 
731  call fms_io_init()
732  idr_geom = register_restart_field(geom_restart, &
733  &self%geom_grid_file, &
734  &'lonh', &
735  &self%lonh(:), &
736  domain=self%Domain%mpp_domain)
737  idr_geom = register_restart_field(geom_restart, &
738  &self%geom_grid_file, &
739  &'lath', &
740  &self%lath(:), &
741  domain=self%Domain%mpp_domain)
742  idr_geom = register_restart_field(geom_restart, &
743  &self%geom_grid_file, &
744  &'lonq', &
745  &self%lonq(:), &
746  domain=self%Domain%mpp_domain)
747  idr_geom = register_restart_field(geom_restart, &
748  &self%geom_grid_file, &
749  &'latq', &
750  &self%latq(:), &
751  domain=self%Domain%mpp_domain)
752  idr_geom = register_restart_field(geom_restart, &
753  &self%geom_grid_file, &
754  &'lon', &
755  &self%lon(:,:), &
756  domain=self%Domain%mpp_domain)
757  idr_geom = register_restart_field(geom_restart, &
758  &self%geom_grid_file, &
759  &'lat', &
760  &self%lat(:,:), &
761  domain=self%Domain%mpp_domain)
762  idr_geom = register_restart_field(geom_restart, &
763  &self%geom_grid_file, &
764  &'lonu', &
765  &self%lonu(:,:), &
766  domain=self%Domain%mpp_domain)
767  idr_geom = register_restart_field(geom_restart, &
768  &self%geom_grid_file, &
769  &'latu', &
770  &self%latu(:,:), &
771  domain=self%Domain%mpp_domain)
772  idr_geom = register_restart_field(geom_restart, &
773  &self%geom_grid_file, &
774  &'lonv', &
775  &self%lonv(:,:), &
776  domain=self%Domain%mpp_domain)
777  idr_geom = register_restart_field(geom_restart, &
778  &self%geom_grid_file, &
779  &'latv', &
780  &self%latv(:,:), &
781  domain=self%Domain%mpp_domain)
782  idr_geom = register_restart_field(geom_restart, &
783  &self%geom_grid_file, &
784  &'sin_rot', &
785  &self%sin_rot(:,:), &
786  domain=self%Domain%mpp_domain)
787  idr_geom = register_restart_field(geom_restart, &
788  &self%geom_grid_file, &
789  &'cos_rot', &
790  &self%cos_rot(:,:), &
791  domain=self%Domain%mpp_domain)
792  idr_geom = register_restart_field(geom_restart, &
793  &self%geom_grid_file, &
794  &'area', &
795  &self%cell_area(:,:), &
796  domain=self%Domain%mpp_domain)
797  idr_geom = register_restart_field(geom_restart, &
798  &self%geom_grid_file, &
799  &'rossby_radius', &
800  &self%rossby_radius(:,:), &
801  domain=self%Domain%mpp_domain)
802  idr_geom = register_restart_field(geom_restart, &
803  &self%geom_grid_file, &
804  &'mask2d', &
805  &self%mask2d(:,:), &
806  domain=self%Domain%mpp_domain)
807  idr_geom = register_restart_field(geom_restart, &
808  &self%geom_grid_file, &
809  &'mask2du', &
810  &self%mask2du(:,:), &
811  domain=self%Domain%mpp_domain)
812  idr_geom = register_restart_field(geom_restart, &
813  &self%geom_grid_file, &
814  &'mask2dv', &
815  &self%mask2dv(:,:), &
816  domain=self%Domain%mpp_domain)
817  idr_geom = register_restart_field(geom_restart, &
818  &self%geom_grid_file, &
819  &'h', &
820  &self%h(:,:,:), &
821  domain=self%Domain%mpp_domain)
822  idr_geom = register_restart_field(geom_restart, &
823  &self%geom_grid_file, &
824  &'distance_from_coast', &
825  &self%distance_from_coast(:,:), &
826  domain=self%Domain%mpp_domain)
827  call restore_state(geom_restart, directory='')
828  call free_restart_type(geom_restart)
829  call fms_io_exit()
830 
831 end subroutine soca_geom_read
832 
833 
834 ! ------------------------------------------------------------------------------
835 !> Get indices for compute or data domain
836 !!
837 !! \related soca_geom_mod::soca_geom
838 subroutine soca_geom_get_domain_indices(self, domain_type, is, ie, js, je, local)
839  class(soca_geom), intent(in) :: self
840  character(len=*), intent(in) :: domain_type !< "compute", "data", or "global"
841  integer, intent(out) :: is, ie, js, je
842  logical, optional, intent(in) :: local
843 
844  integer :: isc, iec, jsc, jec
845  integer :: isd, ied, jsd, jed
846  integer :: isg, ieg, jsg, jeg
847 
848  call mpp_get_compute_domain(self%Domain%mpp_domain,isc,iec,jsc,jec)
849  call mpp_get_data_domain(self%Domain%mpp_domain,isd,ied,jsd,jed)
850  call mpp_get_global_domain(self%Domain%mpp_domain, isg, ieg, jsg, jeg)
851  if (present(local)) then
852  isc = isc - (isd-1) ; iec = iec - (isd-1) ; ied = ied - (isd-1) ; isd = 1
853  jsc = jsc - (jsd-1) ; jec = jec - (jsd-1) ; jed = jed - (jsd-1) ; jsd = 1
854  end if
855 
856  select case (trim(domain_type))
857  case ("compute")
858  is = isc; ie = iec; js = jsc; je = jec;
859  case ("data")
860  is = isd; ie = ied; js = jsd; je = jed;
861  case ("global")
862  is = isg; ie = ieg; js = jsg; je = jeg;
863  end select
864 
865 end subroutine soca_geom_get_domain_indices
866 
867 
868 ! ------------------------------------------------------------------------------
869 !> Get layer depth from layer thicknesses
870 !!
871 !! \related soca_geom_mod::soca_geom
872 subroutine soca_geom_thickness2depth(self, h, z)
873  class(soca_geom), intent(in ) :: self
874  real(kind=kind_real), intent(in ) :: h(:,:,:) !< Layer thickness
875  real(kind=kind_real), intent(inout) :: z(:,:,:) !< Mid-layer depth
876 
877  integer :: is, ie, js, je, i, j, k
878 
879  ! Should check shape of z
880  is = lbound(h,dim=1)
881  ie = ubound(h,dim=1)
882  js = lbound(h,dim=2)
883  je = ubound(h,dim=2)
884 
885  !allocate(z(is:ie, js:je, self%nzo))
886 
887  do i = is, ie
888  do j = js, je
889  do k = 1, self%nzo
890  if (k.eq.1) then
891  z(i,j,k) = 0.5_kind_real*h(i,j,k)
892  else
893  z(i,j,k) = sum(h(i,j,1:k-1))+0.5_kind_real*h(i,j,k)
894  end if
895  end do
896  end do
897  end do
898 end subroutine soca_geom_thickness2depth
899 
900 
901 ! ------------------------------------------------------------------------------
902 !> Copy a structured field into an ATLAS fieldset
903 !!
904 !! \related soca_geom_mod::soca_geom
905 subroutine soca_geom_struct2atlas(self, dx_struct, dx_atlas)
906  class(soca_geom), intent(in ) :: self
907  real(kind=kind_real), intent(in ) :: dx_struct(:,:)
908  type(atlas_fieldset), intent(out) :: dx_atlas
909 
910  real(kind_real), pointer :: real_ptr(:)
911  type(atlas_field) :: afield
912 
913  dx_atlas = atlas_fieldset()
914  afield = self%afunctionspace%create_field('var',kind=atlas_real(kind_real),levels=0)
915  call dx_atlas%add(afield)
916  call afield%data(real_ptr)
917  real_ptr = reshape(dx_struct(self%iscl:self%iecl, self%jscl:self%jecl),(/(self%iecl-self%iscl+1)*(self%jecl-self%jscl+1)/))
918  call afield%final()
919 
920 end subroutine soca_geom_struct2atlas
921 
922 
923 ! ------------------------------------------------------------------------------
924 !> Copy a structured field from an ATLAS fieldset
925 !!
926 !! \related soca_geom_mod::soca_geom
927 subroutine soca_geom_atlas2struct(self, dx_struct, dx_atlas)
928  class(soca_geom), intent(in ) :: self
929  real(kind=kind_real), intent(inout) :: dx_struct(:,:)
930  type(atlas_fieldset), intent(inout) :: dx_atlas
931 
932  real(kind_real), pointer :: real_ptr(:)
933  type(atlas_field) :: afield
934 
935  afield = dx_atlas%field('var')
936  call afield%data(real_ptr)
937  dx_struct(self%iscl:self%iecl, self%jscl:self%jecl) = reshape(real_ptr,(/(self%iecl-self%iscl+1),(self%jecl-self%jscl+1)/))
938  call afield%final()
939 
940 end subroutine soca_geom_atlas2struct
941 
942 ! ------------------------------------------------------------------------------
943 
944 end module soca_geom_mod
Metadata for soca_fields.
Geometry module.
subroutine, public soca_mom6_init(mom6_config, partial_init)
Setup/initialize/prepare mom6 for time integration.
Definition: soca_mom6.F90:114
subroutine, public soca_geomdomain_init(Domain, nk, f_comm)
Initialize mom6's domain.
Definition: soca_mom6.F90:81
various utility functions
Definition: soca_utils.F90:7
subroutine, public soca_remap_idw(lon_src, lat_src, data_src, lon_dst, lat_dst, data_dst)
inverse distance weighted remaping (modified Shepard's method)
Definition: soca_utils.F90:189
subroutine, public write2pe(vec, varname, filename, append)
per-PE file output, used by soca_geom to write per-PE grid
Definition: soca_utils.F90:116
A collection of soca_field_metadata types representing ALL possible fields (state,...
Geometry data structure.
Data structure neccessary to initialize/run mom6.
Definition: soca_mom6.F90:57