2021年深圳杯数学建模B题背向瑞利散射光纤激光传感器设计解题全过程论文及程序

2021年深圳杯数学建模

B题 背向瑞利散射光纤激光传感器设计

原题再现:

  光纤传感技术是一类在近年来受到广泛关注的技术,随着该类技术的不断成熟,他们正慢慢地渗透到更多的商用领域。分布式声学传感(Distributed acoustic sensing,DAS)技术是光纤传感技术中的一类重要技术。这一技术主要利用光纤中的背向瑞利散射(Rayleigh backscattering)现象,其基本原理如图1所示。在光纤的加工过程中,各个位置沿不同方向会存在一些随机的加工密度不平整。对入射光束来说,这些不平整会形成大量随机分布的散射体,使本来沿光纤长度方向传播的光束向各个方向散射。这些散射中,与光束传播方向相反的散射被称为背向瑞利散射1

  在光纤平直铺设的环境中,背向瑞利散射光场的幅度服从瑞利分布,相位服从均匀分布。当光纤在某一位置受到应力作用,例如弯曲,震动等,背向瑞利散射光场将会产生相应的变化。如果在光纤的一端持续监测背向散射光场,即可根据接收信号的变化,判断应力及震动发生的位置,甚至强度和种类等。一般的DAS系统结构如图2所示。

  试探索如下问题:

  1.试参考2,建立数学模型描述在无应力条件下,光源发射单脉冲后,接收机收到的反射信号。光在光纤中的传播会受到很多效应的影响,在理想加工条件下,光纤中单偏振光场的复包络 一般服从如下的非线性薛定谔方程3

  请综合考虑脉冲参数和光纤长度,选择性地将(1)中的各类效应加入模型并请编程验证。

  2.因为背向瑞利散射是一个随机效应,其强度服从瑞利分布,故会出现信号光强度非常小的情形,这个现象通常被称为相干衰落1。如果系统使用背向瑞利散射信号的相位来探测应力及震动发生的位置和大小4,则在相干衰落的状况下,无法获得任何有效信息。请参考5,探讨是否有可能通过改变发射脉冲的形式(例如,脉宽、功率、调制格式、附加编码等)克服相干衰落,并请根据问题1中的模型进行验证。

  3.若设发射机发射的脉冲为线偏振脉冲。在经过光纤传输后,光信号的偏振态可能会发生改变。此时,接收机会在两个相互正交的偏振态上同时收到两个信号。请对问题1中的模型进行补充,尝试考虑偏振效应的影响,并探索偏振效应对应力及震动的探测有什么影响及如何应对。

整体求解过程概述(摘要)

  针对问题一,为了描述无应力条件下单个探测脉冲对应的背向瑞利散射反射信号,本文首先对题目给出的光纤长度、脉冲参数与传播系数实施量纲统一和无量纲化预处理,构造色散数 N_D=L/L_D、非线性相移 B=γP_0L_eff 与往返损耗三类尺度判据。鉴于 0.1---10 km 的传播区间跨越多个工程工况,引入"效应筛选---分步傅里叶传播---随机散射叠加---相干接收"的分层前向模型。以 10 km、100 ns、0.1 W 为基准,计算得到 N_D=8.68e-08、B=1.042 rad;分步傅里叶法的能量相对误差为 6.98e-14。随机散射复场的幅度瑞利检验 p=0.953、相位均匀检验 p=0.524,验证了前向模型的统计一致性。
  针对问题二,为了克服相干衰落导致的相位信息缺失,本文在问题一随机复场模型的基础上进一步构造多频线性调频、互补编码与最大比合并模型。通过解析推导可得,单通道归一化强度服从指数分布,而 K 个近独立通道的合并能量服从 K 阶 Gamma 分布。以归一化门限 0.1 为例,深衰落概率由单频的 0.0952 降至四频的 0.000776;5 dB 条件下相位均方根误差由 0.992 rad 降至 0.392 rad。结果表明,提高功率只能改善加性噪声,延长脉宽会牺牲空间分辨率,而"频率分集+匹配编码"能够同时改变衰落尾部并获得处理增益。
  针对问题三,考虑线偏振脉冲在随机双折射光纤中的偏振态演化,本文将标量模型扩展为 Jones---Manakov 向量模型,并以 Stokes 参数描述偏振轨迹。鉴于单偏振接收存在投影失配,引入双偏振接收和双发双收相干 MIMO 结构,采用跨偏振复相关最大比合并估计应变相位。蒙特卡洛结果表明,在归一化门限 0.1 下,单模态、双模态和四模态的中断概率分别为 0.0952、0.0175 和 0.000776;5 dB 下双发双收的相位误差为 0.395 rad,显著优于单偏振结构。
  综合三个问题,本文形成了"数据理解与尺度诊断---传播模型自适应选择---静态散射前向计算---频率/编码抗衰落---偏振多维接收---不确定性与鲁棒性验证"的完整建模链。模型同时给出可解释的解析判据和可复现的数值算法,能够根据光纤长度、脉宽、峰值功率、空间分辨率及硬件复杂度自动推荐模型复杂度与系统架构。敏感性分析说明,损耗决定动态范围,非线性相移决定相干相位偏置,频率与偏振分集决定深衰落尾部,编码决定噪声处理增益;四者需要联合设计而不能互相替代。

