Delft3非结构网格可视化

70 阅读3分钟

1. netCDF数据解析

1.1 变量

mesh2d_edge_faces: 边邻近的两个面,索引号

mesh2d_edge_nodes: 边的两个节点,索引号

mesh2d_edge_x: 边的特征位置x坐标,例如中点

mesh2d_edge_y: 边的特征位置y坐标,例如中点

mesh2d_face_nodes: 面包含的节点,索引号

mesh2d_face_x: 面的特征位置x坐标

mesh2d_face_y: 面的特征位置y坐标

mesh2d_face_x_bnd: 面的组成顶点的x坐标(面的顶点数维度)

mesh2d_face_y_bnd: 面的组成顶点的y坐标 (面的顶点数维度)

mesh2d_node_x: 顶点x坐标

mesh2d_node_y: 节点y坐标

mesh2d_node_z: 节点z坐标

1.2 维

mesh2d_nEdges: 边的数量

Two: 点坐标维度

mesh2d-nFaces: 面数

mesh2d-nMax-face-nodes: 面最大顶点数

mesh2d-nNodes: 节点数

2. Python网格可视化

from netCDF4 import Dataset
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
import numpy as np
from typing import List


class Node:
    """
    This is a base class Node for delft3d_net.
    """
    def __init__(self, loc: List[float], **kwargs):
        self.__loc = loc
        for key, value in kwargs.items():
            self.__setattr__(key, value)
        return

    def get_loc(self):
        return self.__loc
    
    def set_loc(self, loc:list) -> None:
        self.__loc = loc
        return

    def add_attr(self, **kwargs):
        for key, value in kwargs.items():
            self.__setattr__(key, value)
        return

    def __str__(self):
        return f"Node locates at {self.__loc}."

class Edge:
    """
    This is a base class Edge for delft3d_net
    """
    def __init__(self, edge_loc: List[float], edge_nodes: List[Node], **kwargs):
        """

        Parameters:
        ---------- 
        edge_loc: Characteristic positions of edges, such as midpoints.
        edge_nodes: Two node that makes up the edge.
        **kwargs: Set attributes for the edge, eg: id=1, color='k'
 
        Returns:
        ----------
        """
        self.__edge_loc = edge_loc
        self.__egde_nodes = edge_nodes
        for key, value in kwargs.items():
            self.__setattr__(key, value)
        return

    def get_edge_loc(self):
        return self.__edge_loc

    def get_edge_nodes(self):
        return self.__edge_nodes

    def set_edge_loc(self, edge_loc: List[float]) -> None:
        self.__edge_loc = edge_loc
        return
    
    def set_edge_nodes(self, edge_nodes: List[float]) -> None:
        self.__edge_nodes = edge_nodes
        return

    def add_attr(self, **kwargs):
        for key, value in kwargs:
            self.__setattr__(key, value)
        return

class Face:
    """
    This is a base class Face for delft3d_net.
    """
    def __init__(self, face_loc: List[float], face_nodes_id: List[int], **kwargs):
        self.__face_loc = face_loc
        self.__face_nodes_id = face_nodes_id
        for key, value in kwargs.items():
            self.__setattr__(key, value)
        return

    def get_face_loc(self):
        return self.__face_loc

    def get_face_nodes_id(self):
        return self.__face_nodes_id

    def set_face_loc(self, face_loc):
        self.__face_loc = face_loc
        return

    def set_face_nodes_id(self, face_nodes_id):
        self.__face_nodes_id = face_nodes_id
        return

    def add_attr(self, **kwargs):
        for key, value in kwargs:
            self.__setattr__(key, value)
            return

    def __str__(self):
        return f"Face locates at {self.__face_loc} with nodes {self.__face_nodes_id}"

class Collection:
    """
    Base Collection.
    """
    def __init__(self):
        return

    def get_item(self, id: int):
        return self.items[id]

class NodeCollection(Collection):
    """
    A node collection.
    """
    def __init__(self, nodes_loc:List, nodes_num):
        self.nodes_loc = np.column_stack(nodes_loc)
        self.nodes_num = nodes_num
        self.nodes = np.apply_along_axis(Node, axis=1, arr=self.nodes_loc)
        return
    
    def get_nodes(self):
        return self.nodes

