-
Lionel GUEZ authoredLionel GUEZ authored
local_extrema.f90 7.45 KiB
module local_extrema_m
implicit none
private local_extremum
contains
subroutine local_extrema(extr_map, ind_extr, innermost_level, local_min, &
field, periodic, my_lbound)
! Finds local extrema in a field and closest value around each
! extremum. The extrema are output both in a map and as a
! list. The input field may be periodic in the first
! dimension. Then it is assumed that it has one duplicate column
! at each bound. If periodic then some extrema may be duplicated
! in extr_map but there is no duplication in ind_extr,
! innermost_level and local_min.
! Note that no coordinate grid is used here so there is no
! assumption on the grid underlying "field".
use nr_util, only: assert_eq
integer, intent(out):: extr_map(:, :) ! (m, n) Map of
! identification numbers of extrema. Identification numbers are
! subscripts in the second dimension of ind_extr. During a call to
! local_extrema, we use the array extr_map to temporarily record
! whether the extremum is a maximum or a minimum: extr_map is
! positive for a maximum, negative for a minimum, 0
! elsewhere. But, at the end of the call, all values of extr_map
! are set positive. If periodic then, on output, extr_map(1, :)
! will be equal to extr_map(m - 1, :) and extr_map(m, :) will be
! equal to extr_map(2, :).
integer, intent(out), allocatable:: ind_extr(:, :) ! (2, n_extr)
! List of couple of subscripts of extrema of field, in the two
! dimensions of the field, considering that the lower bounds of
! field is given by my_lbound.
real, intent(out), allocatable:: innermost_level(:) ! (n_extr)
! Level of innermost contour, for each extremum. By construction,
! innermost_level < extremum for a maximum, > extremum for a
! minimum.
logical, intent(out), allocatable:: local_min(:) ! (n_extr)
real, intent(in):: field(:, :) ! (m, n) Missing value is
! huge(0.). If periodic in the first dimension then the period
! should be m - 2, so field(1, :) should be equal to field(m - 1,
! :) and field(m, :) should be equal to field(2, :).
logical, intent(in):: periodic ! field is periodic in the first dimension
integer, intent(in):: my_lbound(:) ! (2)
! lower bounds of actual argument associated to field
! Local:
integer n_extr, i, j, k, m, n
real innermost_map(size(field, 1), size(field, 2)) ! (m, n) map of
! levels for innermost contours, defined only for extremum points
!--------------------------------------------------------------------
m = assert_eq(size(field, 1), size(extr_map, 1), "local_extrema m")
n = assert_eq(size(field, 2), size(extr_map, 2), "local_extrema n")
extr_map = 0
n_extr = 0
! No extremum on the borders of the second dimension of field so
! loop on columns 2 to n - 1.
if (periodic) then
do j = 2, n - 1
! Row 2:
call local_extremum(innermost_map(2, j), extr_map(:3, j - 1:j + 1), &
n_extr, field(:3, j - 1:j + 1))
extr_map(m, j) = extr_map(2, j)
do i = 3, m - 2
call local_extremum(innermost_map(i, j), &
extr_map(i - 1:i + 1, j - 1:j + 1), n_extr, &
field(i - 1:i + 1, j - 1:j + 1))
end do
! Row m - 1:
call local_extremum(innermost_map(m - 1, j), &
extr_map(m - 2:, j - 1:j + 1), n_extr, &
field(m - 2:, j - 1:j + 1))
extr_map(1, j) = extr_map(m - 1, j)
end do
else
do j = 2, n - 1
! No extremum on the borders of the first dimension of field
! so loop on row 2 to n - 1:
do i = 2, m - 1
call local_extremum(innermost_map(i, j), &
extr_map(i - 1:i + 1, j - 1:j + 1), n_extr, &
field(i - 1:i + 1, j - 1:j + 1))
end do
end do
end if
! Define ind_extr, considering that the lower bounds of field are
! 1 and 1. We will adjust it later for lower bounds my_lbound. We
! do not call pack_indices because we already know the number of
! extrema so the following algorithm is faster (stops at n_extr)
! and uses less memory.
allocate(ind_extr(2, n_extr))
i = 1
j = 2
do k = 1, n_extr
do
! next (i, j)
i = i + 1
if (i == m) then
! Go to next column
i = 2
j = j + 1
end if
if (extr_map(i, j) /= 0) exit
end do
ind_extr(1, k) = i
ind_extr(2, k) = j
! {abs(extr_map(ind_extr(1, k), ind_extr(2, k))) == k}
end do
! Assertion: \forall k, ind_extr(1, k) \in \{2, \dots, m - 1\} et
! ind_extr(2, k) \in \{2, \dots, n - 1\}
allocate(innermost_level(n_extr), local_min(n_extr))
forall (k = 1:n_extr)
innermost_level(k) = innermost_map(ind_extr(1, k), ind_extr(2, k))
local_min(k) = extr_map(ind_extr(1, k), ind_extr(2, k)) < 0
end forall
! Make all values of extr_map positive:
forall (k = 1:n_extr, local_min(k)) &
extr_map(ind_extr(1, k), ind_extr(2, k)) &
= - extr_map(ind_extr(1, k), ind_extr(2, k))
extr_map(1, :) = abs(extr_map(1, :))
extr_map(m, :) = abs(extr_map(m, :))
forall (k = 1:n_extr) ind_extr(:, k) = ind_extr(:, k) - 1 + my_lbound
end subroutine local_extrema
!*********************************************************************
subroutine local_extremum(extr_around, local_map, n_extr, local_field)
! Look for an extremum at the center of a 3x3 domain.
real, intent(out):: extr_around
! Value of extremum different from the center value. May not be
! defined, but guaranteed to be defined if the center is a local
! extremum.
integer, intent(inout):: local_map(- 1:, - 1:) ! (3, 3) Map of
! identification numbers of extrema. "local_map" is positive for a
! maximum, negative for a minimum. Only local_map(0, 0) is
! possibly updated.
integer, intent(inout):: n_extr
real, intent(in):: local_field(- 1:, - 1:) ! (3, 3) Missing value
! is huge(0.).
! Local:
logical diff_center(3, 3) ! values different from the center value
integer count_diff
!--------------------------------------------------------------------
if (all(local_field /= huge(0.))) then
diff_center = local_field /= local_field(0, 0)
count_diff = count(diff_center) ! {between 0 and 8}
if (count_diff /= 0) then
! {count_diff >= 1}
extr_around = maxval(local_field, diff_center)
if (local_field(0, 0) > extr_around) then
! {local maximum, possibly degenerate}
if (count_diff == 8 .or. all(local_map <= 0)) then
! {not a degenerate maximum or none of the previously
! examined values is a maximum}
n_extr = n_extr + 1
local_map(0, 0) = n_extr
end if
else
! {local_field(0, 0) < maxval(local_field, diff_center)}
extr_around = minval(local_field, diff_center)
if (local_field(0, 0) < extr_around &
.and. (count_diff == 8 .or. all(local_map >= 0))) then
! {local minimum, not degenerate or none of previously
! examined values is a minimum}
n_extr = n_extr + 1
local_map(0, 0) = - n_extr
end if
end if
end if
end if
end subroutine local_extremum
end module local_extrema_m