模型假设:

  1 弱散射与一阶 Born 近似 单个瑞利散射体的反射很弱,散射不显著改变前向主脉冲,多个散射体的复场在线性接收端叠加。
  2 准单模与窄带包络 光纤在 1550 nm 附近工作,除问题三外先按单偏振标量慢变包络处理。
  3 静态基线 问题一的光纤在一个采集周期内无应力变化,散射复系数对慢时间保持不变。
  4 白噪声接收 相干接收机经平衡探测和基带化后的噪声近似为圆对称复高斯噪声。
  5 局部共同相位 小范围轴向应变主要改变光程,相同频率或偏振通道共享同一机械相位扰动。
  6 分集可控相关 多频通道相关性由频率间隔相对于 1/T_p 的大小近似决定,偏振模态经校准后可视为近独立观测。
  7 参数平稳 α、β_2、γ 在一次测量中沿光纤缓慢变化,可用平均值表示,其不确定性在敏感性分析中单独讨论。
  8 不考虑高阶损伤 功率低于受激布里渊散射等阈值,模型仅保留题目给出的损耗、二阶色散和 Kerr 非线性。

问题分析:

  问题一分析
  针对 0.1--10km 无应力单模光纤静态背向瑞利散射信号建模需求,本题属于基于非线性薛定谔方程的光场前向传播与随机统计耦合建模问题。题目未提供实测波形,仅给出光纤损耗、色散、非线性三类核心传播参数,需先构造色散数、B 积分无量纲尺度判据定量区分各光学效应的强弱,再采用分步傅里叶法数值求解慢变包络光场,叠加复高斯随机散射体生成完整背向接收信号,同时利用 KS 检验验证散射幅度瑞利分布、相位均匀分布的统计特性,区分强度包络与相干相位两类不同传播效应带来的影响,构建可复现的仿真基准模型为后续抗衰落、偏振分析提供统一数据基底。
  问题二分析
  针对相干衰落抑制方案对比建模问题,本题复用问题一完整光传播与随机散射基准模型,仅改变发射脉冲调制形式开展对比分析。单通道背散射光强服从指数分布,单纯提升光功率、拓宽脉冲宽度仅改善信噪比,无法改变归一化深衰落概率;核心思路引入多频分集与互补编码匹配滤波组合方案,推导 K 通道合并后 Gamma 分布衰落概率解析式,通过最大比合并算法融合多路独立散射信号降低相位估计均方误差,量化不同脉冲策略下深衰落中断概率、相位恢复精度与空间分辨率三者的权衡关系,对比各类方案的硬件复杂度与探测性能差异。
  问题三分析
  针对光纤随机双折射引发偏振衰落的优化建模问题,本题将问题一标量光场拓展为 Jones-Mankov 向量光场模型,引入 Stokes 参数描述光纤沿线偏振态演化规律。单偏振接收会因偏振投影失配产生额外信号深谷,因此设计单发双收、双发双收两类偏振分集接收架构,将偏振通道与频率通道统一为独立复模态分集体系,推导多偏振合并后的衰落概率表达式,通过蒙特卡洛振动仿真验证偏振分集对相位波形连续性的提升效果,定量对比不同收发偏振架构下相位 RMSE 指标,给出兼顾探测精度与硬件成本的偏振接收系统设计方案。

