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