Repository: NOAA-EMC/WW3
Pull request #1392: Calculate ww3_bounc geographic distances with haversines and utilize verbose=2 output
Author: login: dahonegger
Created: 2025-03-13T20:13:46Z | Merged: 2025-03-25T17:18:07Z

# Pull Request Summary
Bug fix for [#1344](https://github.com/NOAA-EMC/WW3/issues/1344)

## Description
When assigning NetCDF spectra to input boundary nodes, ww3_bounc uses an iterative method to identify the two nearest neighbor spectra in spec.list. Custom diagnostic output in ww3_bounc reveals that points even one kilometer apart can be calculated as having a separation distance of zero. Moreover, some input point - boundary point pairs result in a returned distance of NaN. As a result, assignment of spectra to boundary nodes is sensitive to the ordering of the boundary nodes and to the ordering of the provided spectra in a manner that is not explicit in the code.

This fix replaces usage of DIST_SPHERE in [ww3_bounc](model/src/ww3_bounc.F90) with a new function, DIST_HAVERSINE, that is more robust when distances are on the order of 1 km or less. Both DIST_SPHERE and DIST_HAVERSINE are defined in [w3servmd](model/src/w3servmd.F90).

This fix additionally includes the option for more detailed diagnostic output from ww3_bounc that is triggered with the VERBOSE=2 namelist value in ww3_bounc.nml.

There are expected changes to regression tests [tp2.8](regtests/ww3_tp2.8) and [tp2.17](regtests/ww3_tp2.17) owing to modifications to the weight assignments of the linearly interpolated boundary spectra. 

<!--
Provide a detailed description of what this PR does.
What bug does it fix, or what feature does it add?
Is a change of answers expected from this PR?

Please also include the following information: 
* Add any suggestions for a reviewer 
* Mention any labels that should be added:  _bug_, _documentation_, _enhancement_, _new feature_
* Are answer changes expected from this PR? Please describe the changes and the reason why in addition to which of the following labels would apply: _mod_def change_, _out_grd change_, _out_pnt change_, _restart file change_, _Regression test_ 
-->

### Issue(s) addressed
<!--
* Please list any issues associated with this PR, including those the PR will fix/close. For example:  
- fixes #<issue_number>
- fixes noaa-emc/ww3/issues/<issue_number>
-->
- Fixes [#1344](https://github.com/NOAA-EMC/WW3/issues/1344)

### Commit Message
<!--
Please provide a short summary of this PR, which will be used during _Squash and Merge_ and will be shown as a git log message.  Be sure to add any co-authors here. 
-->
Calculate ww3_bounc geographic distances with haversines and utilize verbose=2 output

### Check list  

<!-- After creating the PR you can check each of the items below that have been completed -->

- [x] Branch is up to date with the authoritative repository (NOAA-EMC) develop branch. 
- [ ] Checked the [checklist for a developer submitting to develop](https://github.com/NOAA-EMC/WW3/wiki/Code-Management#checklist-for-a-developer-submitting-to-develop). 
- [ ] If a version number update is required, checked the [updating version number](https://github.com/NOAA-EMC/WW3/wiki/Code-Management#checklist-for-updating-version-number) checklist. 
- [ ] If a new feature was added, a regression test for testing the new feature is added. 
 
### Testing

```
mww3_test_03/./work_PR3_UNO_MPI_d2_c                     (4 files differ)
mww3_test_03/./work_PR3_UNO_MPI_e_c                     (1 files differ)
mww3_test_03/./work_PR2_UQ_MPI_e                     (1 files differ)
mww3_test_03/./work_PR3_UNO_MPI_d2                     (3 files differ)
mww3_test_03/./work_PR2_UNO_MPI_d2                     (6 files differ)
mww3_test_03/./work_PR3_UQ_MPI_d2                     (9 files differ)
mww3_test_03/./work_PR2_UQ_MPI_d2                     (6 files differ)
mww3_test_03/./work_PR3_UQ_MPI_d2_c                     (8 files differ)
mww3_test_03/./work_PR1_MPI_e                     (1 files differ)
mww3_test_03/./work_PR1_MPI_d2                     (5 files differ)
mww3_test_03/./work_PR3_UQ_MPI_e                     (1 files differ)
mww3_test_09/./work_MPI_ASCII                     (0 files differ)
ww3_tp2.17/./work_ma                     (1 files differ)
ww3_tp2.17/./work_mc1                     (1 files differ)
ww3_tp2.17/./work_mb                     (1 files differ)
ww3_tp2.17/./work_mc                     (1 files differ)
ww3_tp2.17/./work_ma1                     (1 files differ)
ww3_tp2.17/./work_a                     (1 files differ)
ww3_tp2.17/./work_c                     (1 files differ)
ww3_tp2.17/./work_b                     (1 files differ)
ww3_tp2.6/./work_ST4_ASCII                     (0 files differ)
ww3_tp2.8/./work_PR3_UQ                     (4 files differ)
```

* How were these changes tested? **Via matrix**
* Are the changes covered by regression tests? (If not, why? Do new tests need to be added?) **Tests that use ww3_bounc with FLAGLL=.true. cover the changes, but the newly utilized VERBOSE=2 value in ww3_bounc.nml is not tested.**
* Have the matrix regression tests been run (if yes, please note HPC and compiler)? **Yes, on an ERDC HPC with Intel**
* Please indicate the expected changes in the regression test output, (Note the [list](https://github.com/NOAA-EMC/WW3/wiki/How-to-use-matrix.comp-to-compare-regtests-with-develop#4-look-at-results) of known non-identical tests.) **Expected changes to the regression tests include: modifications to the weight assignments of the linearly interpolated boundary spectra.**
* Please provide the summary output of matrix.comp (_matrix.Diff.txt_, _matrixCompFull.txt_ and _matrixCompSummary.txt_):

[matrixCompSummary.txt](https://github.com/user-attachments/files/19235974/matrixCompSummary.txt)
[matrixDiff.txt](https://github.com/user-attachments/files/19235976/matrixDiff.txt)
[matrixCompFull.txt](https://github.com/user-attachments/files/19235977/matrixCompFull.txt)



Diff:
diff --git a/model/src/w3servmd.F90 b/model/src/w3servmd.F90
index 600e6e5728..44d986f7d9 100644
--- a/model/src/w3servmd.F90
+++ b/model/src/w3servmd.F90
@@ -18,6 +18,7 @@ MODULE W3SERVMD
   !/    01-Mar-2016 : Added W3THRTN and W3XYRTN for post  ( version 6.02 )
   !/                  processing rotated grid data
   !/    15-Jan-2021 : Added UV_TO_MAG_DIR routine         ( version 7.12 )
+  !/    02-Jan-2025 : Added DIST_HAVERSINE routine        ( version 7.xx )
   !/
   !/    Copyright 2009-2012 National Weather Service (NWS),
   !/       National Oceanic and Atmospheric Administration.  All rights
@@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radians to Degrees (180/PI)
+    !/
+    !/ ------------------------------------------------------------------- /
+    !/ Parameter list
+    !/
+    REAL, INTENT(IN)        :: LON1, LAT1, LON2, LAT2
+    !/
+    !/ ------------------------------------------------------------------- /
+    !/ Local parameters
+    !/
+    REAL                    :: DLON, DLAT, A, C 
+    !/
+    !/ ------------------------------------------------------------------- /
+    !/
+    
+    ! Compute differences in latitude and longitude
+    DLAT = (LAT2 - LAT1) * DERA
+    DLON = (LON2 - LON1) * DERA
+
+    ! Compute the haversine of the central angle
+    A = SIN(DLAT / 2.0)**2 + COS(LAT1 * DERA) * COS(LAT2 * DERA) * SIN(DLON / 2.0)**2
+
+    ! Compute the angular distance (c), ensuring no precision issues
+    C = 2.0 * ATAN2(SQRT(A), SQRT(MAX(0.0, 1.0 - A)))
+
+    ! Compute the spherical distance
+    DIST_HAVERSINE = RADE * C
+
+    RETURN
+  END FUNCTION DIST_HAVERSINE
+  !/ ------------------------------------------------------------------- /
+
   !/ ------------------------------------------------------------------- /
   REAL FUNCTION DIST_SPHERE ( lo1,la1,lo2,la2 )
     !/
diff --git a/model/src/ww3_bounc.F90 b/model/src/ww3_bounc.F90
index 549f027d5b..77ac0432cb 100644
--- a/model/src/ww3_bounc.F90
+++ b/model/src/ww3_bounc.F90
@@ -25,8 +25,9 @@ PROGRAM W3BOUNC
   !/                  | WAVEWATCH III           NOAA/NCEP |
   !/                  |           F. Ardhuin              |
   !/                  |           M. Accensi              |
+  !/                  |           D. A. Honegger          |
   !/                  |                        FORTRAN 90 |
-  !/                  | Last update :         21-Jul-2020 |
+  !/                  | Last update :         02-Jan-2025 |
   !/                  +-----------------------------------+
   !/
   !/    24-May-2013 : Adaptation from ww3_bound.ftn       ( version 4.08 )
@@ -38,6 +39,8 @@ PROGRAM W3BOUNC
   !/    15-May-2018 : Add namelist feature                ( version 6.05 )
   !/    04-May-2020 : Update spectral conversion          ( version 7.11 )
   !/    21-Jul-2020 : Support rotated pole grid           ( version 7.11 )
+  !/    02-Jan-2025 : Change geographic distance method   ( version 7.xx )
+  !/    02-Jan-2025 : Add verbose=2 display output        ( version 7.xx )
   !/
   !/
   !/    Copyright 2012-2013 National Weather Service (NWS),
@@ -138,7 +141,7 @@ PROGRAM W3BOUNC
   USE W3IOBCMD, ONLY: VERBPTBC, IDSTRBC
   USE W3IOGRMD, ONLY: W3IOGR
   USE W3TIMEMD
-  USE W3SERVMD, ONLY: ITRACE, NEXTLN, EXTCDE, DIST_SPHERE
+  USE W3SERVMD, ONLY: ITRACE, NEXTLN, EXTCDE, DIST_HAVERSINE
 #ifdef W3_RTD
   USE W3SERVMD, ONLY: W3EQTOLL
 #endif
@@ -534,6 +537,9 @@ PROGRAM W3BOUNC
         CALL CHECK_ERR(IRET)
       END IF
 
+      ! Display the location of the ingested NetCDF file
+      IF (VERBOSE.GE.2) WRITE(NDSO,*) 'FILEID:',IP,'LON:',LONS(IP),'LAT:',LATS(IP)
+
       ! freq and dir variables
       IRET=NF90_INQ_VARID(NCID(IP),"frequency",VARID(4))
       CALL CHECK_ERR(IRET)
@@ -649,7 +655,7 @@ PROGRAM W3BOUNC
       DO IP=1,NBO2
         !           Searches for the nearest 2 points where spectra are available
         IF (FLAGLL)  THEN
-          DIST=DIST_SPHERE ( LONS(IP),LATS(IP),XBPO(IP1),YBPO(IP1) )
+          DIST=DIST_HAVERSINE( LONS(IP),LATS(IP),XBPO(IP1),YBPO(IP1) )
         ELSE
           DIST=SQRT((LONS(IP)-XBPO(IP1))**2+(LATS(IP)-YBPO(IP1))**2)
         END IF
@@ -671,6 +677,12 @@ PROGRAM W3BOUNC
             END IF
           END IF
         END IF
+        ! Display iteration to find distance minima
+        IF (VERBOSE.GE.2) WRITE(NDSO,*) &
+            'BOUNDID:',IP1,'FILEID:',IP, &
+            'DX:',LONS(IP)-XBPO(IP1),'DY:',LATS(IP)-YBPO(IP1), &
+            'DIST:',DIST,'DMIN:',DMIN,'DMIN2:',DMIN2
+
       END DO ! IP1=1,NBO2
       IF (VERBOSE.GE.1) WRITE(NDSO,*) 'DIST:',DMIN,DMIN2,IP1,IPBPO(IP1,1),IPBPO(IP1,2), &
            LONS(IPBPO(IP1,1)),LONS(IPBPO(IP1,2)),XBPO(IP1), &


Reviews:
user: login: sbanihash
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-24T12:05:53Z
user: login: dahonegger
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T21:12:33Z
user: login: sbanihash
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-24T21:21:56Z
user: login: dahonegger
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T22:16:56Z
user: login: sbanihash
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-25T04:07:20Z
user: login: sbanihash
body: Changes look good. Tests have been passed on Hera with expected changes in ww3_tp2.8 and tp_2.17.

**********************************************************************
********************* non-identical cases ****************************
**********************************************************************
mww3_test_03/./work_PR2_UNO_MPI_e                     (1 files differ)
mww3_test_03/./work_PR2_UQ_MPI_d2                     (16 files differ)
mww3_test_03/./work_PR3_UQ_MPI_e_c                     (1 files differ)
mww3_test_03/./work_PR2_UNO_MPI_d2                     (10 files differ)
mww3_test_03/./work_PR3_UQ_MPI_d2_c                     (16 files differ)
mww3_test_03/./work_PR2_UQ_MPI_e                     (1 files differ)
mww3_test_03/./work_PR3_UNO_MPI_e_c                     (1 files differ)
mww3_test_03/./work_PR1_MPI_d2                     (11 files differ)
mww3_test_03/./work_PR3_UNO_MPI_d2_c                     (17 files differ)
mww3_test_03/./work_PR3_UNO_MPI_d2                     (18 files differ)
mww3_test_03/./work_PR3_UQ_MPI_d2                     (15 files differ)
mww3_test_09/./work_MPI_ASCII                     (0 files differ)
ww3_tp2.10/./work_MPI_OMPH                     (7 files differ)
ww3_tp2.16/./work_MPI_OMPH                     (4 files differ)
ww3_tp2.17/./work_b                     (1 files differ)
ww3_tp2.17/./work_mb                     (1 files differ)
ww3_tp2.17/./work_mc                     (1 files differ)
ww3_tp2.17/./work_a                     (1 files differ)
ww3_tp2.17/./work_ma1                     (1 files differ)
ww3_tp2.17/./work_c                     (1 files differ)
ww3_tp2.17/./work_ma                     (1 files differ)
ww3_tp2.17/./work_mc1                     (1 files differ)
ww3_tp2.6/./work_ST4_ASCII                     (0 files differ)
ww3_tp2.8/./work_PR3_UQ                     (8 files differ)
ww3_ufs1.3/./work_a                     (2 files differ)

[matrixCompSummary.txt](https://github.com/user-attachments/files/19441717/matrixCompSummary.txt)
[matrixCompFull.txt](https://github.com/user-attachments/files/19441718/matrixCompFull.txt)
[matrixDiff.txt](https://github.com/user-attachments/files/19441720/matrixDiff.txt)



state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-25T04:27:19Z

Review comments:
diff_hunk: @@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radius of the Earth
path: model/src/w3servmd.F90
user: login: sbanihash
body: If this is intended to be Radius of the Earth , the constants module does not define it that way. Here is what RADE is in constants.F90: 
REAL, PARAMETER :: RADE = 180. / PI !< RADE Conversion factor from radians to degrees 
created_at: 2025-03-24T11:57:24Z
author_association: COLLABORATOR
diff_hunk: @@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radius of the Earth
+    !/
+    !/ ------------------------------------------------------------------- /
+    !/ Parameter list
+    !/
+    REAL, INTENT(IN)        :: LON1, LAT1, LON2, LAT2
+    !/
+    !/ ------------------------------------------------------------------- /
+    !/ Local parameters
+    !/
+    REAL                    :: DLON, DLAT, A, C 
+    !/
+    !/ ------------------------------------------------------------------- /
+    !/
+    
+    ! Compute differences in latitude and longitude
+    DLAT = (LAT2 - LAT1) * DERA
+    DLON = (LON2 - LON1) * DERA
path: model/src/w3servmd.F90
user: login: sbanihash
body: Should we make sure LAT1, LAT2 are within [-90, 90] and LON1, LON2 are within [-180, 180] before computation? 
created_at: 2025-03-24T11:59:17Z
author_association: COLLABORATOR
diff_hunk: @@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radius of the Earth
path: model/src/w3servmd.F90
user: login: dahonegger
body: Thank you @sbanihash for catching this oversight. I am correcting the variable description comment along with an aesthetic spacing adjustment in the header of ww3_bounc, and will push to this branch.
created_at: 2025-03-24T21:12:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radius of the Earth
path: model/src/w3servmd.F90
user: login: sbanihash
body: @dahonegger I see your change and I am a bit confused. my understanding for the DIST_HAVERSINE = RADE * C on line 603 is that you would need to have the Radius of the earth right?
created_at: 2025-03-24T21:21:56Z
author_association: COLLABORATOR
diff_hunk: @@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radius of the Earth
path: model/src/w3servmd.F90
user: login: dahonegger
body: @sbanihash I understand the confusion -- DIST_HAVERSINE is written to be a drop-in replacement for DIST_SPHERE, which calculates the angular distance (in degrees) between the two input locations. My now-corrected variable description for RADE was owing to an erroneous prior reading of DIST_SPHERE, in which I had interpreted RADE as "radius." Replacing RADE with RADIUS in either case will indeed produce the great-arc distance (in meters) instead.
created_at: 2025-03-24T22:16:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -511,6 +512,100 @@ REAL FUNCTION EJ5P ( F, ALFA, FP, YLN, SIGA, SIGB )
     !/ End of NEXTLN ----------------------------------------------------- /
     !/
   END FUNCTION EJ5P
+  !/ ------------------------------------------------------------------- /
+
+  REAL FUNCTION DIST_HAVERSINE ( LON1, LAT1, LON2, LAT2 )
+    !/
+    !/                  +-----------------------------------+
+    !/                  | WAVEWATCH III          USACE/ERDC |
+    !/                  |          D. A. Honegger           |
+    !/                  |                        FORTRAN 90 |
+    !/                  | Last update :         02-Jan-2025 |
+    !/                  +-----------------------------------+
+    !/
+    !/    02-Jan-2025 :  Creation                          ( version 7.xx )
+    !/
+    !  1. Purpose :
+    !
+    !     Computes the haversine distance between two points on a sphere
+    !
+    !  2. Method
+    !
+    !     Haversine Formula: R.W. Sinnott, "Virtues of the Haversine", 
+    !                        Sky and Telescope, vol. 68, no. 2, 1984, p. 159
+    !
+    !  3. Parameters :
+    !
+    !     Parameter list
+    !
+    !     ----------------------------------------------------------------
+    !       LON1     Real   I    Longitude of 1st point
+    !       LAT1     Real   I    Latitude of 1st point
+    !       LON2     Real   I    Longitude of 2nd point
+    !       LAT2     Real   I    Latitude of 2nd point
+    !     ----------------------------------------------------------------
+    !
+    !  4. Subroutines used :
+    !
+    !     None.
+    !
+    !  5. Called by :
+    !
+    !     WW3_BOUNC
+    !
+    !  6. Error messages :
+    !
+    !  7. Remarks :
+    !
+    !     This function uses the haversine formula, which is robust to 
+    !     rounding errors when calculating short distances 
+    !     (less than 1 km).
+    !
+    !  8. Structure :
+    !
+    !     See source code.
+    !
+    !  9. Switches :
+    !
+    !     None.
+    !
+    ! 10. Source code :
+    !
+    !/ ------------------------------------------------------------------- /
+    USE CONSTANTS
+    ! DERA: Degrees to Radians (PI/180)
+    ! RADE: Radius of the Earth
path: model/src/w3servmd.F90
user: login: sbanihash
body: @dahonegger Thanks for the explanation, now I get it. Seems like no where in WAVEWATCH III we actually use the great-circle distance between the points anyways. Angular distance is what is being called in these routines as the expected output from DIST_SPHERE and with this addition in DIST_HAVERSINE as well.  
created_at: 2025-03-25T04:07:20Z
author_association: COLLABORATOR

PR comments:
user: login: MatthewMasarik-NOAA
created_at: 2025-03-13T21:05:17Z
body: Thank you for submitting this fix @dahonegger.  We will start looking at it and I'll reply back tomorrow with the CM assigned and the timeline.
author_association: CONTRIBUTOR
user: login: mickaelaccensi
created_at: 2025-03-14T08:05:13Z
body: Thank you David for this improvement !
author_association: COLLABORATOR
user: login: MatthewMasarik-NOAA
created_at: 2025-03-14T17:37:32Z
body: @dahonegger I spoke with @JessicaMeixner-NOAA offline and we will be assigning a CM on Monday when @sbanihash is back from leave.  We will be back in touch then.  Thank you again for this submission.
author_association: CONTRIBUTOR
user: login: sbanihash
created_at: 2025-03-17T16:40:29Z
body: Thank you @dahonegger for your work on this fix. I would like to request @kestonsmith-noaa to take a look at this PR and review the changes. Either @JessicaMeixner-NOAA or I would also take a look as a second reviewer as well.
author_association: COLLABORATOR
user: login: kestonsmith-noaa
created_at: 2025-03-24T14:09:34Z
body: On the second point, checking coordinate range, I would prefer not to check
coordinate range within haversine, but rather at input.  Longitude and
latitude out of geographic range won't present numerical problems within
haversine and there are cases it may be useful to have longitudes outside
[-180,180], for instance in regional domains spanning the international
date line.   Best, Keston

On Mon, Mar 24, 2025 at 8:06 AM Saeideh Banihashemi <
***@***.***> wrote:

> ***@***.**** commented on this pull request.
> ------------------------------
>
> In model/src/w3servmd.F90
> <https://github.com/NOAA-EMC/WW3/pull/1392#discussion_r2010036129>:
>
> > +    !     (less than 1 km).
> +    !
> +    !  8. Structure :
> +    !
> +    !     See source code.
> +    !
> +    !  9. Switches :
> +    !
> +    !     None.
> +    !
> +    ! 10. Source code :
> +    !
> +    !/ ------------------------------------------------------------------- /
> +    USE CONSTANTS
> +    ! DERA: Degrees to Radians (PI/180)
> +    ! RADE: Radius of the Earth
>
> If this is intended to be Radius of the Earth , the constants module does
> not define it that way. Here is what RADE is in constants.F90:
> REAL, PARAMETER :: RADE = 180. / PI !< RADE Conversion factor from radians
> to degrees
> ------------------------------
>
> In model/src/w3servmd.F90
> <https://github.com/NOAA-EMC/WW3/pull/1392#discussion_r2010040030>:
>
> > +    !/ ------------------------------------------------------------------- /
> +    !/ Parameter list
> +    !/
> +    REAL, INTENT(IN)        :: LON1, LAT1, LON2, LAT2
> +    !/
> +    !/ ------------------------------------------------------------------- /
> +    !/ Local parameters
> +    !/
> +    REAL                    :: DLON, DLAT, A, C
> +    !/
> +    !/ ------------------------------------------------------------------- /
> +    !/
> +
> +    ! Compute differences in latitude and longitude
> +    DLAT = (LAT2 - LAT1) * DERA
> +    DLON = (LON2 - LON1) * DERA
>
> Should we make sure LAT1, LAT2 are within [-90, 90] and LON1, LON2 are
> within [-180, 180] before computation?
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/NOAA-EMC/WW3/pull/1392#pullrequestreview-2710124888>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AZUY35CXAQZWAFQQH5NY5ET2V7YLNAVCNFSM6AAAAABY7EYMSWVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDOMJQGEZDIOBYHA>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>

author_association: COLLABORATOR
user: login: sbanihash
created_at: 2025-03-24T14:30:53Z
body: > On the second point, checking coordinate range, I would prefer not to check coordinate range within haversine, but rather at input. Longitude and latitude out of geographic range won't present numerical problems within haversine and there are cases it may be useful to have longitudes outside [-180,180], for instance in regional domains spanning the international date line. Best, Keston

@kestonsmith-noaa Thank you for your input. I was thinking more about the DLON calculations (as opposed to a [0 360] coordinate), in order to make sure the  haversine of the central angle computation is correct. have to re-think to be honest. maybe it's not even important. Looking at the fact that the trigonometric functions involved are periodic, I think both coordinates should work. 


author_association: COLLABORATOR
user: login: sbanihash
created_at: 2025-03-25T13:50:00Z
body: @dahonegger Thank you for your work on this fix. I have approved your PR, just waiting for @kestonsmith-noaa to also confirm that the changes look good and I will then merge the PR. Thank for your patience.
author_association: COLLABORATOR
user: login: kestonsmith-noaa
created_at: 2025-03-25T15:02:25Z
body: The changes looks fine to me. My only other suggestion would be to include the units of the return variable in the description, given our own internal confusion on this point and change of units within the function.   Something like:

    !  1. Purpose :
    !
    !     Computes the haversine distance between two points on a sphere in decimal degrees

author_association: COLLABORATOR
user: login: sbanihash
created_at: 2025-03-25T16:50:23Z
body: Thank you @kestonsmith-noaa . That would be actually helpful.  @dahonegger would you mind making this small change? I can merge the PR afterwards. Thanks!

author_association: COLLABORATOR
user: login: aliabdolali
created_at: 2025-03-25T16:56:19Z
body: Hi @sbanihash 
David is on AL until next week. You can either wait or add it to the header and merge.
Thanks for helping with the review.
author_association: CONTRIBUTOR
user: login: sbanihash
created_at: 2025-03-25T17:18:29Z
body: > Hi @sbanihash David is on AL until next week. You can either wait or add it to the header and merge. Thanks for helping with the review.

Thanks @aliabdolali. Done!
author_association: COLLABORATOR
user: login: dahonegger
created_at: 2025-03-31T20:47:14Z
body: Thanks all for the constructive feedback on this PR -- I wholeheartedly agree with the suggested modifications. 

And, thank you @aliabdolali for your help making sure @sbanihash was not left waiting for a response on Tuesday!
author_association: CONTRIBUTOR