模型的建立与求解整体论文缩略图

全部论文请见下方" 只会建模 QQ名片" 点击QQ名片即可

程序代码:

python 复制代码
from __future__ import annotations

import argparse
import json
import math
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Dict, Iterable, Tuple

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import patches
from scipy import signal, stats
from scipy.special import gammainc

# ------------------------------ 绘图设置 ------------------------------
plt.rcParams.update({
    "font.sans-serif": ["AR PL UMing CN", "DejaVu Sans"],
    "font.serif": ["AR PL UMing CN", "DejaVu Serif"],
    "axes.unicode_minus": False,
    "figure.dpi": 130,
    "savefig.dpi": 230,
    "axes.grid": True,
    "grid.alpha": 0.22,
    "axes.spines.top": False,
    "axes.spines.right": False,
})

PALETTE = {
    "blue": "#2F6BFF", "cyan": "#13B5C8", "green": "#2DAA6A",
    "orange": "#F28E2B", "red": "#D84A4A", "purple": "#7B61FF",
    "gray": "#6C757D", "navy": "#16324F", "gold": "#D4A72C",
    "light": "#EEF3F8",
}


@dataclass(frozen=True)
class FiberParams:
    wavelength_m: float = 1550e-9
    refractive_index: float = 1.468
    alpha_db_per_km: float = 0.2      # 功率损耗系数
    beta2_ps2_per_km: float = -21.7
    gamma_w_inv_km: float = 1.3
    rayleigh_coeff: float = 1.0

    @property
    def group_velocity(self) -> float:
        return 299_792_458.0 / self.refractive_index

    @property
    def alpha_power_per_km(self) -> float:
        return self.alpha_db_per_km * math.log(10.0) / 10.0

    @property
    def beta2_s2_per_km(self) -> float:
        return self.beta2_ps2_per_km * 1e-24


@dataclass(frozen=True)
class PulseParams:
    width_s: float = 100e-9
    peak_power_w: float = 0.10
    repetition_hz: float = 1_000.0
    super_gaussian_order: int = 8


FIBER = FiberParams()
PULSE = PulseParams()


def ensure_dir(path: Path) -> Path:
    path.mkdir(parents=True, exist_ok=True)
    return path


def savefig(fig: plt.Figure, path: Path) -> None:
    fig.tight_layout()
    fig.savefig(path, bbox_inches="tight")
    plt.close(fig)


def effective_length_km(length_km: np.ndarray | float, alpha_power_per_km: float) -> np.ndarray:
    L = np.asarray(length_km, dtype=float)
    return (1.0 - np.exp(-alpha_power_per_km * L)) / alpha_power_per_km


def dimensionless_numbers(length_km: float, pulse_width_s: float, peak_power_w: float,
                          fiber: FiberParams = FIBER) -> Dict[str, float]:
    # 对近似方脉冲采用 T0 = Tp/2 作为特征时间尺度
    t0_s = pulse_width_s / 2.0
    ld_km = t0_s ** 2 / abs(fiber.beta2_s2_per_km)
    lnl_km = 1.0 / max(fiber.gamma_w_inv_km * peak_power_w, 1e-30)
    leff_km = float(effective_length_km(length_km, fiber.alpha_power_per_km))
    return {
        "dispersion_number": length_km / ld_km,
        "nonlinear_number": length_km / lnl_km,
        "b_integral_rad": fiber.gamma_w_inv_km * peak_power_w * leff_km,
        "loss_power_fraction": float(np.exp(-fiber.alpha_power_per_km * length_km)),
        "dispersion_length_km": ld_km,
        "nonlinear_length_km": lnl_km,
        "effective_length_km": leff_km,
    }


