LCOV - code coverage report
Current view: top level - eos/private - eos_blend.f90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.5 % 67 66
Test Date: 2025-05-08 18:23:42 Functions: 100.0 % 5 5

            Line data    Source code
       1              : ! ***********************************************************************
       2              : !
       3              : !   Copyright (C) 2022  The MESA Team
       4              : !
       5              : !   This program is free software: you can redistribute it and/or modify
       6              : !   it under the terms of the GNU Lesser General Public License
       7              : !   as published by the Free Software Foundation,
       8              : !   either version 3 of the License, or (at your option) any later version.
       9              : !
      10              : !   This program is distributed in the hope that it will be useful,
      11              : !   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              : !   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13              : !   See the GNU Lesser General Public License for more details.
      14              : !
      15              : !   You should have received a copy of the GNU Lesser General Public License
      16              : !   along with this program. If not, see <https://www.gnu.org/licenses/>.
      17              : !
      18              : ! ***********************************************************************
      19              : 
      20              : module eos_blend
      21              :       use const_def, only: dp
      22              :       use math_lib
      23              :       use auto_diff
      24              :       use eos_def
      25              : 
      26              :       implicit none
      27              :       private
      28              :       public :: is_contained, min_distance_to_polygon
      29              : 
      30              :       contains
      31              : 
      32              :       !! Determines which quadrant the given point is in.
      33              :       !!
      34              :       !! @param p The coordinates of the point (x,y).
      35              :       !! @param q The quadrant index.
      36      3860072 :       integer function quadrant(p) result(q)
      37              :          real(dp), intent(in) :: p(2)
      38              : 
      39      3860072 :          if (p(1) >= 0d0 .and. p(2) >= 0d0) then
      40              :             q = 1
      41      2301872 :          else if (p(1) < 0d0 .and. p(2) >= 0d0) then
      42              :             q = 2
      43      1194344 :          else if (p(1) < 0d0 .and. p(2) < 0d0) then
      44              :             q = 3
      45              :          else
      46       918592 :             q = 4
      47              :          end if
      48              : 
      49      3860072 :       end function quadrant
      50              : 
      51              :       !! Determines the winding number of a polygon around the origin.
      52              :       !!
      53              :       !! Implements the winding number algorithm of
      54              :       !! Moscato, Titolo, Feliu, and Munoz (https://shemesh.larc.nasa.gov/people/cam/publications/FM2019-draft.pdf)
      55              :       !!
      56              :       !! @param num_points The number of points specifying the polygon.
      57              :       !! @param coords The coordinates of the polygon. Given as an array of shape (num_coords, 2) storing (x,y) pairs.
      58              :       !! @param w The winding number of the polygon about the origin.
      59       241257 :       integer function winding_number(num_points, coords) result(w)
      60              :          integer, intent(in) :: num_points
      61              :          real(dp), intent(in) :: coords(num_points, 2)
      62      1206285 :          real(dp) :: determinant, x_start(2), x_end(2), dist(2)
      63              :          integer :: i, q_start, q_end, increment
      64              : 
      65              :          w = 0
      66      2171293 :          do i=1,num_points
      67      5790108 :             x_start = coords(i,1:2)
      68              : 
      69      1930036 :             if (i == num_points) then
      70       723771 :                x_end = coords(1,1:2)
      71              :             else
      72      5066337 :                x_end = coords(i+1,1:2)
      73              :             end if
      74              : 
      75      1930036 :             dist(1) = x_end(1) - x_start(1)
      76      1930036 :             dist(2) = x_end(2) - x_start(2)
      77              : 
      78      1930036 :             determinant = x_start(2) * dist(1) - x_start(1) * dist(2)
      79              : 
      80      1930036 :             q_start = quadrant(x_start)
      81      1930036 :             q_end = quadrant(x_end)
      82              : 
      83      1930036 :             if (q_start == q_end) then
      84              :                increment = 0
      85       938260 :             else if (q_end == mod(q_start,4) + 1) then
      86              :                increment = 1
      87       448084 :             else if (q_start == mod(q_end, 4) + 1) then
      88              :                increment = -1
      89        26748 :             else if (determinant < 0d0) then
      90              :                increment = 2
      91              :             else
      92            0 :                increment = -2
      93              :             end if
      94              : 
      95      2171293 :             w = w + increment
      96              :          end do
      97       241257 :       end function winding_number
      98              : 
      99              :       !! Determines if a point is contained within a polygon specified by connecting a sequence of points.
     100              :       !! This is done by checking the winding number of the polygon around the point.
     101              :       !!
     102              :       !! @param num_points The number of points specifying the polygon.
     103              :       !! @param coords The coordinates of the polygon. An array of shape (num_points, 2) storing (x,y) pairs.
     104              :       !! @param p The point whose distance to compute.
     105              :       !! @param contained Whether or not the point p is contained in the polygon.
     106       241257 :       logical function is_contained(num_points, coords, p) result(contained)
     107              :          integer, intent(in) :: num_points
     108              :          real(dp), intent(in) :: coords(num_points,2)
     109              :          type(auto_diff_real_2var_order1), intent(in) :: p(2)
     110              : 
     111              :          integer :: i, w
     112      4583843 :          real(dp) :: diff(num_points,2)
     113              : 
     114      2171293 :          do i=1,num_points
     115      1930036 :             diff(i,1) = coords(i,1) - p(1)%val
     116      2171293 :             diff(i,2) = coords(i,2) - p(2)%val
     117              :          end do
     118              : 
     119       241257 :          w = winding_number(num_points, diff)
     120       241257 :          if (w /= 0) then
     121              :             contained = .true.
     122              :          else
     123       210669 :             contained = .false.
     124              :          end if
     125              : 
     126       241257 :       end function is_contained
     127              : 
     128              :       !! Computes the minimum distance from a given point to a given line segment.
     129              :       !!
     130              :       !! @param line_start The coordinates of the start of the line segment (x,y).
     131              :       !! @param line_end The coordinates of the end of the line segment (x,y).
     132              :       !! @param p The point whose distance to compute.
     133      1930036 :       type(auto_diff_real_2var_order1) function min_distance_from_point_to_line_segment(line_start, line_end, p) result(d)
     134              :          real(dp), intent(in) :: line_start(2), line_end(2)
     135              :          type(auto_diff_real_2var_order1), intent(in) :: p(2)
     136              :          real(dp), parameter :: eps = 1e-10  ! To avoid singularity in the derivatives near edges and corners.
     137              : 
     138              :          type(auto_diff_real_2var_order1) :: diff_line(2), diff_start(2), diff_end(2)
     139              :          type(auto_diff_real_2var_order1) :: length_squared, lambda, nearest_point_on_line(2)
     140              : 
     141              : 
     142      1930036 :          diff_start(1) = p(1) - line_start(1)
     143      1930036 :          diff_start(2) = p(2) - line_start(2)
     144              : 
     145      1930036 :          diff_end(1) = p(1) - line_end(1)
     146      1930036 :          diff_end(2) = p(2) - line_end(2)
     147              : 
     148      1930036 :          diff_line(1) = line_end(1) - line_start(1)
     149      1930036 :          diff_line(2) = line_end(2) - line_start(2)
     150              : 
     151      1930036 :          length_squared = pow2(diff_line(1)) + pow2(diff_line(2))
     152              : 
     153              :          ! First, find the nearest_point_on_line. We parameterize this by
     154              :          ! (nearest) = (start) + lambda (end - start)
     155              :          !
     156              :          ! We can then pretend the line is infinite, solve for lambda, and then restrict it to lie in [0,1].
     157      1930036 :          lambda = (diff_start(1) * diff_line(1) + diff_start(2) * diff_line(2)) / length_squared
     158              : 
     159      1930036 :          if (lambda < 0d0) then  ! Nearest point is line_start
     160       427032 :             d = sqrt(pow2(diff_start(1)) + pow2(diff_start(2)))
     161      1503004 :          else if (lambda > 1d0) then  ! Nearest point is line_end
     162       574339 :             d = sqrt(pow2(diff_end(1)) + pow2(diff_end(2)))
     163              :          else  ! Nearest point is interior to the line segment
     164       928665 :             nearest_point_on_line(1) = line_start(1) + lambda * diff_line(1)
     165       928665 :             nearest_point_on_line(2) = line_start(2) + lambda * diff_line(2)
     166       928665 :             nearest_point_on_line(1) = nearest_point_on_line(1) - p(1)
     167       928665 :             nearest_point_on_line(2) = nearest_point_on_line(2) - p(2)
     168       928665 :             d = sqrt(pow2(nearest_point_on_line(1)) + pow2(nearest_point_on_line(2)) + pow2(eps))
     169              :          end if
     170              : 
     171      1930036 :       end function min_distance_from_point_to_line_segment
     172              : 
     173              :       !! Computes the distance to the nearest line segment.
     174              :       !! This is done by looping over segments, computing the minimum distance to each, and
     175              :       !! returning the smallest of those differences.
     176              :       !!
     177              :       !! @param num_points The number of points specifying the polygon.
     178              :       !! @param coords The coordinates of the polygon. An array of shape (num_points, 2) storing (x,y) pairs.
     179              :       !! @param p The point whose distance to compute.
     180       241257 :       type(auto_diff_real_2var_order1) function min_distance_to_polygon(num_points, coords, p) result(d)
     181              :          integer, intent(in) :: num_points
     182              :          real(dp), intent(in) :: coords(num_points, 2)
     183              :          type(auto_diff_real_2var_order1), intent(in) :: p(2)
     184              : 
     185      1206285 :          real(dp) :: line_start(2), line_end(2)
     186              :          type(auto_diff_real_2var_order1) :: line_dist
     187              :          integer :: i
     188              : 
     189       241257 :          d = 1d99
     190      2171293 :          do i=1,num_points
     191      1930036 :             line_start(1) = coords(i,1)
     192      1930036 :             line_start(2) = coords(i,2)
     193              : 
     194      1930036 :             if (i == num_points) then
     195       241257 :                line_end(1) = coords(1,1)
     196       241257 :                line_end(2) = coords(1,2)
     197              :             else
     198      1688779 :                line_end(1) = coords(i+1,1)
     199      1688779 :                line_end(2) = coords(i+1,2)
     200              :             end if
     201              : 
     202      1930036 :             line_dist = min_distance_from_point_to_line_segment(line_start, line_end, p)
     203      2171293 :             d = min(d, line_dist)
     204              :          end do
     205              : 
     206       241257 :       end function min_distance_to_polygon
     207              : 
     208              : end module eos_blend
        

Generated by: LCOV version 2.0-1