From 53eca7ad27bad6ee6361492468752173efc76bcb Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 17 Feb 2022 11:37:16 -0700 Subject: [PATCH 1/6] handle nan --- pvlib/tests/test_tools.py | 19 +++++++++++++++++++ pvlib/tools.py | 15 +++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/pvlib/tests/test_tools.py b/pvlib/tests/test_tools.py index fa42fbf82d..eabe24bba6 100644 --- a/pvlib/tests/test_tools.py +++ b/pvlib/tests/test_tools.py @@ -45,3 +45,22 @@ def test__golden_sect_DataFrame_vector(): v, x = tools._golden_sect_DataFrame(params, lower, upper, _obj_test_golden_sect) assert np.allclose(x, expected, atol=1e-8) + + +def test__golden_sect_DataFrame_nans(): + # nan in bounds + params = {'c': np.array([1., 2., 1.]), 'n': np.array([1., 1., 1.])} + lower = np.array([0., 0.001, np.nan]) + upper = np.array([1.1, 1.2, 1.]) + expected = np.array([0.5, 0.25, np.nan]) + v, x = tools._golden_sect_DataFrame(params, lower, upper, + _obj_test_golden_sect) + assert np.allclose(x, expected, atol=1e-8, equal_nan=True) + # nan in function values + params = {'c': np.array([1., 2., np.nan]), 'n': np.array([1., 1., 1.])} + lower = np.array([0., 0.001, 0.]) + upper = np.array([1.1, 1.2, 1.]) + expected = np.array([0.5, 0.25, np.nan]) + v, x = tools._golden_sect_DataFrame(params, lower, upper, + _obj_test_golden_sect) + assert np.allclose(x, expected, atol=1e-8, equal_nan=True) \ No newline at end of file diff --git a/pvlib/tools.py b/pvlib/tools.py index a36849f903..95e89242fd 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -323,13 +323,13 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): df = params df['VH'] = upper df['VL'] = lower - + converged = False iterations = 0 - iterlimit = 1 + np.max( + iterlimit = 1 + np.nanmax( np.trunc(np.log(atol / (df['VH'] - df['VL'])) / np.log(phim1))) - while not converged and (iterations < iterlimit): + while not converged and (iterations <= iterlimit): phi = phim1 * (df['VH'] - df['VL']) df['V1'] = df['VL'] + phi @@ -345,15 +345,18 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): err = abs(df['V2'] - df['V1']) # works with single value because err is np.float64 - converged = (err < atol).all() + converged = (err[~np.isnan(err)] < atol).all() # err will be less than atol before iterations hit the limit # but just to be safe iterations += 1 if iterations > iterlimit: - raise Exception("iterations exceeded maximum") # pragma: no cover + raise Exception("Iterations exceeded maximum. Check that func", + " is not NaN in (lower, upper)") - return func(df, 'V1'), df['V1'] + func_result = func(df, 'V1') + x = np.where(np.isnan(func_result), np.nan, df['V1']) + return func_result, x def _get_sample_intervals(times, win_length): From 3955b5290bab1da95197ade02661c7822eff4cd6 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 17 Feb 2022 11:40:23 -0700 Subject: [PATCH 2/6] docstring --- pvlib/tools.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pvlib/tools.py b/pvlib/tools.py index 95e89242fd..30ac2645f9 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -286,14 +286,17 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): Parameters ---------- - params : dict or Dataframe - Parameters to be passed to `func`. + params : dict of numeric + Parameters to be passed to `func`. Each entry must be of the same + length. lower: numeric - Lower bound for the optimization + Lower bound for the optimization. Must be the same length as each + entry of params. upper: numeric - Upper bound for the optimization + Upper bound for the optimization. Must be the same length as each + entry of params. func: function Function to be optimized. Must be in the form @@ -312,6 +315,7 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): Notes ----- This function will find the points where the function is maximized. + Returns nan where lower or upper is nan, or where func evaluates to nan. See also -------- From 00d6058c81db9717c266b360991f75c86771daeb Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 17 Feb 2022 11:44:20 -0700 Subject: [PATCH 3/6] stickler --- pvlib/tests/test_tools.py | 2 +- pvlib/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/tests/test_tools.py b/pvlib/tests/test_tools.py index eabe24bba6..044c3ab85e 100644 --- a/pvlib/tests/test_tools.py +++ b/pvlib/tests/test_tools.py @@ -63,4 +63,4 @@ def test__golden_sect_DataFrame_nans(): expected = np.array([0.5, 0.25, np.nan]) v, x = tools._golden_sect_DataFrame(params, lower, upper, _obj_test_golden_sect) - assert np.allclose(x, expected, atol=1e-8, equal_nan=True) \ No newline at end of file + assert np.allclose(x, expected, atol=1e-8, equal_nan=True) diff --git a/pvlib/tools.py b/pvlib/tools.py index 30ac2645f9..01d43223fa 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -327,7 +327,7 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): df = params df['VH'] = upper df['VL'] = lower - + converged = False iterations = 0 iterlimit = 1 + np.nanmax( From af64129041a1f3fe61139155bc75a26d8f7a505a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 17 Feb 2022 12:48:03 -0700 Subject: [PATCH 4/6] handle all-nan case --- pvlib/tests/test_tools.py | 8 ++++++++ pvlib/tools.py | 19 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/pvlib/tests/test_tools.py b/pvlib/tests/test_tools.py index 044c3ab85e..67debc7034 100644 --- a/pvlib/tests/test_tools.py +++ b/pvlib/tests/test_tools.py @@ -64,3 +64,11 @@ def test__golden_sect_DataFrame_nans(): v, x = tools._golden_sect_DataFrame(params, lower, upper, _obj_test_golden_sect) assert np.allclose(x, expected, atol=1e-8, equal_nan=True) + # all nan in bounds + params = {'c': np.array([1., 2., 1.]), 'n': np.array([1., 1., 1.])} + lower = np.array([np.nan, np.nan, np.nan]) + upper = np.array([1.1, 1.2, 1.]) + expected = np.array([np.nan, np.nan, np.nan]) + v, x = tools._golden_sect_DataFrame(params, lower, upper, + _obj_test_golden_sect) + assert np.allclose(x, expected, atol=1e-8, equal_nan=True) \ No newline at end of file diff --git a/pvlib/tools.py b/pvlib/tools.py index 01d43223fa..f6f5cba8ff 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd import pytz +import warnings def cosd(angle): @@ -330,8 +331,13 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): converged = False iterations = 0 - iterlimit = 1 + np.nanmax( - np.trunc(np.log(atol / (df['VH'] - df['VL'])) / np.log(phim1))) + + # handle all NaN case gracefully + with warnings.catch_warnings(): + warnings.filterwarnings(action='ignore', + message='All-NaN slice encountered') + iterlimit = 1 + np.nanmax( + np.trunc(np.log(atol / (df['VH'] - df['VL'])) / np.log(phim1))) while not converged and (iterations <= iterlimit): @@ -358,8 +364,13 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): raise Exception("Iterations exceeded maximum. Check that func", " is not NaN in (lower, upper)") - func_result = func(df, 'V1') - x = np.where(np.isnan(func_result), np.nan, df['V1']) + try: + func_result = func(df, 'V1') + x = np.where(np.isnan(func_result), np.nan, df['V1']) + except KeyError: + func_result = np.full_like(upper, np.nan) + x = func_result.copy() + return func_result, x From 4fa1895ee9495741429b552c51c1cc5520347e48 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 17 Feb 2022 12:49:31 -0700 Subject: [PATCH 5/6] stickler --- pvlib/tests/test_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/tests/test_tools.py b/pvlib/tests/test_tools.py index 67debc7034..e6b8e9091a 100644 --- a/pvlib/tests/test_tools.py +++ b/pvlib/tests/test_tools.py @@ -71,4 +71,4 @@ def test__golden_sect_DataFrame_nans(): expected = np.array([np.nan, np.nan, np.nan]) v, x = tools._golden_sect_DataFrame(params, lower, upper, _obj_test_golden_sect) - assert np.allclose(x, expected, atol=1e-8, equal_nan=True) \ No newline at end of file + assert np.allclose(x, expected, atol=1e-8, equal_nan=True) From a134c4b41a880b94c14cfaca0ee559b48dea2eb4 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Thu, 17 Feb 2022 13:24:43 -0700 Subject: [PATCH 6/6] coverage --- pvlib/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/tools.py b/pvlib/tools.py index f6f5cba8ff..5c6e1dd293 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -362,7 +362,7 @@ def _golden_sect_DataFrame(params, lower, upper, func, atol=1e-8): if iterations > iterlimit: raise Exception("Iterations exceeded maximum. Check that func", - " is not NaN in (lower, upper)") + " is not NaN in (lower, upper)") # pragma: no cover try: func_result = func(df, 'V1')