def super_gaussian(t: np.ndarray, width_s: float, order: int = 8, power_w: float = 1.0) -> np.ndarray:
    # 使强度半高宽近似等于 width_s
    t0 = width_s / (2.0 * (math.log(2.0) ** (1.0 / (2.0 * order))))
    return np.sqrt(power_w) * np.exp(-0.5 * (np.abs(t) / t0) ** (2 * order))


def ssfm_propagate(a0: np.ndarray, dt_s: float, length_km: float, dz_km: float,
                   fiber: FiberParams = FIBER, include_loss: bool = True,
                   include_dispersion: bool = True, include_nonlinearity: bool = True) -> np.ndarray:
    """对标量 NLSE 采用对称分步傅里叶法。"""
    n = len(a0)
    omega = 2.0 * np.pi * np.fft.fftfreq(n, d=dt_s)
    beta2 = fiber.beta2_s2_per_km
    alpha = fiber.alpha_power_per_km
    gamma = fiber.gamma_w_inv_km
    steps = max(1, int(math.ceil(length_km / dz_km)))
    dz = length_km / steps
    a = a0.astype(complex).copy()

    linear_exp = np.ones(n, dtype=complex)
    if include_loss:
        linear_exp *= np.exp(-alpha * dz / 4.0)  # 每个半步的场衰减
    if include_dispersion:
        linear_exp *= np.exp(0.5j * beta2 * omega ** 2 * dz / 2.0)

    for _ in range(steps):
        a = np.fft.ifft(np.fft.fft(a) * linear_exp)
        if include_nonlinearity:
            a *= np.exp(1j * gamma * np.abs(a) ** 2 * dz)
        a = np.fft.ifft(np.fft.fft(a) * linear_exp)
    return a


def generate_backscatter_trace(length_km: float = 10.0, dz_m: float = 1.0,
                               pulse_width_s: float = PULSE.width_s,
                               seed: int = 202108,
                               fiber: FiberParams = FIBER) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """离散随机散射体与脉冲空间窗卷积,返回位置、复场、强度。"""
    rng = np.random.default_rng(seed)
    n = int(length_km * 1000 / dz_m) + 1
    z_m = np.linspace(0.0, length_km * 1000.0, n)
    xi = (rng.normal(size=n) + 1j * rng.normal(size=n)) / np.sqrt(2.0)
    spatial_res_m = fiber.group_velocity * pulse_width_s / 2.0
    sigma_m = spatial_res_m / 2.355
    half = max(3, int(math.ceil(4 * sigma_m / dz_m)))
    x = np.arange(-half, half + 1) * dz_m
    kernel = np.exp(-0.5 * (x / sigma_m) ** 2)
    kernel /= np.sqrt(np.sum(kernel ** 2))
    field = signal.fftconvolve(xi, kernel, mode="same")
    # 往返传播:复场幅度随 exp(-alpha*z) 衰减
    field *= np.exp(-fiber.alpha_power_per_km * z_m / 1000.0)
    intensity = np.abs(field) ** 2
    return z_m, field, intensity


def rayleigh_tests(z_m: np.ndarray, field: np.ndarray, fiber: FiberParams = FIBER) -> Dict[str, float]:
    # 去除确定性距离衰减后检验随机统计规律
    detrended = field * np.exp(fiber.alpha_power_per_km * z_m / 1000.0)
    # 边界卷积样本剔除,并按脉冲分辨率抽样以减弱相关性
    res_m = fiber.group_velocity * PULSE.width_s / 2.0
    step = max(1, int(round(res_m)))
    sample = detrended[100:-100:step]
    amp = np.abs(sample)
    phase = np.angle(sample)
    sigma_hat = np.sqrt(np.mean(amp ** 2) / 2.0)
    ks_amp = stats.kstest(amp, "rayleigh", args=(0, sigma_hat))
    u = (phase + np.pi) / (2 * np.pi)
    ks_phase = stats.kstest(u, "uniform")
    return {
        "rayleigh_sigma_hat": float(sigma_hat),
        "rayleigh_ks_stat": float(ks_amp.statistic),
        "rayleigh_ks_p": float(ks_amp.pvalue),
        "uniform_phase_ks_stat": float(ks_phase.statistic),
        "uniform_phase_ks_p": float(ks_phase.pvalue),
        "sample_count": int(len(sample)),
    }


