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