钙钛矿太阳能电池反向工程 — 原理与步骤分析

整理时间: 2026-06-04 | 实验时间: 2026-03-09 ~ 04-09
核心工具: SIMsalabim v2.1(漂移-扩散模拟器)、Python


目录

  1. J-V 曲线与太阳能电池基础物理
  2. 三级反向工程分析体系
  3. SIMsalabim 漂移-扩散模拟
  4. 反向工程优化策略
  5. 老化退化分析
  6. 代码实现总结

一、J-V 曲线与太阳能电池基础物理

1.1 原理

单二极管等效电路

太阳能电池在光照下的电流-电压关系可用单二极管模型描述:

        Rs
    ┌───/\/\/\───┬─────────┐
    │            │         │
    │  ┌───D───┐ │         │
    │  │       │ │         │
I →├──┤ Jph   ├─┤ Rsh     ├─→ V
    │  └───────┘ │         │
    │            │         │
    └────────────┴─────────┘

核心方程:

J(V) = Jph - J0[exp(q(V + J·Rs)/(n·kT)) - 1] - (V + J·Rs)/Rsh
符号含义物理来源
Jph光生电流密度光子吸收产生电子-空穴对
J0反向饱和电流密度热激发载流子的复合
n理想因子复合机制判断(n=1:带-带, n≈2:SRH)
Rs串联电阻体电阻 + 接触电阻 + 电极电阻
Rsh并联电阻漏电流路径、针孔、边缘分流
q元电荷1.602×10⁻¹⁹ C
kT/q热电压≈0.0257 V @ 298K

四个关键性能参数

参数定义物理意义
JscV=0 时的电流密度光子吸收→载流子收集的总效率
VocJ=0 时的电压准费米能级劈裂上限,受复合强度限制
FFPmax/(Jsc×Voc)Rs/Rsh/n 对输出功率矩形的约束程度
PCEJsc×Voc×FF/Pin最终转换效率(Pin=100mW/cm²)

1.2 代码实现

Level 1 数据提取(04_3_levels_analysis.py):

def analyze_level1(curve):
    jsc = curve['jsc_measured']  # mA/cm²
    voc = curve['voc_measured']  # V
    ff  = curve['ff_measured']   # 无量纲
    pce = jsc * voc * ff / P_in * 100  # P_in = 100 mW/cm²
    return {'Jsc': jsc, 'Voc': voc, 'FF': ff, 'PCE': pce}

FAPI+ALD 工具中按基底和像素分组提取(fapi_ald_reverse_engineering_tool_v2.py):

devices = []
grouped = df.groupby(['substr.', 'pixel'])
for (substr, pixel), group in grouped:
    best = group.loc[group['Pmax'].idxmax()]  # 取 Pmax 最优扫描方向
    devices.append({
        'device_id': f"S{int(substr)}_P{int(pixel)}",
        'Jsc': float(best['Jsc (mA/cm2)']),
        'Voc': float(best['Voc (V)']),
        'FF': float(best['FF']),
        'PCE': float(best['Pmax']),
        'V': list(v_values), 'J': list(j_values)
    })

二、三级反向工程分析体系

2.1 原理

三级反向工程从测量数据逐层深入推导物理机制

Level 1 (测量参数)     PCE / Jsc / Voc / FF
        ↓
Level 2 (电路参数)     n / Rs / Rsh / Jph / J0  — 单二极管等效模型拟合
        ↓
Level 3 (物理机制)     复合类型 / 损失分解 / 器件质量评估

Level 2 → Level 3 的物理映射规则

电路参数物理含义诊断规则
n < 1.3扩散/双分子复合主导低复合,优质器件
1.3 ≤ n < 1.6SRH 体相复合体相陷阱参与复合
1.6 ≤ n < 2.0SRH + 界面复合界面缺陷显著
n ≥ 2.0高陷阱辅助复合严重的非理想行为
Rs < 2 Ω·cm²接触良好FF 损失可忽略
Rs > 10 Ω·cm²串联电阻过大严重 FF 损失
Rsh > 5000 Ω·cm²无分流并联路径可忽略
Rsh < 200 Ω·cm²严重分流漏电流主导