def correlated_complex_gaussian(n: int, k: int, rho: float, rng: np.random.Generator) -> np.ndarray:
    cov = (1.0 - rho) * np.eye(k) + rho * np.ones((k, k))
    chol = np.linalg.cholesky(cov + 1e-12 * np.eye(k))
    x = (rng.normal(size=(n, k)) + 1j * rng.normal(size=(n, k))) / np.sqrt(2.0)
    return x @ chol.T


def phase_rmse_diversity(snr_db: Iterable[float], k_values=(1, 2, 4, 8), rho=0.05,
                         n_mc: int = 80_000, phase_true: float = 0.30,
                         seed: int = 202109) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    rows = []
    for snr in snr_db:
        noise_var = 10 ** (-snr / 10.0)
        for k in k_values:
            h = correlated_complex_gaussian(n_mc, k, rho, rng)
            n0 = np.sqrt(noise_var / 2) * (rng.normal(size=(n_mc, k)) + 1j * rng.normal(size=(n_mc, k)))
            n1 = np.sqrt(noise_var / 2) * (rng.normal(size=(n_mc, k)) + 1j * rng.normal(size=(n_mc, k)))
            e0 = h + n0
            e1 = h * np.exp(1j * phase_true) + n1
            cross = np.sum(e1 * np.conj(e0), axis=1)
            est = np.angle(cross)
            err = np.angle(np.exp(1j * (est - phase_true)))
            rmse = np.sqrt(np.mean(err ** 2))
            outage = np.mean(np.sum(np.abs(h) ** 2, axis=1) < 0.1 * k)
            rows.append({"snr_db": snr, "diversity_order": k, "rmse_rad": rmse, "outage_prob": outage})
    return pd.DataFrame(rows)


def simulate_event_waterfalls(n_z: int = 900, n_t: int = 180, k: int = 4,
                              snr_db: float = 5.0, seed: int = 202110) -> Dict[str, np.ndarray]:
    rng = np.random.default_rng(seed)
    z_km = np.linspace(0, 10, n_z)
    time_s = np.arange(n_t) / 1000.0
    center = 6.2
    width = 0.055
    spatial = np.exp(-0.5 * ((z_km - center) / width) ** 2)
    phase_t = 0.38 * np.sin(2 * np.pi * 73.0 * time_s)
    truth = phase_t[:, None] * spatial[None, :]
    noise_var = 10 ** (-snr_db / 10.0)

    h = (rng.normal(size=(k, n_z)) + 1j * rng.normal(size=(k, n_z))) / np.sqrt(2)
    e0 = h.copy()
    single = np.zeros((n_t, n_z))
    diversity = np.zeros((n_t, n_z))
    for i in range(n_t):
        noise = np.sqrt(noise_var / 2) * (rng.normal(size=(k, n_z)) + 1j * rng.normal(size=(k, n_z)))
        e = h * np.exp(1j * truth[i][None, :]) + noise
        single[i] = np.angle(e[0] * np.conj(e0[0]))
        diversity[i] = np.angle(np.sum(e * np.conj(e0), axis=0))
    return {"z_km": z_km, "time_s": time_s, "truth": truth, "single": single, "diversity": diversity,
            "h": h}


def stokes_from_jones(e: np.ndarray) -> np.ndarray:
    ex, ey = e[..., 0], e[..., 1]
    s0 = np.abs(ex) ** 2 + np.abs(ey) ** 2 + 1e-15
    s1 = (np.abs(ex) ** 2 - np.abs(ey) ** 2) / s0
    s2 = 2 * np.real(ex * np.conj(ey)) / s0
    s3 = -2 * np.imag(ex * np.conj(ey)) / s0
    return np.stack([s1, s2, s3], axis=-1)


def random_polarization_path(n: int = 800, seed: int = 202111) -> Tuple[np.ndarray, np.ndarray]:
    rng = np.random.default_rng(seed)
    theta = np.cumsum(rng.normal(scale=0.045, size=n))
    phi = np.cumsum(rng.normal(scale=0.060, size=n))
    chi = 0.30 * np.sin(np.linspace(0, 8 * np.pi, n)) + np.cumsum(rng.normal(scale=0.010, size=n))
    e = np.empty((n, 2), dtype=complex)
    e[:, 0] = np.cos(theta) * np.exp(1j * phi / 2)
    e[:, 1] = np.sin(theta) * np.exp(-1j * phi / 2 + 1j * chi)
    e /= np.linalg.norm(e, axis=1, keepdims=True)
    return e, stokes_from_jones(e)


def phase_rmse_polarization(snr_db: Iterable[float], n_mc: int = 100_000,
                            phase_true: float = 0.3, seed: int = 202112) -> pd.DataFrame:
    rng = np.random.default_rng(seed)
    rows = []
    methods = {"单发单收": 1, "单发双收": 2, "双发双收": 4}
    for snr in snr_db:
        nv = 10 ** (-snr / 10.0)
        for name, modes in methods.items():
            h = (rng.normal(size=(n_mc, modes)) + 1j * rng.normal(size=(n_mc, modes))) / np.sqrt(2)
            n0 = np.sqrt(nv / 2) * (rng.normal(size=(n_mc, modes)) + 1j * rng.normal(size=(n_mc, modes)))
            n1 = np.sqrt(nv / 2) * (rng.normal(size=(n_mc, modes)) + 1j * rng.normal(size=(n_mc, modes)))
            e0 = h + n0
            e1 = h * np.exp(1j * phase_true) + n1
            est = np.angle(np.sum(e1 * np.conj(e0), axis=1))
            err = np.angle(np.exp(1j * (est - phase_true)))
            rmse = np.sqrt(np.mean(err ** 2))
            outage = np.mean(np.sum(np.abs(h) ** 2, axis=1) < 0.1 * modes)
            rows.append({"snr_db": snr, "method": name, "modes": modes,
                         "rmse_rad": rmse, "outage_prob": outage})
    return pd.DataFrame(rows)


def add_title(ax: plt.Axes, title: str, subtitle: str | None = None) -> None:
    ax.set_title(title, loc="left", fontsize=13, fontweight="bold", color=PALETTE["navy"], pad=28)
    if subtitle:
        ax.text(0, 1.012, subtitle, transform=ax.transAxes, fontsize=9, color=PALETTE["gray"], va="bottom")


def plot_workflow(out: Path) -> None:
    fig, ax = plt.subplots(figsize=(12, 3.8))
    ax.axis("off")
    steps = ["数据理解与参数量纲化", "传播效应筛选", "随机散射前向建模", "分集与编码优化", "偏振扩展", "验证与设计建议"]
    xs = np.linspace(0.08, 0.92, len(steps))
    for i, (x, s) in enumerate(zip(xs, steps)):
        rect = patches.FancyBboxPatch((x-0.075, 0.38), 0.15, 0.26, boxstyle="round,pad=0.02,rounding_size=0.025",
                                      facecolor=PALETTE["light"], edgecolor=PALETTE["blue"], linewidth=1.5)
        ax.add_patch(rect)
        ax.text(x, 0.51, s, ha="center", va="center", fontsize=10, color=PALETTE["navy"], wrap=True)
        ax.text(x, 0.70, f"S{i+1}", ha="center", va="center", fontsize=9, color=PALETTE["blue"], fontweight="bold")
        if i < len(steps)-1:
            ax.annotate("", xy=(xs[i+1]-0.085, 0.51), xytext=(x+0.085, 0.51),
                        arrowprops=dict(arrowstyle="->", lw=1.5, color=PALETTE["gray"]))
    ax.text(0.5, 0.15, "机理约束贯穿全流程:尺度判据 → 可解释前向模型 → 蒙特卡洛统计验证 → 工程折中",
            ha="center", fontsize=11, color=PALETTE["purple"])
    savefig(fig, out / "fig01_workflow.png")


