Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vectorize geometry calculations #60

Merged
merged 38 commits into from
Aug 2, 2019
Merged

Vectorize geometry calculations #60

merged 38 commits into from
Aug 2, 2019

Conversation

anomam
Copy link
Contributor

@anomam anomam commented Jul 25, 2019

Vectorize geometry calculations of ordered PV arrays to improve computation time

In OrderedPVArray:

  • PV row coordinates calculation can be vectorized

  • Ground shadows and cut points coordinates calculation can also be vectorized
    This means that the class will be instantiated and used differently.

  • Update OrderedPVArray class to use a fit()/transform() approach, just like the irradiance models

  • Update PVEngine to accept that change from PV arrays from now on

  • Update run function as necessary

@anomam anomam added the WIP Work in progress label Jul 25, 2019
@anomam anomam self-assigned this Jul 25, 2019
anomam added 20 commits July 26, 2019 11:30
* because pvrows and ground can change, so it's hard to know
if an update needs to be done or not
* the edge pts list was not re-initialized at every interation
* shaded length is somehow infinitesimally smaller than with previous class,
but this has virtually no impact. To be investigated later
* now surfaces have more logical indexing (from left to right, increasing index)
in ground object, so matrices ordering got shifted a bit
@@ -14,9 +13,8 @@ class PVEngine(object):
as a timeseries when the pvarrays can be build from dictionary parameters
"""

def __init__(self, params, vf_calculator=VFCalculator(),
def __init__(self, pvarray, vf_calculator=VFCalculator(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's meaningless to have pvarray=OrderedPVArray(), because it will make something fail later.
It's better to initialize the PV array outside of the engine and then pass it in

self.vf_calculator = vf_calculator
self.irradiance = irradiance_model
self.cls_pvarray = cls_pvarray
self.is_fast_mode = isinstance(fast_mode_pvrow_index, int) \
and fast_mode_pvrow_index < params['n_pvrows']
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since params is not passed anymore to the engine, we can't do this check for the user anymore

@@ -108,9 +104,11 @@ def fit(self, timestamps, DNI, DHI, solar_zenith, solar_azimuth,

# Fit irradiance model
self.irradiance.fit(timestamps, DNI, DHI, solar_zenith, solar_azimuth,
surface_tilt, surface_azimuth,
self.params['rho_front_pvrow'],
self.params['rho_back_pvrow'], albedo)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now the rho reflectivity values are passed to the irradiance models at initialization: it kind of makes more sense since they are not timeseries values, and bc the fit() function is more or less for timeseries vectorized calculations

pvarray = self.pvarray

# Transform pvarray to time step
pvarray.transform(idx)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now pvarray is also using the "fit()/transform()" paradigm, just like the irradiance models

for idx, surface in geom_dict.items():
surface.update_params({'q0': q0[idx], 'qinc': qinc[idx],
'isotropic': isotropic_vec[idx],
'reflection': reflection_vec[idx]})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there was a non-impactful conflict between the idx passed to run_timestep() and the index of this for loop

x_center, y_center = xy_center
radius = length / 2.
# Get rotation
rotation = get_rotation_from_tilt_azimuth(surface_azimuth, axis_azimuth,
tilt)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the fn get_rotation_from_tilt_azimuth will be re-used in OrderedPVArray.
The rotation output here could actually be returned by this coords_from_center_tilt_length() fn, but it would not really make sense...

self._surface_registry = None
self._view_matrix = None
self._obstr_matrix = None
self._surfaces_indexed = False
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these were used to cache the values of the class properties. But with the new OrderedPVArray, the pv array attributes can change without notice and it doesn't make sense anymore to cache these values: so they are now recalculated every time the properties are called.
(so we try to call the properties only once, or save their values outside if need to be reused)

self.rho_front = None
self.rho_back = None
self.rho_front = rho_front
self.rho_back = rho_back
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now reflectivity values are passed to the irradiance model at initialization, not in the fit() function; which makes more sense since they are always floats

vf_calculator = cls_vf()
eng = cls_engine(pvarray_parameters, cls_pvarray=cls_pvarray,
irradiance_model=irradiance_model,
eng = cls_engine(pvarray, irradiance_model=irradiance_model,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now the pvarray needs to be initialized outside of the engine

[8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 4],
[0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 10, 0, 5],
[8, 8, 8, 8, 0, 0, 0, 8, 0, 0, 0, 0, 0, 10, 0, 0, 4],
[0, 0, 0, 0, 6, 6, 6, 0, 6, 6, 0, 0, 0, 0, 0, 0, 5],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same surfaces are created, but their indexing differs now with the new implementation.
So the matrices are slightly shifted

vf_left_cut_sum_axis_one_rounded = np.array(
[0.009, 0.346, 0.359, 0.28, 0.268, 0.04, 0.001, 0.372, 0.387,
0.346, 0.035, 0.046, 0.064, 0.966, 0.048, 0.976, 0.981, 0.026,
0.977, 0.])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked that the values of this vector stayed consistent when I updated the matrix above (which was updated because of the new surface indexing of the new class implementation)

@anomam anomam removed the WIP Work in progress label Jul 31, 2019
@anomam
Copy link
Contributor Author

anomam commented Jul 31, 2019

Hey @cedricleroy , I would really appreciate if you could take a quick look.
There's a lot going on so you don't need to spend too much time on this and on the details. Any feedback would be greatly appreciated!
I will update the docs tutorials in a follow up PR

@anomam anomam requested a review from cedricleroy July 31, 2019 09:22
Copy link
Contributor

@cedricleroy cedricleroy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @anomam! Just minor cosmetic comments.

- solar zenith > 90, ie the sun is down
- DNI or DHI is negative, which does not make sense
- DNI and DHI are both zero

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little below in the Returns: It looks like None can be returned as well.

"""Calculate ``shapely`` :py:class:`LineString` coordinate from
center coords, surface tilt angle and length of line.
"""Calculate ``shapely`` :py:class:`LineString` coordinates from
center coords, surface angles and length of line.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not on the doc, should it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's a helper function I'll make it private! thx

positive, rotation angles can be negative.
In pvfactors, positive rotation angles will indicate pvrows pointing to the
left, and negative rotation angles will indicate pvrows pointing to the
right (no matter what the the axis azimuth is).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in the doc?


Parameters
----------
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameters order to review.

self.is_fast_mode = isinstance(fast_mode_pvrow_index, int) \
and fast_mode_pvrow_index < params['n_pvrows']
self.pvarray = pvarray
self.is_fast_mode = isinstance(fast_mode_pvrow_index, int)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would personally do this to avoid the type check:

self.is_fast_mode = False if fast_mode_pvrow_index is None else True


@classmethod
def from_dict(cls, parameters, surface_params=[]):
"""Create ordered PV array from dictionary of parameters
def init_from_dict(cls, pvarray_params, surface_params=[]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default list to be avoided.

surface_params=surface_params)

@classmethod
def transform_from_dict_of_scalars(cls, pvarray_params, surface_params=[]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default list to be avoided.

@classmethod
def from_ordered_shadow_and_cut_pt_coords(
cls, x_min_max=None, y_ground=Y_GROUND, ordered_shadow_coords=[],
cut_point_coords=[], surface_params=[]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default list to be avoided.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok got it! thx!


Parameters
----------
list_shaded_surfaces : list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the indent expected?


Parameters
----------
list_shaded_surfaces : list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent?

@anomam anomam merged commit c7d0ff1 into master Aug 2, 2019
@anomam anomam deleted the vectorize_geometry_calcs branch August 2, 2019 03:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants