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

Consider using shapely 2.0's vectorized geometry operations #44

Open
kandersolar opened this issue Jan 11, 2023 · 4 comments
Open

Consider using shapely 2.0's vectorized geometry operations #44

kandersolar opened this issue Jan 11, 2023 · 4 comments

Comments

@kandersolar
Copy link
Member

Right now shading.shaded_fraction is limited to scalar solar position inputs. TrackerField allows array inputs, but uses a python loop to process each element individually. I assume the decision to limit shading.shaded_fraction to scalar inputs was at least in part because shapely's geometry operations were not vectorized.

However, it seems that one of the changes introduced in shapely 2.0 is that geometry operations are now vectorized and allegedly significantly faster than looping like we currently do (see https://shapely.readthedocs.io/en/stable/release/2.x.html#vectorized-element-wise-geometry-operations). @AdamRJensen if you have some time, it may be worth looking into using this new vectorized functionality.

@AdamRJensen
Copy link
Member

AdamRJensen commented Jan 12, 2023

Iøm imaging the first step would be to vectorize this part:

for i, (x, y) in enumerate(zip(xoff, yoff)):
if np.sqrt(x**2+y**2) < min_tracker_spacing:
# Project the geometry of the shading collector (total area) onto
# the plane of the reference collector
shading_geometry = affinity.translate(total_collector_geometry, x, y) # noqa: E501
# Update the unshaded area based on overlapping shade
unshaded_geometry = unshaded_geometry.difference(shading_geometry)
if plot or return_geometries:
shading_geometries.append(shading_geometry)

However, I still think that you have to loop to process each timestep individually. Edit: hmm maybe some vectorization can be done... I'm not quite sure tbh

@AdamRJensen
Copy link
Member

AdamRJensen commented Jan 16, 2023

The Shapely 2.0 documentation states:

The new vectorized functions are available in the top-level shapely namespace.

From the top-level namespace, there is a transform function that is vectorized and takes two arguments: (1) an array of geometries and (2) a transformation function. Here's an example:

from shapely import affinity, geometry
square = geometry.box(-1, -1, 1, 1)
shapely.transform([square]*5, lambda x: x + [2, 2])

I don't think the transform function would work for our case since I cannot see a way for the movement/transformation of the geometries to be described with a lambda function. @kanderso-nrel Perhaps I'm wrong here?

Currently, the twoaxistracking code uses the shapely.affinity.translate function, which takes a geometry and offsets (in the x, y, and z directions). Unfortunately, the translate function does not seem to be vectorized, e.g., this here does not work:

affinity.translate([square]*5, np.zeros(5), np.arange(5))

I posted a question to the Shapely Discussion forum asking more about it: shapely/shapely#1718

@kandersolar
Copy link
Member Author

I cannot see a way for the movement/transformation of the geometries to be described with a lambda function

Making sure I understand the problem: xoff and yoff are different for each geometry, but shapely.transform applies a single transformation to all geometries? That does seem like a problem. I wonder if shapely.set_coordinates() and shapely.get_coordinates() can be used instead?

A general thought: since the advantage of using in-library vectorization is that all iterating and computations can take place in C-land instead of python-land, any solution involving a lambda function might not be any faster than what we have now. But the proof will be in the benchmarks of course :)

@AdamRJensen
Copy link
Member

Making sure I understand the problem: xoff and yoff are different for each geometry, but shapely.transform applies a single transformation to all geometries? That does seem like a problem. I wonder if shapely.set_coordinates() and shapely.get_coordinates() can be used instead?

This is correct. xoff and yoff describes the centers of the shaders (grey rectangles):
image

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

No branches or pull requests

2 participants