def plot_dimensionless_maps(out: Path) -> pd.DataFrame:
    widths = np.logspace(-11, -6, 120)
    powers = np.logspace(-3, 1, 120)
    W, P = np.meshgrid(widths, powers)
    length = 10.0
    D = length * abs(FIBER.beta2_s2_per_km) / (W/2) ** 2
    B = FIBER.gamma_w_inv_km * P * effective_length_km(length, FIBER.alpha_power_per_km)

    fig, ax = plt.subplots(figsize=(8.8, 5.6))
    im = ax.pcolormesh(W*1e9, P, np.log10(D), shading="auto", cmap="viridis")
    ax.contour(W*1e9, P, D, levels=[0.01, 0.1, 1], colors="white", linewidths=1.1)
    ax.set_xscale("log"); ax.set_yscale("log")
    ax.set_xlabel("脉宽 $T_p$ / ns"); ax.set_ylabel("峰值功率 $P_0$ / W")
    add_title(ax, "色散尺度判据图", "颜色表示 log10(L/L_D),白色等值线依次为 0.01、0.1、1")
    cb = fig.colorbar(im, ax=ax); cb.set_label("log10 色散数")
    savefig(fig, out / "fig02_dispersion_map.png")

    fig, ax = plt.subplots(figsize=(8.8, 5.6))
    im = ax.pcolormesh(W*1e9, P, B, shading="auto", cmap="magma", norm=plt.matplotlib.colors.LogNorm(vmin=B.min(), vmax=B.max()))
    ax.contour(W*1e9, P, B, levels=[0.01, 0.1, 1, 5], colors="white", linewidths=1.1)
    ax.set_xscale("log"); ax.set_yscale("log")
    ax.set_xlabel("脉宽 $T_p$ / ns"); ax.set_ylabel("峰值功率 $P_0$ / W")
    add_title(ax, "非线性相移尺度判据图", "B=γP0Leff;B<0.1 rad 时可优先忽略 Kerr 非线性")
    cb = fig.colorbar(im, ax=ax); cb.set_label("B 积分 / rad")
    savefig(fig, out / "fig03_nonlinear_map.png")

    rows = []
    for tp in [10e-12, 100e-12, 1e-9, 10e-9, 100e-9, 1e-6]:
        for p in [0.01, 0.1, 1.0]:
            d = dimensionless_numbers(length, tp, p)
            rows.append({"length_km": length, "pulse_width_ns": tp*1e9, "power_w": p, **d})
    return pd.DataFrame(rows)
全部论文请见下方" 只会建模 QQ名片" 点击QQ名片即可
相关推荐
weitao_6669 小时前
电商商品 CSV 导入校验:把脏字段挡在 AI 客服与上架前
数据分析
极昆仑智慧11 小时前
智能问数五级成熟度模型:从“能问“到“能协作“的演进路径
人工智能·语言模型·数据分析
学掌门11 小时前
超级菜鸟怎么学习数据分析?
python·学习·数据分析
追风少年ii13 小时前
课前准备--蛋白质PDB文件与PDBQT文件的格式与区别
数据库·数据分析·分子动力学·分子对接
数模竞赛Paid answer14 小时前
2026年五一杯数学建模B题多工序协同作业问题求解全过程文档及程序
数学建模·数据分析·五一杯
数模竞赛Paid answer1 天前
2026年五一杯数学建模A题煤矿巷道支护问题求解全过程文档及程序
数学建模·数据分析·五一杯
饼干哥哥1 天前
十年数据分析经验,被我打包成了4个跨境VOC Skill
前端·数据分析·数据可视化
数模竞赛Paid answer1 天前
2026年五一杯数学建模C题边坡预警问题求解全过程文档及程序
算法·数学建模·五一杯
数模竞赛Paid answer1 天前
2021年深圳杯数学建模A题火星探测器着陆控制方案解题全过程论文及程序
算法·数学建模·深圳杯