pvlib-python
pvlib-python copied to clipboard
SingleAxisTrackerMount doesn't store "tracking" values when calling ModelChain.run()
Describe the bug
I don't believe SingleAxisTrackerMount gets properly modeled using a ModelChain. I believe it's being modeled as a Fixed-Tilt system. The following condition lies in ModelChain.prepare_inputs()
:
if isinstance(self.system, SingleAxisTracker):
self._prep_inputs_tracking()
get_irradiance = partial(
self.system.get_irradiance,
self.results.tracking['surface_tilt'],
self.results.tracking['surface_azimuth'],
self.results.solar_position['apparent_zenith'],
self.results.solar_position['azimuth'])
else:
self._prep_inputs_fixed()
get_irradiance = partial(
self.system.get_irradiance,
self.results.solar_position['apparent_zenith'],
self.results.solar_position['azimuth'])
I created a ModelChain using SingleAxisTrackerMount instead of SingleAxisTracker, and the self.results.tracking
fields never got set. Also, isinstance(mc.system, pvlib.tracking.SingleAxisTracker)
is False.
To Reproduce Try running a ModelChain using SingleAxisTrackerMount instead of SingleAxisTracker.
Expected behavior
I would expect ModelChain.run_model()
to check isinstance(self.system, SingleAxisTrackerMount)
in addition to SingleAxisTracker (which is deprecated). (And thus, I'd expect the ModelChain class to import SingleAxisTrackerMount, which it currently doesn't.)
Versions:
-
pvlib.__version__
: 0.9.0
Additional context Happy to submit a PR for the above-mentioned changes if that's the correct expected behavior.
I think the scope of the problem may be larger than I initially thought. I believe pvlib.pvsystem needs some code updates as well. Also, I came across this error when I tried to run mc._prep_inputs_tracking()
:
'PVSystem' object has no attribute 'singleaxis'
Can you provide a full example code to reproduce fixed-tilt outputs coming from tracker inputs? This short example using SingleAxisTrackerMount
with ModelChain
seems to produce a tracker profile as expected:
Click to expand!
import pvlib # v0.9.0
import pandas as pd
location = pvlib.location.Location(40, -80)
times = pd.date_range('2019-06-01', '2019-06-02', freq='15min', tz='Etc/GMT+5')
weather = location.get_clearsky(times)
weather['temp_air'] = 20
weather['windspeed'] = 1
temperature_model_parameters = pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_polymer']
array = pvlib.pvsystem.Array(pvlib.tracking.SingleAxisTrackerMount(),
module_parameters={'pdc0': 1, 'gamma_pdc': -0.004},
temperature_model_parameters=temperature_model_parameters)
system = pvlib.pvsystem.PVSystem([array],
inverter_parameters={'pdc0': 1})
mc = pvlib.modelchain.ModelChain(system, location, spectral_model='no_loss', aoi_model='no_loss')
mc.run_model(weather)
mc.results.dc.plot()
isinstance(self.system, SingleAxisTrackerMount)
This doesn't really make sense; unlike the now-deprecated SingleAxisTracker
, it's not that a PVSystem
is a Mount but rather that it has a Mount (or rather, it has an Array that has a Mount). The idea is that the Mount handles all the module orientation details so that the higher-level objects (Array, PVSystem, ModelChain) don't need to worry about it and are generic enough to handle any mounting configuration so long as the Mount produces the tilt & azimuth values.
and the self.results.tracking fields never got set
That may be a bug -- I don't recall that being an intentional decision. Maybe ModelChain
should always copy the output of mount.get_orientation()
to that field. And possibly rename to something more generic (results.orientation
?) so that it doesn't exclude FT.
I agree that the isinstance() code doesn't make sense. That's the root of the bug, I believe. When ModelChain.prepare_inputs() gets called, it runs that check, which will always turn up false now that SingleAxisTracker is deprecated, meaning self._prep_inputs_fixed() will get called for all PVSystems. That's also why self.results.tracking never gets set.
I'll post an example here later today.
I was able to dig deeper into the source of the problem. The example code you shared fully reproduces it. While ModelChain.run_model() does model the system with self._prep_inputs_fixed(), when it run get_irradiance, eventually this calls the mount.get_orientation() method, which returns the proper surface tilt and azimuth values.
The title for this github issue is misleading. The real bug is that you can't get surface_azimuth or surface_tilt values when you run ModelChain with a SingleAxisTrackerMount. This was stored in self.results.tracking with the previous implementation but doesn't get stored anywhere anymore.
To test this, use the example code above and then check that
mc.results.tracking == None
As a side note (but tied into this general conversation), some of the functions are misleadingly named after the new implementation as well.