From 72d9ce45a48cb3a3c6b2927651a9cd2fcdfc28dc Mon Sep 17 00:00:00 2001
From: tomrink <rink@ssec.wisc.edu>
Date: Wed, 6 Jul 2022 13:28:17 -0500
Subject: [PATCH] improvements to best_fit...

---
 modules/amv/intercompare.py | 66 ++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 27 deletions(-)

diff --git a/modules/amv/intercompare.py b/modules/amv/intercompare.py
index e3ae4142..8e96671c 100644
--- a/modules/amv/intercompare.py
+++ b/modules/amv/intercompare.py
@@ -112,6 +112,8 @@ def filter_amvs(amvs, qitype=amv_cqi_idx, qival=50, lat_range=[-61, 61]):
 def get_raob_dict_cdf(pathname):
     rtgrp = Dataset(pathname, 'r', format='NETCDF3')
 
+    wmo_stat_id_var = rtgrp['wmoStat']
+
     sta_lats_var = rtgrp['staLat']
     sta_lats_var.set_always_mask(False)
     sta_lats_var.set_auto_mask(False)
@@ -122,12 +124,14 @@ def get_raob_dict_cdf(pathname):
 
     sta_lats = sta_lats_var[:]
     sta_lons = sta_lons_var[:]
+    sta_ids = wmo_stat_id_var[:]
 
     sta_msk = np.logical_and(sta_lats > -90.0, sta_lats < 90.0)
     # sta_msk = np.invert(sta_lats._mask)
     num_sta = np.sum(sta_msk)
     sta_lats = sta_lats[sta_msk]
     sta_lons = sta_lons[sta_msk]
+    sta_ids = sta_ids[sta_msk]
 
     # mandatory levels
     man_levs = rtgrp['prMan']
@@ -171,6 +175,7 @@ def get_raob_dict_cdf(pathname):
     for k in range(num_sta):
         lon = sta_lons[k]
         lat = sta_lats[k]
+        id = sta_ids[k]
 
         vld_man = np.invert(man_levs[k]._mask)
         vld_man = np.logical_and(vld_man, np.invert(man_spd[k]._mask))
