当前位置:首页>python>Python 生成三维节理岩体网络:DFN 建模、赤平投影和连通性分析(含代码)

Python 生成三维节理岩体网络:DFN 建模、赤平投影和连通性分析(含代码)

  • 2026-10-05 21:59:54
Python 生成三维节理岩体网络:DFN 建模、赤平投影和连通性分析(含代码)
案例代码见文末,感谢您关注PFC小姐姐,麻烦您多多对推文点赞、收藏及转发,并衷心希望您多多指教🙏,帮助PFC小姐姐进步提升。

引言

传统结构面分析通常会统计倾向、倾角、间距、迹长和节理组优势方向,但这些数据如果只放在表格里,很难直观展示岩体内部的三维裂隙结构。尤其是当节理数量较多、存在多组结构面交切时,仅靠二维剖面图或简单玫瑰图,往往难以表达裂隙网络的空间组合关系。本文用 Python 构建一个三维离散裂隙网络模型,也就是 DFN 模型。代码随机生成多组不同产状的节理面,将每条节理表示为三维空间中的椭圆裂隙盘,并进一步计算节理的倾向、倾角、迹长、面积、最近间距和连通关系。基于这些参数,代码输出三维 DFN 图、赤平投影图、节理连通性网络图。需要说明的是,本文代码生成的是一个模拟 DFN 示例。如果用于真实工程,需要将现场结构面调查结果、数字摄影测量结果、三维激光扫描结果或钻孔裂隙统计结果输入到代码中。只有基于真实产状、间距和迹长参数生成的 DFN,才适合进一步用于工程解释和数值模拟。

1、三维离散裂隙网络 DFN 模型

图1展示的是在立方体岩体中生成的三维离散裂隙网络。每一个半透明椭圆盘代表一条节理或裂隙,不同颜色表示不同节理组。立方体边框表示模拟岩体的空间范围。这张图的主要作用是展示结构面在三维空间中的展布特征。相比二维剖面图,三维 DFN 图可以更直观地反映节理面的方位、倾角、长度和空间交切关系。多组节理同时出现时,可以观察它们是否存在明显的优势产状,以及不同节理组之间是否相互切割。图中的节理不是简单线段,而是具有一定面积的裂隙盘。这样更接近真实岩体中结构面的空间形态,也更便于后续判断裂隙之间是否可能发生连通。节理盘的大小可以理解为迹长或裂隙尺度的近似表达,节理盘越大,说明该裂隙对岩体切割作用越明显。右下角的统计信息给出了节理数量、连通边数量、最大连通簇规模和平均连通度。这些指标可以帮助判断 DFN 的整体复杂程度。如果最大连通簇很大,说明大量节理之间形成了相互连接的裂隙网络;如果平均连通度较高,说明单条节理与周围节理发生交切的概率较大。

2、节理极点赤平投影图