class EdgeCollection(Collection):
    def __init__(self):
        pass


class FaceCollection(Collection):
    def __init__(self, nodes, faces_loc:List[float], faces_nodes_id:List[int], faces_num:int):
        """
        Iinital function

        Parameters
        ----------

        nodes: NodeCollection
        faces_loc: A list of faces location, format as [[x0,x1,x2], [y0, y1, y2]]
        faces_nodes: A list of nodes comprised of faces, format as [[node_id1, node_id2, node_id3], [node_id3], [node_id4], [node_id5]]
        faces_num: number of nodes.
        """
        self.nodes = nodes
        self.faces_loc = np.column_stack(faces_loc)
        _, self.faceloc_dim = self.faces_loc.shape
        self.faces_nodes_id = faces_nodes_id
        self.faces_num = faces_num
        ids = np.arange(0, self.faces_num, 1)
        params = np.column_stack([self.faces_loc, self.faces_nodes_id, ids])
        self.faces = np.apply_along_axis(self.create_face, axis=1, arr=params)
    
    def create_face(self, params):
        return Face(face_loc=params[0:self.faceloc_dim], face_nodes_id=params[self.faceloc_dim+1:-1], id=params[-1])

    def get_node_xy(self, id):
        return self.nodes.nodes_loc[id-1, 0:2]

    def createPatchCollection(self, **kwargs) -> PatchCollection:
        polygons = []
        pol_xy = np.apply_along_axis(self.get_node_xy, axis=1, arr=self.faces_nodes_id)
        for i in range(pol_xy.shape[0]):
            polygons.append(Polygon(pol_xy[i, :, :]))
        patchcollection = PatchCollection(polygons,  **kwargs)
        return patchcollection


class ncNetParser:
    """
    This is a net file parser for delft3d.
    """
    def __init__(self, filepath):
        self.__filepath = filepath
        self.__parse()
        return

    def __parse(self):
        with Dataset(self.__filepath, 'r') as dataset:
            self.mesh2d_nNodes = dataset.dimensions['mesh2d_nNodes'].size
            self.mesh2d_nEdges = dataset.dimensions['mesh2d_nEdges'].size
            self.mesh2d_nFaces = dataset.dimensions['mesh2d_nFaces'].size
            self.mesh2d_node_x = dataset.variables['mesh2d_node_x'][:]
            self.mesh2d_node_y = dataset.variables['mesh2d_node_y'][:]
            self.mesh2d_node_z = dataset.variables['mesh2d_node_z'][:]
            self.mesh2d_edge_x = dataset.variables['mesh2d_edge_x'][:]
            self.mesh2d_edge_y = dataset.variables['mesh2d_edge_y'][:]
            self.mesh2d_edge_nodes = dataset.variables['mesh2d_edge_nodes'][:]
            self.mesh2d_face_x = dataset.variables['mesh2d_face_x'][:]
            self.mesh2d_face_y = dataset.variables['mesh2d_face_y'][:]
            self.mesh2d_face_nodes = dataset.variables['mesh2d_face_nodes'][:]
        return
    
    def createNodes(self)-> NodeCollection:
        nodes = NodeCollection(nodes_loc=[self.mesh2d_node_x, self.mesh2d_node_y, self.mesh2d_node_z],
                               nodes_num = self.mesh2d_nNodes)
        return nodes

    def createFaces(self)-> FaceCollection:
        nodes = self.createNodes()
        faces = FaceCollection(nodes, faces_loc=[self.mesh2d_face_x, self.mesh2d_face_y],
                               faces_nodes_id=self.mesh2d_face_nodes, faces_num= self.mesh2d_nFaces)
        return faces



if __name__ == '__main__':
    filepath = "./plot_net/demo.nc"
    test = ncNetParser(filepath).createFaces().createPatchCollection(linewidths=0.5, edgecolors='#323aa8', facecolors='w')
    fig, ax = plt.subplots()
    ax.add_collection(test)
    ax.autoscale_view()
    ax.set_aspect(1)
    plt.show()

当前代码比较粗糙,初步实现了网格的可视化,后续嵌入可视化实际变量时进一步优化。三个节点和四个节点的网格绘制如下:

Four nodes

Three nodes