2.2 代码实现

Level 2:电路参数估算(简化版)

def analyze_level2(level1):
    # 理想因子 n: 从 FF 偏离理想值(0.85)估计
    ff_ideal, jsc, voc = 0.85, level1['Jsc'], level1['Voc']
    n = 1.0 + (ff_ideal - level1['FF']) * 4
    n = max(1.0, min(3.0, n))
 
    # 串联电阻: 从 FF 损失估计
    Rs = (ff_ideal - level1['FF']) * 15
 
    # 并联电阻: 从 FF 与理想值偏差反算
    Rsh = 2000 / max(0.1, (1 - level1['FF']/ff_ideal))
 
    # 反向饱和电流: 从 Voc 反算(二极管方程反向推导)
    Vt = k * T / q
    J0 = jsc * np.exp(-voc / (n * Vt))
 
    return {'n': n, 'Rs': Rs, 'Rsh': Rsh, 'Jph': jsc, 'J0': J0}

注:以上为”原型”简化版。FAPI+ALD 工具 v2 使用 ImprovedFullDiodeModel 进行完整牛顿迭代二极管拟合,R² ≥ 0.95 才算合格(详见 fapi_ald_reverse_engineering_tool_v2.py 第 101-220 行)。

Level 3:物理机制诊断

def analyze_level3(level1, level2):
    n = level2['n']
    # ========= 复合类型判定 =========
    if n < 1.3:
        recomb_type = "Diffusion/Bimolecular"   # 低复合,优质
    elif n < 1.6:
        recomb_type = "SRH Bulk"                # 体相陷阱复合
    elif n < 2.0:
        recomb_type = "SRH + Interface"         # 界面复合参与
    else:
        recomb_type = "High Trap-assisted"      # 严重陷阱辅助
 
    # ========= 串联电阻质量 =========
    Rs = level2['Rs']
    if   Rs < 2:  rs_quality = "Excellent (Minimal FF loss)"
    elif Rs < 5:  rs_quality = "Good (Slight FF loss)"
    elif Rs < 10: rs_quality = "Moderate (Noticeable FF loss)"
    else:         rs_quality = "Poor (Severe FF loss)"
 
    # ========= 并联电阻质量 =========
    Rsh = level2['Rsh']
    if   Rsh > 5000: rsh_quality = "Excellent (No shunting)"
    elif Rsh > 1000: rsh_quality = "Good (Minor leakage)"
    elif Rsh > 200:  rsh_quality = "Moderate (Some shunt paths)"
    else:            rsh_quality = "Poor (Severe shunting)"
 
    # ========= 损失分解(vs 理论极限) =========
    voc_theory, jsc_theory = 1.3, 25  # 钙钛矿 Eg≈1.55eV 的理论极限
    voc_loss = (1 - level1['Voc']/voc_theory) * 100
    jsc_loss = (1 - level1['Jsc']/jsc_theory) * 100
 
    return {
        'recombination': {'type': recomb_type, 'n': n},
        'series_resistance': {'value': Rs, 'quality': rs_quality},
        'shunt_resistance': {'value': Rsh, 'quality': rsh_quality},
        'losses': {'Voc_loss': voc_loss, 'Jsc_loss': jsc_loss}
    }

FAPI+ALD v2 的物理一致性自动验证

class QualityFlags:
    GOOD = "GOOD"
    WARNING = "WARNING"
    FAILED = "FAILED"
 
