Skip to content
Snippets Groups Projects
inst_eddies.f90 7.51 KiB
program inst_eddies

  use, intrinsic:: ieee_arithmetic, only: IEEE_SUPPORT_DATATYPE, &
       ieee_support_nan, ieee_value, IEEE_QUIET_NAN
  use, intrinsic:: ISO_FORTRAN_ENV

  ! Libraries:
  use contour_531, only: convert_to_ind
  use jumble, only: new_unit
  use netcdf, only: nf90_nowrite
  use netcdf95, only: nf95_open, find_coord, nf95_inquire_dimension, &
       nf95_get_var, nf95_close, nf95_get_att
  use nr_util, only: assert, deg_to_rad, twopi, pi

  use derived_types, only: snapshot, null_ssh_contour, missing_speed, shp_tr
  use get_var_m, only: get_var
  use nearby_extr_m, only: nearby_extr
  use set_all_outerm_m, only: set_all_outerm
  use set_max_speed_m, only: set_max_speed
  use shp_tr_close_m, only: shp_tr_close
  use shp_tr_create_m, only: shp_tr_create
  use write_eddy_m, only: write_eddy

  implicit none

  type(snapshot) s
  TYPE(shp_tr) hshp
  integer i, k, unit

  real:: min_amp = 0.001
  ! minimum amplitude of ssh, between extremum and outermost contour,
  ! in m

  real:: min_radius = 25.
  ! minimum radius of the equal-area disk for an outermost contour, in km

  integer ncid, dimid, varid, copy, iostat

  integer nlon, nlat
  ! size of ssh array in input NetCDF, assuming no repeated point if
  ! the grid is global

  real lon_min, lon_max, lat_min, lat_max ! longitudes and latitudes, in degrees
  real step(2) ! longitude and latitude steps, in rad
  logical periodic ! grid is periodic in longitude
  real time

  integer:: max_radius(2) = [20, 20]
  ! maximum radius of an eddy in longitude and latitude, in number of
  ! grid points

  real, allocatable:: ssh(:, :)
  ! (1 - max_radius(1):nlon + max_radius(1), nlat) if periodic, else
  ! (nlon, nlat) sea-surface height, in m

  real, allocatable:: u(:, :), v(:, :)
  ! (1 - max_radius(1):nlon + max_radius(1), nlat) if periodic, else
  ! (nlon, nlat) wind, in m s-1

  real corner(2)
  ! longitude and latitude of the corner of the whole grid, in rad

  ! Window around each extremum:

  integer llc(2) ! indices in global grid of lower left corner
  integer urc(2) ! indices in global grid of upper right corner

  real corner_window(2) ! longitude and latitude of the window around each
  ! extremum, in rad
  real, allocatable:: outside_points(:, :) ! (2, :) longitude and
  ! latitude, in rad, of all the significant extrema, except the
  ! target extremum

  character(len = 30) time_unit
  character(len = 200) iomsg

  namelist /main_nml/ min_amp, max_radius, min_radius

  !--------------------------------------------------------------

  call assert(IEEE_SUPPORT_DATATYPE(0.), ieee_support_nan(0.), &
       "inst_eddies: not enough IEEE support")

  ! Trick to verify that the directory "SHP_triplet" exists:
  call new_unit(unit)
  open(unit, file = "SHP_triplet/extremum.dbf", status = "replace", &
       action = "write", iostat = iostat, iomsg = iomsg)
  if (iostat /= 0) then
     write(unit = error_unit, fmt = *) trim(iomsg)
     write(unit = error_unit, fmt = *) &
          'Have you created the directory "SHP_triplet"?'
     stop 1
  end if
  close(unit)

  ! We need this before looking at the input files:
  write(unit = error_unit, nml = main_nml)
  write(unit = error_unit, fmt = *) "Enter namelist main_nml."
  read(unit = *, nml = main_nml)
  write(unit = *, nml = main_nml)

  call nf95_open("h.nc", nf90_nowrite, ncid)

  call find_coord(ncid, varid = varid, std_name = "time")
  call nf95_get_att(ncid, varid, name = "units", values = time_unit)
  call assert(time_unit == "days since 1950-01-01 00:00:00" &
       .or. time_unit == "days since 1950-1-1 00:00:00", "main: bad time unit")

  ! We are assuming there is a single date in the input file:
  call nf95_get_var(ncid, varid, time)

  k = nint(time)
  call assert(abs(time - k) < 0.1, "main: bad time value")

  call find_coord(ncid, dimid = dimid, varid = varid, std_name = "longitude")
  call nf95_inquire_dimension(ncid, dimid, nclen = nlon)
  call nf95_get_var(ncid, varid, lon_min)
  call nf95_get_var(ncid, varid, lon_max, start = [nlon])

  call find_coord(ncid, dimid = dimid, varid = varid, std_name = "latitude")
  call nf95_inquire_dimension(ncid, dimid, nclen = nlat)
  call nf95_get_var(ncid, varid, lat_min)
  call nf95_get_var(ncid, varid, lat_max, start = [nlat])

  call assert(lon_max > lon_min .and. lat_max > lat_min, &
       "inst_eddies coordinate order")
  step = [(lon_max - lon_min) / (nlon - 1), (lat_max - lat_min) / (nlat - 1)]  &
       * deg_to_rad

  ! As we are requiring the grid spacing to be uniform, the value of
  ! "periodic" may be deduced from the values of step(1) and nlon:
  periodic = nint(twopi / step(1)) == nlon
  print *, "periodic = ", periodic

  call assert(2 * max_radius(1) * step(1) < pi, "inst_eddies max_radius")
  ! (Let us require this even if not periodic. This is clearer.)

  copy = merge(max_radius(1), 0, periodic)
  allocate(ssh(1 - copy:nlon + copy, nlat), u(1 - copy:nlon + copy, nlat), &
       v(1 - copy:nlon + copy, nlat))
  corner = [lon_min, lat_min] * deg_to_rad

  ! Read ssh, u and v:

  call get_var(periodic, max_radius(1), ssh, ncid, nlon, name = "adt", &
       new_fill_value = huge(0.))
  ! (We cannot keep the original fill value because Contour_531
  ! works with an upper limit for valid values.)
  call nf95_close(ncid)

  call nf95_open("uv.nc", nf90_nowrite, ncid)
  call get_var(periodic, max_radius(1), u, ncid, nlon, name = "ugos", &
       new_fill_value = ieee_value(0., IEEE_QUIET_NAN))
  call get_var(periodic, max_radius(1), v, ncid, nlon, name = "vgos", &
       new_fill_value = ieee_value(0., IEEE_QUIET_NAN))
  ! (We will need quiet NaNs rather the original fill values for u and
  ! v when we compute the max-speed contours and when we search the
  ! ssh of max-speed contours.)
  call nf95_close(ncid)

  call set_all_outerm(s, min_amp, max_radius, step, periodic, ssh, &
       corner, min_area = pi * (min_radius * 1e3)**2)

  ! Done with outermost contours, now let us take care of the
  ! max-speed contours.

  do i = 1, s%number_vis_extr
     if (s%list_vis(i)%valid) then
        ! Restrict the field to the outermost contour:

        llc = floor(convert_to_ind(minval(s%list_vis(i)%out_cont%points, &
             dim = 2), corner, step))

        urc = ceiling(convert_to_ind(maxval( &
             s%list_vis(i)%out_cont%points, dim = 2), corner, step))

        ! Should have no effect except because of roundup error:
        urc(2) = min(urc(2), nlat)
        if (.not. periodic) urc(1) = min(urc(1), nlon)

        corner_window = corner + (llc - 1) * step

        outside_points = nearby_extr(s%extr_map(llc(1):urc(1), &
             llc(2):urc(2)), s%list_vis, i)

        if (periodic) outside_points(1, :) = outside_points(1, :) &
             + ceiling((corner_window(1) - outside_points(1, :)) / twopi) &
             * twopi
        ! (Shift the longitude of each point to a value close to
        ! the longitude of the target extremum.)

        call set_max_speed(s%list_vis(i), s%ind_extr(:, i) - llc + 1, &
             outside_points, ssh(llc(1):urc(1), llc(2):urc(2)), &
             u(llc(1):urc(1), llc(2):urc(2)), &
             v(llc(1):urc(1), llc(2):urc(2)), corner_window, step)
     else
        s%list_vis(i)%speed_cont = null_ssh_contour()
        s%list_vis(i)%max_speed = missing_speed
        s%list_vis(i)%radius4 = 0
     end if
  end do

  call shp_tr_create(hshp, shp_tr_dir = "SHP_triplet")

  ! Write snapshot:
  do i = 1, s%number_vis_extr
     call write_eddy(s%list_vis(i), hshp, k, i)
  end do
  print *, "Number of extrema:", s%number_vis_extr
  CALL shp_tr_close(hshp)
  print *, 'Created shapefiles in SHP_triplet.'

end program inst_eddies