diff --git a/ard/layout/gridfarm.py b/ard/layout/gridfarm.py index b71d6ede..f681dd6c 100644 --- a/ard/layout/gridfarm.py +++ b/ard/layout/gridfarm.py @@ -8,11 +8,11 @@ class GridFarmLayout(templates.LayoutTemplate): A simplified, uniform four-parameter parallelepiped grid farm layout class. This is a class to take a parameterized, structured grid farm defined by a - gridded parallelepiped with spacing variables defined to: - 1) orient the farm with respect to North, - 2) space the rows of turbines along this primary vector, - 3) space the columns of turbines along the perpendicular, and - 4) skew the positioning along a parallel to the primary (orientation) vector. + gridded parallelepiped with spacing variables defined to: + 1) orient the farm with respect to North, + 2) space the rows of turbines along this primary vector, + 3) space the columns of turbines along the perpendicular, and + 4) skew the positioning along a parallel to the primary (orientation) vector. The layout model is shown in a ASCII image below: @@ -27,12 +27,12 @@ class GridFarmLayout(templates.LayoutTemplate): | ' / / / / / (rotated from | ' x ----- x ----- x ----- x ----- x north CW by | ' / / / / / orientation - | NORTH x ----- x ----- x ----- x ----- x angle) + | NORTH x ----- x ----- x ----- x ----- x angle) | /| | / | | / | <- skew angle - + Options ------- modeling_options : dict diff --git a/ard/layout/spacing.py b/ard/layout/spacing.py index a2512dde..5593cf2b 100644 --- a/ard/layout/spacing.py +++ b/ard/layout/spacing.py @@ -7,7 +7,7 @@ class TurbineSpacing(om.ExplicitComponent): """ - A class to return distances between turbines + A class to return distances between each pair of turbines without duplicates. Options ------- @@ -22,6 +22,12 @@ class TurbineSpacing(om.ExplicitComponent): 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`) + turbine_spacing : np.ndarray + a 1D numpy array indicating the distances between turbines, + with length (N_turbines - 1)*N_turbines/2. where, for 3 turbines, turbine_spacing[0] + is the distance between turbines 0 and 1, turbine_spacing[1] is the distance between + turbines 0 and 2, and turbine_spacing[2] is the distance between turbines 1 and 2. + The array is the flattened upper-triangular portion of the distance matrix. """ def initialize(self): @@ -35,7 +41,6 @@ def setup(self): self.modeling_options = self.options["modeling_options"] self.N_turbines = int(self.modeling_options["farm"]["N_turbines"]) self.N_distances = int((self.N_turbines - 1) * self.N_turbines / 2) - # MANAGE ADDITIONAL LATENT VARIABLES HERE!!!!! # set up inputs and outputs for mooring system self.add_input( @@ -82,7 +87,23 @@ def compute_partials(self, inputs, partials, discrete_inputs=None): def calculate_turbine_spacing( x_turbines: np.ndarray, y_turbines: np.ndarray, -): +) -> np.ndarray: + """Calculate the spacing between every pair of turbines with no duplicates. + + Args: + x_turbines (np.ndarray): a 1D numpy array indicating the x-dimension locations of the turbines, + with length `N_turbines + y_turbines (np.ndarray): a 1D numpy array indicating the y-dimension locations of the turbines, + with length `N_turbines + + Returns: + turbine distances (np.ndarray): a 1D numpy array indicating the distances between turbines + with length (N_turbines - 1)*N_turbines/2. The array is the flattened + upper-triangular portion of the distance matrix. For 3 turbines, turbine_spacing[0] is the + distance between turbines 0 and 1, turbine_spacing[1] is the distance between + turbines 0 and 2, and turbine_spacing[2] is the distance between turbines 1 and 2. + """ + N_turbines = len(x_turbines) # Create index pairs for i < j (upper triangle without diagonal) diff --git a/ard/offshore/mooring_constraint.py b/ard/offshore/mooring_constraint.py index 110cf087..afdab5b2 100644 --- a/ard/offshore/mooring_constraint.py +++ b/ard/offshore/mooring_constraint.py @@ -10,7 +10,9 @@ class MooringConstraint(om.ExplicitComponent): """ - A class to reduce complex mooring constraints into a simple violation lengthscale + A class to calculate the mooring line spacing distance for use in optimization + constraints. Mooring lines may be defined in 2D or 3D, but the turbine positions + are always assumed to be at sea level (z=0). Options ------- @@ -56,7 +58,6 @@ def setup(self): ) self.N_anchors = int(self.modeling_options["platform"]["N_anchors"]) self.N_distances = int((self.N_turbines - 1) * self.N_turbines / 2) - # MANAGE ADDITIONAL LATENT VARIABLES HERE!!!!! # set up inputs and outputs for mooring system self.add_input( @@ -81,14 +82,12 @@ def setup(self): jnp.zeros((self.N_turbines, self.N_anchors)), units="km", ) # z location of the mooring anchors in km w.r.t. reference coordinates - # ADD ADDITIONAL (DESIGN VARIABLE) INPUTS HERE!!!!! self.add_output( - "violation_distance", + "mooring_spacing", jnp.zeros(self.N_distances), units="km", ) # consolidated violation length - # ADD ADDITIONAL (DESIGN VARIABLE) OUTPUTS HERE!!!!! def setup_partials(self): """Derivative setup for the OpenMDAO component.""" @@ -117,7 +116,7 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): else: raise (ValueError("modeling_options['farm'][']")) - outputs["violation_distance"] = distances + outputs["mooring_spacing"] = distances def compute_partials(self, inputs, partials, discrete_inputs=None): @@ -140,12 +139,12 @@ def compute_partials(self, inputs, partials, discrete_inputs=None): else: raise (ValueError("modeling_options['farm'][']")) - partials["violation_distance", "x_turbines"] = jacobian[0] - partials["violation_distance", "y_turbines"] = jacobian[1] - partials["violation_distance", "x_anchors"] = jacobian[2] - partials["violation_distance", "y_anchors"] = jacobian[3] + partials["mooring_spacing", "x_turbines"] = jacobian[0] + partials["mooring_spacing", "y_turbines"] = jacobian[1] + partials["mooring_spacing", "x_anchors"] = jacobian[2] + partials["mooring_spacing", "y_anchors"] = jacobian[3] if self.N_anchor_dimensions == 3: - partials["violation_distance", "z_anchors"] = jacobian[4] + partials["mooring_spacing", "z_anchors"] = jacobian[4] def mooring_constraint_xy( @@ -154,7 +153,7 @@ def mooring_constraint_xy( x_anchors: np.ndarray, y_anchors: np.ndarray, ): - """Mooring constraint calculation in 2 dimensions + """Mooring distance calculation in 2 dimensions Args: x_turbines (np.ndarray): array of turbine x positions @@ -187,8 +186,9 @@ def mooring_constraint_xyz( y_anchors: np.ndarray, z_anchors: np.ndarray, ): - """Mooring constraint calculation in 3 dimensions. Third dimension is only required for the anchors since the - turbine foundations are all assumed to be at sea level. + """Mooring distance calculation in 3 dimensions. The third dimension + is only required for the anchors since the turbine platforms are + all assumed to be at sea level. Args: x_turbines (np.ndarray): array of turbine x positions @@ -224,7 +224,8 @@ def calc_mooring_distances(mooring_points: np.ndarray) -> np.ndarray: """Calculate the minimum distances between each set of mooring lines Args: - mooring_points (np.ndarray): array of mooring points of shape (n_turbines, n_anchors+1, n_dimensions) where n_dimensions may be 2 or 3 + mooring_points (np.ndarray): array of mooring points of shape + (n_turbines, n_anchors+1, n_dimensions) where n_dimensions may be 2 or 3 Returns: np.ndarray: 1D array of distances with length (n_turbines - 1)*n_turbines/2 @@ -251,8 +252,10 @@ def convert_inputs_x_y_to_xy( x_anchors: np.ndarray, y_anchors: np.ndarray, ) -> np.ndarray: - """Convert from inputs of x for turbines, y for turbines, x for anchors, and y for anchors to single array for mooring specification - that is of shape (n_turbines, n_anchors+1, 2). for each set of points, the turbine position is given first followed by the anchor positions + """Convert from inputs of x for turbines, y for turbines, x for anchors, and y for + anchors to single array for mooring specification that is of shape + (n_turbines, n_anchors+1, 2). for each set of points, the turbine position is given + first followed by the anchor positions. Args: x_turbines (np.ndarray): array of turbine x positions @@ -261,7 +264,8 @@ def convert_inputs_x_y_to_xy( y_anchors (np.ndarray): array of anchor y positions Returns: - np.ndarray: all input information combined into a single array of shape (n_turbines, n_anchors+1, 2) + np.ndarray: all turbine and anchor location information combined into a single + array of shape (n_turbines, n_anchors+1, 2) """ # Stack turbine positions and anchor positions directly @@ -282,8 +286,10 @@ def convert_inputs_x_y_z_to_xyz( y_anchors: np.ndarray, z_anchors: np.ndarray, ) -> np.ndarray: - """Convert from inputs of x for turbines, y for turbines, z for turbines, x for anchors, y for anchors, and z for anchors to single array for mooring specification - that is of shape (n_turbines, n_anchors+1, 3). for each set of points, the turbine position is given first followed by the anchor positions + """Convert from inputs of x for turbines, y for turbines, z for turbines, x for anchors, + y for anchors, and z for anchors to single array for mooring specification that is of + shape (n_turbines, n_anchors+1, 3). for each set of points, the turbine position is given + first followed by the anchor positions. Args: x_turbines (np.ndarray): array of turbine x positions @@ -338,8 +344,9 @@ def distance_point_to_mooring(point: np.ndarray, P_mooring: np.ndarray) -> float def distance_mooring_to_mooring( P_mooring_A: np.ndarray, P_mooring_B: np.ndarray ) -> float: - """Calculate the distance from one mooring to another. Moorings are defined with center point first - followed by anchor points in no specific order. + """Calculate the distance from one set of mooring lines to another. Moorings + are defined with the center point (platform location) first, followed by the + anchor points in no specific order. Args: P_mooring_A (np.ndarray): ndarray of points of mooring A of shape (npoints, nd) (e.g. (4, (x, y, z))). diff --git a/ard/utils/geometry.py b/ard/utils/geometry.py index a6baf9b5..da5e0a8c 100644 --- a/ard/utils/geometry.py +++ b/ard/utils/geometry.py @@ -12,8 +12,11 @@ def get_nearest_polygons( tol=1e-6, ): """ - Determines the nearest polygon for each point using the ray-casting algorithm. - This implementation is based on FLOWFarm.jl (https://github.com/byuflowlab/FLOWFarm.jl) + Determines the nearest polygon for each point using the ray-casting algorithm. This + function may be used to assign turbines to regions in a wind farm layout, but is not + intended for use in a gradient-based optimization context. The function is not + differentiable. This implementation is based on FLOWFarm.jl + (https://github.com/byuflowlab/FLOWFarm.jl) Args: boundary_vertices (np.ndarray or list of np.ndarray): Vertices of the boundary in @@ -77,14 +80,14 @@ def distance_multi_point_to_multi_polygon_ray_casting( ) -> np.ndarray: """ Calculate the distance from each point to the nearest point on a polygon or set of polygons using - the ray-casting algorithm. Negative means the turbine is inside at least one polygon. - This implementation is based on FLOWFarm.jl (https://github.com/byuflowlab/FLOWFarm.jl) + the ray-casting (Jordan curve theorem) algorithm. Negative means the turbine is inside at least + one polygon. This implementation is based on FLOWFarm.jl (https://github.com/byuflowlab/FLOWFarm.jl) Args: points_x (np.ndarray[list]): points x coordinates. points_y (np.ndarray[list]): points y coordinates. - boundary_vertices (list[list[np.ndarray]]): Vertices of the boundary in - counterclockwise order. + boundary_vertices (list[list[np.ndarray]]): Vertices of the each boundary in + counterclockwise order. Boundaries should be simple polygons but do not need to have the same number of vertices. regions (np.array[int]): Predefined region assignments for each point. Defaults to None. s (float, optional): Smoothing factor for smooth max. Defaults to 700. tol (float, optional): Tolerance for determining proximity of point to polygon to be considered inside the polygon. Defaults to 1e-6. @@ -140,18 +143,26 @@ def distance_point_to_polygon_ray_casting( return_distance: bool = True, ): """ - Determine the signed distance from a point to a polygon in a differentiable way. + Determines the signed distance from a point to a polygon using the Jordan curve + theorem (ray-casting) approach as discussed in [1] and [2]. The polygon is + assumed to be simple and defined in counterclockwise order. Complex polygons + (where edges cross one another) are not supported. The function is + differentiable with respect to the point coordinates. + + [1] Numerical Recipes: The Art of Scientific Computing by Press, et al. 3rd edition, sec. 21.4.3 (p. 1124) + [2] https://en.wikipedia.org/wiki/Point_in_polygon Args: point (jnp.ndarray): Point of interest (2D vector). - vertices (jnp.ndarray): Vertices of the polygon (Nx2 array). + vertices (jnp.ndarray): Vertices of the polygon (Nx2 array) in counterclockwise order. s (float, optional): Smoothing factor for the smoothmin function. Defaults to 700. shift (float, optional): Small shift to handle edge cases. Defaults to 1e-10. return_distance (bool, optional): Whether to return the signed distance or just - inside/outside status. Defaults to True. + inside/outside status. Defaults to True. When False, the function is not + differentiable. Returns: - float: Signed distance or inside/outside status. + float: Signed distance or inside/outside status. Negative if inside, positive if outside. """ # Ensure inputs are JAX arrays with explicit data types point = jnp.asarray(point, dtype=jnp.float32) @@ -181,11 +192,11 @@ def process_edge(edge_start, edge_end, point): return is_below, distance # Vectorize the edge processing function + process_edge_vec = jax.vmap(process_edge, in_axes=(0, 0, None)) + edge_starts = vertices[:-1] edge_ends = vertices[1:] - is_below, distances = jax.vmap(process_edge, in_axes=(0, 0, None))( - edge_starts, edge_ends, point - ) + is_below, distances = process_edge_vec(edge_starts, edge_ends, point) # Count the number of intersections intersection_counter = jnp.sum(is_below) @@ -219,7 +230,8 @@ def polygon_normals_calculator( Args: boundary_vertices (list of np.ndarray): List of m-by-2 arrays, where each array contains - the boundary vertices of a polygon in counterclockwise order. + the boundary vertices of a polygon in counter-clockwise order. The number of vertices (m) + in each polygon does not need to be the same. nboundaries (int, optional): The number of boundaries in the set. Defaults to 1. Returns: @@ -320,7 +332,9 @@ def _distance_lineseg_to_lineseg_coplanar( line_b_end: np.ndarray, ) -> float: """Returns the distance between two finite line segments assuming the segments are coplanar. - It is up to the user to check the required condition. + It is up to the user to check the required condition. There may be some error in the case + when the line segments are parallel since multiple points may have equal distances, leading to + some error from the smooth minimum function. Args: line_a_start (np.ndarray): start point of line a @@ -355,9 +369,14 @@ def distance_lineseg_to_lineseg_nd( line_b_end: np.ndarray, tol=1e-12, ) -> float: - """Find the distance between two line segments in 2d or 3d. This method is primarily based on reference [1]. + """Find the distance between two line segments in 2d or 3d. This method is primarily based on reference [1], + using a parametric approach based on the determinant and cross product to find the closest points on the two line + segments. However, to handle the special case of line segments that are coplanar, we use the smooth minimum of the + distance between the endpoints of the two line segments and the other line segment. In the coplanar case, the + returned distance between the two line segments may have a noticeable error due to possibly having multiple points + with the same distance, which leads to error in the smooth minimum function. - [1] Numerical Recipes: The Art of Scientific Computing by Press, et al. 3rd edition + [1] Numerical Recipes: The Art of Scientific Computing by Press, et al. 3rd edition, sec. 21.4.2 (p. 1121) Args: line_a_start (np.ndarray): The start point of line segment "a" as either [x,y,z] or [x,y] @@ -532,7 +551,11 @@ def st_lt_1(inputs23i) -> np.ndarray: def distance_point_to_lineseg_nd( point: np.ndarray, segment_start: np.ndarray, segment_end: np.ndarray ) -> float: - """Find the distance from a point to a finite line segment in N-Dimensions + """Find the distance from a point to a line segment in N-Dimensions. This + implementation can handle any number of dimensions as well as the reduced case + of point to point distance. If the same point is passed for the start and end + of the line segment, then the distance is simply the distance from the point + of interest to the single start/end point. Args: point (np.ndarray): point of interest [x,y,...] @@ -585,7 +608,8 @@ def get_closest_point_on_line_seg( segment_end: np.ndarray, segment_vector: np.ndarray, ) -> np.ndarray: - """Get the closest point on the line segment to the point of interest in N-Dimensions + """Get the closest point on a line segment to the point of interest in N-Dimensions + using vector projection. Args: point (np.ndarray): point of interest [x,y,...] diff --git a/ard/utils/mathematics.py b/ard/utils/mathematics.py index 47d72b35..a5000a6e 100644 --- a/ard/utils/mathematics.py +++ b/ard/utils/mathematics.py @@ -18,9 +18,9 @@ def smooth_max(x: jnp.ndarray, s: float = 1000.0) -> float: Args: x (list): list of values to be compared - s (float, optional): alpha for smooth max function. Defaults to 100.0. + s (float, optional): alpha for smooth max function. Defaults to 1000.0. Larger values of `s` lead to more accurate results, but reduce the smoothness - of the output values. + of the function. Returns: float: the smooth max of the provided `x` list @@ -47,9 +47,9 @@ def smooth_min(x: np.ndarray, s: float = 1000.0) -> float: Args: x (list): list of values to be compared - s (float, optional): alpha for smooth min function. Defaults to 100.0. + s (float, optional): alpha for smooth min function. Defaults to 1000.0. Larger values of `s` lead to more accurate results, but reduce the smoothness - of the output values. + of the function. Returns: float: the smooth min of the provided `x` list @@ -64,8 +64,8 @@ def smooth_min(x: np.ndarray, s: float = 1000.0) -> float: def smooth_norm(vec: np.ndarray, buf: float = 1e-12) -> float: """Smooth version of the Frobenius, or 2, norm. This version is nearly equivalent to the 2-norm with the maximum absolute error corresponding to the order of the buffer value. The maximum error in the gradient is near unity, but - the error is generally about twice the error in the absolute value. The key benefit of the smooth norm is - that it is differentiable near zero, while the standard norm is undefined. + the error in the gradient is generally about twice the error in the absolute value. The key benefit of the smooth norm is + that it is differentiable at 0.0, while the standard norm is undefined at 0.0. Args: vec (np.ndarray): input vector to be normed diff --git a/docs/intro.md b/docs/intro.md index 301cd247..38d5c5d7 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -19,7 +19,7 @@ The documentation organized as follows: - **Getting Started**: helpful information to get you started using Ard, including installation instructions - **Tutorials**: example problems with solutions - **How-to Guides**: instructions on solving particular problems and using specific features of Ard -- **Reference**: dry, but helpfully useful, reference material including the API reference manual +- **Reference**: dry, but hopefully useful, reference material including the API reference manual - **Explanation**: discursive content on the whys, hows, and the wherefores of Ard for those that are interested diff --git a/examples/offshore/optimization_demo.py b/examples/offshore/optimization_demo.py index 1fd9c701..79ccf588 100644 --- a/examples/offshore/optimization_demo.py +++ b/examples/offshore/optimization_demo.py @@ -279,8 +279,8 @@ "coll_length": float( prob.get_val("optiwindnet_coll.total_length_cables", units="km")[0] ), - "violation_distance": float( - np.min(prob.get_val("mooring_constraint.violation_distance", units="km")) + "mooring_spacing": float( + np.min(prob.get_val("mooring_constraint.mooring_spacing", units="km")) ), } @@ -300,7 +300,7 @@ prob.model.add_design_var("angle_skew", lower=-75.0, upper=75.0) prob.model.add_design_var("phi_platform", lower=-30.0, upper=30.0) prob.model.add_constraint( - "mooring_constraint.violation_distance", units="m", lower=50.0 + "mooring_constraint.mooring_spacing", units="m", lower=50.0 ) prob.model.add_constraint( "spacing_constraint.turbine_spacing", units="m", lower=284.0 * 3.0 @@ -348,8 +348,8 @@ "coll_length": float( prob.get_val("optiwindnet_coll.total_length_cables", units="km")[0] ), - "violation_distance": float( - np.min(prob.get_val("mooring_constraint.violation_distance", units="km")) + "mooring_spacing": float( + np.min(prob.get_val("mooring_constraint.mooring_spacing", units="km")) ), "turbine_spacing": float( np.min(prob.get_val("spacing_constraint.turbine_spacing", units="km")) diff --git a/test/unit/ard/offshore/test_mooringconstraint.py b/test/unit/ard/offshore/test_mooringconstraint.py index 82a43c17..4e3c5b9b 100644 --- a/test/unit/ard/offshore/test_mooringconstraint.py +++ b/test/unit/ard/offshore/test_mooringconstraint.py @@ -36,7 +36,7 @@ def setup_method(self): def test_mooring_constraint_component_output(self): assert np.all( - self.prob0["violation_distance"] + self.prob0["mooring_spacing"] == pytest.approx(np.array([10.0, 30.0, 10.0]), rel=1e-3) ) @@ -72,7 +72,7 @@ def setup_method(self): def test_mooring_constraint_component_output(self): assert np.all( - self.prob0["violation_distance"] + self.prob0["mooring_spacing"] == pytest.approx(np.array([10.0, 30.0, 10.0]), rel=1e-3) ) @@ -102,15 +102,15 @@ def setup_method(self): prob1.setup() prob1.run_model() totals1 = prob1.compute_totals( - of=["violation_distance"], + of=["mooring_spacing"], wrt=["x_turbines", "y_turbines", "x_anchors", "y_anchors"], ) totals_expected1 = { - ("violation_distance", "x_turbines"): np.array([[0.0, 0.0]]), - ("violation_distance", "y_turbines"): np.array([[0.0, 0.0]]), - ("violation_distance", "x_anchors"): np.array([[0.0, -1.0, 1.0, 0.0]]), - ("violation_distance", "y_anchors"): np.array([[0.0, 0.0, 0.0, 0.0]]), + ("mooring_spacing", "x_turbines"): np.array([[0.0, 0.0]]), + ("mooring_spacing", "y_turbines"): np.array([[0.0, 0.0]]), + ("mooring_spacing", "x_anchors"): np.array([[0.0, -1.0, 1.0, 0.0]]), + ("mooring_spacing", "y_anchors"): np.array([[0.0, 0.0, 0.0, 0.0]]), } self.prob1 = prob1 @@ -119,36 +119,31 @@ def setup_method(self): def test_mooring_constraint_component_output(self): assert np.all( - self.prob1["violation_distance"] - == pytest.approx(np.array([14.0]), rel=1e-3) + self.prob1["mooring_spacing"] == pytest.approx(np.array([14.0]), rel=1e-3) ) def test_mooring_constraint_component_jacobian0(self): assert np.all( - self.totals1[("violation_distance", "x_turbines")] - == pytest.approx( - self.totals_expected1[("violation_distance", "x_turbines")] - ) + self.totals1[("mooring_spacing", "x_turbines")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "x_turbines")]) ) def test_mooring_constraint_component_jacobian1(self): assert np.all( - self.totals1[("violation_distance", "y_turbines")] - == pytest.approx( - self.totals_expected1[("violation_distance", "y_turbines")] - ) + self.totals1[("mooring_spacing", "y_turbines")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "y_turbines")]) ) def test_mooring_constraint_component_jacobian2(self): assert np.all( - self.totals1[("violation_distance", "x_anchors")] - == pytest.approx(self.totals_expected1[("violation_distance", "x_anchors")]) + self.totals1[("mooring_spacing", "x_anchors")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "x_anchors")]) ) def test_mooring_constraint_component_jacobian3(self): assert np.all( - self.totals1[("violation_distance", "y_anchors")] - == pytest.approx(self.totals_expected1[("violation_distance", "y_anchors")]) + self.totals1[("mooring_spacing", "y_anchors")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "y_anchors")]) ) @@ -179,18 +174,18 @@ def setup_method(self): prob1.setup() prob1.run_model() totals1 = prob1.compute_totals( - of=["violation_distance"], + of=["mooring_spacing"], wrt=["x_turbines", "y_turbines", "x_anchors", "y_anchors", "z_anchors"], ) totals_expected1 = { - ("violation_distance", "x_turbines"): np.array([[-0.48060241, 0.0]]), - ("violation_distance", "y_turbines"): np.array([[0.0, 0.0]]), - ("violation_distance", "x_anchors"): np.array( + ("mooring_spacing", "x_turbines"): np.array([[-0.48060241, 0.0]]), + ("mooring_spacing", "y_turbines"): np.array([[0.0, 0.0]]), + ("mooring_spacing", "x_anchors"): np.array( [[0.0, -0.51760243, 0.9982048, 0.0]] ), - ("violation_distance", "y_anchors"): np.array([[0.0, 0.0, 0.0, 0.0]]), - ("violation_distance", "z_anchors"): np.array( + ("mooring_spacing", "y_anchors"): np.array([[0.0, 0.0, 0.0, 0.0]]), + ("mooring_spacing", "z_anchors"): np.array( [[0.0, -0.03105615, 0.05989229, 0.0]] ), } @@ -201,42 +196,37 @@ def setup_method(self): def test_mooring_constraint_component_output(self): assert np.all( - self.prob1["violation_distance"] - == pytest.approx(np.array([15.47]), rel=1e-3) + self.prob1["mooring_spacing"] == pytest.approx(np.array([15.47]), rel=1e-3) ) def test_mooring_constraint_component_jacobian0(self): assert np.all( - self.totals1[("violation_distance", "x_turbines")] - == pytest.approx( - self.totals_expected1[("violation_distance", "x_turbines")] - ) + self.totals1[("mooring_spacing", "x_turbines")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "x_turbines")]) ) def test_mooring_constraint_component_jacobian1(self): assert np.all( - self.totals1[("violation_distance", "y_turbines")] - == pytest.approx( - self.totals_expected1[("violation_distance", "y_turbines")] - ) + self.totals1[("mooring_spacing", "y_turbines")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "y_turbines")]) ) def test_mooring_constraint_component_jacobian2(self): assert np.all( - self.totals1[("violation_distance", "x_anchors")] - == pytest.approx(self.totals_expected1[("violation_distance", "x_anchors")]) + self.totals1[("mooring_spacing", "x_anchors")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "x_anchors")]) ) def test_mooring_constraint_component_jacobian3(self): assert np.all( - self.totals1[("violation_distance", "y_anchors")] - == pytest.approx(self.totals_expected1[("violation_distance", "y_anchors")]) + self.totals1[("mooring_spacing", "y_anchors")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "y_anchors")]) ) def test_mooring_constraint_component_jacobian4(self): assert np.all( - self.totals1[("violation_distance", "z_anchors")] - == pytest.approx(self.totals_expected1[("violation_distance", "z_anchors")]) + self.totals1[("mooring_spacing", "z_anchors")] + == pytest.approx(self.totals_expected1[("mooring_spacing", "z_anchors")]) )