|
13 | 13 | from matplotlib.figure import Figure |
14 | 14 | from matplotlib.path import Path |
15 | 15 | from tqdm import tqdm |
| 16 | +from shapely.geometry import LineString, Point |
16 | 17 |
|
17 | 18 | from ..core.operations import get_degrees_from_uv |
18 | 19 | from ..core.plotting.colors import hex_colors_land, hex_colors_water |
19 | 20 | from ..core.plotting.utils import join_colormaps |
20 | 21 |
|
21 | 22 |
|
| 23 | +def add_forcing_edges_to_computational_domain(poly_clip, vert, tria): |
| 24 | + """ |
| 25 | + Add edges to the forcing mesh that align with the computational domain boundary. |
| 26 | + Parameters |
| 27 | + ---------- |
| 28 | + poly_clip : shapely.geometry.Polygon |
| 29 | + The polygon representing the computational domain boundary. |
| 30 | + vert : np.ndarray |
| 31 | + Array of shape (n_vertices, 2) containing the coordinates of the vertices in the forcing mesh. |
| 32 | + tria : np.ndarray |
| 33 | + Array of shape (n_triangles, 3) containing the indices of the vertices for each triangle in the forcing mesh. |
| 34 | + Returns |
| 35 | + ------- |
| 36 | + vert_clean : np.ndarray |
| 37 | + Updated array of vertices including new intersection points. |
| 38 | + edges_clean : np.ndarray |
| 39 | + Updated array of edges including new edges along the computational domain boundary. |
| 40 | + """ |
| 41 | + |
| 42 | + # Extract all edges from forcing mesh triangles |
| 43 | + edges_new_full = np.vstack([ |
| 44 | + tria[:, [0, 1]], |
| 45 | + tria[:, [1, 2]], |
| 46 | + tria[:, [2, 0]] |
| 47 | + ]) |
| 48 | + edges_new = np.unique(np.sort(edges_new_full, axis=1), axis=0) |
| 49 | + |
| 50 | + # Check which vertices are inside computational domain |
| 51 | + mask_node_in = np.array([poly_clip.contains(Point(x, y)) for x, y in vert]) |
| 52 | + |
| 53 | + # Classify edges by how many endpoints are inside |
| 54 | + count_inside = mask_node_in[edges_new].sum(axis=1) |
| 55 | + edges_both_inside = edges_new[count_inside == 2] |
| 56 | + edges_one_inside = edges_new[count_inside == 1] |
| 57 | + |
| 58 | + # Clip edges at computational domain boundary |
| 59 | + edges_one_inside_new = edges_one_inside.copy() |
| 60 | + new_points = [] |
| 61 | + |
| 62 | + for i, (n1, n2) in enumerate(edges_one_inside): |
| 63 | + p1, p2 = vert[n1], vert[n2] |
| 64 | + inside1 = mask_node_in[n1] |
| 65 | + |
| 66 | + line = LineString([p1, p2]) |
| 67 | + inter = line.intersection(poly_clip.boundary) |
| 68 | + |
| 69 | + if inter.is_empty: |
| 70 | + continue |
| 71 | + |
| 72 | + # Handle multiple intersection points |
| 73 | + if inter.geom_type == "MultiPoint": |
| 74 | + points = list(inter.geoms) |
| 75 | + ref = p1 if inside1 else p2 |
| 76 | + dists = [Point(ref).distance(pt) for pt in points] |
| 77 | + inter_pt = np.array(points[np.argmin(dists)].coords[0]) |
| 78 | + else: |
| 79 | + inter_pt = np.array(inter.coords[0]) |
| 80 | + |
| 81 | + new_index = len(vert) + len(new_points) |
| 82 | + new_points.append(inter_pt) |
| 83 | + |
| 84 | + if inside1: |
| 85 | + edges_one_inside_new[i, 1] = new_index |
| 86 | + else: |
| 87 | + edges_one_inside_new[i, 0] = new_index |
| 88 | + |
| 89 | + # Update vertices with new intersection points |
| 90 | + if len(new_points) > 0: |
| 91 | + vert_update = np.vstack([vert, np.array(new_points)]) |
| 92 | + |
| 93 | + # Clean up: keep only used nodes |
| 94 | + edges_used = np.vstack([edges_both_inside, edges_one_inside_new]) |
| 95 | + used_nodes = np.unique(edges_used.flatten()) |
| 96 | + vert_clean = vert_update[used_nodes] |
| 97 | + mapping = {old_idx: new_idx for new_idx, old_idx in enumerate(used_nodes)} |
| 98 | + edges_clean = np.vectorize(mapping.get)(edges_used) |
| 99 | + |
| 100 | + return vert_clean, edges_clean |
| 101 | + |
22 | 102 | def read_adcirc_grd(grd_file: str) -> Tuple[np.ndarray, np.ndarray, List[str]]: |
23 | 103 | """ |
24 | 104 | Reads the ADCIRC grid file and returns the node and element data. |
@@ -376,9 +456,6 @@ def plot_greensurge_setup( |
376 | 456 | ax.set_extent([*bnd], crs=ccrs.PlateCarree()) |
377 | 457 | plt.legend(loc="lower left", fontsize=10, markerscale=2.0) |
378 | 458 | ax.set_title("GreenSurge Mesh Setup") |
379 | | - gl = ax.gridlines(draw_labels=True) |
380 | | - gl.top_labels = False |
381 | | - gl.right_labels = False |
382 | 459 |
|
383 | 460 | return fig, ax |
384 | 461 |
|
|
0 commit comments