def validate_physics(self, params):
    issues = []
    if params['Jph'] < params['Jsc_ext']:
        issues.append("Jph < Jsc — 物理矛盾")
    if params['n'] < 1.0:
        issues.append("n < 1.0 — 非物理")
    elif params['n'] > 2.5:
        issues.append("n > 2.5 — 极高度非理想")
    if params['Rs'] > 10:
        issues.append("Rs > 10 — 串联问题严重")
    if params['Rsh'] < 100:
        issues.append("Rsh < 100 — 严重分流")
    return len(issues) == 0, issues

三、SIMsalabim 漂移-扩散模拟

3.1 原理

核心方程(三个耦合 PDE)

SIMsalabim 数值求解以下三个方程:

(1)泊松方程(电势分布):

d/dx(ε·dφ/dx) = -q(p - n + ND⁺ - NA⁻)

(2)电子连续性方程(传输 + 产生/复合):

∂n/∂t = (1/q)·dJn/dx + G - R
Jn = q·μn·n·E + q·Dn·dn/dx   (Dn = μn·kT/q, Einstein 关系)

(3)空穴连续性方程(传输 + 产生/复合):

∂p/∂t = -(1/q)·dJp/dx + G - R
Jp = q·μp·p·E - q·Dp·dp/dx   (Dp = μp·kT/q, Einstein 关系)

复合机制

机制物理过程关联参数
SRH 体相复合电子-空穴通过深能级陷阱非辐射复合Nt_bulk
界面复合电荷传输层界面处缺陷辅助复合Nt_int
带-带辐射复合导带电子直接与价带空穴复合发光B(辐射复合系数)
俄歇复合三粒子非辐射过程(高注入下重要)

光学建模

SIMsalabim 使用传输矩阵法(TMM)计算各层的光吸收:

  1. Data/ 目录加载各材料的 n(λ)/k(λ) 折射率数据(.txt 文件)
  2. 使用 AM1.5G 光谱(Data/AM15G.txt)作为入射光源
  3. 计算各位置的光子吸收速率 G(x),以此驱动电子-空穴对产生

fitError 的计算

fitError = (1/N) × Σ|Jsim(Vi) - Jexp(Vi)|

SIMsalabim 运行结束后输出到 stdout:

fitError: 0.1721

fitError 越小 → 模拟越准确复现实验 J-V 曲线 → 物理参数越可信。

3.2 器件结构(本项目)

本项目中的钙钛矿太阳能电池为 4 层结构:

阴极 (W_L = 4.05 eV)
    ↓
  L1: C60(电子传输层, 25 nm)      — 不产生光生载流子
    ↓
  L2: MAPI(钙钛矿活性层, 500 nm)   — 主吸收层,含离子迁移
    ↓
  L3: PTAA(空穴传输层, 10 nm)     — 不产生光生载流子
    ↓
  L4: 右接触层(重用 L2 配置)
    ↓
阳极 (W_R = 5.2 eV)

5 个待优化物理参数

参数配置文件中的位置物理含义典型范围
μnL2_parameters.txt钙钛矿层电子迁移率1e-6 ~ 5e-3 m²/V·s
μpL2_parameters.txt钙钛矿层空穴迁移率1e-7 ~ 5e-3 m²/V·s
Nt_bulkL2_parameters.txt体相 SRH 陷阱密度1e18 ~ 1e24 m⁻³
Nt_intL2_parameters.txt界面陷阱密度1e10 ~ 1e16 m⁻²
R_shuntsimulation_setup.txt并联分流电阻1e-4 ~ 1e4 Ω·cm²

3.3 代码实现

SIMsalabim 的单次 eval 流程(exp5 中)

