Code Monkey home page Code Monkey logo

solarfactors's People

Contributors

anomam avatar campanelli-sunpower avatar cedricleroy avatar kandersolar avatar tcapelle avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

kandersolar

solarfactors's Issues

PVEngine.skip_step is not used

Originally posted in SunPower/pvfactors#139:

PVEngine creates a mask indicating values that need not be considered in the calculation:

# Skip timesteps when:
# - solar zenith > 90, ie the sun is down
# - DNI or DHI is negative, which does not make sense
# - DNI and DHI are both zero
self.skip_step = (solar_zenith > 90) | (DNI < 0) | (DHI < 0) \
| ((DNI == 0) & (DHI == 0))

However, that mask is not actually used anywhere today. It used to be, but it looks like it stopped being used in #84 during the switch from iterative to vectorized code.

I think the underlying concept may still be useful even in the current vectorized approach, so I think it's probably worth seeing if we can start using it again somehow, otherwise it should just be deleted IMHO.

Runtime scales poorly with n_pvrows

Originally posted in SunPower/pvfactors#134:

Oftentimes it is desirable to neglect edge effects to model a row in the interior of a large array. Unfortunately, it seems that the runtime scaling with n_pvrows is quite bad -- I'm having trouble pinning down the degree of the asymptotic polynomial complexity (if it even is polynomial, might be factorial?), but it's certainly not linear or even quadratic:

image

The good news is that going all the way to n_pvrows > 30 is overkill for making edge effects negligible -- n_pvrows=11 seems pretty good, at least for this array geometry:

image

The bad news is that n_pvrows=11 is still an order of magnitude slower than n_pvrows=3, the current default in the pvlib wrapper function. The code for the above plots is available here: https://gist.github.com/kanderso-nrel/e88e3f7389b9d144a546dbe5651dfe1e

I've not looked into how we might go about improving this situation. If I had to guess, it would require a pretty substantial refactor, if it's possible at all. It would be a pleasant surprise if that guess is incorrect :) But even if it can't be fixed, I think it's useful to have an issue documenting this effect.

Opportunities to improve the online documentation

Originally posted in SunPower/pvfactors#143:

Here's are two things that would be nice to have in the docs, derived largely or entirely from conversation with @spaneja:

Relevant class methods don't have their own docs page

For example: PVEngine has its own page (link), but its fit() method does not, making it hard to know what parameters the method takes (without referring to the tutorials, anyway). I think it should be straightforward to get this working just with sphinx configuration changes.

Irradiance component names and meanings are not documented

It's possible to extract not only total incident and absorbed irradiance but also various sub-components. The docs have an example of this:

"# Get the calculated outputs from the pv array\n",
"center_row_front_incident_irradiance = pvarray.ts_pvrows[1].front.get_param_weighted('qinc')\n",
"left_row_back_reflected_incident_irradiance = pvarray.ts_pvrows[0].back.get_param_weighted('reflection')\n",
"right_row_back_isotropic_incident_irradiance = pvarray.ts_pvrows[2].back.get_param_weighted('isotropic')\n",

It would be nice for the available component names (e.g. reflection) to be listed somewhere, along with specific descriptions of what each name refers to (e.g. does reflection include ground-reflected irradiance, or just row-row reflections?).

`ax` parameter of BasePVArray.plot_at_idx should be optional

Originally posted in SunPower/pvfactors#142:

It would be a minor convenience if BasePVArray.plot_at_idx could generate its own pyplot Axes object instead of forcing the user to pass one in. I don't mind keeping it as a required parameter in the plot_at_idx methods of other classes, but since BasePVArray is at the top of the tree and likely the one that people will be calling, it makes sense to me for it to be a special case.

It's also convenient that ax is currently the last required parameter, so there's no need to reorder parameters, just tack =None onto the parameter and do a quick check in the method body. However, since the method currently doesn't return anything, the user would have to use plt.gca() or similar to access the plot object, so it might make sense to start returning ax at the end of the method too.

maximum recursion depth with perez_diffuse_luminance

Originally submitted as SunPower/pvfactors#146 by @pasquierjb:

When calling perez_diffuse_luminance irradiance model with a panel configuration where the solar vector is almost orthogonal to the surface normal, the computation falls into an infinite recursion loop.

from pvfactors.irradiance.models import perez_diffuse_luminance