图2为节理极点赤平投影图,用来展示不同节理组的产状分布。图中每一个点代表一条节理面的极点,不同颜色对应不同节理组。背景中的灰色等值区域表示极点密度,颜色越深,说明该方向附近节理越集中。赤平投影是岩体结构面分析中非常常用的图件。它可以把三维空间中的平面产状转换到二维圆图上,从而直观展示节理是否具有明显的优势方向。若同一节理组的点集中在某一区域,说明该组结构面产状较稳定;若点分布较散,说明该节理组的倾向和倾角变化较大。与普通玫瑰图相比,赤平投影能够同时表达倾向和倾角信息,而不是只统计方向频数。因此,它更适合用于结构面分组、优势节理识别、潜在滑移面分析和岩体各向异性判断。

    3、节理连通性网络图

    图3展示的是节理之间的连通关系。左侧为平面投影下的空间连通网络,节点代表节理,节点之间的灰色连线表示两条节理之间存在相交或近似连通关系。节点大小与连通度有关,节点越大,说明该节理与更多节理发生连接。这张图的重点不是节理“长在哪里”,而是节理“和谁连接”。在裂隙岩体中,单条节理本身可能并不会直接控制整体稳定性,但多条节理一旦相互交切,就可能形成贯通裂隙网络、块体边界、渗流通道或潜在破坏路径。因此,连通性分析是 DFN 研究中非常关键的一步。左图中,如果某些区域节点密集且连线较多,说明该区域节理交切程度较高,可能对应结构破碎区或潜在连通通道。若某一组节理节点普遍较大,说明该节理组在网络中具有较强的连接作用,可能是控制岩体结构连通性的主要节理组。右侧邻接矩阵进一步展示了节理之间的连接关系。矩阵中的黑色点表示两条节理之间存在连接。通过按节理组排序,可以观察连接主要发生在组内还是组间。如果矩阵对角线附近黑点较多,说明同组节理之间连接较强;如果不同节理组对应的矩阵块中也存在大量黑点,则说明组间交切明显。

    具体Python如下:

    import osimport numpy as npimport pandas as pdimport matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltfrom scipy.stats import gaussian_kdefrom mpl_toolkits.mplot3d.art3d import Poly3DCollectionnp.random.seed(2026)plt.rcParams["font.family"] = "Times New Roman"plt.rcParams["axes.unicode_minus"] = Falseplt.rcParams["figure.dpi"] = 160plt.rcParams["savefig.dpi"] = 500out_dir = "dfn_joint_network_results"os.makedirs(out_dir, exist_ok=True)# 岩体立方体尺寸,单位可理解为 mL = 100.0# 连接判定半径放大系数# 如果连通边太少,可以调大,例如 1.45 或 1.60CONNECTION_RADIUS_FACTOR = 1.30# 若真实相交边太少,自动放宽一次MIN_EDGE_COUNT = 20def normalize(v):    v = np.asarray(v, dtype=float)    n = np.linalg.norm(v)    if n < 1e-12:        return v    return v / ndef normal_from_dipdir_dip(dipdir_deg, dip_deg):    """    根据倾向 dip direction 和倾角 dip 计算平面法向量。    坐标系:    x = East    y = North    z = Up    返回下半球极点方向,z 分量为负。    """    alpha = np.deg2rad(dipdir_deg)    dip = np.deg2rad(dip_deg)    n = np.array([        np.sin(dip) * np.sin(alpha),        np.sin(dip) * np.cos(alpha),        -np.cos(dip)    ])    return normalize(n)def plane_basis_from_normal(n):    """    给定平面法向量,构造平面内两个正交基向量。    """    n = normalize(n)    ref = np.array([0.0, 0.0, 1.0])    if abs(np.dot(ref, n)) > 0.90:        ref = np.array([1.0, 0.0, 0.0])    u = normalize(np.cross(n, ref))    v = normalize(np.cross(n, u))    return u, vdef ellipse_polygon(center, u, v, a, b, n_points=72):    """    生成一个椭圆节理盘面的三维多边形点。    """    theta = np.linspace(0, 2 * np.pi, n_points)    pts = (        center[None, :]        + a * np.cos(theta)[:, None] * u[None, :]        + b * np.sin(theta)[:, None] * v[None, :]    )    return ptsdef draw_cube(ax, L_box):    """    绘制三维立方体边框。    """    corners = np.array([        [0, 0, 0],        [L_box, 0, 0],        [L_box, L_box, 0],        [0, L_box, 0],        [0, 0, L_box],        [L_box, 0, L_box],        [L_box, L_box, L_box],        [0, L_box, L_box]    ])    edges = [        (0, 1), (1, 2), (2, 3), (3, 0),        (4, 5), (5, 6), (6, 7), (7, 4),        (0, 4), (1, 5), (2, 6), (3, 7)    ]    for i, j in edges:        ax.plot(            [corners[i, 0], corners[j, 0]],            [corners[i, 1], corners[j, 1]],            [corners[i, 2], corners[j, 2]],            color="black",            linewidth=0.8,            alpha=0.65        )def stereonet_project_pole(dipdir_deg, dip_deg):    """    Schmidt 等面积赤平投影中的极点投影。    返回投影坐标 x, y。    """    n = normal_from_dipdir_dip(dipdir_deg, dip_deg)    # 与下半球竖直向下方向的夹角    theta = np.arccos(np.clip(-n[2], -1.0, 1.0))    # 方位角    trend = np.arctan2(n[0], n[1])    # Schmidt net,外圈半径归一为 1    r = np.sqrt(2.0) * np.sin(theta / 2.0)    x = r * np.sin(trend)    y = r * np.cos(trend)    return x, ydef disks_intersect_approx(c1, n1, r1, c2, n2, r2):    """    近似判断两个圆盘是否相交。    椭圆节理在这里用等效连接半径近似。    """    c1 = np.asarray(c1, dtype=float)    c2 = np.asarray(c2, dtype=float)    n1 = normalize(n1)    n2 = normalize(n2)    direction = np.cross(n1, n2)    dn = np.linalg.norm(direction)    # 近似平行节理    if dn < 1e-6:        plane_gap = abs(np.dot(n1, c2 - c1))        if plane_gap > 0.25 * min(r1, r2):            return False        delta = c2 - c1        delta_in_plane = delta - np.dot(delta, n1) * n1        center_dist = np.linalg.norm(delta_in_plane)        return center_dist <= 0.85 * (r1 + r2)    d = direction / dn    # 两平面交线:n1·x = n1·c1, n2·x = n2·c2, d·x = 0    A = np.vstack([n1, n2, d])    b = np.array([        np.dot(n1, c1),        np.dot(n2, c2),        0.0    ])    try:        p0 = np.linalg.solve(A, b)    except np.linalg.LinAlgError:        return False    intervals = []    for c, r in [(c1, r1), (c2, r2)]:        t0 = np.dot(c - p0, d)        closest = p0 + t0 * d        dist2 = np.sum((closest - c) ** 2)        if dist2 > r ** 2:            return False        half_len = np.sqrt(max(r ** 2 - dist2, 0.0))        intervals.append((t0 - half_len, t0 + half_len))    t_start = max(intervals[0][0], intervals[1][0])    t_end = min(intervals[0][1], intervals[1][1])    return t_start <= t_enddef build_edges(frac_df, radius_factor):    """    建立节理连通边。    """    edges = []    centers = frac_df[["center_x", "center_y", "center_z"]].values    normals = frac_df[["normal_x", "normal_y", "normal_z"]].values    r_conn = radius_factor * np.sqrt(        frac_df["semi_major_axis"] * frac_df["semi_minor_axis"]    ).values    n_frac = len(frac_df)    for i in range(n_frac):        for j in range(i + 1, n_frac):            connected = disks_intersect_approx(                centers[i],                normals[i],                r_conn[i],                centers[j],                normals[j],                r_conn[j]            )            if connected:                edges.append((i, j))    return edgesdef connected_components(n_nodes, edge_list):    """    简单并查集计算连通分量。    """    parent = np.arange(n_nodes)    def find(x):        while parent[x] != x:            parent[x] = parent[parent[x]]            x = parent[x]        return x    def union(a, b):        ra = find(a)        rb = find(b)        if ra != rb:            parent[rb] = ra    for i, j in edge_list:        union(i, j)    roots = np.array([find(i) for i in range(n_nodes)])    unique_roots = {r: k + 1 for k, r in enumerate(np.unique(roots))}    comp_ids = np.array([unique_roots[r] for r in roots])    comp_sizes = {}    for cid in comp_ids:        comp_sizes[cid] = comp_sizes.get(cid, 0) + 1    return comp_ids, comp_sizesdef draw_stereonet_grid(ax):    """    绘制简易 Schmidt 赤平投影网。    """    outer = plt.Circle(        (0, 0),        1.0,        edgecolor="black",        facecolor="none",        linewidth=1.2    )    ax.add_patch(outer)    # 倾角环    for dip in [15, 30, 45, 60, 75]:        r = np.sqrt(2.0) * np.sin(np.deg2rad(dip) / 2.0)        circ = plt.Circle(            (0, 0),            r,            edgecolor="gray",            facecolor="none",            linewidth=0.6,            alpha=0.35        )        ax.add_patch(circ)        ax.text(            r / np.sqrt(2),            r / np.sqrt(2),            f"{dip}°",            fontsize=8,            color="gray",            ha="left",            va="bottom"        )    # 方位线    for az in np.arange(0, 360, 30):        a = np.deg2rad(az)        ax.plot(            [0, np.sin(a)],            [0, np.cos(a)],            color="gray",            linewidth=0.5,            alpha=0.28        )    ax.text(0, 1.08, "N", ha="center", va="center", fontsize=11)    ax.text(1.08, 0, "E", ha="center", va="center", fontsize=11)    ax.text(0, -1.08, "S", ha="center", va="center", fontsize=11)    ax.text(-1.08, 0, "W", ha="center", va="center", fontsize=11)    ax.set_aspect("equal")    ax.set_xlim(-1.12, 1.12)    ax.set_ylim(-1.12, 1.12)    ax.axis("off")# 生成三组节理数据joint_sets = [    {        "set_id": "J1",        "name": "J1 steep NE",        "n": 42,        "dipdir_mean": 45,        "dipdir_std": 10,        "dip_mean": 68,        "dip_std": 7,        "radius_mean": 12.0,        "color": "#4C72B0"    },    {        "set_id": "J2",        "name": "J2 steep SE",        "n": 40,        "dipdir_mean": 135,        "dipdir_std": 11,        "dip_mean": 72,        "dip_std": 8,        "radius_mean": 11.0,        "color": "#DD8452"    },    {        "set_id": "J3",        "name": "J3 low-angle",        "n": 32,        "dipdir_mean": 285,        "dipdir_std": 18,        "dip_mean": 28,        "dip_std": 8,        "radius_mean": 15.0,        "color": "#55A868"    }]records = []polygons = []label_id = 1for js in joint_sets:    for _ in range(js["n"]):        dipdir = (js["dipdir_mean"] + np.random.normal(0, js["dipdir_std"])) % 360        dip = np.clip(js["dip_mean"] + np.random.normal(0, js["dip_std"]), 8, 88)        center = np.random.uniform(8, L - 8, size=3)        # 半长轴、半短轴        a = np.random.lognormal(mean=np.log(js["radius_mean"]), sigma=0.28)        a = np.clip(a, 5.5, 24.0)        b = a * np.random.uniform(0.55, 0.95)        n = normal_from_dipdir_dip(dipdir, dip)        u, v = plane_basis_from_normal(n)        # 在平面内随机旋转椭圆方向        phi = np.random.uniform(0, 2 * np.pi)        u2 = np.cos(phi) * u + np.sin(phi) * v        v2 = -np.sin(phi) * u + np.cos(phi) * v        poly = ellipse_polygon(center, u2, v2, a, b, n_points=72)        polygons.append(poly)        stereo_x, stereo_y = stereonet_project_pole(dipdir, dip)        strike = (dipdir - 90) % 360        trace_length = 2.0 * a        area = np.pi * a * b        records.append({            "label": label_id,            "set_id": js["set_id"],            "set_name": js["name"],            "center_x": center[0],            "center_y": center[1],            "center_z": center[2],            "dip_direction_deg": dipdir,            "dip_deg": dip,            "strike_deg": strike,            "semi_major_axis": a,            "semi_minor_axis": b,            "trace_length": trace_length,            "fracture_area": area,            "normal_x": n[0],            "normal_y": n[1],            "normal_z": n[2],            "basis_u_x": u2[0],            "basis_u_y": u2[1],            "basis_u_z": u2[2],            "basis_v_x": v2[0],            "basis_v_y": v2[1],            "basis_v_z": v2[2],            "stereo_x": stereo_x,            "stereo_y": stereo_y        })        label_id += 1frac_df = pd.DataFrame(records)#计算组内最近间距nearest_spacing = np.full(len(frac_df), np.nan)for sid in frac_df["set_id"].unique():    idx = np.where(frac_df["set_id"].values == sid)[0]    centers = frac_df.loc[idx, ["center_x", "center_y", "center_z"]].values    for local_i, global_i in enumerate(idx):        if len(idx) <= 1:            nearest_spacing[global_i] = np.nan            continue        dist = np.linalg.norm(centers - centers[local_i], axis=1)        dist[local_i] = np.inf        nearest_spacing[global_i] = np.min(dist)frac_df["nearest_same_set_spacing"] = nearest_spacing# 建立节理连通网络edges = build_edges(frac_df, CONNECTION_RADIUS_FACTOR)if len(edges) < MIN_EDGE_COUNT:    print("提示:初始连通边较少,已自动放宽连接半径。")    edges = build_edges(frac_df, CONNECTION_RADIUS_FACTOR * 1.35)n_frac = len(frac_df)degree = np.zeros(n_frac, dtype=int)for i, j in edges:    degree[i] += 1    degree[j] += 1component_ids, component_sizes = connected_components(n_frac, edges)frac_df["degree"] = degreefrac_df["component_id"] = component_idsfrac_df["component_size"] = [component_sizes[cid] for cid in component_ids]edge_records = []for eid, (i, j) in enumerate(edges, start=1):    ci = frac_df.loc[i, ["center_x", "center_y", "center_z"]].values.astype(float)    cj = frac_df.loc[j, ["center_x", "center_y", "center_z"]].values.astype(float)    edge_records.append({        "edge_id": eid,        "fracture_i": int(frac_df.loc[i, "label"]),        "fracture_j": int(frac_df.loc[j, "label"]),        "set_i": frac_df.loc[i, "set_id"],        "set_j": frac_df.loc[j, "set_id"],        "center_distance": float(np.linalg.norm(ci - cj))    })edge_df = pd.DataFrame(edge_records)frac_table_path = os.path.join(out_dir, "dfn_fracture_table.csv")edge_table_path = os.path.join(out_dir, "dfn_connection_edges.csv")frac_df.to_csv(frac_table_path, index=False, encoding="utf-8-sig")edge_df.to_csv(edge_table_path, index=False, encoding="utf-8-sig")# 输出总体统计largest_component_size = int(frac_df["component_size"].max())connected_cluster_count = int(len(np.unique(component_ids)))mean_degree = float(np.mean(degree))max_degree = int(np.max(degree))summary_records = []summary_records.append({    "item": "total_fractures",    "value": n_frac})summary_records.append({    "item": "total_connections",    "value": len(edges)})summary_records.append({    "item": "connected_cluster_count",    "value": connected_cluster_count})summary_records.append({    "item": "largest_component_size",    "value": largest_component_size})summary_records.append({    "item": "mean_degree",    "value": mean_degree})summary_records.append({    "item": "max_degree",    "value": max_degree})summary_records.append({    "item": "mean_trace_length",    "value": float(frac_df["trace_length"].mean())})summary_records.append({    "item": "mean_nearest_same_set_spacing",    "value": float(np.nanmean(frac_df["nearest_same_set_spacing"]))})for sid in frac_df["set_id"].unique():    sub = frac_df[frac_df["set_id"] == sid]    summary_records.append({        "item": f"{sid}_count",        "value": int(len(sub))    })    summary_records.append({        "item": f"{sid}_mean_dip",        "value": float(sub["dip_deg"].mean())    })    summary_records.append({        "item": f"{sid}_mean_trace_length",        "value": float(sub["trace_length"].mean())    })    summary_records.append({        "item": f"{sid}_mean_degree",        "value": float(sub["degree"].mean())    })summary_df = pd.DataFrame(summary_records)summary_path = os.path.join(out_dir, "dfn_summary.csv")summary_df.to_csv(summary_path, index=False, encoding="utf-8-sig")#图1:三维 DFN 模型group_color = {    js["set_id"]: js["color"]    for js in joint_sets}fig = plt.figure(figsize=(9.0, 7.6))ax = fig.add_subplot(111, projection="3d")for idx, poly in enumerate(polygons):    sid = frac_df.loc[idx, "set_id"]    face_color = group_color[sid]    patch = Poly3DCollection(        [poly],        facecolors=face_color,        edgecolors="k",        linewidths=0.25,        alpha=0.38    )    ax.add_collection3d(patch)draw_cube(ax, L)ax.set_xlim(0, L)ax.set_ylim(0, L)ax.set_zlim(0, L)ax.set_xlabel("X / East", labelpad=8)ax.set_ylabel("Y / North", labelpad=8)ax.set_zlabel("Z / Up", labelpad=8)ax.set_title("3D discrete fracture network (DFN)", fontsize=16, pad=18)ax.view_init(elev=24, azim=-55)ax.set_box_aspect((1, 1, 1))legend_handles = []for js in joint_sets:    h = plt.Line2D(        [0], [0],        marker="s",        color="w",        markerfacecolor=js["color"],        markeredgecolor="k",        markersize=10,        label=js["name"]    )    legend_handles.append(h)ax.legend(    handles=legend_handles,    loc="upper left",    bbox_to_anchor=(0.02, 0.98),    frameon=True,    fontsize=9)txt = (    f"Fractures = {n_frac}\n"    f"Connections = {len(edges)}\n"    f"Largest cluster = {largest_component_size}\n"    f"Mean degree = {mean_degree:.2f}")ax.text2D(    0.72, 0.04, txt,    transform=ax.transAxes,    fontsize=10,    bbox=dict(        boxstyle="round,pad=0.35",        facecolor="white",        edgecolor="#999999",        alpha=0.90    ))plt.tight_layout()fig1_path = os.path.join(out_dir, "fig1_3d_dfn_model.png")plt.savefig(fig1_path, bbox_inches="tight", facecolor="white")plt.close(fig)# 图2:赤平投影图fig, ax = plt.subplots(figsize=(7.4, 7.4))draw_stereonet_grid(ax)# 极点密度背景xy = frac_df[["stereo_x", "stereo_y"]].values.Ttry:    jitter = np.random.normal(0, 1e-4, size=xy.shape)    kde = gaussian_kde(xy + jitter)    grid_n = 180    xg = np.linspace(-1, 1, grid_n)    yg = np.linspace(-1, 1, grid_n)    Xg, Yg = np.meshgrid(xg, yg)    pos = np.vstack([Xg.ravel(), Yg.ravel()])    Z = kde(pos).reshape(Xg.shape)    Z[Xg ** 2 + Yg ** 2 > 1] = np.nan    ax.contourf(        Xg,        Yg,        Z,        levels=10,        cmap="Greys",        alpha=0.28    )except Exception:    passfor js in joint_sets:    sub = frac_df[frac_df["set_id"] == js["set_id"]]    ax.scatter(        sub["stereo_x"],        sub["stereo_y"],        s=42,        color=js["color"],        edgecolors="white",        linewidths=0.7,        alpha=0.88,        label=js["name"]    )ax.set_title("Stereonet projection of fracture poles", fontsize=16, pad=12)ax.legend(    loc="lower left",    bbox_to_anchor=(0.02, 0.02),    frameon=True,    fontsize=9)plt.tight_layout()fig2_path = os.path.join(out_dir, "fig2_stereonet_poles.png")plt.savefig(fig2_path, bbox_inches="tight", facecolor="white")plt.close(fig)# 图3:节理连通性网络图fig, axes = plt.subplots(1, 2, figsize=(13.6, 6.0))# ---- 左图:空间连通网络 ----ax = axes[0]centers_xy = frac_df[["center_x", "center_y"]].valuesfor i, j in edges:    xi, yi = centers_xy[i]    xj, yj = centers_xy[j]    ax.plot(        [xi, xj],        [yi, yj],        color="0.55",        linewidth=0.55,        alpha=0.35,        zorder=1    )for js in joint_sets:    sub = frac_df[frac_df["set_id"] == js["set_id"]]    node_size = 30 + 20 * sub["degree"].values    ax.scatter(        sub["center_x"],        sub["center_y"],        s=node_size,        color=js["color"],        edgecolors="black",        linewidths=0.45,        alpha=0.90,        label=js["name"],        zorder=3    )ax.set_xlim(0, L)ax.set_ylim(0, L)ax.set_aspect("equal")ax.set_xlabel("X / East")ax.set_ylabel("Y / North")ax.set_title("Spatial connectivity network", fontsize=14)ax.grid(alpha=0.18)ax.legend(loc="upper right", frameon=True, fontsize=9)net_txt = (    f"Nodes = {n_frac}\n"    f"Edges = {len(edges)}\n"    f"Mean degree = {mean_degree:.2f}\n"    f"Max degree = {max_degree}")ax.text(    0.03, 0.03, net_txt,    transform=ax.transAxes,    ha="left",    va="bottom",    fontsize=9.5,    bbox=dict(        boxstyle="round,pad=0.35",        facecolor="white",        edgecolor="#999999",        alpha=0.92    ))# ---- 右图:邻接矩阵 ----ax = axes[1]set_order = []for js in joint_sets:    set_order.extend(frac_df.index[frac_df["set_id"] == js["set_id"]].tolist())adj = np.zeros((n_frac, n_frac), dtype=float)for i, j in edges:    adj[i, j] = 1    adj[j, i] = 1adj_sorted = adj[np.ix_(set_order, set_order)]ax.imshow(    adj_sorted,    cmap="Greys",    interpolation="nearest",    vmin=0,    vmax=1)ax.set_title("Fracture adjacency matrix", fontsize=14)ax.set_xlabel("Fracture index sorted by joint set")ax.set_ylabel("Fracture index sorted by joint set")# 节理组分隔线start = 0for js in joint_sets[:-1]:    start += js["n"]    ax.axhline(start - 0.5, color="red", linewidth=0.8, alpha=0.8)    ax.axvline(start - 0.5, color="red", linewidth=0.8, alpha=0.8)plt.tight_layout()fig3_path = os.path.join(out_dir, "fig3_fracture_connectivity_network.png")plt.savefig(fig3_path, bbox_inches="tight", facecolor="white")plt.close(fig)

    特别声明:

    以上代码与文案均为网上资料整合而成,仅供广大同行们参考学习,如有侵权请联系删除。

    如有其他需要,欢迎关注我的咸鱼号:pfc小姐姐

    最新文章

    随机文章