# ===== Step 1: 修改参数文件 =====
def set_params(p):
    # L2_parameters.txt — 4 个层参数
    l2 = os.path.join(WORK_DIR, 'L2_parameters.txt')
    with open(l2, 'r+', encoding='utf-8') as f:
        c = f.read()
        # 正则替换:匹配 "param = 数字" 并替换为新区
        c = re.sub(r'mu_n\s*=\s*[\d.Ee+-]+',      f'mu_n = {p["mu_n"]:.6e}', c)
        c = re.sub(r'mu_p\s*=\s*[\d.Ee+-]+',      f'mu_p = {p["mu_p"]:.6e}', c)
        c = re.sub(r'N_t_bulk\s*=\s*[\d.Ee+-]+',  f'N_t_bulk = {p["Nt_bulk"]:.6e}', c)
        c = re.sub(r'N_t_int\s*=\s*[\d.Ee+-]+',   f'N_t_int = {p["Nt_int"]:.6e}', c)
        f.seek(0); f.write(c); f.truncate()
 
    # simulation_setup.txt — R_shunt
    setup = os.path.join(WORK_DIR, 'simulation_setup.txt')
    with open(setup, 'r+', encoding='utf-8') as f:
        c = f.read()
        c = re.sub(r'R_shunt\s*=\s*-?[\d.Ee+-]+', f'R_shunt = {p["R_shunt"]:.6e}', c)
        f.seek(0); f.write(c); f.truncate()
 
# ===== Step 2: 准备实验 J-V 数据 =====
def prepare_exp_data():
    df = pd.read_csv(os.path.join(KEY_CURVES_DIR, f'{STAGE}_curve.csv'))
    lines = [" Vext Jext"]  # SIMsalabim 要求的文件头
    for _, row in df.iterrows():
        # 电流取负号:实验数据 Jsc<0 (第四象限),SIMsalabim 用 Jsc>0 (第一象限)
        lines.append(f" {row['Voltage(V)']:12.6E} {-row['Current(mA/cm2)']:12.6E}")
    with open(os.path.join(WORK_DIR, 'JV_Exp.dat'), 'w') as f:
        f.write('\n'.join(lines))
 
# ===== Step 3: 调用 simss.exe =====
def run_simss():
    # 清除上次输出文件,避免误读
    for f in ['JV.dat', 'scPars.dat', 'log.txt', 'Var.dat']:
        fp = os.path.join(WORK_DIR, f)
        if os.path.exists(fp): os.remove(fp)
 
    # 调用 SIMsalabim,90 秒超时
    r = subprocess.run(
        [SIMSS_EXE, 'simulation_setup.txt'],
        cwd=WORK_DIR,
        capture_output=True, text=True,
        timeout=90, encoding='utf-8', errors='replace'
    )
 
    # 检查是否崩溃
    if r.returncode != 0:
        for line in (r.stdout + r.stderr).split('\n'):
            if 'Cannot' in line:
                return None  # SIMsalabim 无法求解此参数组合
 
    # 从 stdout 解析 fitError
    for line in r.stdout.split('\n'):
        if 'fitError:' in line:
            return float(line.split(':')[1].strip().split()[0])
    return None

关键细节:

  • 电流取负号(-row['Current(mA/cm2)']):因为 SIMsalabim 以第一象限正电流为正,而实验 Jsc 为负(第四象限测量)
  • 超时保护(timeout=90):某些参数组合可能让 SIMsalabim 无限迭代
  • 输出文件清理:每次运行前删除 JV.dat 等旧输出,防止读到上次结果
  • 正则替换的精度:.6e 格式保证数值不回退到文本解析的精度损失

四、反向工程优化策略

4.1 问题形式化

  • 输入: 某老化阶段的实验 J-V 曲线(~30 个电压点)
  • 输出: 一组物理参数 θ = (μn, μp, Nt_bulk, Nt_int, R_shunt)
  • 目标: min fitError(SIMsalabim(θ), JV_exp)
  • 约束: θ 必须在物理合理范围内

4.2 策略一:网格搜索(Grid Search,exp1)

原理

在参数空间建立离散网格,枚举所有组合:

