@@ -1539,36 +1539,66 @@ def _find_vertical_intersections(self, x, y, vertices):
1539
1539
1540
1540
cells_ij = []
1541
1541
cells_dy = []
1542
- h_inds = np .argmax (x [:, None ] > vertices [None , :, 0 ], axis = 0 )
1543
- h_inds [vertices [:, 0 ] > x [- 1 ]] = len (x )
1542
+
1543
+ # for each polygon vertex find the index of the first grid line on the right
1544
+ grid_lines_on_right = np .argmax (x [:, None ] > vertices [None , :, 0 ], axis = 0 )
1545
+ grid_lines_on_right [vertices [:, 0 ] > x [- 1 ]] = len (x )
1546
+ # once we know these indices then we can find grid lines intersected by the i-th
1547
+ # segment of the polygon as
1548
+ # [grid_lines_on_right[i], grid_lines_on_right[i+1]) for grid_lines_on_right[i] > grid_lines_on_right[i+1]
1549
+ # or
1550
+ # [grid_lines_on_right[i+1], grid_lines_on_right[i]) for grid_lines_on_right[i] < grid_lines_on_right[i+1]
1551
+
1552
+ # loop over segments of the polygon and determine in which cells and where exactly they cross grid lines
1553
+ # v_beg and v_end are the starting and ending points of the segment
1554
+ # ind_beg and ind_end are starting and ending indices of vertical grid lines that the segment intersects
1555
+ # as described above
1544
1556
for ind_beg , ind_end , v_beg , v_end in zip (
1545
- h_inds , np .roll (h_inds , - 1 ), vertices , np .roll (vertices , axis = 0 , shift = - 1 )
1557
+ grid_lines_on_right ,
1558
+ np .roll (grid_lines_on_right , - 1 ),
1559
+ vertices ,
1560
+ np .roll (vertices , axis = 0 , shift = - 1 ),
1546
1561
):
1547
- # sort vertices in ascending order
1562
+ # no intersections
1563
+ if ind_end == ind_beg :
1564
+ continue
1565
+
1566
+ # sort vertices in ascending order to make treatmeant unifrom
1548
1567
if ind_beg > ind_end :
1549
1568
ind_beg , ind_end , v_beg , v_end = ind_end , ind_beg , v_end , v_beg
1550
1569
1551
- if ind_end > ind_beg :
1552
- # x coordinates are simply grid x points that are covered by each segment
1553
- h_x = x [ind_beg :ind_end ]
1570
+ # x coordinates are simply x coordinates of intersected vertical grid lines
1571
+ intersections_x = x [ind_beg :ind_end ]
1572
+
1573
+ # y coordinates can be found from line equation
1574
+ intersections_y = v_beg [1 ] + (v_end [1 ] - v_beg [1 ]) / (v_end [0 ] - v_beg [0 ]) * (
1575
+ intersections_x - v_beg [0 ]
1576
+ )
1554
1577
1555
- # y coordinates can be found from line equation
1556
- h_y = v_beg [1 ] + (v_end [1 ] - v_beg [1 ]) / (v_end [0 ] - v_beg [0 ]) * (h_x - v_beg [0 ])
1578
+ # however, some of the vertical lines might be crossed
1579
+ # outside of computational domain
1580
+ # so we need to see which ones are actually inside along y axis
1581
+ inds_inside_grid = np .logical_and (intersections_y >= y [0 ], intersections_y <= y [- 1 ])
1557
1582
1558
- # however, we need to see which ones are actually inside along y axis
1559
- inds_inside_grid = np .logical_and (h_y >= y [0 ], h_y <= y [- 1 ])
1583
+ intersections_y = intersections_y [inds_inside_grid ]
1560
1584
1561
- h_y = h_y [ inds_inside_grid ]
1585
+ # find i and j indices of cells which contain these intersections
1562
1586
1563
- # find cell j coordinates which contain these intersections
1564
- cell_is = np .arange (ind_beg , ind_end )[inds_inside_grid ]
1565
- cell_js = np .searchsorted (y , h_y ) - 1
1587
+ # i indices are simply indices of crossed vertical grid lines
1588
+ cell_is = np .arange (ind_beg , ind_end )[inds_inside_grid ]
1566
1589
1567
- # find local dy
1568
- dy = h_y - y [cell_js ]
1590
+ # j indices can be computed by finding insertion indices
1591
+ # of y coordinates of intersection points into array of y coordinates
1592
+ # of the grid lines that preserve sorting
1593
+ cell_js = np .searchsorted (y , intersections_y ) - 1
1569
1594
1570
- cells_ij .append (np .transpose ([cell_is , cell_js ]))
1571
- cells_dy .append (dy )
1595
+ # find local dy, that is, the distance between the intersection point
1596
+ # and the bottom edge of the cell
1597
+ dy = intersections_y - y [cell_js ]
1598
+
1599
+ # record info
1600
+ cells_ij .append (np .transpose ([cell_is , cell_js ]))
1601
+ cells_dy .append (dy )
1572
1602
1573
1603
if len (cells_ij ) > 0 :
1574
1604
cells_ij = np .concatenate (cells_ij )
@@ -1580,26 +1610,47 @@ def _find_vertical_intersections(self, x, y, vertices):
1580
1610
1581
1611
def _process_poly (self , x , y , vertices ):
1582
1612
"""Detect intersection points of single polygon and grid lines."""
1613
+
1614
+ # find cells that contain intersections of vertical grid lines
1615
+ # and locations of those intersections (along y axis)
1583
1616
v_cells_ij , v_cells_dy = self ._find_vertical_intersections (x , y , vertices )
1617
+
1618
+ # find cells that contain intersections of horizontal grid lines
1619
+ # and locations of those intersections (along x axis)
1584
1620
# reuse the same command but flip dimensions
1585
- h_cells_ij , h_cells_dx = self ._find_vertical_intersections (y , x , vertices [:, [ 1 , 0 ]] )
1621
+ h_cells_ij , h_cells_dx = self ._find_vertical_intersections (y , x , np . flip ( vertices , axis = 1 ) )
1586
1622
if len (h_cells_ij ) > 0 :
1587
1623
h_cells_ij = np .roll (h_cells_ij , axis = 1 , shift = 1 )
1624
+
1588
1625
return v_cells_ij , v_cells_dy , h_cells_ij , h_cells_dx
1589
1626
1590
1627
def _process_slice (self , x , y , merged_geos ):
1591
1628
"""Detect intersection points of geometries boundaries and grid lines."""
1629
+
1630
+ # cells that contain intersections of vertical grid lines
1592
1631
v_cells_ij = []
1632
+ # locations of those intersections (along y axis)
1593
1633
v_cells_dy = []
1634
+
1635
+ # cells that contain intersections of horizontal grid lines
1594
1636
h_cells_ij = []
1637
+ # locations of those intersections (along x axis)
1595
1638
h_cells_dx = []
1596
1639
1640
+ # loop over all shapes
1597
1641
for mat , shapes in merged_geos :
1598
1642
if not mat .is_pec :
1599
1643
continue
1600
1644
polygon_list = ClipOperation .to_polygon_list (shapes )
1601
1645
for poly in polygon_list :
1602
1646
poly = poly .normalize ().buffer (0 )
1647
+
1648
+ # find intersections of a polygon with grid lines
1649
+ # specifically:
1650
+ # 0. cells that contain intersections of vertical grid lines
1651
+ # 1. locations of those intersections along y axis
1652
+ # 2. cells that contain intersections of horizontal grid lines
1653
+ # 3. locations of those intersections along x axis
1603
1654
data = self ._process_poly (x , y , np .array (poly .exterior .coords )[:- 1 ])
1604
1655
1605
1656
if len (data [0 ]) > 0 :
@@ -1643,48 +1694,88 @@ def _resolve_gaps(self, structures: List, grid: Grid):
1643
1694
coord = self .center_axis , normal_axis = self .axis , structure_list = structures
1644
1695
)
1645
1696
1697
+ # get x and y coordinates of grid lines
1646
1698
_ , tan_dims = Box .pop_axis ([0 , 1 , 2 ], self .axis )
1647
1699
x = grid .boundaries .to_list [tan_dims [0 ]]
1648
1700
y = grid .boundaries .to_list [tan_dims [1 ]]
1649
1701
1650
- # find in which cells pec boundaries cross grid lines and locations of intersections
1702
+ # find intersections of pec polygons with grid lines
1703
+ # specifically:
1704
+ # 0. cells that contain intersections of vertical grid lines
1705
+ # 1. locations of those intersections along y axis
1706
+ # 2. cells that contain intersections of horizontal grid lines
1707
+ # 3. locations of those intersections along x axis
1651
1708
v_cells_ij , v_cells_dy , h_cells_ij , h_cells_dx = self ._process_slice (x , y , plane_slice )
1652
1709
1653
1710
detected_gap_width = inf
1654
1711
1655
- # count crossings of vertical grid lines
1712
+ # generate horizontal snapping lines
1656
1713
snapping_lines_y = []
1657
1714
if len (v_cells_ij ) > 0 :
1658
- # add number of intersections
1715
+ # count intersections of vertical grid lines in each cell
1659
1716
ind_unique , counts = np .unique (
1660
1717
v_cells_ij [:, 0 ] * len (y ) + v_cells_ij [:, 1 ], return_counts = True
1661
1718
)
1719
+ # when we count intersections we use linearized 2d index because we really
1720
+ # need to count intersections in each cell separately
1721
+
1722
+ # but when we need to decide about refinement, due to cartesian nature of grid
1723
+ # we will need to consider all cells with a given j index at a time
1724
+
1725
+ # so, let's compute j index for each cell in the inique list
1662
1726
ind_unique_j = ind_unique % len (y )
1727
+
1728
+ # loop through all j rows that contain intersections
1663
1729
for ind_j in np .unique (ind_unique_j ):
1730
+ # we need to refine between two grid lines corresponding to index j
1731
+ # if at least one cell with given j contains > 1 intersections
1664
1732
max_count = np .max (counts [ind_unique_j == ind_j ])
1665
- # for those where we have more than one crossing, place a snapping line in the middle
1666
1733
if max_count > 1 :
1734
+ # This could use some improvement in future:
1735
+ # currently we just calculate the position of the lowest intersection
1736
+ # and the highest intersection in row j
1737
+ # and just place a snapping line in between.
1667
1738
min_dy = np .min (v_cells_dy [v_cells_ij [:, 1 ] == ind_j ])
1668
1739
max_dy = np .max (v_cells_dy [v_cells_ij [:, 1 ] == ind_j ])
1669
1740
snapping_lines_y .append (y [ind_j ] + 0.5 * (min_dy + max_dy ))
1670
1741
detected_gap_width = min (detected_gap_width , max_dy - min_dy )
1742
+ # This is expected to work fine when we have a simple gap/strip passing
1743
+ # through a number of cells. But not optimal if we have slanted/curved
1744
+ # boundaries and/or more than 2 intersections.
1671
1745
1672
- # count crossings of horizontal grid lines
1746
+ # generate vertical snapping lines
1673
1747
snapping_lines_x = []
1674
1748
if len (h_cells_ij ) > 0 :
1675
- # add number of intersections
1749
+ # count intersections of horizontal grid lines in each cell
1676
1750
ind_unique , counts = np .unique (
1677
1751
h_cells_ij [:, 0 ] * len (y ) + h_cells_ij [:, 1 ], return_counts = True
1678
1752
)
1753
+ # when we count intersections we use linearized 2d index because we really
1754
+ # need to count intersections in each cell separately
1755
+
1756
+ # but when we need to decide about refinement, due to cartesian nature of grid
1757
+ # we will need to consider all cells with a given i index at a time
1758
+
1759
+ # so, let's compute i index for each cell in the inique list
1679
1760
ind_unique_i = ind_unique // len (y )
1761
+
1762
+ # loop through all i columns that contain intersections
1680
1763
for ind_i in np .unique (ind_unique_i ):
1764
+ # we need to refine between two grid lines corresponding to index i
1765
+ # if at least one cell with given i contains > 1 intersections
1681
1766
max_count = np .max (counts [ind_unique_i == ind_i ])
1682
- # for those where we have more than one crossing, place a snapping line in the middle
1683
1767
if max_count > 1 :
1768
+ # This could use some improvement in future:
1769
+ # currently we just calculate the position of the leftmost intersection
1770
+ # and the rightmost intersection in column i
1771
+ # and just place a snapping line in between.
1684
1772
min_dx = np .min (h_cells_dx [h_cells_ij [:, 0 ] == ind_i ])
1685
1773
max_dx = np .max (h_cells_dx [h_cells_ij [:, 0 ] == ind_i ])
1686
1774
snapping_lines_x .append (x [ind_i ] + 0.5 * (min_dx + max_dx ))
1687
1775
detected_gap_width = min (detected_gap_width , max_dx - min_dx )
1776
+ # This is expected to work fine when we have a simple gap/strip passing
1777
+ # through a number of cells. But not optimal if we have slanted/curved
1778
+ # boundaries and/or more than 2 intersections.
1688
1779
1689
1780
return [Box .unpop_axis (X , (None , None ), axis = tan_dims [0 ]) for X in snapping_lines_x ] + [
1690
1781
Box .unpop_axis (Y , (None , None ), axis = tan_dims [1 ]) for Y in snapping_lines_y
0 commit comments