timestamps = ['2020-01-02 15:00:00+00:00']
surface_tilt=[86.43564127244697]
surface_azimuth=[312.7]
solar_zenith=[78.53023197697105]
solar_azimuth=[221.9758276910072]
DNI=[100]
DHI=[50]

perez_diffuse_luminance(
timestamps, surface_tilt, surface_azimuth, solar_zenith,
solar_azimuth, DNI, DHI)
RecursionError: maximum recursion depth exceeded while calling a Python object

This is probably because the dot product of the solar vector and both surface normal (front and back) is computed as negative by pvlib.

from pvlib import irradiance
surface_tilt = 86.43564127244697
surface_azimuth = 312.7
solar_azimuth = 221.9758276910072
solar_zenith = 78.53023197697105
            
irradiance.aoi_projection(surface_tilt, surface_azimuth, solar_zenith, solar_azimuth)
-3.642919299551295e-17

irradiance.aoi_projection(180-surface_tilt, surface_azimuth-180, solar_zenith, solar_azimuth)
-3.642919299551295e-17

Default MAX_X_GROUND is too small for simulations with many rows

Originally posted in SunPower/pvfactors#135:

By adjusting the gcr, pvrow_width, and n_pvrows inputs it is possible to simulate arrays covering an arbitrarily large distance. For typical inputs (e.g. n_pvrows=3, pvrow_width=4, gcr=0.4), the spanned distance is relatively small, but it's not wholly unreasonable to model a system with, for example, n_pvrows=15, gcr=0.5, pvrow_width=6, in which case the simulated array spans over 100 meters between the first and last rows. The reason this matters is because the default value of MAX_X_GROUND is 100, so the farthest segments in the scene get ignored entirely in the above extreme example. This leads to strange effects like the following, where legend entries indicate pvrow_height and pvrow_width:

image

Because the row width to height ratio is constant across all three variants, I'd expect the returned irradiance values to be identical. However, they are not, and it's due to an increasingly large portion of the array crossing that MAX_X_GROUND boundary as the array scale increases. By setting MAX_X_GROUND to a number large enough to capture the entire array, the difference disappears and the three curves overlap as expected. Here is some code to reproduce:

Click to expand!
from pvfactors import config
# uncomment these lines to make the three lines overlap
#config.MAX_X_GROUND = 1e3
#config.MIN_X_GROUND = -config.MAX_X_GROUND

import pvfactors
from pvfactors import geometry
import pvlib
import pandas as pd
import matplotlib.pyplot as plt

times = pd.date_range('2019-12-21 11:30', '2019-12-21 13:05', freq='5T', tz='Etc/GMT+5')
loc = pvlib.location.Location(40, -80)
sp = loc.get_solarposition(times)
cs = loc.get_clearsky(times)
tr = pvlib.tracking.singleaxis(sp.zenith, sp.azimuth, gcr=0.5)

data = {}
for height, width in [(1.5, 2.0), (3.0, 4.0), (4.5, 6.0)]:
    ret = pvlib.bifacial.pvfactors_timeseries(sp.azimuth, sp.zenith,
                                              tr.surface_azimuth, tr.surface_tilt,
                                              180, times, cs.dni, cs.dhi,
                                              0.5, height, width, 0.2,
                                              n_pvrows=15, index_observed_pvrow=7)
    data[f'({height}, {width})'] = ret[1]
pd.DataFrame(data).plot(figsize=(7, 4))
plt.ylabel('Rear-Side Irradiance [W/m2]')

For reference, the above plot was generated with pvfactors 1.5.1 and pvlib 0.8.1.


The above scenario is certainly unusual, but I don't think it's out of the realm of reasonable usage. Here are some ideas:

  • Increase the MAX_X_GROUND and MIN_X_GROUND values. I'm not sure what consequences this would have, if any, for more typical simulations.
  • Emit a warning if the simulated array is large enough for the default values to be problematic, for example if any of the rows extends beyond the limit, or maybe any of the ground shadows or something.
  • Mention in the documentation somewhere how to increase these limits on the user's end like I've done in the above example. I think pvfactors.config is not mentioned anywhere in the sphinx docs, though maybe I've just missed it.

cc @spaneja

Negative irradiance values when PV modules go underground

Originally posted in SunPower/pvfactors#141:

Currently there is nothing stopping a user from specifying an array much wider than it is tall and proceeding to model it at steep tilts that cause the lower module edge to go to negative heights. When that happens, negative irradiance values are returned. For example, for static solar position and irradiance, here's how the returned rearside irradiance varies with surface tilt for an array with pvrow_width/pvrow_height=3 showing how irradiance goes negative when pvrow_width/2 * sin(surface_tilt) > pvrow_height:

image

Code to reproduce above plot (v1.5.2):

Click to expand!
from pvfactors.geometry import OrderedPVArray
from pvfactors.engine import PVEngine
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

ts_inputs = pd.DataFrame({
    "timestamps": 0,
    "DNI": 1000,
    "DHI": 200,
    "solar_zenith": 45,
    "solar_azimuth": 90,
    "surface_tilt": np.linspace(0, 90, num=1000),
    "surface_azimuth": 90,
    "albedo": 0.2,
})

pvarray_parameters = {
    "n_pvrows": 3,
    "pvrow_height": 1,
    "pvrow_width": 3,
    "axis_azimuth": 180,
    "gcr": 0.5,
}

# tilt when part of the module goes underground
boundary_tilt = np.degrees(np.arcsin(
    pvarray_parameters["pvrow_height"] / (pvarray_parameters["pvrow_width"] / 2)
))

pvarray = OrderedPVArray.init_from_dict(pvarray_parameters)
engine = PVEngine(pvarray)
engine.fit(**ts_inputs)

engine.run_full_mode()
grear = pvarray.ts_pvrows[0].back.get_param_weighted("qinc")
plt.plot(ts_inputs["surface_tilt"], grear)
plt.axvline(boundary_tilt, c='k', ls='--')
plt.xlabel("Surface Tilt [degrees]")
plt.ylabel("Rearside Irradiance [W/m2]")

This behavior has tripped people up at least twice:

I think it would be good to raise an error, or at least emit a warning, in this situation. Perhaps a check like this in OrderedPVArray.fit?

        # Check whether the lower module edge ever goes underground
        lower_edge_height = self.height - 0.5 * self.width * np.sin(np.radians(surface_tilt))
        if np.any(lower_edge_height < 0):
            msg = ("Input pvrow_height is not large enough to prevent modules "
                   "from colliding with ground. Check pvrow_width, "
                   "pvrow_height, and surface_tilt.")
            raise ValueError(msg)

Front-side irradiance is not affected by horizon_band_angle

Originally posted in SunPower/pvfactors#131:

Changing the horizon_band_angle input to HybridPerezOrdered doesn't seem to affect front-side irradiance nearly as much as it does rear-side. Code to reproduce:

from pvfactors.geometry import OrderedPVArray
from pvfactors.engine import PVEngine
from pvfactors.irradiance.models import HybridPerezOrdered
import pandas as pd
import numpy as np

times = pd.to_datetime(['2019-06-01 12:00'])
dni = np.array([600])
dhi = np.array([100])
solar_zenith = np.array([45])
solar_azimuth = np.array([270])
surface_tilt = np.array([45])
surface_azimuth = np.array([270])
albedo = np.array([0.25])

def build_report(pvarray):
    row = pvarray.ts_pvrows[1]
    return {
        'front': row.front.get_param_weighted('qabs')[0],
        'back': row.back.get_param_weighted('qabs')[0],
    }

for band_angle in [5, 15]:
    irradiance_model = HybridPerezOrdered(horizon_band_angle=band_angle)
    pvarray = OrderedPVArray(n_pvrows=3, pvrow_height=1, pvrow_width=1, axis_azimuth=180, gcr=0.5)
    engine = PVEngine(pvarray, irradiance_model=irradiance_model)
    engine.fit(times, dni, dhi, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, albedo)
    out = engine.run_full_mode(fn_build_report=build_report)
    print(f'band_angle = {band_angle}\t-->', out)
band_angle = 5  --> {'front': 737.1206177322375, 'back': 43.08209190810574}
band_angle = 15 --> {'front': 737.1517229315606, 'back': 50.38605014260793}

Looking at HybridPerezOrdered.transform, I guess the horizon band shading calculation for the front is done differently from the rear. In fact it's not clear to me that horizon band shading is calculated at all for the front side, but maybe I'm missing it. If it is, I would naively expect horizon band shading loss to be more or less the same for front and rear side.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.