mu_n    ∈ {1e-6, 1e-5, ..., 5e-3}      (对数均匀取 8 点)
mu_p    ∈ {1e-7, 1e-6, ..., 5e-3}      (对数均匀取 8 点)
Nt_bulk ∈ {1e18, 1e19, ..., 1e24}      (对数均匀取 7 点)
Nt_int  ∈ {1e10, 1e11, ..., 1e16}      (对数均匀取 7 点)
R_shunt ∈ {1e-4, 1e-3, ..., 1e4}       (对数均匀取 7 点)
  • 优点: 简单、全局搜索、不依赖梯度
  • 缺点: 维度灾难 — 5 个参数 × 每维 8 点 = 32,768 次 eval,每次 ~10s,不可行
  • exp1 实际执行: 减少采样密度,限制在 ~1000-2000 次 eval

代码实现(概念)

mu_n_vals = np.logspace(np.log10(1e-6), np.log10(5e-3), 6)
mu_p_vals = np.logspace(np.log10(1e-7), np.log10(5e-3), 6)
Nt_vals   = np.logspace(np.log10(1e18),  np.log10(1e24), 5)
Nt_int_vals = np.logspace(np.log10(1e10), np.log10(1e16), 5)
Rsh_vals  = np.logspace(np.log10(1e-4),  np.log10(1e4),  5)
 
best = {'fitError': float('inf')}
for mu_n in mu_n_vals:
    for mu_p in mu_p_vals:
        for nt in Nt_vals:
            for n_int in Nt_int_vals:
                for rsh in Rsh_vals:
                    set_params({...}); e = run_simss()
                    if e and e < best['fitError']:
                        best = {...}; best['fitError'] = e

4.3 策略二:坐标下降(Coordinate Descent,exp4/exp5)

原理

核心思想: 每次只优化一个参数,固定其余,一轮一轮迭代。

轮次 1: μn → μp → Nt_bulk → Nt_int → R_shunt
轮次 2: μn → μp → Nt_bulk → Nt_int → R_shunt
轮次 3: ...(直到所有参数无法进一步改善)

每个参数的局部搜索:在对数空间中均匀采样 15 个点,扫描当前值 ±1 decade(第1轮)→ ±2 decades(第2轮)。

为什么比网格搜索高效?

  • 网格搜索:N⁵ 复杂度(指数级)
  • 坐标下降:每轮 5×15 = 75 次 eval,2-3 轮收敛 → 150-225 次 eval vs 32,768 次

代码实现(exp5 核心算法)

def coord_descent_clamped(start, max_cyc=5, dr=1.0, n=15):
    """带物理约束的坐标下降"""
    best = clamp_params(dict(start))      # Step 0: 物理约束
    set_params(best)
    best['fitError'] = run_simss()
 
    for cyc in range(max_cyc):
        improved = False
        for pn in PARAM_NAMES:             # 逐参数优化
            log_val = np.log10(max(best[pn], 1e-30))
 
            # 搜索范围 = 当前值 ±dr 对数空间,并 clamp 到物理边界
            lo_log = max(np.log10(BOUNDS[pn][0]), log_val - dr)
            hi_log = min(np.log10(BOUNDS[pn][1]), log_val + dr)
            if hi_log - lo_log < 0.01: continue  # 搜索空间太窄,跳过
 
            vals = np.linspace(lo_log, hi_log, n)  # 对数均匀取 n 点
            for v in 10**vals:
                trial = dict(best); trial[pn] = v
                trial = clamp_params(trial)         # 每次都要约束!
                set_params(trial)
                e = run_simss()
                if e is not None and e < best['fitError']:
                    best[pn] = v
                    best['fitError'] = e
                    improved = True
 
        if not improved:      # 本轮无任何参数改善 → 已收敛
            break
    return best

4.4 物理约束(Bounds Enforcement)

原理

exp4 中无约束的坐标下降出现了严重的参数补偿过拟合

阶段μn (m²/V·s)Nt_bulk (m⁻³)fitError问题
Initial4.75e-23.04e230.1439μn 超 47×
Mid3.40e-25.67e230.1505μn 超 34×
Final1.30e-13.24e270.0982μn 超 130×,Nt 超 32,400×

