前言
SPINS(Simulation Platform for Integrated Nanophotonic Systems)是一款用于集成纳米光子器件设计与优化的强大工具,其光栅耦合器优化示例(grating_coupler)是学习光子器件 Inverse Design 的经典案例。然而在 Windows 环境下运行该示例时,会遇到多进程启动失败、句柄数量超限等兼容性问题,同时还会出现复数转实数的警告。本文将详细记录问题排查、解决过程,帮助更多开发者快速跑通示例代码。
问题背景
运行环境:
- 操作系统:Windows 10/11
- Python 环境:Anaconda 虚拟环境(spins-env)
- 核心依赖:spins、scipy、numpy、gdspy
- 目标代码:SPINS examples/invdes/grating_coupler 中的 grating.py
核心需求:优先跑通示例代码,后续再基于自身平台调整参数,暂不深入优化精度问题。
第一个问题:多进程启动失败(RuntimeError)
问题现象
运行命令 python grating.py run my_test 后,立即抛出以下错误:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
错误堆栈指向 spins/fdfd_solvers/local_matrix_solvers.py 中的 MultiprocessingSolver 类,提示在模块导入阶段就创建了多进程池,违反了 Windows 系统的多进程启动规范。
问题根源
Windows 系统默认使用 spawn 方式启动多进程,要求所有多进程相关代码必须放在 if __name__ == '__main__': 保护块内。而 SPINS 库的 MultiprocessingSolver 在模块顶层(creator_em.py 第 23 行)直接初始化进程池,导致导入模块时就触发进程创建,引发冲突。
解决方案:延迟初始化多进程池
步骤 1:修改 local_matrix_solvers.py 中的 MultiprocessingSolver 类
将进程池创建延迟到第一次调用求解方法时,避免模块导入阶段启动进程:
使用以下的class MultiprocessingSolver(LocalMatrixSolver类替换原spins-b-master/spins/fdfd_solvers中的local_matrix_solvers.py 中的 MultiprocessingSolver 类
import multiprocessing
import signal
import scipy.sparse
import numpy as np
class MultiprocessingSolver(LocalMatrixSolver):
def __init__(self, wrapped_solvee, num_processes=0):
"""创建多进程求解器池(延迟初始化进程池)"""
self.wrapped_solvee = wrapped_solvee
# 限制进程数上限为60,避免Windows句柄超限
self.num_processes = min(num_processes if num_processes != 0 else multiprocessing.cpu_count(), 60)
self.pool = None # 延迟初始化进程池
def solve_matrix_equation(self, A: scipy.sparse.csr_matrix, b: np.ndarray):
# 第一次调用时才初始化进程池
if self.pool is None:
# 处理SIGINT信号,确保Ctrl+C能正常终止进程
original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
self.pool = multiprocessing.Pool(self.num_processes)
signal.signal(signal.SIGINT, original_sigint_handler)
try:
return self.pool.apply(_worker_simulate, (A, b, self.wrapped_solvee))
except KeyboardInterrupt:
self.pool.terminate()
self.pool.join()
raise # 重新抛出异常,确保程序优雅退出
步骤 2:修改主程序入口(grating.py)
将所有业务逻辑放入 main() 函数,在保护块内调用,配合 freeze_support() 支持 Windows 多进程:
def main():
import argparse
from spins.invdes.problem_graph.creator import run_opt, view_opt, view_opt_quick, resume_opt, gen_gds
parser = argparse.ArgumentParser()
parser.add_argument(
"action",
choices=("run", "view", "view_quick", "resume", "gen_gds"),
help="Must be either \"run\" to run an optimization, \"view\" to "
"view the results, \"resume\" to resume an optimization, or "
"\"gen_gds\" to generate the grating GDS file.")
parser.add_argument(
"save_folder", help="Folder containing optimization logs.")
grating_len = 12000
wg_width = 12000
args = parser.parse_args()
if args.action == "run":
run_opt(args.save_folder, grating_len=grating_len, wg_width=wg_width)
elif args.action == "view":
view_opt(args.save_folder)
elif args.action == "view_quick":
view_opt_quick(args.save_folder)
elif args.action == "resume":
resume_opt(args.save_folder)
elif args.action == "gen_gds":
gen_gds(args.save_folder, grating_len=grating_len, wg_width=wg_width)
if __name__ == "__main__":
# Windows多进程支持
from multiprocessing import freeze_support
freeze_support()
main()
关键说明
- 延迟进程池初始化:避免模块导入时创建进程,符合 Windows 多进程规范;
- 限制进程数:Windows 默认最多支持 63 个进程句柄,设置上限 60 避免超限;
- 信号处理:保留原有的 SIGINT 信号处理,确保 Ctrl+C 能正常终止进程池。
第二个问题:复数转实数警告(ComplexWarning)(暂时忽略)
问题现象
解决多进程问题后,运行程序出现以下警告,但优化过程仍继续:
D:\Anaconda\envs\spins-env\lib\site-packages\scipy\sparse\_data.py:72: ComplexWarning: Casting complex values to real discards the imaginary part
self._deduped_data().astype(dtype, casting=casting, copy=copy),
问题解释
警告表示程序在将复数类型数据转换为实数类型时,丢弃了虚部。光栅耦合器优化属于电磁仿真类问题,中间过程涉及电场、相位等复数计算,理论上虚部有物理意义,但当前警告未阻断程序运行,暂时不影响 “跑通示例” 的核心目标。
临时处理方案
由于优先目标是跑通示例,暂不深入优化精度,可通过以下方式抑制警告(不影响程序运行):在主程序入口(grating.py)的 if __name__ == "__main__": 块中添加警告过滤:
if __name__ == "__main__":
# 抑制复数转换警告(临时方案)
import warnings
warnings.filterwarnings('ignore', category=np.ComplexWarning)
# Windows多进程支持
from multiprocessing import freeze_support
freeze_support()
main()
后续优化建议(跑通后处理)
若后续需要提高优化精度,需修改稀疏矩阵创建逻辑,指定复数类型 dtype=np.complex128,避免虚部丢失:
- 找到创建稀疏矩阵
A的代码(通常在_worker_simulate函数或求解器内部); - 添加复数类型指定:
A = scipy.sparse.csr_matrix(data, dtype=np.complex128); - 同步修改向量
b的类型:b = b.astype(np.complex128)。
最终运行效果
修改后重新运行命令:
python grating.py run my_test_20251115
程序正常启动,无多进程错误,警告被抑制,优化过程持续进行:
[2025-11-15 14:35:43,191][INFO][solver][run_plan] Setting up workspace.
[2025-11-15 14:35:43,192][INFO][solver][run_plan] Running transformation opt_cont.
[2025-11-15 14:36:07,438][INFO][workspace][write] Saving monitors for transformation opt_cont with event info {'state': 'start'} [1].
[2025-11-15 14:36:35,115][INFO][workspace][write] Saving monitors for transformation opt_cont with event info {'iteration': 1, 'state': 'optimizing'} [2].
[2025-11-15 14:37:11,553][INFO][workspace][write] Saving monitors for transformation opt_cont with event info {'iteration': 2, 'state': 'optimizing'} [3].
总结
本文针对 Windows 环境下 SPINS 光栅耦合器示例的运行问题,提供了分步骤解决方案:
- 通过延迟多进程池初始化和限制进程数,解决了 Windows 多进程启动失败和句柄超限问题;
- 通过警告过滤,暂时抑制了复数转实数警告,优先保证示例代码跑通;
- 预留了后续精度优化的方案,满足后续实际应用需求。
对于初学者而言,优先跑通示例、熟悉优化流程是核心目标,后续可根据自身平台(如 SOI、SiN 等)调整光栅长度、波导宽度、蚀刻比例等参数,逐步深入 Inverse Design 的核心逻辑。
772

被折叠的 条评论
为什么被折叠?



