|
29 | 29 | from tidy3d.plugins.smatrix.utils import s_to_z, validate_square_matrix |
30 | 30 |
|
31 | 31 | from ...utils import run_emulated |
32 | | -from .terminal_component_modeler_def import make_coaxial_component_modeler, make_component_modeler |
| 32 | +from .terminal_component_modeler_def import ( |
| 33 | + make_coaxial_component_modeler, |
| 34 | + make_component_modeler, |
| 35 | + make_differential_stripline_modeler, |
| 36 | +) |
33 | 37 |
|
34 | 38 | mm = 1e3 |
35 | 39 |
|
@@ -1595,3 +1599,182 @@ def test_S_parameter_deembedding(monkeypatch, tmp_path): |
1595 | 1599 | S_dmb_shortcut = modeler_data_LP.smatrix_deembedded(port_shifts=port_shifts_LP) |
1596 | 1600 | assert not np.allclose(S_dmb.data.values, s_matrix_LP.data.values) |
1597 | 1601 | assert np.allclose(S_dmb_shortcut.data.values, S_dmb.data.values) |
| 1602 | + |
| 1603 | + |
| 1604 | +def test_wave_port_extrusion_coaxial(): |
| 1605 | + """Test extrusion of structures wave port absorber.""" |
| 1606 | + |
| 1607 | + # define a terminal component modeler |
| 1608 | + tcm = make_coaxial_component_modeler( |
| 1609 | + length=100000, |
| 1610 | + port_types=(WavePort, WavePort), |
| 1611 | + ) |
| 1612 | + |
| 1613 | + # update ports and set flag to extrude structures |
| 1614 | + ports = tcm.ports |
| 1615 | + port_1 = ports[0] |
| 1616 | + port_2 = ports[1] |
| 1617 | + port_1 = port_1.updated_copy(center=(0, 0, -50000), extrude_structures=True) |
| 1618 | + |
| 1619 | + # test that structure extrusion requires an internal absorber (should raise ValidationError) |
| 1620 | + with pytest.raises(pd.ValidationError): |
| 1621 | + _ = port_2.updated_copy(center=(0, 0, 50000), extrude_structures=True, absorber=False) |
| 1622 | + |
| 1623 | + # define a valid waveport |
| 1624 | + port_2 = port_2.updated_copy(center=(0, 0, 50000), extrude_structures=True) |
| 1625 | + |
| 1626 | + # update component modeler |
| 1627 | + tcm = tcm.updated_copy(ports=[port_1, port_2]) |
| 1628 | + |
| 1629 | + # generate simulations from component modeler |
| 1630 | + sim = tcm.base_sim |
| 1631 | + |
| 1632 | + # get injection axis that would be used to extrude structure |
| 1633 | + inj_axis = sim.internal_absorbers[0].size.index(0.0) |
| 1634 | + |
| 1635 | + # get grid boundaries |
| 1636 | + bnd_coords = sim.grid.boundaries.to_list[inj_axis] |
| 1637 | + |
| 1638 | + # get size of structures along injection axis directions |
| 1639 | + str_bnds = [ |
| 1640 | + np.min(sim.structures[-4].geometry.geometries[0].slab_bounds), |
| 1641 | + np.max(sim.structures[-2].geometry.geometries[0].slab_bounds), |
| 1642 | + ] |
| 1643 | + |
| 1644 | + pec_bnds = [] |
| 1645 | + |
| 1646 | + # infer placement of PEC plates beyond internal absorber |
| 1647 | + for absorber in sim.internal_absorbers: |
| 1648 | + absorber_cntr = absorber.center[inj_axis] |
| 1649 | + right_ind = np.searchsorted(bnd_coords, absorber_cntr, side="right") |
| 1650 | + left_ind = np.searchsorted(bnd_coords, absorber_cntr, side="left") - 1 |
| 1651 | + pec_bnds.append(bnd_coords[right_ind + 1]) |
| 1652 | + pec_bnds.append(bnd_coords[left_ind - 1]) |
| 1653 | + |
| 1654 | + # get range of coordinates along injection axis for PEC plates |
| 1655 | + pec_bnds = [np.min(pec_bnds), np.max(pec_bnds)] |
| 1656 | + |
| 1657 | + # ensure that structures were extruded up to PEC plates |
| 1658 | + assert all(np.isclose(str_bnd, pec_bnd) for str_bnd, pec_bnd in zip(str_bnds, pec_bnds)) |
| 1659 | + |
| 1660 | + # generate a new TCM simulation to test edge case when wave port plane does not intersect any structures |
| 1661 | + tcm = make_coaxial_component_modeler( |
| 1662 | + length=100000, port_types=(WavePort, WavePort), use_current=False |
| 1663 | + ) |
| 1664 | + |
| 1665 | + # update ports and set flag to extrude structures |
| 1666 | + ports = tcm.ports |
| 1667 | + port_1 = ports[0] |
| 1668 | + port_2 = ports[1] |
| 1669 | + port_1 = port_1.updated_copy(center=(0, 0, -50000), extrude_structures=True) |
| 1670 | + port_2 = port_2.updated_copy(center=(0, 0, 50000), extrude_structures=True) |
| 1671 | + |
| 1672 | + # move wave port plane so that is does not intersect any structures |
| 1673 | + port_1_center_new = (638.4, 0.0, -51000) |
| 1674 | + |
| 1675 | + # update voltage integral |
| 1676 | + voltage_int = port_1.voltage_integral.updated_copy(center=port_1_center_new) |
| 1677 | + # update WavePort |
| 1678 | + port_1 = port_1.updated_copy(center=port_1_center_new, voltage_integral=voltage_int) |
| 1679 | + |
| 1680 | + # update component modeler |
| 1681 | + tcm = tcm.updated_copy(ports=[port_1, port_2]) |
| 1682 | + |
| 1683 | + # make sure that |
| 1684 | + with pytest.raises(SetupError): |
| 1685 | + sim = tcm.base_sim |
| 1686 | + |
| 1687 | + |
| 1688 | +def test_wave_port_extrusion_differential_stripline(): |
| 1689 | + """Test extrusion of structures wave port absorber for differential stripline.""" |
| 1690 | + |
| 1691 | + tcm = make_differential_stripline_modeler() |
| 1692 | + |
| 1693 | + # update ports and set flag to extrude structures |
| 1694 | + ports = tcm.ports |
| 1695 | + port_1 = ports[0] |
| 1696 | + port_2 = ports[1] |
| 1697 | + port_1 = port_1.updated_copy(extrude_structures=True) |
| 1698 | + |
| 1699 | + # test that structure extrusion requires an internal absorber (should raise ValidationError) |
| 1700 | + with pytest.raises(pd.ValidationError): |
| 1701 | + _ = port_2.updated_copy(extrude_structures=True, absorber=False) |
| 1702 | + |
| 1703 | + # define a valid waveport |
| 1704 | + port_2 = port_2.updated_copy(extrude_structures=True) |
| 1705 | + |
| 1706 | + # update component modeler |
| 1707 | + tcm = tcm.updated_copy(ports=[port_1, port_2]) |
| 1708 | + |
| 1709 | + # generate simulations from component modeler |
| 1710 | + sim = tcm.base_sim |
| 1711 | + |
| 1712 | + # get injection axis that would be used to extrude structure |
| 1713 | + inj_axis = sim.internal_absorbers[0].size.index(0.0) |
| 1714 | + |
| 1715 | + # get grid boundaries |
| 1716 | + bnd_coords = sim.grid.boundaries.to_list[inj_axis] |
| 1717 | + |
| 1718 | + # get size of structures along injection axis directions |
| 1719 | + str_bnds = [ |
| 1720 | + np.min(sim.structures[-6].geometry.geometries[0].slab_bounds), |
| 1721 | + np.max(sim.structures[-1].geometry.geometries[0].slab_bounds), |
| 1722 | + ] |
| 1723 | + |
| 1724 | + pec_bnds = [] |
| 1725 | + |
| 1726 | + # infer placement of PEC plates beyond internal absorber |
| 1727 | + for absorber in sim._shifted_internal_absorbers: |
| 1728 | + # get the PEC box with its face surfaces |
| 1729 | + (box, inj_axis, direction) = sim._pec_frame_box(absorber) |
| 1730 | + surfaces = box.surfaces(box.size, box.center) |
| 1731 | + |
| 1732 | + # get extrusion coordinates and a cutting plane for inference of intersecting structures. |
| 1733 | + sign = 1 if direction == "+" else -1 |
| 1734 | + cutting_plane = surfaces[2 * inj_axis + (1 if direction == "+" else 0)] |
| 1735 | + |
| 1736 | + # get extrusion extent along injection axis |
| 1737 | + pec_bnds.append(cutting_plane.center[inj_axis]) |
| 1738 | + |
| 1739 | + # get range of coordinates along injection axis for PEC plates |
| 1740 | + pec_bnds = [np.min(pec_bnds), np.max(pec_bnds)] |
| 1741 | + |
| 1742 | + # ensure that structures were extruded up to PEC plates |
| 1743 | + assert all(np.isclose(str_bnd, pec_bnd) for str_bnd, pec_bnd in zip(str_bnds, pec_bnds)) |
| 1744 | + |
| 1745 | + # test scenario when wave port extrusion is requested, but port plane does not intersect any structures |
| 1746 | + mil = 25.4 |
| 1747 | + port_1_center_new = (0, 0, -2010 * mil) |
| 1748 | + |
| 1749 | + # re-assemble a new component modeler |
| 1750 | + tcm = make_differential_stripline_modeler() |
| 1751 | + |
| 1752 | + # update ports and set flag to extrude structures |
| 1753 | + ports = tcm.ports |
| 1754 | + port_1 = ports[0] |
| 1755 | + port_2 = ports[1] |
| 1756 | + port_1 = port_1.updated_copy(extrude_structures=True) |
| 1757 | + port_2 = port_2.updated_copy(extrude_structures=True) |
| 1758 | + |
| 1759 | + # update current and voltage integrals |
| 1760 | + current_int = port_1.current_integral.updated_copy(center=port_1_center_new) |
| 1761 | + voltage_int = port_1.voltage_integral.updated_copy(center=port_1_center_new) |
| 1762 | + |
| 1763 | + # update WavePort |
| 1764 | + port_1 = port_1.updated_copy( |
| 1765 | + center=port_1_center_new, current_integral=current_int, voltage_integral=voltage_int |
| 1766 | + ) |
| 1767 | + |
| 1768 | + # update component modeler |
| 1769 | + tcm = tcm.updated_copy(ports=[port_1, port_2]) |
| 1770 | + |
| 1771 | + # make sure that the error is triggered |
| 1772 | + with pytest.raises(SetupError): |
| 1773 | + sim = tcm.base_sim |
| 1774 | + |
| 1775 | + # update component modeler |
| 1776 | + tcm = tcm.updated_copy(ports=[port_2, port_1]) |
| 1777 | + |
| 1778 | + # make sure that the error is triggered even when ports are reshuffled |
| 1779 | + with pytest.raises(SetupError): |
| 1780 | + sim = tcm.base_sim |
0 commit comments