过拟合机制: 高迁移率→载流子快速收集(提升 Jsc),但同时超高陷阱密度→大量复合(压制回来)。数学上两者抵消产生了低 fitError,但物理上 μ=0.13 m²/V·s 远超钙钛矿单晶测量值(~0.01),Nt=3.24e27 意味 “每 nm³ 有 3 个陷阱”——不可能。

代码实现

# ===== 物理硬约束边界 =====
BOUNDS = {
    'mu_n':    (1e-7, 5e-3),      # max 50 cm²/V·s
    'mu_p':    (1e-7, 5e-3),
    'Nt_bulk': (1e18, 1e24),      # 接近 Nc/2 上限
    'Nt_int':  (1e10, 1e16),
    'R_shunt': (1e-4, 1e4),
}
 
# ===== 迁移率比值约束 =====
RATIO_MIN = 0.001   # μp ≥ μn/1000
RATIO_MAX = 2.0     # μp ≤ 2×μn
 
def clamp_params(p):
    # 1. 边界约束
    for k in PARAM_NAMES:
        lo, hi = BOUNDS[k]
        p[k] = max(lo, min(hi, p[k]))
 
    # 2. 比值约束(防 μp/μn 极端不合理)
    if p['mu_p'] / p['mu_n'] < RATIO_MIN:
        p['mu_p'] = p['mu_n'] * RATIO_MIN
    if p['mu_p'] / p['mu_n'] > RATIO_MAX:
        p['mu_p'] = p['mu_n'] * RATIO_MAX
 
    return p

exp5 效果: 全部参数物理合理,avg fitError 0.1721(vs exp1 基线 0.2416,改善 29.1%)。

4.5 多起点搜索策略

exp5 使用 5 个不同起点运行坐标下降,取最优结果:

起点类型理由
exp1-clamped网格搜索结果(约束后)已知合理区域,FM 起点
exp3v4-clamped加密网格结果(约束后)更精细的 FM 起点
random-1,2,3对数均匀随机采样探索其他区域,防局部最优
def random_start():
    """在对数空间中均匀随机采样"""
    p = {}
    for name in PARAM_NAMES:
        lo, hi = BOUNDS[name]
        log_val = np.random.uniform(np.log10(lo), np.log10(hi))
        p[name] = 10**log_val
    return clamp_params(p)
 
# 两阶段优化
def run_optimization(name, start):
    # Phase 1: ±1 decade, 5 cycles — 粗搜索
    p1 = coord_descent_clamped(start, max_cyc=5, dr=1.0, n=15)
    # Phase 2: ±2 decades, 3 cycles — 扩大搜索
    p2 = coord_descent_clamped(p1, max_cyc=3, dr=2.0, n=15)
    return p2 if p2['fitError'] < p1['fitError'] else p1

4.6 并行化策略

5 个老化阶段完全独立,通过独立的 SIMsalabim 工作目录实现并行:

# 并行运行 5 个阶段
python exp5_constrained.py initial &
python exp5_constrained.py early   &
python exp5_constrained.py mid     &
python exp5_constrained.py late    &
python exp5_constrained.py final   &

每个阶段的工作目录独立:

WORK_DIR = f"G:\\OpenClaw-Workspace\\simsalabim_parallel\\{STAGE}_exp5"
 
def setup_workspace():
    shutil.rmtree(WORK_DIR)                           # 清除旧副本
    shutil.copytree(ORIGINAL_DIR, WORK_DIR)            # 创建独立副本
    shutil.copytree(data_src, WORK_DIR + "\\Data")     # 复制材料数据

五、老化退化分析

5.1 原理

老化五阶段

对器件施加持续光照/偏压应力,在不同时间点测量 J-V 曲线:

Initial → Early → Mid → Late → Final
(0 h)    (24h)   (72h)  (168h) (336h+)

对每个阶段的 J-V 曲线独立进行反向工程,得到物理参数随时间演化曲线。

