@@ -185,3 +185,274 @@ def test_boundaryspec_classmethods():
185
185
assert all (
186
186
isinstance (boundary , PML ) for boundary_dim in boundaries for boundary in boundary_dim
187
187
)
188
+
189
+
190
+ def test_abc_boundary ():
191
+ # check basic instance
192
+ _ = td .ABCBoundary ()
193
+
194
+ # check enforced perm (and conductivity)
195
+ _ = td .ABCBoundary (permittivity = 2 )
196
+ _ = td .ABCBoundary (permittivity = 2 , conductivity = 0.1 )
197
+
198
+ with pytest .raises (pydantic .ValidationError ):
199
+ _ = td .ABCBoundary (permittivity = 0 )
200
+
201
+ with pytest .raises (pydantic .ValidationError ):
202
+ _ = td .ABCBoundary (permittivity = 2 , conductivity = - 0.1 )
203
+
204
+ with pytest .raises (pydantic .ValidationError ):
205
+ _ = td .ABCBoundary (permittivity = None , conductivity = - 0.1 )
206
+
207
+ # test mode abc
208
+ wvl_um = 1
209
+ freq0 = td .C_0 / wvl_um
210
+ mode_abc = td .ModeABCBoundary (
211
+ plane = td .Box (size = (1 , 1 , 0 )),
212
+ mode_spec = td .ModeSpec (num_modes = 2 ),
213
+ mode_index = 1 ,
214
+ frequency = freq0 ,
215
+ )
216
+
217
+ with pytest .raises (pydantic .ValidationError ):
218
+ _ = td .ModeABCBoundary (
219
+ plane = td .Box (size = (1 , 1 , 0 )),
220
+ mode_spec = td .ModeSpec (num_modes = 2 ),
221
+ mode_index = 1 ,
222
+ frequency = - 1 ,
223
+ )
224
+
225
+ with pytest .raises (pydantic .ValidationError ):
226
+ _ = td .ModeABCBoundary (
227
+ plane = td .Box (size = (1 , 1 , 0 )),
228
+ mode_spec = td .ModeSpec (num_modes = 2 ),
229
+ mode_index = - 1 ,
230
+ frequency = freq0 ,
231
+ )
232
+
233
+ with pytest .raises (pydantic .ValidationError ):
234
+ _ = td .ModeABCBoundary (
235
+ plane = td .Box (size = (1 , 1 , 1 )),
236
+ mode_spec = td .ModeSpec (num_modes = 2 ),
237
+ mode_index = 0 ,
238
+ frequency = freq0 ,
239
+ )
240
+
241
+ # from mode source
242
+ mode_source = td .ModeSource (
243
+ size = (1 , 1 , 0 ),
244
+ source_time = td .GaussianPulse (freq0 = freq0 , fwidth = 0.2 * freq0 ),
245
+ mode_spec = td .ModeSpec (num_modes = 2 ),
246
+ mode_index = 1 ,
247
+ direction = "+" ,
248
+ )
249
+ mode_abc_from_source = td .ModeABCBoundary .from_source (mode_source )
250
+ assert mode_abc == mode_abc_from_source
251
+
252
+ # from mode monitor
253
+ mode_monitor = td .ModeMonitor (
254
+ size = (1 , 1 , 0 ), mode_spec = td .ModeSpec (num_modes = 2 ), freqs = [freq0 ], name = "mnt"
255
+ )
256
+ mode_abc_from_monitor = td .ModeABCBoundary .from_monitor (
257
+ mode_monitor , mode_index = 1 , frequency = freq0
258
+ )
259
+ assert mode_abc == mode_abc_from_monitor
260
+
261
+ # in Boundary
262
+ _ = td .Boundary (
263
+ minus = td .ABCBoundary (permittivity = 3 ), plus = td .ModeABCBoundary (plane = td .Box (size = (1 , 1 , 0 )))
264
+ )
265
+ _ = td .Boundary .abc (permittivity = 3 , conductivity = 1e-5 )
266
+ abc_boundary = td .Boundary .mode_abc (
267
+ plane = td .Box (size = (1 , 1 , 0 )),
268
+ mode_spec = td .ModeSpec (num_modes = 2 ),
269
+ mode_index = 1 ,
270
+ frequency = freq0 ,
271
+ )
272
+ abc_boundary_from_source = td .Boundary .mode_abc_from_source (mode_source )
273
+ abc_boundary_from_monitor = td .Boundary .mode_abc_from_monitor (
274
+ mode_monitor , mode_index = 1 , frequency = freq0
275
+ )
276
+ assert abc_boundary == abc_boundary_from_source
277
+ assert abc_boundary == abc_boundary_from_monitor
278
+
279
+ with pytest .raises (pydantic .ValidationError ):
280
+ _ = td .Boundary (minus = td .Periodic (), plus = td .ABCBoundary ())
281
+
282
+ with pytest .raises (pydantic .ValidationError ):
283
+ _ = td .Boundary (minus = td .Periodic (), plus = td .ModeABCBoundary (plane = td .Box (size = (1 , 1 , 0 ))))
284
+
285
+ # in Simulation
286
+ _ = td .Simulation (
287
+ center = [0 , 0 , 0 ],
288
+ size = [1 , 1 , 1 ],
289
+ grid_spec = td .GridSpec .auto (
290
+ min_steps_per_wvl = 10 ,
291
+ wavelength = wvl_um ,
292
+ ),
293
+ sources = [],
294
+ run_time = 1e-20 ,
295
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
296
+ )
297
+
298
+ # validate ABC medium is not anisotorpic
299
+ with pytest .raises (pydantic .ValidationError ):
300
+ _ = td .Simulation (
301
+ center = [0 , 0 , 0 ],
302
+ size = [1 , 1 , 1 ],
303
+ grid_spec = td .GridSpec .auto (
304
+ min_steps_per_wvl = 10 ,
305
+ wavelength = wvl_um ,
306
+ ),
307
+ sources = [],
308
+ medium = td .AnisotropicMedium (xx = td .Medium (), yy = td .Medium (), zz = td .Medium ()),
309
+ run_time = 1e-20 ,
310
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
311
+ )
312
+
313
+ # validate homogeneous medium when permittivity=None, that is, automatic detection
314
+ box_crossing_boundary = td .Structure (
315
+ geometry = td .Box (size = (0.3 , 0.2 , td .inf )),
316
+ medium = td .Medium (permittivity = 2 ),
317
+ )
318
+ # ok if ABC boundary is not crossed
319
+ _ = td .Simulation (
320
+ center = [0 , 0 , 0 ],
321
+ size = [1 , 1 , 1 ],
322
+ grid_spec = td .GridSpec .auto (
323
+ min_steps_per_wvl = 10 ,
324
+ wavelength = wvl_um ,
325
+ ),
326
+ sources = [],
327
+ structures = [box_crossing_boundary ],
328
+ run_time = 1e-20 ,
329
+ boundary_spec = td .BoundarySpec (
330
+ x = td .Boundary .abc (),
331
+ y = td .Boundary .abc (),
332
+ z = td .Boundary .pml (),
333
+ ),
334
+ )
335
+ # or if we override manually
336
+ _ = td .Simulation (
337
+ center = [0 , 0 , 0 ],
338
+ size = [1 , 1 , 1 ],
339
+ grid_spec = td .GridSpec .auto (
340
+ min_steps_per_wvl = 10 ,
341
+ wavelength = wvl_um ,
342
+ ),
343
+ sources = [],
344
+ structures = [box_crossing_boundary ],
345
+ run_time = 1e-20 ,
346
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary (permittivity = 2 )),
347
+ )
348
+ # not ok if ABC boudary is crossed
349
+ with pytest .raises (pydantic .ValidationError ):
350
+ _ = td .Simulation (
351
+ center = [0 , 0 , 0 ],
352
+ size = [1 , 1 , 1 ],
353
+ grid_spec = td .GridSpec .auto (
354
+ min_steps_per_wvl = 10 ,
355
+ wavelength = wvl_um ,
356
+ ),
357
+ sources = [],
358
+ structures = [box_crossing_boundary ],
359
+ run_time = 1e-20 ,
360
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
361
+ )
362
+ # edge case when a structure exactly coincides with simulation domain
363
+ _ = td .Simulation (
364
+ center = [0 , 0 , 0 ],
365
+ size = [1 , 1 , 1 ],
366
+ grid_spec = td .GridSpec .auto (
367
+ min_steps_per_wvl = 10 ,
368
+ wavelength = wvl_um ,
369
+ ),
370
+ sources = [],
371
+ structures = [box_crossing_boundary .updated_copy (geometry = td .Box (size = (1 , 1 , 1 )))],
372
+ run_time = 1e-20 ,
373
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
374
+ )
375
+
376
+ # warning for possibly non-uniform custom medium
377
+ with AssertLogLevel (
378
+ "WARNING" , contains_str = "Nonuniform custom medium detected on an 'ABCBoundary'"
379
+ ):
380
+ _ = td .Simulation (
381
+ center = [0 , 0 , 0 ],
382
+ size = [1 , 1 , 1 ],
383
+ grid_spec = td .GridSpec .auto (
384
+ min_steps_per_wvl = 10 ,
385
+ wavelength = wvl_um ,
386
+ ),
387
+ sources = [],
388
+ medium = td .CustomMedium (
389
+ permittivity = td .SpatialDataArray ([[[2 , 3 ]]], coords = dict (x = [0 ], y = [0 ], z = [0 , 1 ]))
390
+ ),
391
+ run_time = 1e-20 ,
392
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
393
+ )
394
+
395
+ # disallow ABC boundaries in zero dimensions
396
+ with pytest .raises (pydantic .ValidationError ):
397
+ _ = td .Simulation (
398
+ center = [0 , 0 , 0 ],
399
+ size = [1 , 1 , 0 ],
400
+ grid_spec = td .GridSpec .auto (
401
+ min_steps_per_wvl = 10 ,
402
+ wavelength = wvl_um ,
403
+ ),
404
+ sources = [],
405
+ structures = [box_crossing_boundary ],
406
+ run_time = 1e-20 ,
407
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
408
+ )
409
+
410
+ # need to define frequence for ModeABCBoundary
411
+ # manually
412
+ _ = td .Simulation (
413
+ center = [0 , 0 , 0 ],
414
+ size = [1 , 1 , 1 ],
415
+ grid_spec = td .GridSpec .auto (
416
+ min_steps_per_wvl = 10 ,
417
+ wavelength = wvl_um ,
418
+ ),
419
+ sources = [],
420
+ run_time = 1e-20 ,
421
+ boundary_spec = td .BoundarySpec .all_sides (
422
+ td .ModeABCBoundary (plane = td .Box (size = (1 , 1 , 0 )), frequency = freq0 )
423
+ ),
424
+ )
425
+ # or at least one source
426
+ _ = td .Simulation (
427
+ center = [0 , 0 , 0 ],
428
+ size = [1 , 1 , 1 ],
429
+ grid_spec = td .GridSpec .auto (
430
+ min_steps_per_wvl = 10 ,
431
+ wavelength = wvl_um ,
432
+ ),
433
+ sources = [mode_source ],
434
+ run_time = 1e-20 ,
435
+ boundary_spec = td .BoundarySpec .all_sides (td .ModeABCBoundary (plane = td .Box (size = (1 , 1 , 0 )))),
436
+ )
437
+ # multiple sources with different central freqs is still ok, but show warning
438
+ with AssertLogLevel (
439
+ "WARNING" , contains_str = "The central frequency of the first source will be used"
440
+ ):
441
+ _ = td .Simulation (
442
+ center = [0 , 0 , 0 ],
443
+ size = [1 , 1 , 1 ],
444
+ grid_spec = td .GridSpec .auto (
445
+ min_steps_per_wvl = 10 ,
446
+ wavelength = wvl_um ,
447
+ ),
448
+ sources = [
449
+ mode_source ,
450
+ mode_source .updated_copy (
451
+ source_time = td .GaussianPulse (freq0 = 2 * freq0 , fwidth = 0.2 * freq0 )
452
+ ),
453
+ ],
454
+ run_time = 1e-20 ,
455
+ boundary_spec = td .BoundarySpec .all_sides (
456
+ td .ModeABCBoundary (plane = td .Box (size = (1 , 1 , 0 )))
457
+ ),
458
+ )
0 commit comments