LumAPI 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
lumapi/lumapi.py ADDED
@@ -0,0 +1,626 @@
1
+ import numpy as np
2
+ import os
3
+ import sys
4
+ import json
5
+ import importlib
6
+ import importlib.util
7
+ import re, platform
8
+
9
+ current_dir = os.path.dirname(os.path.abspath(__file__))
10
+ CONFIG_PATH = os.path.join(current_dir, 'config.json')
11
+
12
+ def detect_version(lumerical_root):
13
+ """检测Lumerical安装目录下的有效版本号"""
14
+ try:
15
+ if not os.path.exists(lumerical_root):
16
+ return None
17
+
18
+ # 检查是否存在v+三位数字的文件夹
19
+ for item in os.listdir(lumerical_root):
20
+ if os.path.isdir(os.path.join(lumerical_root, item)):
21
+ # 匹配v+三位数字的模式,例如v231, v242
22
+ if re.match(r'^v\d{3}$', item):
23
+ # 验证该目录下是否存在lumapi.py
24
+ lumapi_path = os.path.join(lumerical_root, item, "api", "python", "lumapi.py")
25
+ if os.path.exists(lumapi_path):
26
+ return item
27
+ return None
28
+ except Exception:
29
+ return None
30
+
31
+ def get_lumapi_path(lumerical_root, version):
32
+ """从Lumerical根路径和版本获取lumapi.py路径"""
33
+ return os.path.join(lumerical_root, version, "api", "python", "lumapi.py")
34
+
35
+ def validate_path(lumerical_root: str, version: str = None) -> object:
36
+ """验证Lumerical路径有效性并返回lumapi对象
37
+
38
+ 参数:
39
+ lumerical_root: Lumerical安装根目录
40
+ version: 版本号(可选),如"v241"
41
+
42
+ 返回:
43
+ lumapi对象或None
44
+ """
45
+ try:
46
+ if not lumerical_root:
47
+ print("错误:路径不能为空")
48
+ return None
49
+
50
+ lumerical_root = os.path.abspath(lumerical_root)
51
+
52
+ # 如果没有提供版本号,尝试自动检测
53
+ if not version:
54
+ version = detect_version(lumerical_root)
55
+ if not version:
56
+ print(f"错误:在指定路径未找到有效的Lumerical版本 (查找路径:{lumerical_root})")
57
+ return None
58
+
59
+ # 获取lumapi.py的完整路径
60
+ lumapi_path = get_lumapi_path(lumerical_root, version)
61
+
62
+ if not os.path.exists(lumapi_path):
63
+ print(f"错误:在指定路径未找到 lumapi.py 文件(查找路径:{lumapi_path})")
64
+ return None
65
+
66
+ # 测试导入
67
+ spec = importlib.util.spec_from_file_location('lumapi', lumapi_path)
68
+ lumapi = importlib.util.module_from_spec(spec)
69
+ spec.loader.exec_module(lumapi)
70
+
71
+ if platform.system() == "Windows":
72
+ # windows系统导入dll目录
73
+ os.add_dll_directory(lumerical_root)
74
+
75
+ return lumapi
76
+
77
+ except Exception as e:
78
+ print(f"错误:路径验证失败 - {str(e)}")
79
+ return None
80
+
81
+ def create_cmap(color_type):
82
+ """
83
+ 创建从黑色到指定颜色再到白色的渐变色映射
84
+
85
+ 参数:
86
+ color_type (str): 颜色类型,可以是 'green', 'blue' 或 'red'
87
+
88
+ 返回:
89
+ LinearSegmentedColormap: 对应的颜色映射对象
90
+ """
91
+ from matplotlib.colors import LinearSegmentedColormap
92
+ # 定义颜色字典
93
+ colors = {
94
+ 'green': [(0, 0, 0), (0, 0.5, 0), (1, 1, 1)], # 黑色 -> 绿色 -> 白色
95
+ 'blue': [(0, 0, 0), (0, 0, 0.5), (1, 1, 1)], # 黑色 -> 蓝色 -> 白色
96
+ 'red': [(0, 0, 0), (0.5, 0, 0), (1, 1, 1)] # 黑色 -> 红色 -> 白色
97
+ }
98
+
99
+ if color_type not in colors:
100
+ raise ValueError("参数必须是 'green', 'blue' 或 'red'")
101
+
102
+ # 创建颜色映射
103
+ cmap = LinearSegmentedColormap.from_list(
104
+ f'black_{color_type}_white',
105
+ colors[color_type],
106
+ N=256
107
+ )
108
+
109
+ return cmap
110
+
111
+ def Kirchhoff(lamb, x_near, y_near, E_near, x_far, y_far, z_far, mode='numba'):
112
+ '''
113
+ lamb: 波长
114
+ x_near, y_near: 近场位置数据,x_near和y_near应当是一维ndarry数组
115
+ E_near: 近场的电场数据,E_near应当是二维ndarry数组
116
+ x_far, y_far, z_far: 远场的位置数据,应当是一维数据或者数值
117
+ mode: 计算模式
118
+ 'common'('c'), : 普通循环计算模式,兼容所有平台,最稳定,但速度最慢
119
+ 'threaded'('t') : 多线程计算模式,能够吃满CPU资源,测试仅windows下可用,需要joblib库
120
+ 'vectorized'('v') : 矢量化计算模式,计算小数据非常快,但大数据会容易爆内存(目前还没写好)
121
+ 'numba'('n') : numba计算模式,计算速度非常快,兼容windows和linux,需要numba库,**推荐使用**
122
+
123
+ return: 远场电场数据np.ndarray(len(x_far),len(y_far),len(z_far))
124
+ '''
125
+ from tqdm import tqdm
126
+
127
+ # 确保远场坐标为一维数组
128
+ x_far = np.asarray(x_far)
129
+ y_far = np.asarray(y_far)
130
+ z_far = np.asarray(z_far)
131
+ if x_far.ndim == 0: x_far = x_far[np.newaxis]
132
+ if y_far.ndim == 0: y_far = y_far[np.newaxis]
133
+ if z_far.ndim == 0: z_far = z_far[np.newaxis]
134
+
135
+ k = 2 * np.pi / lamb
136
+ # 生成远场网格(使用 'ij' 索引)
137
+ X_far, Y_far, Z_far = np.meshgrid(x_far, y_far, z_far, indexing='ij')
138
+ E_far = np.zeros_like(X_far, dtype=np.complex128)
139
+ if mode == 'common' or mode == 'c':
140
+ print('Using normal mode...')
141
+ # 直接积分计算
142
+ E_far = np.zeros_like(X_far, dtype=complex)
143
+ for ii in tqdm(range(len(y_near))):
144
+ for jj in range(len(x_near)):
145
+ def E(r1, r2, x, y, z):
146
+ r = np.sqrt((x - r1)**2 + (y - r2)**2 + z**2)
147
+ return (1/(2j*lamb) * E_near[ii,jj]/r *
148
+ np.exp(1j*k*r) * (1 + z/r))
149
+
150
+ E_far += E(x_near[jj], y_near[ii], X_far, Y_far, Z_far)
151
+
152
+ elif mode == 'threaded' or mode == 't':
153
+ print('Using joblib threaded mode...')
154
+ from joblib import Parallel, delayed
155
+ # 使用joblib多线程实现
156
+ def compute_row(ii):
157
+ """计算单行的远场贡献"""
158
+ row_result = np.zeros_like(X_far, dtype=np.complex128)
159
+ for jj in range(len(x_near)):
160
+ r = np.sqrt((X_far - x_near[jj])**2 +
161
+ (Y_far - y_near[ii])**2 +
162
+ Z_far**2)
163
+ row_result += (1/(2j*lamb) * E_near[ii,jj]/r *
164
+ np.exp(1j*k*r) * (1 + Z_far/r))
165
+ return row_result
166
+
167
+ # 并行执行计算
168
+ results = Parallel(n_jobs=-1)(
169
+ delayed(compute_row)(ii)
170
+ for ii in tqdm(range(len(y_near)))
171
+ )
172
+
173
+ # 合并结果
174
+ for row_result in results:
175
+ E_far += row_result
176
+
177
+ elif mode == 'vectorized' or mode == 'v':
178
+ print('Using vectorized mode...')
179
+ # 生成近场网格
180
+ X_near, Y_near = np.meshgrid(x_near, y_near, indexing='ij')
181
+
182
+ # 计算距离
183
+ dx = X_far[np.newaxis, :, :, np.newaxis] - X_near[:, :, np.newaxis, np.newaxis]
184
+ dy = Y_far[np.newaxis, :, :, np.newaxis] - Y_near[:, :, np.newaxis, np.newaxis]
185
+ dz = Z_far[np.newaxis, np.newaxis, :, :] # 形状为 (1,1,len(y_far),len(z_far))
186
+ r = np.sqrt(dx**2 + dy**2 + dz**2)
187
+
188
+ # 计算标量因子
189
+ factor = (1/(2j*lamb)) * E_near[:, :, np.newaxis, np.newaxis] / r * np.exp(1j*k*r) * (1 + dz/r)
190
+
191
+ # 累加所有近场点贡献
192
+ E_far = np.sum(factor, axis=(0, 1))
193
+
194
+ elif mode == 'numba' or mode == 'n':
195
+ print('Using numba mode...(numba mode has no progress bar)')
196
+ # 使用numba加速循环
197
+ # @jit(nopython=True, fastmath=True) # 更高精度
198
+ # @jit(nopython=True, parallel=True) # 并行加速
199
+ import numba as nb
200
+ # Numba 加速的积分内核(不含 tqdm)
201
+ # 使用 Numba 并行加速的积分内核
202
+ @nb.njit(parallel=True, fastmath=True)
203
+ def compute_row_parallel(y_len, x_len, x_near, y_near, E_near, X_far, Y_far, Z_far, lamb, k, E_far):
204
+ for ii in nb.prange(y_len): # prange 启用多线程
205
+ for jj in range(x_len):
206
+ # 原始积分计算逻辑
207
+ r = np.sqrt((X_far - x_near[jj])**2 +
208
+ (Y_far - y_near[ii])**2 +
209
+ Z_far**2)
210
+ E_far += (1/(2j*lamb) * E_near[ii,jj]/r *
211
+ np.exp(1j*k*r) * (1 + Z_far/r))
212
+ return E_far
213
+
214
+ # 调用 Numba 并行函数
215
+ E_far = compute_row_parallel(len(y_near), len(x_near), x_near, y_near, E_near, X_far, Y_far, Z_far, lamb, k, E_far)
216
+
217
+ else:
218
+ raise ValueError('Invalid mode(请检查输入的mode参数)')
219
+ return E_far
220
+
221
+ def RorySommerfeld_Scalar(lamb, x_near, y_near, E_near, x_far, y_far, z_far, mode='numba'):
222
+ '''
223
+ lamb: 波长
224
+ x_near, y_near: 近场位置数据,x_near和y_near应当是一维ndarry数组
225
+ E_near: 近场的电场数据,E_near应当是二维ndarry数组
226
+ x_far, y_far, z_far: 远场的位置数据,应当是一维数据或者数值
227
+ mode: 计算模式
228
+ 'common'('c'), : 普通循环计算模式,兼容所有平台,最稳定,但速度最慢
229
+ 'threaded'('t') : 多线程计算模式,能够吃满CPU资源,测试仅windows下可用,需要joblib库
230
+ 'vectorized'('v') : 矢量化计算模式,计算小数据非常快,但大数据会容易爆内存(目前还没写好)
231
+ 'numba'('n') : numba计算模式,计算速度非常快,兼容windows和linux,需要numba库,**推荐使用**
232
+
233
+ return: 远场电场数据np.ndarray(len(x_far),len(y_far),len(z_far))
234
+ '''
235
+ from tqdm import tqdm
236
+
237
+ # 确保远场坐标为一维数组
238
+ x_far = np.asarray(x_far)
239
+ y_far = np.asarray(y_far)
240
+ z_far = np.asarray(z_far)
241
+ if x_far.ndim == 0: x_far = x_far[np.newaxis]
242
+ if y_far.ndim == 0: y_far = y_far[np.newaxis]
243
+ if z_far.ndim == 0: z_far = z_far[np.newaxis]
244
+
245
+ k = 2 * np.pi / lamb
246
+ # 生成远场网格(使用 'ij' 索引)
247
+ X_far, Y_far, Z_far = np.meshgrid(x_far, y_far, z_far, indexing='ij')
248
+ E_far = np.zeros_like(X_far, dtype=np.complex128)
249
+ if mode == 'common' or mode == 'c':
250
+ print('Using normal mode...')
251
+ # 直接积分计算
252
+ E_far = np.zeros_like(X_far, dtype=complex)
253
+ for ii in tqdm(range(len(y_near))):
254
+ for jj in range(len(x_near)):
255
+ def E(r1, r2, x, y, z):
256
+ r = np.sqrt((x - r1)**2 + (y - r2)**2 + z**2)
257
+ return (1/(1j*lamb) * E_near[ii,jj]/r *
258
+ np.exp(1j*k*r) * (z/r))
259
+
260
+ E_far += E(x_near[jj], y_near[ii], X_far, Y_far, Z_far)
261
+
262
+ elif mode == 'threaded' or mode == 't':
263
+ print('Using joblib threaded mode...')
264
+ from joblib import Parallel, delayed
265
+ # 使用joblib多线程实现
266
+ def compute_row(ii):
267
+ """计算单行的远场贡献"""
268
+ row_result = np.zeros_like(X_far, dtype=np.complex128)
269
+ for jj in range(len(x_near)):
270
+ r = np.sqrt((X_far - x_near[jj])**2 +
271
+ (Y_far - y_near[ii])**2 +
272
+ Z_far**2)
273
+ row_result += (1/(1j*lamb) * E_near[ii,jj]/r *
274
+ np.exp(1j*k*r) * (Z_far/r))
275
+ return row_result
276
+
277
+ # 并行执行计算
278
+ results = Parallel(n_jobs=-1)(
279
+ delayed(compute_row)(ii)
280
+ for ii in tqdm(range(len(y_near)))
281
+ )
282
+
283
+ # 合并结果
284
+ for row_result in results:
285
+ E_far += row_result
286
+
287
+ elif mode == 'vectorized' or mode == 'v':
288
+ print('Using vectorized mode...')
289
+ # 生成近场网格
290
+ X_near, Y_near = np.meshgrid(x_near, y_near, indexing='ij')
291
+
292
+ # 计算距离
293
+ dx = X_far[np.newaxis, :, :, np.newaxis] - X_near[:, :, np.newaxis, np.newaxis]
294
+ dy = Y_far[np.newaxis, :, :, np.newaxis] - Y_near[:, :, np.newaxis, np.newaxis]
295
+ dz = Z_far[np.newaxis, np.newaxis, :, :] # 形状为 (1,1,len(y_far),len(z_far))
296
+ r = np.sqrt(dx**2 + dy**2 + dz**2)
297
+
298
+ # 计算标量因子
299
+ factor = (1/(2j*lamb)) * E_near[:, :, np.newaxis, np.newaxis] / r * np.exp(1j*k*r) * (1 + dz/r)
300
+
301
+ # 累加所有近场点贡献
302
+ E_far = np.sum(factor, axis=(0, 1))
303
+
304
+ elif mode == 'numba' or mode == 'n':
305
+ print('Using numba mode...(numba mode has no progress bar)')
306
+ # 使用numba加速循环
307
+ # @jit(nopython=True, fastmath=True) # 更高精度
308
+ # @jit(nopython=True, parallel=True) # 并行加速
309
+ import numba as nb
310
+ # Numba 加速的积分内核(不含 tqdm)
311
+ # 使用 Numba 并行加速的积分内核
312
+ @nb.njit(parallel=True, fastmath=True)
313
+ def compute_row_parallel(y_len, x_len, x_near, y_near, E_near, X_far, Y_far, Z_far, lamb, k, E_far):
314
+ for ii in nb.prange(y_len): # prange 启用多线程
315
+ for jj in range(x_len):
316
+ # 原始积分计算逻辑
317
+ r = np.sqrt((X_far - x_near[jj])**2 +
318
+ (Y_far - y_near[ii])**2 +
319
+ Z_far**2)
320
+ E_far += (1/(1j*lamb) * E_near[ii,jj]/r *
321
+ np.exp(1j*k*r) * (Z_far/r))
322
+ return E_far
323
+
324
+ # 调用 Numba 并行函数
325
+ E_far = compute_row_parallel(len(y_near), len(x_near), x_near, y_near, E_near, X_far, Y_far, Z_far, lamb, k, E_far)
326
+
327
+ else:
328
+ raise ValueError('Invalid mode(请检查输入的mode参数)')
329
+ return E_far
330
+
331
+ def RorySommerfeld_Vector(lamb, x_near, y_near, E_near_x, E_near_y, x_far, y_far, z_far, mode='numba'):
332
+ '''
333
+ lamb: 波长
334
+ x_near, y_near: 近场位置数据,x_near和y_near应当是一维ndarry数组
335
+ E_near_x, E_near_y: 近场的电场数据的xy分量,E_near_x和E_near_y应当是二维ndarry数组
336
+ x_far, y_far, z_far: 远场的位置数据,应当是一维数据或者数值
337
+ mode: 计算模式
338
+ 'common'('c'), : 普通循环计算模式,兼容所有平台,最稳定,但速度最慢
339
+ 'threaded'('t') : 多线程计算模式,能够吃满CPU资源,测试仅windows下可用,需要joblib库
340
+ 'vectorized'('v') : 矢量化计算模式,计算小数据非常快,但大数据会容易爆内存(目前还没写好)
341
+ 'numba'('n') : numba计算模式,计算速度非常快,兼容windows和linux,需要numba库,**推荐使用**
342
+
343
+ return: 远场电场数据
344
+ '''
345
+ from tqdm import tqdm
346
+
347
+ # 确保远场坐标为一维数组
348
+ x_far = np.asarray(x_far)
349
+ y_far = np.asarray(y_far)
350
+ z_far = np.asarray(z_far)
351
+ if x_far.ndim == 0: x_far = x_far[np.newaxis]
352
+ if y_far.ndim == 0: y_far = y_far[np.newaxis]
353
+ if z_far.ndim == 0: z_far = z_far[np.newaxis]
354
+
355
+ k = 2 * np.pi / lamb
356
+ # 生成远场网格(使用 'ij' 索引)
357
+ X_far, Y_far, Z_far = np.meshgrid(x_far, y_far, z_far, indexing='ij')
358
+ E_far_x = np.zeros_like(X_far, dtype=np.complex128)
359
+ E_far_y = np.zeros_like(Y_far, dtype=np.complex128)
360
+ E_far_z = np.zeros_like(Z_far, dtype=np.complex128)
361
+
362
+
363
+ if mode == 'common' or mode == 'c':
364
+ print('Using normal mode...')
365
+ # 直接积分计算
366
+ for ii in tqdm(range(len(y_near))):
367
+ for jj in range(len(x_near)):
368
+ r = np.sqrt((X_far - x_near[jj])**2 +
369
+ (Y_far - y_near[ii])**2 +
370
+ Z_far**2)
371
+ exp_term = np.exp(1j*k*r)
372
+ common_factor = (-1/(2*np.pi) * Z_far / (r**2) * (1j*k - 1/r))
373
+
374
+ E_far_x += E_near_x[ii,jj] * exp_term * common_factor
375
+
376
+ E_far_y += E_near_y[ii,jj] * exp_term * common_factor
377
+
378
+ z_common_factor = (1/(2*np.pi) * Z_far / (r**2) * (1j*k - 1/r))
379
+ E_far_z += ((E_near_x[ii,jj] + E_near_y[ii,jj]) * exp_term * z_common_factor)
380
+
381
+ elif mode == 'vectorized' or mode == 'v':
382
+ print('Using vectorized mode...')
383
+ # 生成近场网格
384
+ X_near, Y_near = np.meshgrid(x_near, y_near, indexing='ij')
385
+
386
+ # 计算距离
387
+ dx = X_far[np.newaxis, :, :, np.newaxis] - X_near[:, :, np.newaxis, np.newaxis]
388
+ dy = Y_far[np.newaxis, :, :, np.newaxis] - Y_near[:, :, np.newaxis, np.newaxis]
389
+ dz = Z_far[np.newaxis, np.newaxis, :, :] # 形状为 (1,1,len(y_far),len(z_far))
390
+ r = np.sqrt(dx**2 + dy**2 + dz**2)
391
+
392
+ # 计算x分量
393
+ factor_x = (-1/(2*np.pi) * E_near_x[:, :, np.newaxis, np.newaxis] *
394
+ np.exp(1j*k*r) * dz / (r**2) * (1j*k - 1/r))
395
+
396
+ # 计算y分量
397
+ factor_y = (-1/(2*np.pi) * E_near_y[:, :, np.newaxis, np.newaxis] *
398
+ np.exp(1j*k*r) * dz / (r**2) * (1j*k - 1/r))
399
+
400
+ # 计算z分量
401
+ factor_z = (1/(2*np.pi) * E_near_x[:, :, np.newaxis, np.newaxis] *
402
+ np.exp(1j*k*r) * dz / (r**2) * (1j*k - 1/r)) + \
403
+ (1/(2*np.pi) * E_near_y[:, :, np.newaxis, np.newaxis] *
404
+ np.exp(1j*k*r) * dz / (r**2) * (1j*k - 1/r))
405
+
406
+ # 累加所有近场点贡献
407
+ E_far_x = np.sum(factor_x, axis=(0, 1))
408
+ E_far_y = np.sum(factor_y, axis=(0, 1))
409
+ E_far_z = np.sum(factor_z, axis=(0, 1))
410
+
411
+ elif mode == 'threaded' or mode == 't':
412
+ print('Using joblib threaded mode...')
413
+ from joblib import Parallel, delayed
414
+
415
+ # 使用joblib多线程实现
416
+ def compute_row(ii):
417
+ """计算单行的远场贡献"""
418
+ row_x = np.zeros_like(X_far, dtype=np.complex128)
419
+ row_y = np.zeros_like(Y_far, dtype=np.complex128)
420
+ row_z = np.zeros_like(Z_far, dtype=np.complex128)
421
+
422
+ for jj in range(len(x_near)):
423
+ r = np.sqrt((X_far - x_near[jj])**2 +
424
+ (Y_far - y_near[ii])**2 +
425
+ Z_far**2)
426
+ exp_term = np.exp(1j*k*r)
427
+ common_factor = (-1/(2*np.pi) * Z_far / (r**2) * (1j*k - 1/r))
428
+
429
+ # 计算x分量
430
+ row_x += E_near_x[ii,jj] * exp_term * common_factor
431
+
432
+ # 计算y分量
433
+ row_y += E_near_y[ii,jj] * exp_term * common_factor
434
+
435
+ # 计算z分量
436
+ z_common_factor = (1/(2*np.pi) * Z_far / (r**2) * (1j*k - 1/r))
437
+ row_z += (E_near_x[ii,jj] + E_near_y[ii,jj]) * exp_term * z_common_factor
438
+
439
+ return row_x, row_y, row_z
440
+
441
+ # 并行执行计算
442
+ results = Parallel(n_jobs=-1)(
443
+ delayed(compute_row)(ii)
444
+ for ii in tqdm(range(len(y_near)), desc="Processing rows")
445
+ )
446
+
447
+ # 合并结果
448
+ for row_x, row_y, row_z in results:
449
+ E_far_x += row_x
450
+ E_far_y += row_y
451
+ E_far_z += row_z
452
+
453
+ elif mode == 'numba' or mode == 'n':
454
+ print('Using numba mode...(numba mode has no progress bar)')
455
+ import numba as nb
456
+
457
+ # Numba 加速的积分内核
458
+ @nb.njit(parallel=True, fastmath=True)
459
+ def compute_row_parallel(y_len, x_len, x_near, y_near, E_near_x, E_near_y,
460
+ X_far, Y_far, Z_far, k, E_far_x, E_far_y, E_far_z):
461
+ for ii in nb.prange(y_len): # prange 启用多线程
462
+ for jj in range(x_len):
463
+ # 计算距离
464
+ r = np.sqrt((X_far - x_near[jj])**2 +
465
+ (Y_far - y_near[ii])**2 +
466
+ Z_far**2)
467
+ exp_term = np.exp(1j*k*r)
468
+ common_factor = (-1/(2*np.pi) * Z_far / (r**2) * (1j*k - 1/r))
469
+
470
+ # 计算x分量
471
+ E_far_x += (E_near_x[ii,jj] * exp_term * common_factor)
472
+
473
+ # 计算y分量
474
+ E_far_y += (E_near_y[ii,jj] * exp_term * common_factor)
475
+
476
+ # 计算z分量
477
+ z_common_factor = (1/(2*np.pi) * Z_far / (r**2) * (1j*k - 1/r))
478
+ E_far_z += ((E_near_x[ii,jj] + E_near_y[ii,jj]) * exp_term * z_common_factor)
479
+ return E_far_x, E_far_y, E_far_z
480
+
481
+ # 调用 Numba 并行函数
482
+ E_far_x, E_far_y, E_far_z = compute_row_parallel(
483
+ len(y_near), len(x_near), x_near, y_near,
484
+ E_near_x, E_near_y, X_far, Y_far, Z_far, k,
485
+ E_far_x, E_far_y, E_far_z
486
+ )
487
+ # 计算总体电场强度(模值)
488
+ E_far = np.sqrt(np.abs(E_far_x)**2 + np.abs(E_far_y)**2 + np.abs(E_far_z)**2)
489
+ return E_far, E_far_x, E_far_y, E_far_z
490
+
491
+
492
+ class LumAPI:
493
+ def __init__(self, lumerical_path='', version='', config_path=CONFIG_PATH):
494
+ self.config_path = config_path
495
+
496
+ # 如果没有提供路径,尝试从配置文件加载
497
+ if not lumerical_path:
498
+ try:
499
+ with open(self.config_path, 'r') as f:
500
+ config = json.load(f)
501
+ lumerical_path = config.get('lumerical_path')
502
+ version = config.get('version')
503
+
504
+ if not lumerical_path:
505
+ raise ValueError("配置文件中缺少lumerical_path字段")
506
+
507
+ except Exception as e:
508
+ raise ValueError(f"配置文件读取失败: {str(e)}")
509
+
510
+ # 验证路径
511
+ self.lumapi = validate_path(lumerical_path, version)
512
+
513
+ # 检测路径是否有效
514
+ if not self.lumapi:
515
+ raise ValueError(f"错误:Lumerical路径无效,请检查路径{lumerical_path}和版本{version}")
516
+
517
+ self.lumerical_path = lumerical_path
518
+ self.version = version
519
+
520
+ def FDTD(self, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
521
+ return FDTD(self.lumapi, filename, key, hide, serverArgs, remoteArgs, **kwargs)
522
+
523
+ def MODE(self, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
524
+ return MODE(self.lumapi, filename, key, hide, serverArgs, remoteArgs, **kwargs)
525
+
526
+ def DEVICE(self, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
527
+ return MODE(self.lumapi, filename, key, hide, serverArgs, remoteArgs, **kwargs)
528
+
529
+ def INTERCONNECT(self, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
530
+ return MODE(self.lumapi, filename, key, hide, serverArgs, remoteArgs, **kwargs)
531
+
532
+ class FDTD():
533
+ def __init__(self, lumapi, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
534
+ self.lumapi = lumapi
535
+ self.filename = filename
536
+
537
+ self.fdtd = lumapi.FDTD(filename, key, hide, serverArgs, remoteArgs, **kwargs)
538
+
539
+ def __getattr__(self, name):
540
+ '''
541
+ 将原本函数转发回去
542
+ '''
543
+ return getattr(self.fdtd, name)
544
+
545
+ class MODE():
546
+ def __init__(self, lumapi, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
547
+ self.lumapi = lumapi
548
+ self.filename = filename
549
+
550
+ if not filename:
551
+ self.mode = lumapi.MODE()
552
+ else:
553
+ self.mode = lumapi.MODE(filename, key, hide, serverArgs, remoteArgs, **kwargs)
554
+
555
+ def __getattr__(self, name):
556
+ '''
557
+ 将原本函数转发回去
558
+ '''
559
+ return getattr(self.mode, name)
560
+
561
+ class DEVICE():
562
+ def __init__(self, lumapi, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
563
+ self.lumapi = lumapi
564
+ self.filename = filename
565
+
566
+ if not filename:
567
+ self.device = lumapi.DEVICE()
568
+ else:
569
+ self.device = lumapi.DEVICE(filename, key, hide, serverArgs, remoteArgs, **kwargs)
570
+
571
+ def __getattr__(self, name):
572
+ '''
573
+ 将原本函数转发回去
574
+ '''
575
+ return getattr(self.device, name)
576
+
577
+ class INTERCONNECT():
578
+ def __init__(self, lumapi, filename=None, key = None, hide = False, serverArgs = {}, remoteArgs = {}, **kwargs):
579
+ self.lumapi = lumapi
580
+ self.filename = filename
581
+
582
+ self.interconnect = lumapi.INTERCONNECT(filename, key, hide, serverArgs, remoteArgs, **kwargs)
583
+
584
+ def __getattr__(self, name):
585
+ '''
586
+ 将原本函数转发回去
587
+ '''
588
+ return getattr(self.interconnect, name)
589
+
590
+
591
+ if __name__ == '__main__':
592
+ um = 1e-6
593
+ nx, ny = 100, 100
594
+ S = 0.5*um
595
+ material_base = 'Au (Gold) - CRC'
596
+
597
+ lumapi = LumAPI()
598
+ fdtd = lumapi.FDTD()
599
+ fdtd.addrect(
600
+ name="base",
601
+ x=0,
602
+ y=0,
603
+ x_span=nx*S,
604
+ y_span=ny*S,
605
+ z_min=-0.3*um,
606
+ z_max=0,
607
+ material=material_base,
608
+ )
609
+ # fdtd.save()
610
+ # fdtd.run()
611
+ fdtd.close()
612
+
613
+
614
+
615
+
616
+
617
+
618
+
619
+
620
+
621
+
622
+
623
+
624
+
625
+
626
+