退化机制的物理诊断

观测物理机制根因推测
Voc 下降J0 增大 → 复合增强陷阱密度增加,准费米能级收缩
Jsc 下降扩散长度 LD 缩短μ↓ 和 τ↓ 同时退化
FF 下降Rs↑ + Rsh↓ + n↑ 三者叠加传输+复合双向退化
n 持续增大复合中心能级展宽新陷阱态(深能级)涌现
Nt_bulk 增大体相缺陷生成离子迁移、卤素空位形成
Nt_int 增大界面钝化退化界面氧化物/氢氧化钾生成
R_shunt 下降分流路径形成金属电极迁移、针孔扩展
μ 下降载流子散射增强声子散射 + 电离杂质散射 + 中性缺陷散射

两种老化模式的物理对比

特征激进老化(数据集1)温和老化(数据集2)
PCE 损失88.1%33.2%
退化动力学指数衰减→饱和(快→慢)近线性缓降(匀速)
主导机制复合损耗 60-70%传输+复合均衡
μn 下降96%(5e-4→2e-5)57%(2.56e-4→1.10e-4)
Nt_int 增加50×(1e13→5e14)2.2×(1e13→3.23e13)
n 终值2.53.0

关键物理发现: 存在”界面崩溃”阈值——Nt_int 在温和老化中仅增 2.2×,但在激进老化中”跃迁”到 50×。当界面钝化层整体崩坏后,老化进入不可逆加速阶段。

5.2 代码实现

老化反向工程流水线

STAGES = ['initial', 'early', 'mid', 'late', 'final']
 
all_results = {}
for stage in STAGES:
    # 1. 加载实验 J-V(来自 key_curves 目录的 CSV)
    df = pd.read_csv(f'{KEY_CURVES_DIR}/{stage}_curve.csv')
 
    # 2. 写入 SIMsalabim 输入文件(格式:Vext Jext)
    lines = [" Vext Jext"]
    for _, row in df.iterrows():
        lines.append(f" {row['Voltage(V)']:12.6E} {-row['Current(mA/cm2)']:12.6E}")
    with open(f'{WORK_DIR}/JV_Exp.dat', 'w') as f:
        f.write('\n'.join(lines))
 
    # 3. 运行坐标下降(exp5 最优方法)
    result = coord_descent_clamped(start_params[stage], max_cyc=3, dr=1.0, n=15)
 
    # 4. 保存各阶段结果
    all_results[stage] = result
    with open(f'results/{stage}_results.json', 'w') as f:
        json.dump(result, f)

退化幅度计算

# 从两个阶段的结果计算参数变化
def compute_degradation_metrics(initial, final):
    metrics = {
        # 各参数相对变化
        'mu_n_degradation': (initial['mu_n'] - final['mu_n']) / initial['mu_n'],
        'Nt_bulk_increase': final['Nt_bulk'] / initial['Nt_bulk'],
        'Nt_int_increase': final['Nt_int'] / initial['Nt_int'],
        'PCE_loss': (initial['PCE'] - final['PCE']) / initial['PCE'],
        # fitError 对比
        'fitError_improvement': (exp1_fitError - optimized_fitError),
    }
    return metrics

数据集交叉对比(v2)

v2 通过 generate_comparison.py 将两个数据集的参数演化放到同一张图中:

# 读取两个数据集的结果
ds1 = load_results('aging_analysis_r1-r4/results/')
ds2 = load_results('aging_analysis_v2/results/')
 
# 关键对比
comparisons = {
    'PCE_loss':        {'ds1': 0.881, 'ds2': 0.332},
    'mu_n_degradation': {'ds1': 0.96,  'ds2': 0.57},
    'Nt_int_increase':  {'ds1': 50,    'ds2': 2.2},
    'n_final':          {'ds1': 2.5,   'ds2': 3.0},
}

六、代码实现总结

6.1 整体架构

