Source code for haskoning_atr_tools.vibration_contour_plot.mesh.generate_mesh

### ===================================================================================================================
###  Functions to generate mesh for the vibration simulation
### ===================================================================================================================
# Copyright ©2026 Haskoning Nederland B.V.

### ===================================================================================================================
###  1. Import modules
### ===================================================================================================================

# General imports
import triangle
import numpy as np
from typing import Optional, List, Tuple
from collections.abc import Iterable, Sized, Sequence

# References for functions and classes in the haskoning_atr_tools package
from haskoning_atr_tools.vibration_contour_plot.mesh.mesh import Mesh
from haskoning_atr_tools.vibration_contour_plot.mesh.mesh_node import MeshNode
from haskoning_atr_tools.vibration_contour_plot.mesh.mesh_element import MeshElement


### ===================================================================================================================
###  2. Function to generate mesh for the vibration simulation
### ===================================================================================================================

[docs] def generate_constrained_mesh( project, x_range: Tuple[float, float], y_range: Tuple[float, float], mesh_area: float, constraint_points: Optional[List] = None, line_segments: Optional[List[List[float]]] = None) -> Mesh: """ Function to generate mesh for the vibration simulation. The mesh is constrained for the positions of equipment and targets. Input: - project (obj): Project object containing collections of objects and project variables. - x_range (tuple): Tuple of lower and upper bound of the x-axis limits, values in [m]. - y_range (tuple): Tuple of lower and upper bound of the y-axis limits, values in [m]. - mesh_area (float): Maximum size of the mesh elements, in [m2]. - constraint_points (list of lists of 2 floats): The user can specify additional points that are used to constrain the mesh on. Default value is None. - line_segments (list of lists of 2 floats): The user can specify additional lines that are used to constrain the mesh on. Default value is None. Output: - Returns the generated mesh. - The mesh is created within the x- and y- range defined by the user. The equipment and targets in the project constrain the mesh. - The created mesh is added to the project. """ def is_valid_range_input(x) -> bool: """ Checks if the input for range is valid.""" if isinstance(x, (str, bytes)): return False if not isinstance(x, Sized): return False if len(x) != 2: return False if not isinstance(x, Iterable): return False if isinstance(x, Sequence): a = x[0] b = x[1] else: a, b = tuple(x) if isinstance(a, bool) or isinstance(b, bool): return False if not (isinstance(a, (float, int)) and isinstance(b, (float, int))): return False return a < b # Collect constraining points if constraint_points is None: constraint_points = [] constraint_points += \ [(obj.coordinates[0], obj.coordinates[1]) for obj in project.equipment_list + project.target_list] # 1. Process line segments: [[x1, y1], [x2, y2]] -> ((x1, y1), (x2, y2)) processed_line_segments = [] if line_segments: # seg is the inner list: [[x1, y1], [x2, y2]] for seg in line_segments: # pt is a coordinate pair: [x, y] processed_seg = tuple(tuple(pt) for pt in seg) processed_line_segments.append(processed_seg) # Rectangle boundary points if not is_valid_range_input(x=x_range): raise ValueError( f"ERROR: Input for the x-range is invalid. Input should be a tuple with two floats, first one smaller than " f"the second one. Provided was {x_range}.") if not is_valid_range_input(x=y_range): raise ValueError( f"ERROR: Input for the y-range is invalid. Input should be a tuple with two floats, first one smaller than " f"the second one. Provided was {y_range}.") rect = [ (x_range[0], y_range[0]), (x_range[1], y_range[0]), (x_range[1], y_range[1]), (x_range[0], y_range[1])] points = rect[:] point_idx = {pt: i for i, pt in enumerate(points)} # Filter constrained points within range constraint_points = list(set( [pt for pt in constraint_points if x_range[0] <= pt[0] <= x_range[1] and y_range[0] <= pt[1] <= y_range[1]])) # Add line segment points # TODO Add filter for line segments if processed_line_segments: for seg in processed_line_segments: for pt in seg: if pt not in point_idx: point_idx[pt] = len(points) points.append(pt) # Add the additional constraint points for pt in constraint_points: if pt not in point_idx: point_idx[pt] = len(points) points.append(pt) # Rectangle boundary segments segments = [[point_idx[rect[i]], point_idx[rect[(i + 1) % 4]]] for i in range(4)] # User line segments if processed_line_segments: for seg in processed_line_segments: segments.append([point_idx[seg[0]], point_idx[seg[1]]]) # Prepare input for triangle A = {'vertices': np.array(points), 'segments': np.array(segments)} # Triangulate with constraints, using mesh_area as the max area if not isinstance(mesh_area, (float, int)) or isinstance(mesh_area, bool): raise TypeError(f"ERROR: Input for mesh area should be a float. Provided was {type(mesh_area)}.") if mesh_area <= 0: raise ValueError(f"ERROR: Input for mesh area should be larger than 0. Provided was {mesh_area}.") tri_mesh = triangle.triangulate(A, f'pq30a{mesh_area}') # Create MeshNode objects mesh_nodes = [ MeshNode(id=project.mesh_node_IdGenerator.get_next_id(), coordinates=point.tolist()) for point in tri_mesh['vertices']] # Create MeshElement objects mesh_elements = [] for tri in tri_mesh['triangles']: id = project.mesh_element_IdGenerator.get_next_id() tri_mesh_nodes = [node for node in mesh_nodes if node.id in tri.tolist()] mesh_elements.append(MeshElement(id=id, mesh_nodes=tri_mesh_nodes)) # Create Mesh object project.mesh = Mesh(mesh_elements=mesh_elements) project.mesh.project = project return project.mesh
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================