diff --git a/README.md b/README.md index 32d82bc3f9..04f164678f 100644 --- a/README.md +++ b/README.md @@ -105,10 +105,6 @@ Installation pvlib-python releases may be installed using the ``pip`` and ``conda`` tools. Please see the [Installation page](http://pvlib-python.readthedocs.io/en/stable/installation.html) of the documentation for complete instructions. -pvlib-python is compatible with Python 3.5 and above. - -**Python 2.7 support ended on June 1, 2019, with pvlib-python 0.6.3.** - Contributing ============ diff --git a/ci/azure/conda_linux.yml b/ci/azure/conda_linux.yml index 48ac58b573..4fef14754b 100644 --- a/ci/azure/conda_linux.yml +++ b/ci/azure/conda_linux.yml @@ -8,10 +8,8 @@ jobs: vmImage: ${{ parameters.vmImage }} strategy: matrix: - Python35: - python.version: '35' - Python35-min: - python.version: '35' + Python36-min: + python.version: '36' suffix: '-min' Python36: python.version: '36' diff --git a/ci/azure/conda_windows.yml b/ci/azure/conda_windows.yml index 46edfe41e2..3c27f385e0 100644 --- a/ci/azure/conda_windows.yml +++ b/ci/azure/conda_windows.yml @@ -8,8 +8,6 @@ jobs: vmImage: ${{ parameters.vmImage }} strategy: matrix: - Python35-windows: - python.version: '35' Python36-windows: python.version: '36' Python37-windows: diff --git a/ci/azure/posix.yml b/ci/azure/posix.yml index 9b77bde9ac..806cd27e77 100644 --- a/ci/azure/posix.yml +++ b/ci/azure/posix.yml @@ -8,8 +8,6 @@ jobs: vmImage: ${{ parameters.vmImage }} strategy: matrix: - Python35: - python.version: '3.5' Python36: python.version: '3.6' Python37: diff --git a/ci/requirements-py35.yml b/ci/requirements-py35.yml deleted file mode 100644 index b5979d7108..0000000000 --- a/ci/requirements-py35.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: test_env -channels: - - defaults - - conda-forge -dependencies: - - coveralls - - cython - - ephem - - netcdf4 - - nose - - numba - - numpy - - pandas - - pip - - pytables # tables when using pip+PyPI - - pytest - - pytest-cov - - pytest-mock - - pytest-timeout - - python=3.5 - - pytz - - requests - - scipy - - shapely # pvfactors dependency - - siphon # conda-forge - - statsmodels - - pip: - - nrel-pysam>=2.0 - - pvfactors==1.4.1 - - pytest-rerunfailures # conda version is >3.6 - - pytest-remotedata # needs > 0.3.1 diff --git a/ci/requirements-py35-min.yml b/ci/requirements-py36-min.yml similarity index 90% rename from ci/requirements-py35-min.yml rename to ci/requirements-py36-min.yml index edfd1cafcc..31e64f1ba0 100644 --- a/ci/requirements-py35-min.yml +++ b/ci/requirements-py36-min.yml @@ -9,11 +9,12 @@ dependencies: - pytest-cov - pytest-mock - pytest-timeout - - python=3.5 + - python=3.6 - pytz - requests - pip: - numpy==1.12.0 - pandas==0.22.0 + - scipy==1.2.0 - pytest-rerunfailures # conda version is >3.6 - pytest-remotedata # conda package is 0.3.0, needs > 0.3.1 diff --git a/ci/requirements-py36.yml b/ci/requirements-py36.yml index fede76dbf5..b799da7393 100644 --- a/ci/requirements-py36.yml +++ b/ci/requirements-py36.yml @@ -9,8 +9,8 @@ dependencies: - netcdf4 - nose - numba - - numpy - - pandas + - numpy >= 1.12.0 + - pandas >= 0.22.0 - pip - pytables # tables when using pip+PyPI - pytest @@ -22,7 +22,7 @@ dependencies: - python=3.6 - pytz - requests - - scipy + - scipy >= 1.2.0 - shapely # pvfactors dependency - siphon # conda-forge - statsmodels diff --git a/ci/requirements-py37.yml b/ci/requirements-py37.yml index 25175341c7..1542bb35d9 100644 --- a/ci/requirements-py37.yml +++ b/ci/requirements-py37.yml @@ -9,8 +9,8 @@ dependencies: - netcdf4 - nose - numba - - numpy - - pandas + - numpy >= 1.12.0 + - pandas >= 0.22.0 - pip - pytables # tables when using pip+PyPI - pytest @@ -22,7 +22,7 @@ dependencies: - python=3.7 - pytz - requests - - scipy + - scipy >= 1.2.0 - shapely # pvfactors dependency - siphon # conda-forge - statsmodels diff --git a/ci/requirements-py38.yml b/ci/requirements-py38.yml index ff3ac1e63e..6db508fd53 100644 --- a/ci/requirements-py38.yml +++ b/ci/requirements-py38.yml @@ -9,8 +9,8 @@ dependencies: - netcdf4 - nose - numba - - numpy - - pandas + - numpy >= 1.12.0 + - pandas >= 0.22.0 - pip - pytables # tables when using pip+PyPI - pytest @@ -22,7 +22,7 @@ dependencies: - python=3.8 - pytz - requests - - scipy + - scipy >= 1.2.0 - shapely # pvfactors dependency - siphon # conda-forge - statsmodels diff --git a/docs/sphinx/source/contributing.rst b/docs/sphinx/source/contributing.rst index 2b6b3efc23..1945d53126 100644 --- a/docs/sphinx/source/contributing.rst +++ b/docs/sphinx/source/contributing.rst @@ -196,8 +196,6 @@ pvlib python generally follows the `PEP 8 -- Style Guide for Python Code `_. Maximum line length for code is 79 characters. -Code must be compatible with Python 3.5 and above. - pvlib python uses a mix of full and abbreviated variable names. See :ref:`variables_style_rules`. We could be better about consistency. Prefer full names for new contributions. This is especially important @@ -475,9 +473,10 @@ PVSystem method is called through ``ModelChain.run_model``. mc.run_model(times) # assertion fails if PVSystem.sapm is not called once - # if using returned m, prefer this over m.assert_called_once() - # for compatibility with python < 3.6 - assert m.call_count == 1 + m.assert_called_once() + + # use `assert m.call_count == num` if function should be called + # more than once # ensure that dc attribute now exists and is correct type assert isinstance(mc.dc, (pd.Series, pd.DataFrame)) diff --git a/docs/sphinx/source/installation.rst b/docs/sphinx/source/installation.rst index b00920d580..39b7bfc974 100644 --- a/docs/sphinx/source/installation.rst +++ b/docs/sphinx/source/installation.rst @@ -224,9 +224,9 @@ environment) when you start a new shell or terminal. Compatibility ------------- -pvlib-python is compatible with Python 3.5 and above. +pvlib-python is compatible with Python 3. -pvlib-python requires Pandas and Numpy. The minimum version requirements +pvlib-python requires Pandas, Numpy, and SciPy. The minimum version requirements are specified in `setup.py `_. They are typically releases from several years ago. @@ -235,8 +235,8 @@ A handful of pvlib-python features require additional packages that must be installed separately using pip or conda. These packages/features include: -* scipy: single diode model, clear sky detection * pytables (tables on PyPI): Linke turbidity look up for clear sky models +* statsmodels: parameter fitting * numba: fastest solar position calculations * pyephem: solar positions calculations using an astronomical library * siphon: forecasting PV power using the pvlib.forecast module diff --git a/docs/sphinx/source/whatsnew/v0.8.0.rst b/docs/sphinx/source/whatsnew/v0.8.0.rst index e19ddb71f9..7c7272e70b 100644 --- a/docs/sphinx/source/whatsnew/v0.8.0.rst +++ b/docs/sphinx/source/whatsnew/v0.8.0.rst @@ -143,7 +143,10 @@ Documentation Requirements ~~~~~~~~~~~~ +* Python 3.6 or greater. (:pull:`1035`) * Minimum pandas version increased to v0.22.0, released Dec 31, 2017. (:pull:`1003`) +* Scipy >= v1.2.0, released Dec 17, 2018, is now a required dependency + rather an optional dependency. (:issue:`972`, :pull:`1035`) Contributors ~~~~~~~~~~~~ diff --git a/pvlib/ivtools/sdm.py b/pvlib/ivtools/sdm.py index 73ef58dc98..aab41db086 100644 --- a/pvlib/ivtools/sdm.py +++ b/pvlib/ivtools/sdm.py @@ -8,6 +8,10 @@ import numpy as np +import scipy.constants +from scipy import optimize +from scipy.special import lambertw + from pvlib.pvsystem import singlediode, v_from_i from pvlib.ivtools.utility import constants, rectify_iv_curve, _numdiff @@ -197,12 +201,6 @@ def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series, 2006. """ - try: - from scipy.optimize import root - import scipy.constants - except ImportError: - raise ImportError("The fit_desoto function requires scipy.") - # Constants k = scipy.constants.value('Boltzmann constant in eV/K') Tref = temp_ref + 273.15 # [K] @@ -222,8 +220,8 @@ def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series, Tref, k) # computing with system of equations described in [1] - optimize_result = root(_system_of_equations_desoto, x0=params_i, - args=(specs,), **root_kwargs) + optimize_result = optimize.root(_system_of_equations_desoto, x0=params_i, + args=(specs,), **root_kwargs) if optimize_result.success: sdm_params = optimize_result.x @@ -657,8 +655,8 @@ def _fit_desoto_sandia_diode(ee, voc, vth, tc, specs, const): try: import statsmodels.api as sm except ImportError: - raise ImportError('Parameter extraction using Sandia method requires', - ' statsmodels') + raise ImportError( + 'Parameter extraction using Sandia method requires statsmodels') x = specs['cells_in_series'] * vth * np.log(ee / const['E0']) y = voc - specs['beta_voc'] * (tc - const['T0']) @@ -791,11 +789,10 @@ def _extract_sdm_params(ee, tc, iph, io, rs, rsh, n, u, specs, const, # Get single diode model parameters from five parameters iph, io, rs, rsh # and n vs. effective irradiance and temperature try: - from scipy import optimize import statsmodels.api as sm except ImportError: - raise ImportError('Parameter extraction using Sandia method requires', - ' scipy and statsmodels') + raise ImportError( + 'Parameter extraction using Sandia method requires statsmodels') tck = tc + 273.15 tok = const['T0'] + 273.15 # convert to to K @@ -1183,12 +1180,6 @@ def _calc_theta_phi_exact(vmp, imp, iph, io, rs, rsh, nnsvth): real solar cells using Lambert W-function", Solar Energy Materials and Solar Cells, 81 (2004) 269-277. """ - - try: - from scipy.special import lambertw - except ImportError: - raise ImportError('calc_theta_phi_exact requires scipy') - # handle singleton inputs vmp = np.asarray(vmp) imp = np.asarray(imp) diff --git a/pvlib/scaling.py b/pvlib/scaling.py index c7ec11b7c3..b6a6caaf66 100644 --- a/pvlib/scaling.py +++ b/pvlib/scaling.py @@ -6,6 +6,9 @@ import numpy as np import pandas as pd +import scipy.optimize +from scipy.spatial.distance import pdist + def wvm(clearsky_index, positions, cloud_speed, dt=None): """ @@ -59,12 +62,6 @@ def wvm(clearsky_index, positions, cloud_speed, dt=None): # Added by Joe Ranalli (@jranalli), Penn State Hazleton, 2019 - try: - import scipy.optimize - from scipy.spatial.distance import pdist - except ImportError: - raise ImportError("The WVM function requires scipy.") - pos = np.array(positions) dist = pdist(pos, 'euclidean') wavelet, tmscales = _compute_wavelet(clearsky_index, dt) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 697a0f1a0c..743629a25d 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -7,23 +7,11 @@ import numpy as np from pvlib.tools import _golden_sect_DataFrame -# Try to import brentq from scipy to use when specified in bishop88_i_from_v, -# bishop88_v_from_i, and bishop88_mpp methods below. If not imported, raises -# ImportError when 'brentq' method is specified for those methods. -try: - from scipy.optimize import brentq -except ImportError: - def brentq(*a, **kw): - raise ImportError( - "brentq couldn't be imported. Is SciPy installed?") - -# FIXME: change this to newton when scipy-1.2 is released -try: - from scipy.optimize import _array_newton -except ImportError: - from pvlib.tools import _array_newton -# rename newton and set keyword arguments -newton = partial(_array_newton, tol=1e-6, maxiter=100, fprime2=None) +from scipy.optimize import brentq, newton +from scipy.special import lambertw + +# set keyword arguments for all uses of newton in this module +newton = partial(newton, tol=1e-6, maxiter=100, fprime2=None) # intrinsic voltage per cell junction for a:Si, CdTe, Mertens et al. VOLTAGE_BUILTIN = 0.9 # [V] @@ -510,11 +498,6 @@ def _prepare_newton_inputs(i_or_v_tup, args, v0): def _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, current, saturation_current, photocurrent): - try: - from scipy.special import lambertw - except ImportError: - raise ImportError('This function requires scipy') - # Record if inputs were all scalar output_is_scalar = all(map(np.isscalar, [resistance_shunt, resistance_series, nNsVth, @@ -592,11 +575,6 @@ def _lambertw_v_from_i(resistance_shunt, resistance_series, nNsVth, current, def _lambertw_i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, saturation_current, photocurrent): - try: - from scipy.special import lambertw - except ImportError: - raise ImportError('This function requires scipy') - # Record if inputs were all scalar output_is_scalar = all(map(np.isscalar, [resistance_shunt, resistance_series, nNsVth, diff --git a/pvlib/soiling.py b/pvlib/soiling.py index 4e7096b0c6..33ffcc4b7e 100644 --- a/pvlib/soiling.py +++ b/pvlib/soiling.py @@ -6,6 +6,8 @@ import datetime import numpy as np import pandas as pd +from scipy.special import erf + from pvlib.tools import cosd @@ -62,11 +64,6 @@ def hsu(rainfall, cleaning_threshold, tilt, pm2_5, pm10, Change. J. Seinfeld and S. Pandis. Wiley and Sons 2001. """ - try: - from scipy.special import erf - except ImportError: - raise ImportError("The pvlib.soiling.hsu function requires scipy.") - # never use mutable input arguments if depo_veloc is None: depo_veloc = {'2_5': 0.0009, '10': 0.004} diff --git a/pvlib/solarposition.py b/pvlib/solarposition.py index fbd7b9b830..ee7cb24763 100644 --- a/pvlib/solarposition.py +++ b/pvlib/solarposition.py @@ -20,6 +20,7 @@ import numpy as np import pandas as pd +import scipy.optimize as so import warnings from pvlib import atmosphere @@ -911,12 +912,6 @@ def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value, If the given attribute is not an attribute of a PyEphem.Sun object. """ - - try: - import scipy.optimize as so - except ImportError: - raise ImportError('The calc_time function requires scipy') - obs, sun = _ephem_setup(latitude, longitude, altitude, pressure, temperature, horizon) diff --git a/pvlib/tests/conftest.py b/pvlib/tests/conftest.py index 1001abd29d..aa04af406d 100644 --- a/pvlib/tests/conftest.py +++ b/pvlib/tests/conftest.py @@ -2,7 +2,6 @@ import platform import warnings -import numpy as np import pandas as pd from pkg_resources import parse_version import pytest @@ -81,14 +80,6 @@ def assert_frame_equal(left, right, **kwargs): skip_windows = pytest.mark.skipif(platform_is_windows, reason='does not run on windows') -try: - import scipy - has_scipy = True -except ImportError: - has_scipy = False - -requires_scipy = pytest.mark.skipif(not has_scipy, reason='requires scipy') - try: import statsmodels # noqa: F401 @@ -118,14 +109,6 @@ def assert_frame_equal(left, right, **kwargs): requires_ephem = pytest.mark.skipif(not has_ephem, reason='requires ephem') -def numpy_1_10(): - return parse_version(np.__version__) >= parse_version('1.10.0') - - -needs_numpy_1_10 = pytest.mark.skipif( - not numpy_1_10(), reason='requires numpy 1.10 or greater') - - def has_spa_c(): try: from pvlib.spa_c_files.spa_py import spa_calc diff --git a/pvlib/tests/ivtools/test_sde.py b/pvlib/tests/ivtools/test_sde.py index af9af36510..c67ac573c4 100644 --- a/pvlib/tests/ivtools/test_sde.py +++ b/pvlib/tests/ivtools/test_sde.py @@ -2,7 +2,6 @@ import pytest from pvlib import pvsystem from pvlib.ivtools import sde -from pvlib.tests.conftest import requires_scipy @pytest.fixture @@ -10,7 +9,6 @@ def get_test_iv_params(): return {'IL': 8.0, 'I0': 5e-10, 'Rs': 0.2, 'Rsh': 1000, 'nNsVth': 1.61864} -@requires_scipy def test_fit_sandia_simple(get_test_iv_params, get_bad_iv_curves): test_params = get_test_iv_params testcurve = pvsystem.singlediode(photocurrent=test_params['IL'], @@ -41,7 +39,6 @@ def test_fit_sandia_simple(get_test_iv_params, get_bad_iv_curves): assert np.allclose(result, expected, rtol=5e-5) -@requires_scipy def test_fit_sandia_simple_bad_iv(get_bad_iv_curves): # bad IV curves for coverage of if/then in sde._sandia_simple_params v1, i1, v2, i2 = get_bad_iv_curves diff --git a/pvlib/tests/ivtools/test_sdm.py b/pvlib/tests/ivtools/test_sdm.py index 3d9cfdacd3..67bae225bb 100644 --- a/pvlib/tests/ivtools/test_sdm.py +++ b/pvlib/tests/ivtools/test_sdm.py @@ -6,8 +6,7 @@ from pvlib.ivtools import sdm from pvlib import pvsystem -from pvlib.tests.conftest import requires_scipy, requires_pysam -from pvlib.tests.conftest import requires_statsmodels +from pvlib.tests.conftest import requires_pysam, requires_statsmodels from conftest import DATA_DIR @@ -61,7 +60,6 @@ def test_fit_cec_sam_estimation_failure(cec_params_cansol_cs5p_220p): gamma_pmp=0.0055, cells_in_series=1, temp_ref=25) -@requires_scipy def test_fit_desoto(): result, _ = sdm.fit_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43, alpha_sc=0.005658, beta_voc=-0.13788, @@ -80,7 +78,6 @@ def test_fit_desoto(): rtol=1e-4) -@requires_scipy def test_fit_desoto_failure(): with pytest.raises(RuntimeError) as exc: sdm.fit_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43, @@ -89,7 +86,6 @@ def test_fit_desoto_failure(): assert ('Parameter estimation failed') in str(exc.value) -@requires_scipy @requires_statsmodels def test_fit_desoto_sandia(cec_params_cansol_cs5p_220p): # this test computes a set of IV curves for the input fixture, fits @@ -224,7 +220,6 @@ def _read_pvsyst_expected(datafile): return pvsyst_specs, pvsyst -@requires_scipy @requires_statsmodels def test_fit_pvsyst_sandia(npts=3000): @@ -295,7 +290,6 @@ def test_fit_pvsyst_sandia(npts=3000): equal_nan=True, rtol=0.63) -@requires_scipy @pytest.mark.parametrize('vmp, imp, iph, io, rs, rsh, nnsvth, expected', [ (2., 2., 2., 2., 2., 2., 2., np.nan), (2., 2., 0., 2., 2., 2., 2., np.nan), @@ -309,14 +303,12 @@ def test__update_rsh_fixed_pt_nans(vmp, imp, iph, io, rs, rsh, nnsvth, assert np.all(np.isnan(outrsh)) -@requires_scipy def test__update_rsh_fixed_pt_vmp0(): outrsh = sdm._update_rsh_fixed_pt(vmp=0., imp=2., iph=2., io=2., rs=2., rsh=2., nnsvth=2.) np.testing.assert_allclose(outrsh, np.array([502.]), atol=.0001) -@requires_scipy def test__update_rsh_fixed_pt_vector(): outrsh = sdm._update_rsh_fixed_pt(rsh=np.array([-1., 3, .5, 2.]), rs=np.array([1., -.5, 2., 2.]), @@ -329,7 +321,6 @@ def test__update_rsh_fixed_pt_vector(): np.testing.assert_allclose(outrsh[3], np.array([502.]), atol=.0001) -@requires_scipy @pytest.mark.parametrize('voc, iph, io, rs, rsh, nnsvth, expected', [ (2., 2., 2., 2., 2., 2., 0.5911), (2., 2., 2., 0., 2., 2., 0.5911), @@ -341,7 +332,6 @@ def test__update_io(voc, iph, io, rs, rsh, nnsvth, expected): np.testing.assert_allclose(outio, expected, atol=.0001) -@requires_scipy @pytest.mark.parametrize('voc, iph, io, rs, rsh, nnsvth', [ (2., 2., 2., 2., 2., 0.), (-1., -1., -1., -1., -1., -1.)]) @@ -350,7 +340,6 @@ def test__update_io_nan(voc, iph, io, rs, rsh, nnsvth): assert np.isnan(outio) -@requires_scipy @pytest.mark.parametrize('vmp, imp, iph, io, rs, rsh, nnsvth, expected', [ (2., 2., 2., 2., 2., 2., 2., (1.8726, 2.)), (2., 0., 2., 2., 2., 2., 2., (1.8726, 3.4537)), @@ -362,7 +351,6 @@ def test__calc_theta_phi_exact(vmp, imp, iph, io, rs, rsh, nnsvth, expected): np.testing.assert_allclose(phi, expected[1], atol=.0001) -@requires_scipy @pytest.mark.parametrize('vmp, imp, iph, io, rs, rsh, nnsvth', [ (2., 2., 2., 0., 2., 2., 2.), (2., 2., 2., 2., 2., 2., 0.), @@ -373,7 +361,6 @@ def test__calc_theta_phi_exact_both_nan(vmp, imp, iph, io, rs, rsh, nnsvth): assert np.isnan(phi) -@requires_scipy def test__calc_theta_phi_exact_one_nan(): theta, phi = sdm._calc_theta_phi_exact(imp=2., iph=2., vmp=2., io=2., nnsvth=2., rs=0., rsh=2.) @@ -381,7 +368,6 @@ def test__calc_theta_phi_exact_one_nan(): np.testing.assert_allclose(phi, 2., atol=.0001) -@requires_scipy def test__calc_theta_phi_exact_vector(): theta, phi = sdm._calc_theta_phi_exact(imp=np.array([1., -1.]), iph=np.array([-1., 1.]), diff --git a/pvlib/tests/test_clearsky.py b/pvlib/tests/test_clearsky.py index 9ec4b1dfb9..02613fc4fb 100644 --- a/pvlib/tests/test_clearsky.py +++ b/pvlib/tests/test_clearsky.py @@ -15,7 +15,7 @@ from pvlib import atmosphere from pvlib import irradiance -from conftest import requires_scipy, requires_tables, DATA_DIR +from conftest import requires_tables, DATA_DIR def test_ineichen_series(): @@ -546,7 +546,6 @@ def detect_clearsky_data(): return expected, cs -@requires_scipy def test_detect_clearsky(detect_clearsky_data): expected, cs = detect_clearsky_data clear_samples = clearsky.detect_clearsky( @@ -555,7 +554,6 @@ def test_detect_clearsky(detect_clearsky_data): check_dtype=False, check_names=False) -@requires_scipy def test_detect_clearsky_components(detect_clearsky_data): expected, cs = detect_clearsky_data clear_samples, components, alpha = clearsky.detect_clearsky( @@ -566,7 +564,6 @@ def test_detect_clearsky_components(detect_clearsky_data): assert np.allclose(alpha, 0.9633903181941296) -@requires_scipy def test_detect_clearsky_iterations(detect_clearsky_data): expected, cs = detect_clearsky_data alpha = 1.0448 @@ -581,7 +578,6 @@ def test_detect_clearsky_iterations(detect_clearsky_data): check_dtype=False, check_names=False) -@requires_scipy def test_detect_clearsky_kwargs(detect_clearsky_data): expected, cs = detect_clearsky_data clear_samples = clearsky.detect_clearsky( @@ -591,7 +587,6 @@ def test_detect_clearsky_kwargs(detect_clearsky_data): assert clear_samples.all() -@requires_scipy def test_detect_clearsky_window(detect_clearsky_data): expected, cs = detect_clearsky_data clear_samples = clearsky.detect_clearsky( @@ -602,7 +597,6 @@ def test_detect_clearsky_window(detect_clearsky_data): check_dtype=False, check_names=False) -@requires_scipy def test_detect_clearsky_arrays(detect_clearsky_data): expected, cs = detect_clearsky_data clear_samples = clearsky.detect_clearsky( @@ -611,7 +605,6 @@ def test_detect_clearsky_arrays(detect_clearsky_data): assert (clear_samples == expected['Clear or not'].values).all() -@requires_scipy def test_detect_clearsky_irregular_times(detect_clearsky_data): expected, cs = detect_clearsky_data times = cs.index.values.copy() diff --git a/pvlib/tests/test_iam.py b/pvlib/tests/test_iam.py index 9b1d2a01d8..607e35a072 100644 --- a/pvlib/tests/test_iam.py +++ b/pvlib/tests/test_iam.py @@ -13,10 +13,8 @@ from numpy.testing import assert_allclose from pvlib import iam as _iam -from conftest import needs_numpy_1_10, requires_scipy -@needs_numpy_1_10 def test_ashrae(): thetas = np.array([-90., -67.5, -45., -22.5, 0., 22.5, 45., 67.5, 89., 90., np.nan]) @@ -28,7 +26,6 @@ def test_ashrae(): assert_series_equal(iam_series, pd.Series(expected)) -@needs_numpy_1_10 def test_ashrae_scalar(): thetas = -45. iam = _iam.ashrae(thetas, .05) @@ -40,7 +37,6 @@ def test_ashrae_scalar(): assert_allclose(iam, expected, equal_nan=True) -@needs_numpy_1_10 def test_physical(): aoi = np.array([-90., -67.5, -45., -22.5, 0., 22.5, 45., 67.5, 90., np.nan]) @@ -56,7 +52,6 @@ def test_physical(): assert_series_equal(iam, expected) -@needs_numpy_1_10 def test_physical_scalar(): aoi = -45. iam = _iam.physical(aoi, 1.526, 0.002, 4) @@ -147,7 +142,6 @@ def test_martin_ruiz_diffuse(): assert_series_equal(iam[1], expected_gnd) -@requires_scipy def test_iam_interp(): aoi_meas = [0.0, 45.0, 65.0, 75.0] diff --git a/pvlib/tests/test_inverter.py b/pvlib/tests/test_inverter.py index de018e95c5..968b524bb6 100644 --- a/pvlib/tests/test_inverter.py +++ b/pvlib/tests/test_inverter.py @@ -6,7 +6,7 @@ from conftest import assert_series_equal from numpy.testing import assert_allclose -from conftest import needs_numpy_1_10, DATA_DIR +from conftest import DATA_DIR import pytest from pvlib import inverter @@ -115,7 +115,6 @@ def test_pvwatts_possible_negative(): assert_allclose(out, expected) -@needs_numpy_1_10 def test_pvwatts_arrays(): pdc = np.array([[np.nan], [0], [50], [100]]) pdc0 = 100 diff --git a/pvlib/tests/test_irradiance.py b/pvlib/tests/test_irradiance.py index 3a7399e39c..8bea0816cc 100644 --- a/pvlib/tests/test_irradiance.py +++ b/pvlib/tests/test_irradiance.py @@ -13,7 +13,7 @@ from pvlib import irradiance -from conftest import (needs_numpy_1_10, requires_ephem, requires_numba) +from conftest import requires_ephem, requires_numba # fixtures create realistic test input data @@ -245,7 +245,6 @@ def test_perez_components(irrad_data, ephem_data, dni_et, relative_airmass): assert_series_equal(sum_components, expected_for_sum, check_less_precise=2) -@needs_numpy_1_10 def test_perez_arrays(irrad_data, ephem_data, dni_et, relative_airmass): dni = irrad_data['dni'].copy() dni.iloc[2] = np.nan @@ -680,7 +679,6 @@ def test_erbs_all_scalar(): assert_allclose(v, expected[k], 5) -@needs_numpy_1_10 def test_dirindex(times): ghi = pd.Series([0, 0, 1038.62, 254.53], index=times) ghi_clearsky = pd.Series( diff --git a/pvlib/tests/test_location.py b/pvlib/tests/test_location.py index 6bff7cb3ff..82ab4f8a6d 100644 --- a/pvlib/tests/test_location.py +++ b/pvlib/tests/test_location.py @@ -16,7 +16,7 @@ from pvlib.solarposition import declination_spencer71 from pvlib.solarposition import equation_of_time_spencer71 from test_solarposition import expected_solpos, golden, golden_mst -from conftest import requires_ephem, requires_scipy +from conftest import requires_ephem, requires_tables def test_location_required(): @@ -78,7 +78,7 @@ def times(): freq='3H') -@requires_scipy +@requires_tables def test_get_clearsky(mocker, times): tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson') m = mocker.spy(pvlib.clearsky, 'ineichen') diff --git a/pvlib/tests/test_modelchain.py b/pvlib/tests/test_modelchain.py index af7d631dc9..3c33425db6 100644 --- a/pvlib/tests/test_modelchain.py +++ b/pvlib/tests/test_modelchain.py @@ -13,7 +13,7 @@ from conftest import assert_series_equal import pytest -from conftest import fail_on_pvlib_version, requires_scipy +from conftest import fail_on_pvlib_version, requires_tables @pytest.fixture(scope='function') @@ -333,12 +333,7 @@ def poadc(mc): @pytest.mark.parametrize('dc_model', [ - 'sapm', - pytest.param('cec', marks=requires_scipy), - pytest.param('desoto', marks=requires_scipy), - pytest.param('pvsyst', marks=requires_scipy), - pytest.param('singlediode', marks=requires_scipy), - 'pvwatts_dc']) + 'sapm', 'cec', 'desoto', 'pvsyst', 'singlediode', 'pvwatts_dc']) def test_infer_dc_model(sapm_dc_snl_ac_system, cec_dc_snl_ac_system, pvsyst_dc_snl_ac_system, pvwatts_dc_pvwatts_ac_system, location, dc_model, weather, mocker): @@ -377,10 +372,7 @@ def test_infer_dc_model(sapm_dc_snl_ac_system, cec_dc_snl_ac_system, assert isinstance(mc.dc, (pd.Series, pd.DataFrame)) -@pytest.mark.parametrize('dc_model', [ - 'sapm', - pytest.param('cec', marks=requires_scipy), - pytest.param('cec_native', marks=requires_scipy)]) +@pytest.mark.parametrize('dc_model', ['sapm', 'cec', 'cec_native']) def test_infer_spectral_model(location, sapm_dc_snl_ac_system, cec_dc_snl_ac_system, cec_dc_native_snl_ac_system, dc_model): @@ -394,8 +386,7 @@ def test_infer_spectral_model(location, sapm_dc_snl_ac_system, @pytest.mark.parametrize('temp_model', [ - 'sapm_temp', 'faiman_temp', - pytest.param('pvsyst_temp', marks=requires_scipy)]) + 'sapm_temp', 'faiman_temp', 'pvsyst_temp']) def test_infer_temp_model(location, sapm_dc_snl_ac_system, pvwatts_dc_pvwatts_ac_pvsyst_temp_system, pvwatts_dc_pvwatts_ac_faiman_temp_system, @@ -411,7 +402,6 @@ def test_infer_temp_model(location, sapm_dc_snl_ac_system, assert isinstance(mc, ModelChain) -@requires_scipy def test_infer_temp_model_invalid(location, sapm_dc_snl_ac_system): sapm_dc_snl_ac_system.temperature_model_parameters.pop('a') with pytest.raises(ValueError): @@ -421,7 +411,6 @@ def test_infer_temp_model_invalid(location, sapm_dc_snl_ac_system): # ModelChain.infer_temperature_model. remove or statement in v0.9 -@requires_scipy @fail_on_pvlib_version('0.9') def test_infer_temp_model_no_params(location, system_no_temp, weather): mc = ModelChain(system_no_temp, location, aoi_model='physical', @@ -431,7 +420,6 @@ def test_infer_temp_model_no_params(location, system_no_temp, weather): mc.run_model(weather) -@requires_scipy def test_temperature_model_inconsistent(location, sapm_dc_snl_ac_system): with pytest.raises(ValueError): ModelChain(sapm_dc_snl_ac_system, location, @@ -454,8 +442,7 @@ def acdc(mc): mc.ac = mc.dc -@pytest.mark.parametrize('ac_model', [ - 'sandia', pytest.param('adr', marks=requires_scipy), 'pvwatts']) +@pytest.mark.parametrize('ac_model', ['sandia', 'adr', 'pvwatts']) def test_ac_models(sapm_dc_snl_ac_system, cec_dc_adr_ac_system, pvwatts_dc_pvwatts_ac_system, location, ac_model, weather, mocker): @@ -478,8 +465,7 @@ def test_ac_models(sapm_dc_snl_ac_system, cec_dc_adr_ac_system, # TODO in v0.9: remove this test for a deprecation warning -@pytest.mark.parametrize('ac_model', [ - 'snlinverter', pytest.param('adrinverter', marks=requires_scipy)]) +@pytest.mark.parametrize('ac_model', ['snlinverter', 'adrinverter']) def test_ac_models_deprecated(sapm_dc_snl_ac_system, cec_dc_adr_ac_system, location, ac_model, weather): ac_systems = {'snlinverter': sapm_dc_snl_ac_system, @@ -576,9 +562,8 @@ def constant_spectral_loss(mc): mc.spectral_modifier = 0.9 -@requires_scipy @pytest.mark.parametrize('spectral_model', [ - 'sapm', 'first_solar', 'no_loss', constant_spectral_loss + 'sapm', 'first_solar', 'no_loss', constant_spectral_loss ]) def test_spectral_models(sapm_dc_snl_ac_system, location, spectral_model, weather): @@ -698,7 +683,6 @@ def test_deprecated_09(sapm_dc_snl_ac_system, cec_dc_adr_ac_system, aoi_model='no_loss', spectral_model='no_loss') -@requires_scipy def test_basic_chain_required(sam_data, cec_inverter_parameters, sapm_temperature_cs5p_220m): times = pd.date_range(start='20160101 1200-0700', @@ -716,7 +700,7 @@ def test_basic_chain_required(sam_data, cec_inverter_parameters, ) -@requires_scipy +@requires_tables def test_basic_chain_alt_az(sam_data, cec_inverter_parameters, sapm_temperature_cs5p_220m): times = pd.date_range(start='20160101 1200-0700', @@ -739,7 +723,7 @@ def test_basic_chain_alt_az(sam_data, cec_inverter_parameters, assert_series_equal(ac, expected) -@requires_scipy +@requires_tables def test_basic_chain_strategy(sam_data, cec_inverter_parameters, sapm_temperature_cs5p_220m): times = pd.date_range(start='20160101 1200-0700', @@ -760,7 +744,7 @@ def test_basic_chain_strategy(sam_data, cec_inverter_parameters, assert_series_equal(ac, expected) -@requires_scipy +@requires_tables def test_basic_chain_altitude_pressure(sam_data, cec_inverter_parameters, sapm_temperature_cs5p_220m): times = pd.date_range(start='20160101 1200-0700', @@ -824,7 +808,6 @@ def test_ModelChain___repr__(sapm_dc_snl_ac_system, location, strategy, assert mc.__repr__() == expected -@requires_scipy def test_complete_irradiance_clean_run(sapm_dc_snl_ac_system, location): """The DataFrame should not change if all columns are passed""" mc = ModelChain(sapm_dc_snl_ac_system, location) @@ -842,7 +825,7 @@ def test_complete_irradiance_clean_run(sapm_dc_snl_ac_system, location): pd.Series([9, 5], index=times, name='ghi')) -@requires_scipy +@requires_tables def test_complete_irradiance(sapm_dc_snl_ac_system, location): """Check calculations""" mc = ModelChain(sapm_dc_snl_ac_system, location) diff --git a/pvlib/tests/test_pvsystem.py b/pvlib/tests/test_pvsystem.py index 2ad26937b4..e7a0aac882 100644 --- a/pvlib/tests/test_pvsystem.py +++ b/pvlib/tests/test_pvsystem.py @@ -15,7 +15,7 @@ from pvlib import temperature from pvlib._deprecation import pvlibDeprecationWarning -from conftest import needs_numpy_1_10, requires_scipy, fail_on_pvlib_version +from conftest import fail_on_pvlib_version @pytest.mark.parametrize('iam_model,model_params', [ @@ -637,7 +637,6 @@ def fixture_v_from_i(request): return request.param -@requires_scipy @pytest.mark.parametrize( 'method, atol', [('lambertw', 1e-11), ('brentq', 1e-11), ('newton', 1e-8)] ) @@ -659,7 +658,6 @@ def test_v_from_i(fixture_v_from_i, method, atol): assert_allclose(V, V_expected, atol=atol) -@requires_scipy def test_i_from_v_from_i(fixture_v_from_i): # Solution set loaded from fixture Rsh = fixture_v_from_i['Rsh'] @@ -757,7 +755,6 @@ def fixture_i_from_v(request): return request.param -@requires_scipy @pytest.mark.parametrize( 'method, atol', [('lambertw', 1e-11), ('brentq', 1e-11), ('newton', 1e-11)] ) @@ -779,7 +776,6 @@ def test_i_from_v(fixture_i_from_v, method, atol): assert_allclose(I, I_expected, atol=atol) -@requires_scipy def test_PVSystem_i_from_v(mocker): system = pvsystem.PVSystem() m = mocker.patch('pvlib.pvsystem.i_from_v', autospec=True) @@ -788,7 +784,6 @@ def test_PVSystem_i_from_v(mocker): m.assert_called_once_with(*args) -@requires_scipy def test_i_from_v_size(): with pytest.raises(ValueError): pvsystem.i_from_v(20, [0.1] * 2, 0.5, [7.5] * 3, 6.0e-7, 7.0) @@ -800,7 +795,6 @@ def test_i_from_v_size(): method='newton') -@requires_scipy def test_v_from_i_size(): with pytest.raises(ValueError): pvsystem.v_from_i(20, [0.1] * 2, 0.5, [3.0] * 3, 6.0e-7, 7.0) @@ -812,7 +806,6 @@ def test_v_from_i_size(): method='newton') -@requires_scipy def test_mpp_floats(): """test max_power_point""" IL, I0, Rs, Rsh, nNsVth = (7, 6e-7, .1, 20, .5) @@ -828,7 +821,6 @@ def test_mpp_floats(): assert np.isclose(v, expected[k]) -@requires_scipy def test_mpp_array(): """test max_power_point""" IL, I0, Rs, Rsh, nNsVth = (np.array([7, 7]), 6e-7, .1, 20, .5) @@ -844,7 +836,6 @@ def test_mpp_array(): assert np.allclose(v, expected[k]) -@requires_scipy def test_mpp_series(): """test max_power_point""" idx = ['2008-02-17T11:30:00-0800', '2008-02-17T12:30:00-0800'] @@ -863,7 +854,6 @@ def test_mpp_series(): assert np.allclose(v, expected[k]) -@requires_scipy def test_singlediode_series(cec_module_params): times = pd.date_range(start='2015-01-01', periods=2, freq='12H') effective_irradiance = pd.Series([0.0, 800.0], index=times) @@ -883,7 +873,6 @@ def test_singlediode_series(cec_module_params): assert isinstance(out, pd.DataFrame) -@requires_scipy def test_singlediode_array(): # github issue 221 photocurrent = np.linspace(0, 10, 11) @@ -913,7 +902,6 @@ def test_singlediode_array(): assert_allclose(sd['i_mp'], expected, atol=0.01) -@requires_scipy def test_singlediode_floats(): out = pvsystem.singlediode(7, 6e-7, .1, 20, .5, method='lambertw') expected = {'i_xx': 4.2498, @@ -933,7 +921,6 @@ def test_singlediode_floats(): assert_allclose(v, expected[k], atol=1e-3) -@requires_scipy def test_singlediode_floats_ivcurve(): out = pvsystem.singlediode(7, 6e-7, .1, 20, .5, ivcurve_pnts=3, method='lambertw') expected = {'i_xx': 4.2498, @@ -950,7 +937,6 @@ def test_singlediode_floats_ivcurve(): assert_allclose(v, expected[k], atol=1e-3) -@requires_scipy def test_singlediode_series_ivcurve(cec_module_params): times = pd.date_range(start='2015-06-01', periods=3, freq='6H') effective_irradiance = pd.Series([0.0, 400.0, 800.0], index=times) @@ -1187,7 +1173,6 @@ def test_pvwatts_dc_scalars(): assert_allclose(out, expected) -@needs_numpy_1_10 def test_pvwatts_dc_arrays(): irrad_trans = np.array([np.nan, 900, 900]) temp_cell = np.array([30, np.nan, 30]) @@ -1213,7 +1198,6 @@ def test_pvwatts_losses_default(): assert_allclose(out, expected) -@needs_numpy_1_10 def test_pvwatts_losses_arrays(): expected = np.array([nan, 14.934904]) age = np.array([nan, 1]) diff --git a/pvlib/tests/test_scaling.py b/pvlib/tests/test_scaling.py index 62f37794d2..09c81ae3c7 100644 --- a/pvlib/tests/test_scaling.py +++ b/pvlib/tests/test_scaling.py @@ -5,7 +5,6 @@ from numpy.testing import assert_almost_equal from pvlib import scaling -from conftest import requires_scipy # Sample cloud speed @@ -119,20 +118,17 @@ def test_compute_wavelet_array_invalid(clear_sky_index): scaling._compute_wavelet(clear_sky_index) -@requires_scipy def test_wvm_series(clear_sky_index, time, positions, expect_cs_smooth): csi_series = pd.Series(clear_sky_index, index=time) cs_sm, _, _ = scaling.wvm(csi_series, positions, cloud_speed) assert_almost_equal(cs_sm[5000:5005], expect_cs_smooth, decimal=4) -@requires_scipy def test_wvm_array(clear_sky_index, positions, expect_cs_smooth): cs_sm, _, _ = scaling.wvm(clear_sky_index, positions, cloud_speed, dt=dt) assert_almost_equal(cs_sm[5000:5005], expect_cs_smooth, decimal=4) -@requires_scipy def test_wvm_series_xyaslist(clear_sky_index, time, positions, expect_cs_smooth): csi_series = pd.Series(clear_sky_index, index=time) @@ -140,7 +136,6 @@ def test_wvm_series_xyaslist(clear_sky_index, time, positions, assert_almost_equal(cs_sm[5000:5005], expect_cs_smooth, decimal=4) -@requires_scipy def test_wvm_invalid(clear_sky_index, positions): with pytest.raises(ValueError): scaling.wvm(clear_sky_index, positions, cloud_speed) diff --git a/pvlib/tests/test_singlediode.py b/pvlib/tests/test_singlediode.py index ca2a2e1270..ee20e4885e 100644 --- a/pvlib/tests/test_singlediode.py +++ b/pvlib/tests/test_singlediode.py @@ -7,13 +7,11 @@ from pvlib.singlediode import (bishop88_mpp, estimate_voc, VOLTAGE_BUILTIN, bishop88, bishop88_i_from_v, bishop88_v_from_i) import pytest -from conftest import requires_scipy POA = 888 TCELL = 55 -@requires_scipy @pytest.mark.parametrize('method', ['brentq', 'newton']) def test_method_spr_e20_327(method, cec_module_spr_e20_327): """test pvsystem.singlediode with different methods on SPR-E20-327""" @@ -42,7 +40,6 @@ def test_method_spr_e20_327(method, cec_module_spr_e20_327): assert np.isclose(pvs_ixx, ixx) -@requires_scipy @pytest.mark.parametrize('method', ['brentq', 'newton']) def test_newton_fs_495(method, cec_module_fs_495): """test pvsystem.singlediode with different methods on FS495""" @@ -99,7 +96,6 @@ def get_pvsyst_fs_495(): # DeSoto @(888[W/m**2], 55[degC]) = {Pmp: 72.71, Isc: 1.402, Voc: 75.42) -@requires_scipy @pytest.mark.parametrize( 'poa, temp_cell, expected, tol', [ # reference conditions @@ -187,7 +183,6 @@ def test_pvsyst_recombination_loss(method, poa, temp_cell, expected, tol): assert np.isclose(vsc_88, 0.0, *tol) -@requires_scipy @pytest.mark.parametrize( 'brk_params, recomb_params, poa, temp_cell, expected, tol', [ # reference conditions without breakdown model diff --git a/pvlib/tests/test_soiling.py b/pvlib/tests/test_soiling.py index 200bcfe59f..3bdd52bde1 100644 --- a/pvlib/tests/test_soiling.py +++ b/pvlib/tests/test_soiling.py @@ -7,7 +7,7 @@ from conftest import assert_series_equal from pvlib.soiling import hsu, kimber from pvlib.iotools import read_tmy3 -from conftest import requires_scipy, DATA_DIR +from conftest import DATA_DIR import pytest @@ -26,6 +26,7 @@ def expected_output(): index=dt) return expected_no_cleaning + @pytest.fixture def expected_output_1(): dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0), @@ -39,6 +40,7 @@ def expected_output_1(): index=dt) return expected_output_1 + @pytest.fixture def expected_output_2(): dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0), @@ -69,6 +71,7 @@ def expected_output_3(): index=dt_new) return expected_output_3 + @pytest.fixture def rainfall_input(): @@ -80,7 +83,6 @@ def rainfall_input(): return rainfall -@requires_scipy def test_hsu_no_cleaning(rainfall_input, expected_output): """Test Soiling HSU function""" @@ -97,7 +99,6 @@ def test_hsu_no_cleaning(rainfall_input, expected_output): assert_series_equal(result, expected_no_cleaning) -@requires_scipy def test_hsu(rainfall_input, expected_output_2): """Test Soiling HSU function with cleanings""" @@ -115,7 +116,6 @@ def test_hsu(rainfall_input, expected_output_2): assert_series_equal(result, expected_output_2) -@requires_scipy def test_hsu_defaults(rainfall_input, expected_output_1): """ Test Soiling HSU function with default deposition velocity and default rain @@ -126,7 +126,6 @@ def test_hsu_defaults(rainfall_input, expected_output_1): assert np.allclose(result.values, expected_output_1) -@requires_scipy def test_hsu_variable_time_intervals(rainfall_input, expected_output_3): """ Test Soiling HSU function with variable time intervals. @@ -145,6 +144,7 @@ def test_hsu_variable_time_intervals(rainfall_input, expected_output_3): rain_accum_period=pd.Timedelta('2h')) assert np.allclose(result, expected_output_3) + @pytest.fixture def greensboro_rain(): # get TMY3 data with rain diff --git a/pvlib/tools.py b/pvlib/tools.py index 1f8c1b7cbb..b6ee3e7c3a 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -2,9 +2,7 @@ Collection of functions used in pvlib_python """ -from collections import namedtuple import datetime as dt -import warnings import numpy as np import pandas as pd import pytz @@ -251,111 +249,6 @@ def _build_kwargs(keys, input_dict): return kwargs -# FIXME: remove _array_newton when SciPy-1.2.0 is released -# pvlib.singlediode.bishop88_i_from_v(..., method='newton') and other -# functions in singlediode call scipy.optimize.newton with a vector -# unfortunately wrapping the functions with np.vectorize() was too slow -# a vectorized newton method was merged into SciPy but isn't released yet, so -# in the meantime, we just copied the relevant code: "_array_newton" for more -# info see: https://github.com/scipy/scipy/pull/8357 - -def _array_newton(func, x0, fprime, args, tol, maxiter, fprime2, - converged=False): - """ - A vectorized version of Newton, Halley, and secant methods for arrays. Do - not use this method directly. This method is called from :func:`newton` - when ``np.isscalar(x0)`` is true. For docstring, see :func:`newton`. - """ - try: - p = np.asarray(x0, dtype=float) - except TypeError: # can't convert complex to float - p = np.asarray(x0) - failures = np.ones_like(p, dtype=bool) # at start, nothing converged - nz_der = np.copy(failures) - if fprime is not None: - # Newton-Raphson method - for iteration in range(maxiter): - # first evaluate fval - fval = np.asarray(func(p, *args)) - # If all fval are 0, all roots have been found, then terminate - if not fval.any(): - failures = fval.astype(bool) - break - fder = np.asarray(fprime(p, *args)) - nz_der = (fder != 0) - # stop iterating if all derivatives are zero - if not nz_der.any(): - break - # Newton step - dp = fval[nz_der] / fder[nz_der] - if fprime2 is not None: - fder2 = np.asarray(fprime2(p, *args)) - dp = dp / (1.0 - 0.5 * dp * fder2[nz_der] / fder[nz_der]) - # only update nonzero derivatives - p[nz_der] -= dp - failures[nz_der] = np.abs(dp) >= tol # items not yet converged - # stop iterating if there aren't any failures, not incl zero der - if not failures[nz_der].any(): - break - else: - # Secant method - dx = np.finfo(float).eps**0.33 - p1 = p * (1 + dx) + np.where(p >= 0, dx, -dx) - q0 = np.asarray(func(p, *args)) - q1 = np.asarray(func(p1, *args)) - active = np.ones_like(p, dtype=bool) - for iteration in range(maxiter): - nz_der = (q1 != q0) - # stop iterating if all derivatives are zero - if not nz_der.any(): - p = (p1 + p) / 2.0 - break - # Secant Step - dp = (q1 * (p1 - p))[nz_der] / (q1 - q0)[nz_der] - # only update nonzero derivatives - p[nz_der] = p1[nz_der] - dp - active_zero_der = ~nz_der & active - p[active_zero_der] = (p1 + p)[active_zero_der] / 2.0 - active &= nz_der # don't assign zero derivatives again - failures[nz_der] = np.abs(dp) >= tol # not yet converged - # stop iterating if there aren't any failures, not incl zero der - if not failures[nz_der].any(): - break - p1, p = p, p1 - q0 = q1 - q1 = np.asarray(func(p1, *args)) - zero_der = ~nz_der & failures # don't include converged with zero-ders - if zero_der.any(): - # secant warnings - if fprime is None: - nonzero_dp = (p1 != p) - # non-zero dp, but infinite newton step - zero_der_nz_dp = (zero_der & nonzero_dp) - if zero_der_nz_dp.any(): - rms = np.sqrt( - sum((p1[zero_der_nz_dp] - p[zero_der_nz_dp]) ** 2) - ) - warnings.warn('RMS of {:g} reached'.format(rms), - RuntimeWarning) - # newton or halley warnings - else: - all_or_some = 'all' if zero_der.all() else 'some' - msg = '{:s} derivatives were zero'.format(all_or_some) - warnings.warn(msg, RuntimeWarning) - elif failures.any(): - all_or_some = 'all' if failures.all() else 'some' - msg = '{0:s} failed to converge after {1:d} iterations'.format( - all_or_some, maxiter - ) - if failures.all(): - raise RuntimeError(msg) - warnings.warn(msg, RuntimeWarning) - if converged: - result = namedtuple('result', ('root', 'converged', 'zero_der')) - p = result(p, ~failures, zero_der) - return p - - # Created April,2014 # Author: Rob Andrews, Calama Consulting diff --git a/setup.py b/setup.py index 44dac1ecc3..11b8b79df1 100755 --- a/setup.py +++ b/setup.py @@ -40,15 +40,16 @@ INSTALL_REQUIRES = ['numpy >= 1.12.0', 'pandas >= 0.22.0', 'pytz', - 'requests'] + 'requests', + 'scipy >= 1.2.0'] TESTS_REQUIRE = ['nose', 'pytest', 'pytest-cov', 'pytest-mock', 'pytest-timeout', 'pytest-rerunfailures', 'pytest-remotedata'] EXTRAS_REQUIRE = { 'optional': ['cython', 'ephem', 'netcdf4', 'nrel-pysam', 'numba', - 'pvfactors', 'scipy', 'siphon', 'statsmodels', 'tables', + 'pvfactors', 'siphon', 'statsmodels', 'tables', 'cftime >= 1.1.1'], 'doc': ['ipython', 'matplotlib', 'sphinx == 1.8.5', 'sphinx_rtd_theme', - 'sphinx-gallery', 'docutils == 0.15.2', 'pillow', 'scipy', + 'sphinx-gallery', 'docutils == 0.15.2', 'pillow', 'netcdf4', 'siphon', 'tables'], 'test': TESTS_REQUIRE } @@ -61,9 +62,6 @@ 'Intended Audience :: Science/Research', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Topic :: Scientific/Engineering', ] @@ -71,7 +69,7 @@ 'zip_safe': False, 'scripts': [], 'include_package_data': True, - 'python_requires': '~=3.5' + 'python_requires': '>=3.6' } # set up pvlib packages to be installed and extensions to be compiled