Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/sphinx/source/whatsnew/v0.15.3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Deprecations

Bug fixes
~~~~~~~~~
~~~~~~~~

* Fixed ``ModelChain`` no longer replacing missing (NaN) values in the DC
and AC results with zero. Genuine gaps in the input now propagate as NaN,
following the "NaN in, NaN out" policy, while nighttime correctly remains
zero power. (:issue:`1409`)


Enhancements
Expand Down
10 changes: 8 additions & 2 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,10 @@ def _make_diode_params(photocurrent, saturation_current,
self.results.dc,
unwrap=False
)
self.results.dc = tuple(dc.fillna(0) for dc in self.results.dc)
# Missing (NaN) values in the inputs are intentionally preserved so
# that the result follows the "NaN in, NaN out" policy. At night the
# single diode model already returns zero power, so night is not
# affected by not filling NaNs here.
# If the system has one Array, unwrap the single return value
# to preserve the original behavior of ModelChain
if self.system.num_arrays == 1:
Expand Down Expand Up @@ -766,7 +769,10 @@ def adr_inverter(self):

def pvwatts_inverter(self):
ac = self.system.get_ac('pvwatts', self.results.dc)
self.results.ac = ac.fillna(0)
# Missing (NaN) values in the DC input are preserved so that the
# result follows the "NaN in, NaN out" policy instead of silently
# reporting zero AC for missing data.
self.results.ac = ac
return self

@property
Expand Down
36 changes: 36 additions & 0 deletions tests/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,42 @@ def test_run_model_with_irradiance(sapm_dc_snl_ac_system, location):
assert_series_equal(ac, expected)


def test_run_model_preserves_nan_inputs(cec_dc_snl_ac_system, location):
# regression test for gh-1409: genuine missing (NaN) values in the
# weather must not be silently replaced with zero in the DC results.
# Nighttime (zero irradiance) should still yield zero power.
mc = ModelChain(cec_dc_snl_ac_system, location, dc_model='cec')
times = pd.date_range('20160101 1200-0700', periods=3, freq='6h')
weather = pd.DataFrame(
{'ghi': [500, np.nan, 0], 'dni': [800, np.nan, 0],
'dhi': [100, np.nan, 0]},
index=times)
mc.run_model(weather)
dc = mc.results.dc
# a missing input is preserved as NaN ...
assert np.isnan(dc['p_mp'].iloc[1])
# ... while nighttime remains zero power, not NaN ...
assert dc['p_mp'].iloc[2] == pytest.approx(0)
# ... and the daytime value is a sensible positive number.
assert dc['p_mp'].iloc[0] > 0


def test_pvwatts_inverter_preserves_nan_inputs(
pvwatts_dc_pvwatts_ac_system, location):
# regression test for gh-1409: the pvwatts inverter must not replace
# missing DC values with zero AC power.
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location,
aoi_model='no_loss')
times = pd.date_range('20160101 1200-0700', periods=3, freq='6h')
weather = pd.DataFrame(
{'ghi': [500, np.nan, 0], 'dni': [800, np.nan, 0],
'dhi': [100, np.nan, 0]},
index=times)
ac = mc.run_model(weather).results.ac
assert np.isnan(ac.iloc[1])
assert ac.iloc[2] == pytest.approx(0)


@pytest.fixture(scope='function')
def multi_array_sapm_dc_snl_ac_system(
sapm_temperature_cs5p_220m, sapm_module_params,
Expand Down