┌─────────────────────────────────────────────────┐
│ 输入层(数据读取)                                │
│  Excel → pandas    CSV → 老化曲线     WPD JSON   │
├─────────────────────────────────────────────────┤
│ 分析层(参数提取)                                │
│  L1: PCE/Jsc/Voc/FF 直接提取                     │
│  L2: 二极管拟合(ImprovedFullDiodeModel)        │
│  L3: 物理机制分类 + 损失分解                     │
├─────────────────────────────────────────────────┤
│ 仿真层(SIMsalabim 接口)                         │
│  参数模板修改 (re.sub)                            │
│  simss.exe 调用 (subprocess, timeout=90s)        │
│  fitError 解析 (stdout 正则)                      │
│  并行工作空间 (shutil.copytree)                   │
├─────────────────────────────────────────────────┤
│ 优化层(参数搜索)                                │
│  网格搜索 → 坐标下降 → 物理约束 + 多起点        │
│  对数空间采样 (logspace) + 并行独立工作区          │
├─────────────────────────────────────────────────┤
│ 输出层(报告生成)                                │
│  Markdown 报告 → JSON 结果 → PNG 图表 → Word 文档 │
└─────────────────────────────────────────────────┘

6.2 关键技术细节总结

技术点实现方式为何如此设计
正则替换参数re.sub(r'mu_n\s*=\s*[\d.Ee+-]+', ...)直接修改 SIMsalabim 文本配置文件,避免重新生成
对数空间搜索np.logspace(log10(lo), log10(hi), n)参数跨度 5 个数量级,线性搜索无效
超时保护subprocess.run(..., timeout=90)防 SIMsalabim 在非物理参数组合处死循环
物理硬约束max(lo, min(hi, val))最小侵入式约束,不改变优化算法结构
μp/μn 比值约束ratio ∈ [0.001, 2.0]防止迁移率比值脱离钙钛矿物理规律
独立工作空间shutil.copytree 创建全局副本5 个阶段并行运行时避免 JV.dat 输出冲突
多策略起点exp1 + exp3v4 + 3×random已知合理区域(exploit)+ 探索未知区域(explore)
两阶段搜索Phase 1: dr=1.0 → Phase 2: dr=2.0先精细搜索局部最小,再扩大窗口防遗漏
正则解析 fitErrorr.stdout → 'fitError:' → float从 stdout 解析而非文件,避免 I/O 竞态条件
电流符号转换-row['Current(mA/cm2)']实验 Jsc<0(第四象限),SIMsalabim 用 Jsc>0(第一象限)

6.3 五轮实验的代码演化

实验核心文件代码量方法特征
exp1heester_systematic.py, heester_v2.py~30 KB固定网格,全枚举
exp2hybrid_v3_experiment.py~17 KB多方法混合,并行尝试
exp3exp3_v4_proven.py(5 个版本)~58 KB网格加密,4 轮迭代
exp4exp4_v3_coord_descent.py 等 7 个版本~57 KB坐标下降(无约束),收敛但过拟合
exp5exp5_constrained.py~12 KB坐标下降+硬约束,最优方案

代码量先增后减:exp1 用简单方法→exp3/4 复杂化以追求更低 fitError→exp5 用物理约束化繁为简,用更少代码实现更好的物理合理性。

6.4 后续可扩展方向

  1. 贝叶斯优化替代坐标下降:利用高斯过程代理模型减少 eval 次数
  2. 梯度辅助优化:改造 SIMsalabim 输出 Jacobian 矩阵
  3. 深度学习代理模型:用神经网络学习 SIMsalabim 的输入-输出映射
  4. 多目标优化:同时最小化所有 5 个老化阶段的 fitError(而非每个阶段独立)
  5. 自动化报告流水线:老炼数据→自动运行 exp5→自动生成对比图表→自动审核

撰写完成:2026-06-04 10:40 GMT+8 | 基于 321 个代码/数据文件