Files
2025AsianB/org/use/q3.py
2025-11-22 18:08:14 +08:00

349 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import itertools
import math
import os
import random
from dataclasses import dataclass
from typing import Dict, List, Sequence, Tuple
import numpy as np
from matplotlib import pyplot as plt
from scipy.interpolate import CubicSpline
plt.rcParams["font.sans-serif"] = ["DejaVu Sans", "Arial", "Helvetica"]
plt.rcParams["axes.unicode_minus"] = False
# -----------------------------
# 你的PDMS光学常数模型
# -----------------------------
def make_strictly_increasing(wl, n, k):
# 去除完全相同的点
unique_wl, indices = np.unique(wl, return_index=True)
if len(unique_wl) != len(wl):
print(f"Removed {len(wl) - len(unique_wl)} duplicate wavelength points")
wl, n, k = wl[indices], n[indices], k[indices]
# 确保严格递增
is_increasing = np.diff(wl) > 0
if not all(is_increasing):
# 移除不满足严格递增的点
valid_indices = np.concatenate([[True], is_increasing])
wl, n, k = wl[valid_indices], n[valid_indices], k[valid_indices]
return wl, n, k
def read_split_data(file_path):
"""
解析分块数据格式:
第一部分wl n多行数据
第二部分wl k多行数据
返回sorted_wl, n, k波长已排序保证n和k一一对应
"""
with open(file_path, 'r', encoding='utf-8') as f:
lines = [line.strip() for line in f if line.strip() and not line.startswith('#')]
# 分割n数据和k数据以"wl k"为分界)
split_idx = None
for i, line in enumerate(lines):
if line == 'wl k': # 找到k数据的表头
split_idx = i
break
# 提取n数据表头后到split_idx前
n_lines = lines[1:split_idx] # 跳过"wl n"表头
wl_n = []
n_list = []
for line in n_lines:
wl, n_val = line.split()
wl_n.append(float(wl))
n_list.append(float(n_val))
# 提取k数据split_idx后
k_lines = lines[split_idx + 1:] # 跳过"wl k"表头
wl_k = []
k_list = []
for line in k_lines:
wl, k_val = line.split()
wl_k.append(float(wl))
k_list.append(float(k_val))
# 转换为numpy数组
wl_n = np.array(wl_n)
n_list = np.array(n_list)
wl_k = np.array(wl_k)
k_list = np.array(k_list)
# 确保n和k的波长完全一致否则插值会出错
assert np.allclose(wl_n, wl_k), "n和k的波长列表不一致"
# 排序(按波长递增,避免插值异常)
sorted_idx = np.argsort(wl_n)
sorted_wl = wl_n[sorted_idx]
sorted_n = n_list[sorted_idx]
sorted_k = k_list[sorted_idx]
return sorted_wl, sorted_n, sorted_k
# 全局变量存储PDMS插值函数
_PDMS_CS_N = None
_PDMS_CS_K = None
_PDMS_WL_MIN = None
_PDMS_WL_MAX = None
def initialize_pdms_model(data_file_path):
"""
初始化PDMS光学常数模型
"""
global _PDMS_CS_N, _PDMS_CS_K, _PDMS_WL_MIN, _PDMS_WL_MAX
# 读取数据
wl_all, n_all, k_all = read_split_data(data_file_path)
wl_all, n_all, k_all = make_strictly_increasing(wl_all, n_all, k_all)
# 创建插值函数
_PDMS_CS_N = CubicSpline(wl_all, n_all)
_PDMS_CS_K = CubicSpline(wl_all, k_all)
_PDMS_WL_MIN = wl_all.min()
_PDMS_WL_MAX = wl_all.max()
print(f"PDMS模型初始化完成: 波长范围 [{_PDMS_WL_MIN:.2f}, {_PDMS_WL_MAX:.2f}] μm")
def pdms_nk(wavelength_um: np.ndarray) -> np.ndarray:
"""
使用你的PDMS模型计算复折射率
"""
global _PDMS_CS_N, _PDMS_CS_K, _PDMS_WL_MIN, _PDMS_WL_MAX
if _PDMS_CS_N is None:
raise ValueError("PDMS模型未初始化请先调用initialize_pdms_model()")
# 确保波长在有效范围内
wl_clipped = np.clip(wavelength_um, _PDMS_WL_MIN, _PDMS_WL_MAX)
# 计算n和k
n = _PDMS_CS_N(wl_clipped)
k = _PDMS_CS_K(wl_clipped)
# 返回复折射率
return n - 1j * k
# 初始化PDMS模型请修改为你的实际数据文件路径
initialize_pdms_model('/Users/spasolreisa/IdeaProjects/asiaMath/data.txt')
def planck_weight(wavelength_um: np.ndarray, temperature: float = 300.0) -> np.ndarray:
wl_m = wavelength_um * 1e-6
c1 = 3.7418e-16
c2 = 1.4388e-2
spectral = c1 / (wl_m ** 5 * (np.exp(c2 / (wl_m * temperature)) - 1))
return spectral
def solar_weight(wavelength_um: np.ndarray) -> np.ndarray:
center1, width1 = 0.6, 0.35
center2, width2 = 1.6, 0.45
return np.exp(-((wavelength_um - center1) / width1) ** 2) + 0.35 * np.exp(
-((wavelength_um - center2) / width2) ** 2
)
@dataclass
class Material:
name: str
n_const: float
k_const: float
def nk(self, wavelength_um: np.ndarray) -> np.ndarray:
n = np.full_like(wavelength_um, self.n_const, dtype=np.complex128)
k = np.full_like(wavelength_um, self.k_const, dtype=np.complex128)
return n - 1j * k
def pdms_index(wavelength_um: np.ndarray) -> np.ndarray:
"""
修改为使用你的PDMS模型
"""
return pdms_nk(wavelength_um)
def ag_index(wavelength_um: np.ndarray) -> np.ndarray:
n = 0.15 + 0.6 * np.exp(-((wavelength_um - 0.5) / 0.4) ** 2)
k = 4.5 + 3.5 * np.exp(-((wavelength_um - 10) / 6) ** 2)
return n - 1j * k
def transfer_matrix_stack(
wavelength_um: np.ndarray,
layer_nk: Sequence[np.ndarray],
thickness_um: Sequence[float],
substrate_nk: np.ndarray,
n0: float = 1.0,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
beta = 2 * np.pi / (wavelength_um * 1e-6)
q0 = n0
qs = substrate_nk
R = np.zeros_like(wavelength_um)
T = np.zeros_like(wavelength_um)
for idx, wl in enumerate(wavelength_um):
M = np.identity(2, dtype=complex)
for nk, d in zip(layer_nk, thickness_um):
n_layer = nk[idx]
delta = beta[idx] * n_layer * d * 1e-6
cos = np.cos(delta)
sin = 1j * np.sin(delta)
q = n_layer
Mj = np.array([[cos, sin / q], [q * sin, cos]], dtype=complex)
M = M @ Mj
numerator = (
q0 * M[0, 0]
+ q0 * qs[idx] * M[0, 1]
- M[1, 0]
- qs[idx] * M[1, 1]
)
denominator = (
q0 * M[0, 0]
+ q0 * qs[idx] * M[0, 1]
+ M[1, 0]
+ qs[idx] * M[1, 1]
)
r = numerator / denominator
t = 2 * q0 / denominator
R[idx] = np.abs(r) ** 2
T[idx] = np.real(qs[idx] / q0) * np.abs(t) ** 2
A = np.clip(1 - R - T, 0, 1)
return R, T, A
def evaluate_stack(design: Dict) -> Dict:
solar_wl = np.linspace(0.35, 2.5, 120)
ir_wl = np.linspace(8, 13, 200)
solar_w = solar_weight(solar_wl)
ir_w = planck_weight(ir_wl)
substrate = ag_index
layer_funcs = []
thickness = []
for layer in design["layers"]:
material = layer["material"]
thickness.append(layer["thickness"])
if material == "PDMS":
layer_funcs.append(pdms_index)
else:
mat = MATERIAL_LIBRARY[material]
layer_funcs.append(lambda wl, m=mat: m.nk(wl))
solar_nk = [func(solar_wl) for func in layer_funcs]
ir_nk = [func(ir_wl) for func in layer_funcs]
solar_R, _, solar_A = transfer_matrix_stack(
solar_wl, solar_nk, thickness, substrate(solar_wl)
)
ir_R, _, ir_A = transfer_matrix_stack(ir_wl, ir_nk, thickness, substrate(ir_wl))
alpha = float(np.trapz(solar_A * solar_w, solar_wl) / np.trapz(solar_w, solar_wl))
epsilon = float(np.trapz(ir_A * ir_w, ir_wl) / np.trapz(ir_w, ir_wl))
score = epsilon - 0.3 * alpha
return {"alpha": alpha, "epsilon": epsilon, "score": score}
MATERIAL_LIBRARY: Dict[str, Material] = {
"SiO2": Material("SiO2", 1.45, 1e-4),
"Al2O3": Material("Al2O3", 1.76, 1.5e-3),
"TiO2": Material("TiO2", 2.40, 5e-3),
"Si3N4": Material("Si3N4", 2.05, 2e-3),
"HfO2": Material("HfO2", 1.9, 2e-3),
}
def random_design() -> Dict:
num_layers = random.choice([2, 3])
middle_materials = random.sample(list(MATERIAL_LIBRARY.keys()), num_layers)
layers = [{"material": "PDMS", "thickness": random.uniform(10, 50)}]
for mat in middle_materials:
layers.append(
{
"material": mat,
"thickness": random.uniform(0.05, 2.0),
}
)
return {"layers": layers}
def optimize(iterations: int = 800) -> List[Dict]:
best_designs: List[Dict] = []
for _ in range(iterations):
design = random_design()
metrics = evaluate_stack(design)
design.update(metrics)
best_designs.append(design)
best_designs.sort(key=lambda x: x["score"], reverse=True)
return best_designs[:15]
def write_summary(designs: List[Dict], path: str) -> None:
with open(path, "w", encoding="utf-8") as f:
f.write("rank,score,epsilon,alpha,layers\n")
for idx, design in enumerate(designs, start=1):
layer_desc = ";".join(
f"{layer['material']}@{layer['thickness']:.3f}um"
for layer in design["layers"]
)
f.write(
f"{idx},{design['score']:.4f},{design['epsilon']:.4f},"
f"{design['alpha']:.4f},{layer_desc}\n"
)
def plot_pareto(designs: List[Dict], path: str) -> None:
eps = [d["epsilon"] for d in designs]
alpha = [d["alpha"] for d in designs]
scores = [d["score"] for d in designs]
fig, ax = plt.subplots(figsize=(6, 5))
scatter = ax.scatter(alpha, eps, c=scores, cmap="viridis", s=80)
ax.set_xlabel("Solar-weighted Absorption α")
ax.set_ylabel("8-13 µm Emissivity ε")
ax.set_title("Multilayer Design Performance Distribution")
plt.colorbar(scatter, label="Composite Score ε - 0.3α")
for idx, design in enumerate(designs[:5]):
ax.annotate(str(idx + 1), (design["alpha"], design["epsilon"]))
fig.tight_layout()
plt.savefig(path, dpi=300)
plt.close(fig)
def main():
designs = optimize()
outdir = os.path.join(os.path.dirname(__file__), "outputs")
os.makedirs(outdir, exist_ok=True)
summary_path = os.path.join(outdir, "question3_multilayer_summary.csv")
write_summary(designs, summary_path)
plot_path = os.path.join(outdir, "question3_pareto.png")
plot_pareto(designs, plot_path)
print(f"Optimal designs written to: {summary_path}")
print(f"Performance scatter plot: {plot_path}")
top = designs[0]
layer_desc = "; ".join(
f"{layer['material']}@{layer['thickness']:.2f}um" for layer in top["layers"]
)
print(
"Best design: score={:.3f}, ε={:.3f}, α={:.3f}, layers={}".format(
top["score"], top["epsilon"], top["alpha"], layer_desc
)
)
if __name__ == "__main__":
main()