@@ -976,23 +981,24 @@ def best_fit(amv_spd, amv_dir, amv_prs, amv_lat, amv_lon, fcst_spd, fcst_dir, fc
 
     fcst_num_levels = fcst_prs.shape[0]
 
-    PressDiff = 150.0          # pressure above and below AMV to look for fit
-    TopPress = 50.0            # highest level to allow search
+    PressDiff = 150.0       # pressure above and below AMV to look for fit
+    TopPress = 50.0         # highest level to allow search
+    BotPress = fcst_prs[0]  # lowest level to allow search
 
     if amv_prs < TopPress:
         if verbose:
-            print('AMV location lat,lon,prs ({0},{1},{2}) is higher than pressure {3}'.format(amv_lat,amv_lon,amv_prs,TopPress))
+            print('AMV location lat,lon,prs ({0},{1},{2}) is higher than pressure {3}'.format(amv_lat, amv_lon, amv_prs, TopPress))
         return bf_data
 
 # Calculate the pressure +/- 150 hPa from the AMV pressure.
-    PressMax = amv_prs + PressDiff
+    PressMax = min((amv_prs + PressDiff), BotPress)
     PressMin = max((amv_prs - PressDiff), TopPress)
 
 # 1d array of indicies to consider for best fit location
     kk = np.where((fcst_prs < PressMax) & (fcst_prs > PressMin))
     if len(kk[0]) == 0:
         if verbose:
-            print('AMV location lat,lon,prs ({0},{1},{2}) failed to find fcst prs around AMV'.format(amv_lat,amv_lon,amv_prs))
+            print('AMV location lat,lon,prs ({0},{1},{2}) failed to find fcst prs around AMV'.format(amv_lat, amv_lon, amv_prs))
         return bf_data
 
 # Diagnostic field: Find the model minimum speed and maximum speed within PressDiff of the AMV.
@@ -1015,7 +1021,7 @@ def best_fit(amv_spd, amv_dir, amv_prs, amv_lat, amv_lon, fcst_spd, fcst_dir, fc
     imin = np.where(VecDiff == MinVecDiff)
     if imin[0].size == 0:
         if verbose:
-            print('AMV location lat,lon,prs ({0},{1},{2}) failed to find the min vector difference in layers around AMV'.format(amv_lat,amv_lon,amv_prs))
+            print('AMV location lat,lon,prs ({0},{1},{2}) failed to find the min vector difference in layers around AMV'.format(amv_lat, amv_lon, amv_prs))
         return bf_data
 
     imin = imin[0][0]  # index of min vec diff in absolute coordinates
@@ -1038,44 +1044,50 @@ def best_fit(amv_spd, amv_dir, amv_prs, amv_lat, amv_lon, fcst_spd, fcst_dir, fc
 # if bottom model level
     if imin == 0 or imin == fcst_num_levels - 1:
         if verbose:
-            print('AMV location lat,lon,alt ({0},{1},{2}) min vector difference at top or bottom'.format(amv_lat,amv_lon,amv_prs))
+            print('AMV location lat,lon,alt ({0},{1},{2}) min vector difference at top or bottom'.format(amv_lat, amv_lon, amv_prs))
     else:
         p3 = fcst_prs[imin+1]
         p1 = fcst_prs[imin-1]
         v3 = VecDiff[imin+1]
         v1 = VecDiff[imin-1]
 
-        if p3 > TopPress and v1 != v2 and v2 != v3:  # check not collinear, above allowed region
+        if p3 >= TopPress and v1 != v2 and v2 != v3:  # check not collinear, above allowed region
             SatwindBestFitPress = p2 - (0.5 *
             ((((p2 - p1) * (p2 - p1) * (v2 - v3)) - ((p2 - p3) * (p2 - p3) * (v2 - v1))) /
             (((p2 - p1) * (v2 - v3)) - ((p2 - p3) * (v2 - v1)))))
             if (SatwindBestFitPress < p3) or (SatwindBestFitPress > p1):
                 if (verbose):
                     print('Best Fit not found between two pressure layers')
-                    print('SatwindBestFitPress {0} p1 {1} p2 {2} p3 {3} imin {4}'.format(SatwindBestFitPress,p1,p2,p3,imin))
+                    print('SatwindBestFitPress {0} p1 {1} p2 {2} p3 {3} imin {4}'.format(SatwindBestFitPress, p1, p2, p3, imin))
                 SatwindBestFitPress = p2
-
-# Find best fit U and V by linear interpolation.
-    if p2 != SatwindBestFitPress:
-        if p2 < SatwindBestFitPress:
-            LevBelow = imin - 1
-            LevAbove = imin
-            Prop = (SatwindBestFitPress - p1) / (p2 - p1)
         else:
-            LevBelow = imin
-            LevAbove = imin + 1
-            Prop = (SatwindBestFitPress - p2) / (p3 - p2)
+            if verbose:
+                print('Top of parabolic fit window above TopPress, or co-linear vector difference profile')
+                print('SatwindBestFitPress {0} p1 {1} p2 {2} p3 {3} imin {4}'.format(SatwindBestFitPress, p1, p2, p3, imin))
+                print('Vector difference profile p1 {0} p2 {1} p3 {2}'.format(v1, v2, v3))
+
+        # Find best fit U and V by linear interpolation.
+        if p2 != SatwindBestFitPress:
+            if p2 < SatwindBestFitPress:
+                LevBelow = imin - 1
+                LevAbove = imin
+                Prop = (SatwindBestFitPress - p1) / (p2 - p1)
+            else:
+                LevBelow = imin
+                LevAbove = imin + 1
+                Prop = (SatwindBestFitPress - p2) / (p3 - p2)
 
-        SatwindBestFitU = fcst_uwind[LevBelow] * (1.0 - Prop) + fcst_uwind[LevAbove] * Prop
-        SatwindBestFitV = fcst_vwind[LevBelow] * (1.0 - Prop) + fcst_vwind[LevAbove] * Prop
+            SatwindBestFitU = fcst_uwind[LevBelow] * (1.0 - Prop) + fcst_uwind[LevAbove] * Prop
+            SatwindBestFitV = fcst_vwind[LevBelow] * (1.0 - Prop) + fcst_vwind[LevAbove] * Prop
 
+    # best fit success and good constraints -----
     SatwindGoodConstraint = 1
     flag = 0
 
     # Check for overall good agreement
-    vdiff = np.sqrt((fcst_uwind - SatwindBestFitU) ** 2 + (fcst_vwind - SatwindBestFitV) ** 2)
-    min_vdiff = min(vdiff)
-    if min_vdiff > 4.0:
+    vdiff_best = np.sqrt((fcst_uwind - SatwindBestFitU) ** 2 + (fcst_vwind - SatwindBestFitV) ** 2)
+    min_vdiff_best = min(vdiff_best)
+    if min_vdiff_best > 4.0:
         SatwindGoodConstraint = 0
         flag = 2
 
@@ -1083,7 +1095,7 @@ def best_fit(amv_spd, amv_dir, amv_prs, amv_lat, amv_lon, fcst_spd, fcst_dir, fc
     if SatwindGoodConstraint == 1:
         mm = np.where(fcst_prs > (SatwindBestFitPress + 100))[0]
         nn = np.where(fcst_prs < (SatwindBestFitPress - 100))[0]
-        if (np.sum(vdiff[mm] < (min_vdiff + 2.0)) + np.sum(vdiff[nn] < (min_vdiff + 2.0))) > 0:
+        if (np.sum(vdiff_best[mm] < (min_vdiff_best + 2.0)) + np.sum(vdiff_best[nn] < (min_vdiff_best + 2.0))) > 0:
             SatwindGoodConstraint = 0
             flag = 1
 
@@ -1103,9 +1115,9 @@ def best_fit(amv_spd, amv_dir, amv_prs, amv_lat, amv_lon, fcst_spd, fcst_dir, fc
         print('Bestfit -> pbest,bfu,bfv,amvu,amvv,bgu,bgv: {0} {1} {2} {3} {4} {5} {6}'.format(
         SatwindBestFitPress,SatwindBestFitU,SatwindBestFitV,amv_uwind,amv_vwind,fcst_uwind[imin],fcst_vwind[imin]))
         print('Good Constraint: {0}'.format(SatwindGoodConstraint))
-        print('Minimum Vector Difference: {0}'.format(VecDiff[imin]))
+        print('Minimum Vector Difference: {0}'.format(min_vdiff_best))
         print('Vector Difference Profile: ')
-        print(vdiff)
+        print(vdiff_best)
         print('Pressure Profile: ')
         print(fcst_prs)
 
-- 
GitLab