@@ -185,3 +185,259 @@ 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 abc mode spec
208
+ wvl_um = 1
209
+ freq0 = td .C_0 / wvl_um
210
+ abc_mode_spec = td .ABCModeSpec (
211
+ 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 .ABCModeSpec (
219
+ 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 .ABCModeSpec (
227
+ 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 .ABCModeSpec (
235
+ 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
+
250
+ abc_mode_spec_from_source = td .ABCModeSpec .from_source (mode_source )
251
+
252
+ assert abc_mode_spec == abc_mode_spec_from_source
253
+
254
+ _ = td .ABCBoundary (permittivity = abc_mode_spec )
255
+
256
+ with pytest .raises (pydantic .ValidationError ):
257
+ _ = td .ABCBoundary (permittivity = abc_mode_spec , conductivity = 0.1 )
258
+
259
+ # in Boundary
260
+ _ = td .Boundary (
261
+ minus = td .ABCBoundary (permittivity = 3 ), plus = td .ABCBoundary (permittivity = abc_mode_spec )
262
+ )
263
+ _ = td .Boundary .abc (permittivity = 3 , conductivity = 1e-5 )
264
+
265
+ with pytest .raises (pydantic .ValidationError ):
266
+ _ = td .Boundary (minus = td .Periodic (), plus = td .ABCBoundary (permittivity = abc_mode_spec ))
267
+
268
+ # in Simulation
269
+ _ = td .Simulation (
270
+ center = [0 , 0 , 0 ],
271
+ size = [1 , 1 , 1 ],
272
+ grid_spec = td .GridSpec .auto (
273
+ min_steps_per_wvl = 10 ,
274
+ wavelength = wvl_um ,
275
+ ),
276
+ sources = [],
277
+ run_time = 1e-20 ,
278
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
279
+ )
280
+
281
+ # validate ABC medium is not anisotorpic
282
+ with pytest .raises (pydantic .ValidationError ):
283
+ _ = td .Simulation (
284
+ center = [0 , 0 , 0 ],
285
+ size = [1 , 1 , 1 ],
286
+ grid_spec = td .GridSpec .auto (
287
+ min_steps_per_wvl = 10 ,
288
+ wavelength = wvl_um ,
289
+ ),
290
+ sources = [],
291
+ medium = td .AnisotropicMedium (xx = td .Medium (), yy = td .Medium (), zz = td .Medium ()),
292
+ run_time = 1e-20 ,
293
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
294
+ )
295
+
296
+ # validate homogeneous medium when permittivity=None, that is, automatic detection
297
+ box_crossing_boundary = td .Structure (
298
+ geometry = td .Box (size = (0.3 , 0.2 , td .inf )),
299
+ medium = td .Medium (permittivity = 2 ),
300
+ )
301
+ # ok if ABC boundary is not crossed
302
+ _ = td .Simulation (
303
+ center = [0 , 0 , 0 ],
304
+ size = [1 , 1 , 1 ],
305
+ grid_spec = td .GridSpec .auto (
306
+ min_steps_per_wvl = 10 ,
307
+ wavelength = wvl_um ,
308
+ ),
309
+ sources = [],
310
+ structures = [box_crossing_boundary ],
311
+ run_time = 1e-20 ,
312
+ boundary_spec = td .BoundarySpec (
313
+ x = td .Boundary .abc (),
314
+ y = td .Boundary .abc (),
315
+ z = td .Boundary .pml (),
316
+ ),
317
+ )
318
+ # or if we override manually
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 .all_sides (td .ABCBoundary (permittivity = 2 )),
330
+ )
331
+ # not ok if ABC boudary is crossed
332
+ with pytest .raises (pydantic .ValidationError ):
333
+ _ = td .Simulation (
334
+ center = [0 , 0 , 0 ],
335
+ size = [1 , 1 , 1 ],
336
+ grid_spec = td .GridSpec .auto (
337
+ min_steps_per_wvl = 10 ,
338
+ wavelength = wvl_um ,
339
+ ),
340
+ sources = [],
341
+ structures = [box_crossing_boundary ],
342
+ run_time = 1e-20 ,
343
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
344
+ )
345
+ # edge case when a structure exactly coincides with simulation domain
346
+ _ = td .Simulation (
347
+ center = [0 , 0 , 0 ],
348
+ size = [1 , 1 , 1 ],
349
+ grid_spec = td .GridSpec .auto (
350
+ min_steps_per_wvl = 10 ,
351
+ wavelength = wvl_um ,
352
+ ),
353
+ sources = [],
354
+ structures = [box_crossing_boundary .updated_copy (geometry = td .Box (size = (1 , 1 , 1 )))],
355
+ run_time = 1e-20 ,
356
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
357
+ )
358
+
359
+ # warning for possibly non-uniform custom medium
360
+ with AssertLogLevel (
361
+ "WARNING" , contains_str = "Nonuniform custom medium detected on an 'ABCBoundary'"
362
+ ):
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
+ medium = td .CustomMedium (
372
+ permittivity = td .SpatialDataArray ([[[2 , 3 ]]], coords = dict (x = [0 ], y = [0 ], z = [0 , 1 ]))
373
+ ),
374
+ run_time = 1e-20 ,
375
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
376
+ )
377
+
378
+ # disallow ABC boundaries in zero dimensions
379
+ with pytest .raises (pydantic .ValidationError ):
380
+ _ = td .Simulation (
381
+ center = [0 , 0 , 0 ],
382
+ size = [1 , 1 , 0 ],
383
+ grid_spec = td .GridSpec .auto (
384
+ min_steps_per_wvl = 10 ,
385
+ wavelength = wvl_um ,
386
+ ),
387
+ sources = [],
388
+ structures = [box_crossing_boundary ],
389
+ run_time = 1e-20 ,
390
+ boundary_spec = td .BoundarySpec .all_sides (td .ABCBoundary ()),
391
+ )
392
+
393
+ # need to define frequence for ABCModeSpec
394
+ # manually
395
+ _ = td .Simulation (
396
+ center = [0 , 0 , 0 ],
397
+ size = [1 , 1 , 1 ],
398
+ grid_spec = td .GridSpec .auto (
399
+ min_steps_per_wvl = 10 ,
400
+ wavelength = wvl_um ,
401
+ ),
402
+ sources = [],
403
+ run_time = 1e-20 ,
404
+ boundary_spec = td .BoundarySpec .all_sides (
405
+ td .ABCBoundary (permittivity = td .ABCModeSpec (size = (1 , 1 , 0 ), frequency = freq0 ))
406
+ ),
407
+ )
408
+ # or at least one source
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 = [mode_source ],
417
+ run_time = 1e-20 ,
418
+ boundary_spec = td .BoundarySpec .all_sides (
419
+ td .ABCBoundary (permittivity = td .ABCModeSpec (size = (1 , 1 , 0 )))
420
+ ),
421
+ )
422
+ # multiple sources with different central freqs is still ok, but show warning
423
+ with AssertLogLevel (
424
+ "WARNING" , contains_str = "The central frequency of the first source will be used"
425
+ ):
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 = [
434
+ mode_source ,
435
+ mode_source .updated_copy (
436
+ source_time = td .GaussianPulse (freq0 = 2 * freq0 , fwidth = 0.2 * freq0 )
437
+ ),
438
+ ],
439
+ run_time = 1e-20 ,
440
+ boundary_spec = td .BoundarySpec .all_sides (
441
+ td .ABCBoundary (permittivity = td .ABCModeSpec (size = (1 , 1 , 0 )))
442
+ ),
443
+ )
0 commit comments