Source code for ard.layout.boundary
import numpy as np
import jax.numpy as jnp
import jax
jax.config.update("jax_enable_x64", True)
import ard.utils.geometry
import openmdao.api as om
[docs]
class FarmBoundaryDistancePolygon(om.ExplicitComponent):
"""
A class to return distances between turbines and a polygonal boundary, or
sets of polygonal boundary regions.
Options
-------
modeling_options : dict
a modeling options dictionary
Inputs
------
x_turbines : np.ndarray
a 1D numpy array indicating the x-dimension locations of the turbines,
with length `N_turbines` (mirrored w.r.t. `FarmAeroTemplate`)
y_turbines : np.ndarray
a 1D numpy array indicating the y-dimension locations of the turbines,
with length `N_turbines` (mirrored w.r.t. `FarmAeroTemplate`)
"""
[docs]
def initialize(self):
"""Initialization of the OpenMDAO component."""
self.options.declare("modeling_options")
[docs]
def setup(self):
"""Setup of the OpenMDAO component."""
# load modeling options
self.modeling_options = self.options["modeling_options"]
self.windIO = self.modeling_options["windIO_plant"]
self.N_turbines = int(self.modeling_options["layout"]["N_turbines"])
# load boundary vertices from windIO file
if "boundaries" not in self.windIO["site"]:
raise KeyError(
"You have requested a boundary but no boundaries were found in the windIO file."
)
if "circle" in self.windIO["site"]["boundaries"]:
raise NotImplementedError(
"The circular boundaries from windIO have not been implemented here, yet."
)
if "polygons" not in self.windIO["site"]["boundaries"]:
raise KeyError(
"Currently only polygon boundaries from windIO have been implemented and none were found."
)
self.boundary_vertices = [
np.array(
[
polygon["x"],
polygon["y"],
]
).T
for polygon in self.windIO["site"]["boundaries"]["polygons"]
]
self.boundary_regions = self.modeling_options.get("boundary", {}).get(
"turbine_region_assignments", # get the region assignments from modeling_options, if there
np.zeros(self.N_turbines, dtype=int), # default to zero for all turbines
)
# prep the jacobian
self.distance_multi_point_to_multi_polygon_ray_casting_jac = jax.jacfwd(
ard.utils.geometry.distance_multi_point_to_multi_polygon_ray_casting, [0, 1]
)
# set up inputs and outputs for mooring system
self.add_input(
"x_turbines", jnp.zeros((self.N_turbines,)), units="m"
) # x location of the mooring platform in m w.r.t. reference coordinates
self.add_input(
"y_turbines", jnp.zeros((self.N_turbines,)), units="m"
) # y location of the mooring platform in m w.r.t. reference coordinates
self.add_output(
"boundary_distances",
jnp.zeros(self.N_turbines),
units="m",
)
[docs]
def setup_partials(self):
"""Derivative setup for the OpenMDAO component."""
# the default (but not preferred!) derivatives are FDM
self.declare_partials(
"*",
"*",
method="exact",
rows=np.arange(self.N_turbines),
cols=np.arange(self.N_turbines),
)
[docs]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
"""Computation for the OpenMDAO component."""
# unpack the working variables
x_turbines = inputs["x_turbines"]
y_turbines = inputs["y_turbines"]
boundary_distances = (
ard.utils.geometry.distance_multi_point_to_multi_polygon_ray_casting(
x_turbines,
y_turbines,
boundary_vertices=self.boundary_vertices,
regions=self.boundary_regions,
)
)
outputs["boundary_distances"] = boundary_distances
[docs]
def compute_partials(self, inputs, partials, discrete_inputs=None):
# unpack the working variables
x_turbines = inputs["x_turbines"]
y_turbines = inputs["y_turbines"]
jacobian = self.distance_multi_point_to_multi_polygon_ray_casting_jac(
x_turbines, y_turbines, self.boundary_vertices, self.boundary_regions
)
partials["boundary_distances", "x_turbines"] = jacobian[0].diagonal()
partials["boundary_distances", "y_turbines"] = jacobian[1].diagonal()