@@ -1052,8 +1052,9 @@ class LayerRefinementSpec(Box):
1052
1052
1053
1053
dl_min_from_gap_width : bool = pd .Field (
1054
1054
True ,
1055
- title = "Set ``dl_min`` from Gap Width" ,
1056
- description = "Take into account autodetected minimal PEC gap width when determining ``dl_min``." ,
1055
+ title = "Set ``dl_min`` from Estimated Gap Width" ,
1056
+ description = "Take into account autodetected minimal PEC gap width when determining ``dl_min``. "
1057
+ "This only applies if ``dl_min`` in ``AutoGrid`` specification is not set." ,
1057
1058
)
1058
1059
1059
1060
@pd .validator ("axis" , always = True )
@@ -1554,7 +1555,7 @@ def _override_structures_along_axis(
1554
1555
return override_structures
1555
1556
1556
1557
def _find_vertical_intersections (
1557
- self , grid_x_coords , grid_y_coords , poly_vertices
1558
+ self , grid_x_coords , grid_y_coords , poly_vertices , boundary
1558
1559
) -> Tuple [List [Tuple [int , int ]], List [float ]]:
1559
1560
"""Detect intersection points of single polygon and vertical grid lines."""
1560
1561
@@ -1587,7 +1588,9 @@ def _find_vertical_intersections(
1587
1588
continue
1588
1589
1589
1590
# sort vertices in ascending order to make treatmeant unifrom
1591
+ reverse = False
1590
1592
if ind_beg > ind_end :
1593
+ reverse = True
1591
1594
ind_beg , ind_end , v_beg , v_end = ind_end , ind_beg , v_end , v_beg
1592
1595
1593
1596
# x coordinates are simply x coordinates of intersected vertical grid lines
@@ -1623,6 +1626,12 @@ def _find_vertical_intersections(
1623
1626
grid_y_coords [cell_js + 1 ] - grid_y_coords [cell_js ]
1624
1627
)
1625
1628
1629
+ # preserve uniform ordering along perimeter of the polygon
1630
+ if reverse :
1631
+ cell_is = cell_is [::- 1 ]
1632
+ cell_js = cell_js [::- 1 ]
1633
+ dy = dy [::- 1 ]
1634
+
1626
1635
# record info
1627
1636
cells_ij .append (np .transpose ([cell_is , cell_js ]))
1628
1637
cells_dy .append (dy )
@@ -1665,39 +1674,55 @@ def _find_vertical_intersections(
1665
1674
points_to_duplicate_near_zero = cells_ij [close_to_zero ]
1666
1675
points_to_duplicate_near_one = cells_ij [close_to_one ]
1667
1676
1677
+ # if we go beyond simulation domain boundary, either ignore
1678
+ # or wrap periodically depending on boundary conditions
1679
+ cells_ij_zero_side = points_to_duplicate_near_zero - np .array ([0 , 1 ])
1680
+ cells_zero_side_out = cells_ij_zero_side [:, 1 ] == - 1
1681
+ if boundary [0 ] == "periodic" :
1682
+ cells_ij_zero_side [cells_zero_side_out , 1 ] = len (grid_y_coords ) - 2
1683
+ else :
1684
+ cells_ij_zero_side = cells_ij_zero_side [cells_zero_side_out == 0 ]
1685
+
1686
+ cells_ij_one_side = points_to_duplicate_near_one + np .array ([0 , 1 ])
1687
+ cells_one_side_out = cells_ij_one_side [:, 1 ] == len (grid_y_coords ) - 1
1688
+ if boundary [1 ] == "periodic" :
1689
+ cells_ij_one_side [cells_one_side_out , 1 ] = 0
1690
+ else :
1691
+ cells_ij_one_side = cells_ij_one_side [cells_one_side_out == 0 ]
1692
+
1668
1693
cells_ij = np .concatenate (
1669
1694
[
1670
1695
cells_ij ,
1671
- points_to_duplicate_near_zero - np . array ([ 0 , 1 ]) ,
1672
- points_to_duplicate_near_one + np . array ([ 0 , 1 ]) ,
1696
+ cells_ij_zero_side ,
1697
+ cells_ij_one_side ,
1673
1698
]
1674
1699
)
1675
1700
cells_dy = np .concatenate (
1676
1701
[
1677
1702
cells_dy ,
1678
- np .ones (len (points_to_duplicate_near_zero )),
1679
- np .zeros (len (points_to_duplicate_near_one )),
1703
+ np .ones (len (cells_ij_zero_side )),
1704
+ np .zeros (len (cells_ij_one_side )),
1680
1705
]
1681
1706
)
1682
1707
1683
1708
return cells_ij , cells_dy
1684
1709
1685
1710
def _process_poly (
1686
- self , grid_x_coords , grid_y_coords , poly_vertices
1711
+ self , grid_x_coords , grid_y_coords , poly_vertices , boundaries
1687
1712
) -> Tuple [List [Tuple [int , int ]], List [float ], List [Tuple [int , int ]], List [float ]]:
1688
1713
"""Detect intersection points of single polygon and grid lines."""
1689
1714
1690
1715
# find cells that contain intersections of vertical grid lines
1691
1716
# and relative locations of those intersections (along y axis)
1692
1717
v_cells_ij , v_cells_dy = self ._find_vertical_intersections (
1693
- grid_x_coords , grid_y_coords , poly_vertices
1718
+ grid_x_coords , grid_y_coords , poly_vertices , boundaries [ 1 ]
1694
1719
)
1695
1720
1696
1721
# find cells that contain intersections of horizontal grid lines
1697
1722
# and relative locations of those intersections (along x axis)
1698
1723
# reuse the same command but flip dimensions
1699
1724
h_cells_ij , h_cells_dx = self ._find_vertical_intersections (
1700
- grid_y_coords , grid_x_coords , np .flip (poly_vertices , axis = 1 )
1725
+ grid_y_coords , grid_x_coords , np .flip (poly_vertices , axis = 1 ), boundaries [ 0 ]
1701
1726
)
1702
1727
if len (h_cells_ij ) > 0 :
1703
1728
# flip dimensions back
@@ -1706,7 +1731,7 @@ def _process_poly(
1706
1731
return v_cells_ij , v_cells_dy , h_cells_ij , h_cells_dx
1707
1732
1708
1733
def _process_slice (
1709
- self , x , y , merged_geos
1734
+ self , x , y , merged_geos , boundaries
1710
1735
) -> Tuple [List [Tuple [int , int ]], List [float ], List [Tuple [int , int ]], List [float ]]:
1711
1736
"""Detect intersection points of geometries boundaries and grid lines."""
1712
1737
@@ -1720,6 +1745,27 @@ def _process_slice(
1720
1745
# relative locations of those intersections (along x axis)
1721
1746
h_cells_dx = []
1722
1747
1748
+ # for PEC and PMC boundary - treat them as PEC structure
1749
+ # so that gaps are resolved near boundaries if any
1750
+ nx = len (x )
1751
+ ny = len (y )
1752
+
1753
+ if boundaries [0 ][0 ] == "pec/pmc" :
1754
+ h_cells_ij .append (np .transpose ([np .zeros (ny ), np .arange (ny )]).astype (int ))
1755
+ h_cells_dx .append (np .zeros (ny ))
1756
+
1757
+ if boundaries [0 ][1 ] == "pec/pmc" :
1758
+ h_cells_ij .append (np .transpose ([(nx - 2 ) * np .ones (ny ), np .arange (ny )]).astype (int ))
1759
+ h_cells_dx .append (np .ones (ny ))
1760
+
1761
+ if boundaries [1 ][0 ] == "pec/pmc" :
1762
+ v_cells_ij .append (np .transpose ([np .arange (nx ), np .zeros (nx )]).astype (int ))
1763
+ v_cells_dy .append (np .zeros (nx , dtype = int ))
1764
+
1765
+ if boundaries [1 ][1 ] == "pec/pmc" :
1766
+ v_cells_ij .append (np .transpose ([np .arange (nx ), (ny - 2 ) * np .ones (nx )]).astype (int ))
1767
+ v_cells_dy .append (np .ones (nx ))
1768
+
1723
1769
# loop over all shapes
1724
1770
for mat , shapes in merged_geos :
1725
1771
if not mat .is_pec :
@@ -1736,7 +1782,7 @@ def _process_slice(
1736
1782
# 1. relative locations of those intersections along y axis
1737
1783
# 2. cells that contain intersections of horizontal grid lines
1738
1784
# 3. relative locations of those intersections along x axis
1739
- data = self ._process_poly (x , y , np .array (poly .exterior .coords )[:- 1 ])
1785
+ data = self ._process_poly (x , y , np .array (poly .exterior .coords )[:- 1 ], boundaries )
1740
1786
1741
1787
if len (data [0 ]) > 0 :
1742
1788
v_cells_ij .append (data [0 ])
@@ -1748,7 +1794,7 @@ def _process_slice(
1748
1794
1749
1795
# in case the polygon has holes
1750
1796
for poly_inner in poly .interiors :
1751
- data = self ._process_poly (x , y , np .array (poly_inner .coords )[:- 1 ])
1797
+ data = self ._process_poly (x , y , np .array (poly_inner .coords )[:- 1 ], boundaries )
1752
1798
if len (data [0 ]) > 0 :
1753
1799
v_cells_ij .append (data [0 ])
1754
1800
v_cells_dy .append (data [1 ])
@@ -1845,27 +1891,82 @@ def _generate_horizontal_snapping_lines(
1845
1891
1846
1892
return snapping_lines_y , min_gap_width
1847
1893
1848
- def _resolve_gaps (self , structures : List , grid : Grid ) -> Tuple [List [CoordinateOptional ], float ]:
1894
+ def _resolve_gaps (
1895
+ self , structures : List [Structure ], grid : Grid , boundaries : Tuple , center , size
1896
+ ) -> Tuple [List [CoordinateOptional ], float ]:
1849
1897
"""Detect underresolved gaps and place snapping lines in them. Also return the detected minimal gap width."""
1850
1898
1851
- # get merged pec structures on plane
1852
- # note that we expect this function to also convert all LossyMetal's into PEC
1853
- plane_slice = CornerFinderSpec ._merged_pec_on_plane (
1854
- coord = self .center_axis , normal_axis = self .axis , structure_list = structures
1855
- )
1856
-
1857
1899
# get x and y coordinates of grid lines
1858
1900
_ , tan_dims = Box .pop_axis ([0 , 1 , 2 ], self .axis )
1859
1901
x = grid .boundaries .to_list [tan_dims [0 ]]
1860
1902
y = grid .boundaries .to_list [tan_dims [1 ]]
1861
1903
1904
+ _ , boundaries_tan = Box .pop_axis (boundaries , self .axis )
1905
+
1906
+ # restrict to the size of layer spec
1907
+ rmin , rmax = self .bounds
1908
+ _ , rmin = Box .pop_axis (rmin , self .axis )
1909
+ _ , rmax = Box .pop_axis (rmax , self .axis )
1910
+
1911
+ new_coords = []
1912
+ new_boundaries = []
1913
+ for coord , cmin , cmax , bdry in zip ([x , y ], rmin , rmax , boundaries_tan ):
1914
+ if cmax <= coord [0 ] or cmin >= coord [- 1 ]:
1915
+ return [], inf
1916
+ else :
1917
+ if cmin < coord [0 ]:
1918
+ ind_min = 0
1919
+ else :
1920
+ ind_min = max (0 , np .argmax (coord >= cmin ) - 1 )
1921
+
1922
+ if cmax > coord [- 1 ]:
1923
+ ind_max = len (coord ) - 1
1924
+ else :
1925
+ ind_max = np .argmax (coord >= cmax )
1926
+
1927
+ if ind_min >= ind_max :
1928
+ return [], inf
1929
+
1930
+ new_coords .append (coord [ind_min : (ind_max + 1 )])
1931
+ # ignore boundary conditions if we are not touching them
1932
+ new_boundaries .append (
1933
+ [
1934
+ None if ind_min > 0 else bdry [0 ],
1935
+ None if ind_max < len (coord ) - 1 else bdry [1 ],
1936
+ ]
1937
+ )
1938
+
1939
+ x , y = new_coords
1940
+
1941
+ # restrict size of the plane where pec polygons are found in case of periodic boundary conditions
1942
+ # this is to make sure gaps across periodic boundary conditions are resolved
1943
+ # (if there is a PEC structure going into periodic boundary, now it will generate a grid line
1944
+ # intersection next to that boundary and it will be propagated to the other side)
1945
+ restricted_size_tan = [
1946
+ s - GAP_MESHING_TOL / 2 if b [0 ] == "periodic" else inf
1947
+ for b , s in zip (new_boundaries , size )
1948
+ ]
1949
+ restricted_size = Box .unpop_axis (size [self .axis ], restricted_size_tan , self .axis )
1950
+
1951
+ # get merged pec structures on plane
1952
+ # note that we expect this function to also convert all LossyMetal's into PEC
1953
+ plane_slice = CornerFinderSpec ._merged_pec_on_plane (
1954
+ coord = self .center_axis ,
1955
+ normal_axis = self .axis ,
1956
+ structure_list = structures ,
1957
+ center = center ,
1958
+ size = restricted_size ,
1959
+ )
1960
+
1862
1961
# find intersections of pec polygons with grid lines
1863
1962
# specifically:
1864
1963
# 0. cells that contain intersections of vertical grid lines
1865
1964
# 1. relative locations of those intersections along y axis
1866
1965
# 2. cells that contain intersections of horizontal grid lines
1867
1966
# 3. relative locations of those intersections along x axis
1868
- v_cells_ij , v_cells_dy , h_cells_ij , h_cells_dx = self ._process_slice (x , y , plane_slice )
1967
+ v_cells_ij , v_cells_dy , h_cells_ij , h_cells_dx = self ._process_slice (
1968
+ x , y , plane_slice , new_boundaries
1969
+ )
1869
1970
1870
1971
# generate horizontal snapping lines
1871
1972
snapping_lines_y , min_gap_width_along_y = self ._generate_horizontal_snapping_lines (
@@ -2289,6 +2390,11 @@ def make_grid(
2289
2390
lumped_elements : List [LumpedElementType ] = (),
2290
2391
internal_override_structures : List [MeshOverrideStructure ] = None ,
2291
2392
internal_snapping_points : List [CoordinateOptional ] = None ,
2393
+ boundary_types : Tuple [Tuple [str , str ], Tuple [str , str ], Tuple [str , str ]] = [
2394
+ [None , None ],
2395
+ [None , None ],
2396
+ [None , None ],
2397
+ ],
2292
2398
) -> Grid :
2293
2399
"""Make the entire simulation grid based on some simulation parameters.
2294
2400
@@ -2313,6 +2419,9 @@ def make_grid(
2313
2419
If `None`, recomputes internal override structures.
2314
2420
internal_snapping_points : List[CoordinateOptional]
2315
2421
If `None`, recomputes internal snapping points.
2422
+ boundary_types : Tuple[Tuple[str, str], Tuple[str, str], Tuple[str, str]] = [[None, None], [None, None], [None, None]]
2423
+ Type of boundary conditions along each dimension: "pec/pmc", "periodic", or
2424
+ None for any other. This is relevant only for gap meshing.
2316
2425
2317
2426
Returns
2318
2427
-------
@@ -2343,6 +2452,11 @@ def _make_grid_and_snapping_lines(
2343
2452
lumped_elements : List [LumpedElementType ] = (),
2344
2453
internal_override_structures : List [MeshOverrideStructure ] = None ,
2345
2454
internal_snapping_points : List [CoordinateOptional ] = None ,
2455
+ boundary_types : Tuple [Tuple [str , str ], Tuple [str , str ], Tuple [str , str ]] = [
2456
+ [None , None ],
2457
+ [None , None ],
2458
+ [None , None ],
2459
+ ],
2346
2460
) -> Tuple [Grid , List [CoordinateOptional ]]:
2347
2461
"""Make the entire simulation grid based on some simulation parameters.
2348
2462
Also return snappiung point resulted from iterative gap meshing.
@@ -2368,11 +2482,14 @@ def _make_grid_and_snapping_lines(
2368
2482
If `None`, recomputes internal override structures.
2369
2483
internal_snapping_points : List[CoordinateOptional]
2370
2484
If `None`, recomputes internal snapping points.
2485
+ boundary_types : Tuple[Tuple[str, str], Tuple[str, str], Tuple[str, str]] = [[None, None], [None, None], [None, None]]
2486
+ Type of boundary conditions along each dimension: "pec/pmc", "periodic", or
2487
+ None for any other. This is relevant only for gap meshing.
2371
2488
2372
2489
Returns
2373
2490
-------
2374
- Grid:
2375
- Entire simulation grid.
2491
+ Tuple[ Grid, List[CoordinateOptional]] :
2492
+ Entire simulation grid and snapping points generated during iterative gap meshing .
2376
2493
"""
2377
2494
2378
2495
old_grid = self ._make_grid_one_iteration (
@@ -2386,6 +2503,8 @@ def _make_grid_and_snapping_lines(
2386
2503
internal_snapping_points = internal_snapping_points ,
2387
2504
)
2388
2505
2506
+ sim_geometry = structures [0 ].geometry
2507
+
2389
2508
snapping_lines = []
2390
2509
if len (self .layer_refinement_specs ) > 0 :
2391
2510
num_iters = max (
@@ -2398,7 +2517,11 @@ def _make_grid_and_snapping_lines(
2398
2517
for layer_spec in self .layer_refinement_specs :
2399
2518
if layer_spec .gap_meshing_iters > ind :
2400
2519
one_layer_snapping_lines , gap_width = layer_spec ._resolve_gaps (
2401
- structures , old_grid
2520
+ structures ,
2521
+ old_grid ,
2522
+ boundary_types ,
2523
+ center = sim_geometry .center ,
2524
+ size = sim_geometry .size ,
2402
2525
)
2403
2526
new_snapping_lines = new_snapping_lines + one_layer_snapping_lines
2404
2527
if layer_spec .dl_min_from_gap_width :
0 commit comments