Skip to content

add a TerminalSpec for RF specific mode information #2444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

dmarek-flex
Copy link
Contributor

@dmarek-flex dmarek-flex commented May 9, 2025

Calculating impedance automatically:

  1. Find all conductors in mode plane and merge into isolated conductors, each possible carrying a conduction current.
  2. Place current integrals around each conductor (right now just using bounding box)
  3. Calculate total current using a new type of current integral (CompositeCurrentIntegral) made up of multiple current integrals. The total current flowing in a transmission line is net 0 because of current flowing in and out . To isolate these two terms, we split the currents based on their phase (using an initial reference phase). Then we choose the maximum of the two at the end. We choose the max, because it is possible that some current is flowing back in the PEC boundary or ground plane, which are not totally enclosed by the modal plane and their contribution will be missing.

Need at least one small backend change

Todo on:

  • Update Changelog
  • Check coverage and improve

Greptile Summary

Implements automated impedance calculation for RF transmission lines by introducing a new TerminalSpec system that detects conductors and computes characteristic impedance using voltage/current path integrals.

  • Added tidy3d.components.microwave.terminal_spec.TerminalSpec class for automated transmission line impedance calculation using conductor detection
  • Introduced CompositeCurrentIntegral to handle complex current flows with phase-based current isolation for accurate impedance measurements
  • Added PathSpecGenerator in components/microwave/path_spec_generator.py to automatically detect conductors and generate appropriate current paths
  • Enhanced ModeSolver with characteristic impedance (Z0) calculations integrated with the new terminal system
  • Improved code organization by moving visualization code to dedicated components/microwave/viz.py module

@dmarek-flex dmarek-flex self-assigned this May 9, 2025
@dmarek-flex dmarek-flex linked an issue May 9, 2025 that may be closed by this pull request
@dmarek-flex
Copy link
Contributor Author

dmarek-flex commented May 14, 2025

Still very rough, and I am not particularly happy about needing to define all of these different path spec types. But right now it is the best way I found to get around the circular dependency issue I am having. Basically, I cannot import path integral code into TerminalSpec, or I get circular dependencies.

@weiliangjin2021 @dbochkov-flexcompute Any ideas to solve this?

Otherwise you can retrieve impedance info by doing something as simple as:

mode_spec = td.ModeSpec(num_modes=5, target_neff=1.8, terminal_spec = TerminalSpec())

@dmarek-flex
Copy link
Contributor Author

dmarek-flex commented May 14, 2025

Hmm, might be possible if I just add TerminalSpec directly to the various monitors and solvers.

Nah, having path specs seems to be the best. I'll just add some factory class to convert path specs to the correct path integral version.

@dmarek-flex dmarek-flex force-pushed the dmarek/add_terminal_mode_monitor branch 2 times, most recently from 44634db to e12f1a7 Compare May 26, 2025 15:13
@dmarek-flex dmarek-flex marked this pull request as ready for review May 26, 2025 15:25
@dmarek-flex
Copy link
Contributor Author

dmarek-flex commented May 26, 2025

Ready for review finally! The main feature this PR provided is automatically computing impedance and adding it to the ModeData or ModeSolverData. By itself, the feature is not so complicated, but I had a lot of challenges with getting around circular dependencies and organizing the classes. To get impedance information, all the user needs to do is pass ModeSpec like:

mode_spec = td.ModeSpec(num_modes=4, target_neff=1.8, terminal_spec=td.TerminalSpec())

to some object that calculates and returns mode data. The simulation will not be affected if used in a ModeSource. The user can also pass their own voltage and current specifications to the TerminalSpec for a custom definition of impedance. The mode data now has a new field TerminalSpec which is populated by the ModeSpec, perhaps automatically. This allows users to swap the TerminalSpec to a new definition in a post-processing step, if they desire.

Now, to get around all of the circular dependence stuff, I had to split the path integral classes in the microwave plugin into the specification part, and the part that actually computes the integral. At some later point we will most likely move the everything out of the microwave plugin, since these path integrals are needed for many things in RF. If anyone has suggestions on how to better get around these issues, please let me know! The main issue was the need to import the monitor data module in the path integral modules.

Finally, the auto generated paths are bounding boxes around each conductor, which will work for 99% of transmission lines, but there might be improvements needed in the future to auto generate the line string type path integrals for handling more complex structures.

@dmarek-flex
Copy link
Contributor Author

@yuanshen-flexcompute It would be nice to test out this automatic version of impedance calculation on some of your test cases to make sure I did not miss anything.

@dmarek-flex dmarek-flex added the 2.9 will go into version 2.9.* label May 26, 2025
Copy link
Contributor

github-actions bot commented May 26, 2025

badge

Code Coverage Summary

Filename                                                      Stmts    Miss  Cover    Missing
----------------------------------------------------------  -------  ------  -------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tidy3d/__init__.py                                               80       0  100.00%
tidy3d/__main__.py                                               52       1  98.08%   115
tidy3d/compat.py                                                  6       2  66.67%   7-8
tidy3d/config.py                                                 24       0  100.00%
tidy3d/constants.py                                              91       0  100.00%
tidy3d/exceptions.py                                             18       0  100.00%
tidy3d/log.py                                                   190      19  90.00%   55, 92-93, 219, 228, 301, 305, 371, 409, 413-418, 422, 426-428, 445
tidy3d/packaging.py                                              78      17  78.21%   109, 146-149, 175-180, 194-204
tidy3d/updater.py                                               196     120  38.78%   30, 35-36, 50, 58-62, 66-70, 74, 78, 96-112, 117-118, 125, 130-134, 138-141, 148-154, 158, 180, 195-204, 211-223, 230-276, 282-284, 291-296, 303-329, 336-341
tidy3d/version.py                                                 2       0  100.00%
tidy3d/components/__init__.py                                     0       0  100.00%
tidy3d/components/apodization.py                                 44       0  100.00%
tidy3d/components/base.py                                       498      12  97.59%   83, 90, 365, 418-419, 868, 1024, 1160-1164
tidy3d/components/bc_placement.py                                30       2  93.33%   51, 74
tidy3d/components/beam.py                                       190      23  87.89%   149, 291-293, 297-299, 313, 318-321, 327, 330-334, 342-348
tidy3d/components/boundary.py                                   191       2  98.95%   926-927
tidy3d/components/dispersion_fitter.py                          420      22  94.76%   103-109, 195, 270, 367, 441, 461-465, 585, 660-661, 693, 829, 857, 914-915, 940, 962
tidy3d/components/field_projection.py                           354      33  90.68%   111, 154, 223, 258, 338-341, 424-425, 437, 525, 529-556, 628-644, 802-808
tidy3d/components/file_util.py                                   21       0  100.00%
tidy3d/components/frequencies.py                                105      49  53.33%   51-65, 67-71, 74, 76, 78-104
tidy3d/components/lumped_element.py                             455       6  98.68%   551-555, 1034, 1084, 1118, 1156
tidy3d/components/medium.py                                    2357     153  93.51%   156, 161, 166, 676, 691, 787, 945, 1382, 1389-1400, 1699, 1712-1713, 1912, 2017, 2020, 2033, 2036, 2046, 2131-2144, 2207-2210, 2213-2216, 2232, 2234, 2238, 2299, 2305-2306, 2312, 2325, 2406, 2438, 2494, 2811, 2821, 2861, 2914, 2923-2924, 3077, 3264, 3340, 3501, 3505, 3523, 3536, 3602, 3637, 3654, 3769-3773, 3873, 3891, 3896, 3899, 4042-4047, 4209, 4296, 4298, 4300, 4325, 4330, 4386, 4578, 4580, 4644-4648, 4700, 4705, 4710, 4715, 4875, 4877, 4903-4907, 4959, 4964, 4969, 5028, 5031, 5132, 5134, 5175-5179, 5231, 5236, 5241, 5570, 5624, 5632, 5670-5688, 5835, 6097-6099, 6134-6135, 6202-6216, 6322, 6427, 6680, 6898, 7113, 7245, 7423, 7432-7435
tidy3d/components/mode_spec.py                                   65       1  98.46%   221
tidy3d/components/monitor.py                                    360      25  93.06%   75, 83, 263, 310-319, 373, 402-405, 426, 537-538, 767, 774-776, 818, 828, 870, 1003
tidy3d/components/parameter_perturbation.py                     438      28  93.61%   11-12, 1547, 1570-1596, 1604-1624, 1782
tidy3d/components/run_time_spec.py                                6       0  100.00%
tidy3d/components/scene.py                                      598     148  75.25%   13-14, 160, 353-357, 382, 384, 521-522, 537, 549, 556, 929, 939, 959-968, 972-984, 990, 1024, 1134-1213, 1248, 1279, 1290, 1429-1435, 1502, 1533-1534, 1576, 1590-1596, 1614, 1627, 1709-1715, 1807, 1821-1854, 1873-1928
tidy3d/components/simulation.py                                1782     112  93.71%   15-16, 140-141, 205, 213, 221, 362, 371, 407, 424, 439, 773-777, 825-829, 1048-1059, 1387, 1513-1514, 1551, 1557, 1598, 1603, 1615, 1688-1694, 1754, 2954, 3182, 3231, 3292, 3321, 3329, 3342, 3385, 3404, 3423-3432, 3458, 3466, 3527, 3589, 3616, 3695, 3819, 4161-4165, 4183, 4208, 4350, 4478-4482, 4499, 4669-4674, 4716, 4782-4783, 4844, 4849, 4853-4905, 4929, 4968-4970, 5008-5009, 5039, 5149, 5155-5166, 5200, 5221
tidy3d/components/structure.py                                  223      17  92.38%   38-39, 125, 268, 300, 323, 353, 504-508, 562-563, 592, 598, 613, 623, 719
tidy3d/components/subpixel_spec.py                               44       2  95.45%   202, 206
tidy3d/components/time.py                                        75       2  97.33%   79, 89
tidy3d/components/time_modulation.py                             93       0  100.00%
tidy3d/components/transformation.py                              83       5  93.98%   69, 103, 154, 172, 188
tidy3d/components/type_util.py                                    6       1  83.33%   12
tidy3d/components/types.py                                      163      12  92.64%   12-13, 34-36, 63-67, 86, 170
tidy3d/components/types_extra.py                                  7       1  85.71%   14
tidy3d/components/validators.py                                 212      10  95.28%   62, 129-136, 255, 271, 363
tidy3d/components/autograd/__init__.py                            6       0  100.00%
tidy3d/components/autograd/boxes.py                              59      11  81.36%   31, 72, 79, 87-88, 92, 95-96, 134, 142, 147
tidy3d/components/autograd/derivative_utils.py                  123       5  95.93%   207, 215, 222, 227, 309
tidy3d/components/autograd/functions.py                          63       0  100.00%
tidy3d/components/autograd/types.py                              28       0  100.00%
tidy3d/components/autograd/utils.py                              11       0  100.00%
tidy3d/components/base_sim/__init__.py                            0       0  100.00%
tidy3d/components/base_sim/monitor.py                            33       0  100.00%
tidy3d/components/base_sim/simulation.py                        161       5  96.89%   605-609, 671-675, 700
tidy3d/components/base_sim/source.py                             11       0  100.00%
tidy3d/components/base_sim/data/__init__.py                       0       0  100.00%
tidy3d/components/base_sim/data/monitor_data.py                  10       0  100.00%
tidy3d/components/base_sim/data/sim_data.py                      59       0  100.00%
tidy3d/components/data/__init__.py                                0       0  100.00%
tidy3d/components/data/data_array.py                            430      21  95.12%   78, 85, 131, 156-167, 252-256, 260-263, 377-382, 386-394, 588, 613
tidy3d/components/data/dataset.py                               181       4  97.79%   295, 397-400
tidy3d/components/data/monitor_data.py                         1384      68  95.09%   118, 170, 180, 322, 447, 458, 492, 636-648, 659, 806, 852, 1185-1186, 1190-1191, 1205, 1375, 1636, 1755, 1772, 2024-2034, 2066-2067, 2083, 2092, 2106-2107, 2225, 2233-2244, 2303, 2533, 2612, 2625, 2748, 3111, 3230, 3252-3253, 3419, 3453, 3667-3668, 3775
tidy3d/components/data/sim_data.py                              419      24  94.27%   303, 349, 396, 401, 418, 427, 521, 529, 562, 566, 613, 625, 636, 936, 982, 1047, 1054, 1063, 1090, 1292, 1294, 1298, 1317-1318
tidy3d/components/data/utils.py                                  46       4  91.30%   40, 51, 72, 80
tidy3d/components/data/validators.py                             43       1  97.67%   53
tidy3d/components/data/zbf.py                                    50       4  92.00%   118-119, 130-131
tidy3d/components/data/unstructured/__init__.py                   0       0  100.00%
tidy3d/components/data/unstructured/base.py                     640      37  94.22%   77, 89, 100, 110, 137, 149, 198, 200-210, 374, 381, 388, 391, 424, 585-590, 599, 608, 630, 857, 860, 866, 904-908, 981, 1233, 1533, 1599, 1672, 1699, 1724, 1751
tidy3d/components/data/unstructured/tetrahedral.py              109       4  96.33%   113, 217, 331, 350
tidy3d/components/data/unstructured/triangular.py               194      11  94.33%   13-14, 148, 165, 295, 356, 486-487, 490, 506, 525
tidy3d/components/eme/__init__.py                                 0       0  100.00%
tidy3d/components/eme/grid.py                                   304       2  99.34%   118, 164
tidy3d/components/eme/monitor.py                                 41       0  100.00%
tidy3d/components/eme/simulation.py                             466       7  98.50%   9-10, 719, 932, 946, 961, 969
tidy3d/components/eme/sweep.py                                   40       0  100.00%
tidy3d/components/eme/data/__init__.py                            0       0  100.00%
tidy3d/components/eme/data/dataset.py                            27       0  100.00%
tidy3d/components/eme/data/monitor_data.py                       14       0  100.00%
tidy3d/components/eme/data/sim_data.py                          220       8  96.36%   251-256, 279-282
tidy3d/components/geometry/__init__.py                            0       0  100.00%
tidy3d/components/geometry/base.py                             1031      81  92.14%   17-18, 132, 556-559, 584, 588, 713-722, 788, 1247-1251, 1265-1268, 1389-1393, 1431-1432, 1445, 1460, 1462, 1468-1472, 1477, 1483, 1489, 1495, 1501, 1506, 1510, 1516, 1814, 1860-1864, 2063, 2292-2321, 2732, 2890, 2901, 2928-2932, 2980, 3034-3036, 3136, 3287
tidy3d/components/geometry/bound_ops.py                          29       0  100.00%
tidy3d/components/geometry/mesh.py                              255      23  90.98%   82, 200, 212-214, 253, 295, 299, 354, 358, 376, 503, 550-554, 609-623
tidy3d/components/geometry/polyslab.py                          783      35  95.53%   113, 348-354, 472, 602, 628, 970-975, 1147, 1176, 1179, 1356-1365, 1441, 1524-1547, 1566, 1928, 1987
tidy3d/components/geometry/primitives.py                        318      67  78.93%   98, 254, 318, 321, 331-334, 364, 395-468
tidy3d/components/geometry/triangulation.py                      64       2  96.88%   135, 149
tidy3d/components/geometry/utils.py                             213      13  93.90%   75, 80, 83-86, 114, 324, 342, 345-348, 513
tidy3d/components/geometry/utils_2d.py                          103       0  100.00%
tidy3d/components/grid/__init__.py                                0       0  100.00%
tidy3d/components/grid/corner_finder.py                          77       0  100.00%
tidy3d/components/grid/grid.py                                  209      10  95.22%   142, 252-258, 597, 701
tidy3d/components/grid/grid_spec.py                             759      21  97.23%   211, 229-232, 666, 764, 1418-1420, 1425-1426, 1440, 1485, 1487, 1495, 1510, 2585-2589, 2663, 2671
tidy3d/components/grid/mesher.py                                484      10  97.93%   813, 907, 996, 998, 1000, 1066, 1115, 1194, 1266, 1317
tidy3d/components/material/__init__.py                            0       0  100.00%
tidy3d/components/material/multi_physics.py                      34       0  100.00%
tidy3d/components/material/solver_types.py                       10       0  100.00%
tidy3d/components/material/types.py                               6       0  100.00%
tidy3d/components/material/tcad/__init__.py                       0       0  100.00%
tidy3d/components/material/tcad/charge.py                        30       3  90.00%   37, 40, 43
tidy3d/components/material/tcad/heat.py                          35       0  100.00%
tidy3d/components/microwave/__init__.py                           0       0  100.00%
tidy3d/components/microwave/path_integral_factory.py             20       0  100.00%
tidy3d/components/microwave/path_spec.py                        274      44  83.94%   107, 165-179, 269, 468-474, 503-526, 566-574, 701
tidy3d/components/microwave/path_spec_generator.py              115       0  100.00%
tidy3d/components/microwave/terminal_spec.py                      8       0  100.00%
tidy3d/components/microwave/viz.py                               12       0  100.00%
tidy3d/components/microwave/data/__init__.py                      0       0  100.00%
tidy3d/components/microwave/data/monitor_data.py                 52       0  100.00%
tidy3d/components/microwave/formulas/__init__.py                  0       0  100.00%
tidy3d/components/microwave/formulas/circuit_parameters.py       35       0  100.00%
tidy3d/components/mode/__init__.py                                0       0  100.00%
tidy3d/components/mode/derivatives.py                           128       2  98.44%   173, 230
tidy3d/components/mode/mode_solver.py                           900     243  73.00%   77-79, 108, 186-187, 197, 212, 258-260, 344-347, 378, 432, 457, 465-471, 476-547, 555-561, 567-605, 611-615, 649-731, 851-972, 977-984, 992-996, 1001-1034, 1045-1047, 1115, 1269-1281, 1311-1315, 1368-1380, 1397-1398, 1421-1422, 1472, 1529, 1603, 1721, 1736, 1741-1743, 1748-1754, 1762-1768, 1779, 1781, 1790, 1861, 2304, 2366-2368, 2531
tidy3d/components/mode/simulation.py                            128       6  95.31%   222-229
tidy3d/components/mode/solver.py                                416      72  82.69%   118, 125, 141-142, 147-152, 165, 240, 250-251, 257, 281, 393, 479-485, 503, 510-512, 551-552, 561-591, 598-604, 651, 654, 896, 920, 977, 988, 1002, 1007-1020, 1028-1034, 1039, 1047
tidy3d/components/mode/transforms.py                             32       0  100.00%
tidy3d/components/mode/validators.py                             11       0  100.00%
tidy3d/components/mode/data/__init__.py                           0       0  100.00%
tidy3d/components/mode/data/sim_data.py                          18       0  100.00%
tidy3d/components/source/__init__.py                              0       0  100.00%
tidy3d/components/source/base.py                                 53       6  88.68%   94-101
tidy3d/components/source/current.py                              33       0  100.00%
tidy3d/components/source/field.py                               175      13  92.57%   124, 456-461, 526-532, 542
tidy3d/components/source/time.py                                147       9  93.88%   170-171, 184-186, 261, 364, 400, 432
tidy3d/components/source/utils.py                                 5       0  100.00%
tidy3d/components/spice/__init__.py                               0       0  100.00%
tidy3d/components/spice/types.py                                  4       0  100.00%
tidy3d/components/spice/analysis/__init__.py                      0       0  100.00%
tidy3d/components/spice/analysis/dc.py                           14       0  100.00%
tidy3d/components/spice/sources/__init__.py                       0       0  100.00%
tidy3d/components/spice/sources/dc.py                            21       0  100.00%
tidy3d/components/spice/sources/types.py                          5       0  100.00%
tidy3d/components/tcad/__init__.py                                0       0  100.00%
tidy3d/components/tcad/bandgap.py                                 9       0  100.00%
tidy3d/components/tcad/doping.py                                124      29  76.61%   21-28, 36-38, 51, 56-58, 63-64, 122-133, 232-235, 319
tidy3d/components/tcad/generation_recombination.py               22       0  100.00%
tidy3d/components/tcad/grid.py                                   60      10  83.33%   106-108, 113-115, 145-149
tidy3d/components/tcad/mobility.py                               14       0  100.00%
tidy3d/components/tcad/types.py                                  17       0  100.00%
tidy3d/components/tcad/viz.py                                    11       0  100.00%
tidy3d/components/tcad/analysis/__init__.py                       0       0  100.00%
tidy3d/components/tcad/analysis/heat_simulation_type.py          10       0  100.00%
tidy3d/components/tcad/boundary/__init__.py                       0       0  100.00%
tidy3d/components/tcad/boundary/abstract.py                       4       0  100.00%
tidy3d/components/tcad/boundary/charge.py                        10       0  100.00%
tidy3d/components/tcad/boundary/heat.py                          11       0  100.00%
tidy3d/components/tcad/boundary/specification.py                 10       0  100.00%
tidy3d/components/tcad/data/__init__.py                           0       0  100.00%
tidy3d/components/tcad/data/sim_data.py                         102       9  91.18%   212, 226, 232, 242, 277-280, 285, 308
tidy3d/components/tcad/data/types.py                              5       0  100.00%
tidy3d/components/tcad/data/monitor_data/__init__.py              0       0  100.00%
tidy3d/components/tcad/data/monitor_data/abstract.py             52       0  100.00%
tidy3d/components/tcad/data/monitor_data/charge.py              174       7  95.98%   81-84, 134-135, 251, 434
tidy3d/components/tcad/data/monitor_data/heat.py                 34       0  100.00%
tidy3d/components/tcad/monitors/__init__.py                       0       0  100.00%
tidy3d/components/tcad/monitors/abstract.py                      12       2  83.33%   38-39
tidy3d/components/tcad/monitors/charge.py                        11       0  100.00%
tidy3d/components/tcad/monitors/heat.py                           5       0  100.00%
tidy3d/components/tcad/simulation/__init__.py                     0       0  100.00%
tidy3d/components/tcad/simulation/heat.py                        21       0  100.00%
tidy3d/components/tcad/simulation/heat_charge.py                619     100  83.84%   14-15, 336-337, 572, 606, 628-629, 712, 802-804, 811, 824, 952, 964, 1045-1057, 1123-1124, 1129, 1144-1156, 1162-1164, 1222, 1245-1259, 1266-1268, 1271, 1285, 1291-1299, 1346-1364, 1384, 1502, 1507-1508, 1531, 1567-1568, 1586-1600, 1612-1619, 1664, 1680-1681, 1685, 1692, 1706, 1713, 1720
tidy3d/components/tcad/source/__init__.py                         0       0  100.00%
tidy3d/components/tcad/source/abstract.py                        20       1  95.00%   23
tidy3d/components/tcad/source/coupled.py                          3       0  100.00%
tidy3d/components/tcad/source/heat.py                            14       0  100.00%
tidy3d/components/viz/__init__.py                                10       0  100.00%
tidy3d/components/viz/axes_utils.py                              43       0  100.00%
tidy3d/components/viz/descartes.py                               38       4  89.47%   21-22, 64-65
tidy3d/components/viz/flex_color_palettes.py                      4       4  0.00%    1-1823
tidy3d/components/viz/flex_style.py                              27      11  59.26%   21-25, 39-40, 43-46
tidy3d/components/viz/plot_params.py                             45       0  100.00%
tidy3d/components/viz/plot_sim_3d.py                             17       2  88.24%   13-14
tidy3d/components/viz/styles.py                                  17       2  88.24%   7-8
tidy3d/components/viz/visualization_spec.py                      29       3  89.66%   13-15
tidy3d/material_library/__init__.py                               0       0  100.00%
tidy3d/material_library/material_library.py                     188       8  95.74%   86, 92, 95, 154, 157, 173, 224, 2081
tidy3d/material_library/material_reference.py                    10       0  100.00%
tidy3d/material_library/parametric_materials.py                 159       3  98.11%   20-21, 253
tidy3d/material_library/util.py                                 131      53  59.54%   51-52, 98, 114-121, 127-139, 192-207, 213-251
tidy3d/plugins/__init__.py                                        0       0  100.00%
tidy3d/plugins/adjoint/__init__.py                               16       2  87.50%   10-11
tidy3d/plugins/adjoint/web.py                                   287      55  80.84%   72, 77, 226-240, 246-255, 283-285, 321-324, 338-349, 363-377, 566-582, 596-621
tidy3d/plugins/adjoint/components/__init__.py                    10       0  100.00%
tidy3d/plugins/adjoint/components/base.py                       141       0  100.00%
tidy3d/plugins/adjoint/components/geometry.py                   379      16  95.78%   77, 228, 347, 538, 590, 634-636, 685, 860, 868-871, 970, 973, 978
tidy3d/plugins/adjoint/components/medium.py                     175       2  98.86%   300, 439
tidy3d/plugins/adjoint/components/simulation.py                 301       9  97.01%   227-228, 243-248, 328, 615-616, 723-724
tidy3d/plugins/adjoint/components/structure.py                   98       1  98.98%   167
tidy3d/plugins/adjoint/components/types.py                       22       2  90.91%   37-41
tidy3d/plugins/adjoint/components/data/__init__.py                0       0  100.00%
tidy3d/plugins/adjoint/components/data/data_array.py            296      26  91.22%   77, 227, 247-252, 289, 338, 388, 399-409, 413-418, 452-453
tidy3d/plugins/adjoint/components/data/dataset.py                12       0  100.00%
tidy3d/plugins/adjoint/components/data/monitor_data.py          198       8  95.96%   165, 191, 199, 232, 238, 243, 256, 429
tidy3d/plugins/adjoint/components/data/sim_data.py              122       4  96.72%   157, 257-259
tidy3d/plugins/adjoint/utils/__init__.py                          0       0  100.00%
tidy3d/plugins/adjoint/utils/filter.py                           78       1  98.72%   55
tidy3d/plugins/adjoint/utils/penalty.py                         100       3  97.00%   238, 244, 276
tidy3d/plugins/autograd/__init__.py                               7       0  100.00%
tidy3d/plugins/autograd/constants.py                              3       0  100.00%
tidy3d/plugins/autograd/differential_operators.py                27       0  100.00%
tidy3d/plugins/autograd/functions.py                            148       6  95.95%   55, 61, 275, 282, 329, 336
tidy3d/plugins/autograd/types.py                                  4       0  100.00%
tidy3d/plugins/autograd/utilities.py                             73       4  94.52%   208, 225-226, 240
tidy3d/plugins/autograd/invdes/__init__.py                        7       0  100.00%
tidy3d/plugins/autograd/invdes/filters.py                        67       3  95.52%   53-54, 203
tidy3d/plugins/autograd/invdes/misc.py                            5       1  80.00%   25
tidy3d/plugins/autograd/invdes/parametrizations.py               26       0  100.00%
tidy3d/plugins/autograd/invdes/penalties.py                      60      21  65.00%   87, 139-141, 165-169, 190-192, 229-238
tidy3d/plugins/autograd/invdes/projections.py                    15       5  66.67%   32-36, 69
tidy3d/plugins/autograd/primitives/__init__.py                    4       0  100.00%
tidy3d/plugins/autograd/primitives/interpolate.py               263       4  98.48%   17, 616, 644, 680
tidy3d/plugins/autograd/primitives/misc.py                        5       0  100.00%
tidy3d/plugins/design/__init__.py                                 6       0  100.00%
tidy3d/plugins/design/design.py                                 227       4  98.24%   107, 143-144, 374
tidy3d/plugins/design/method.py                                 316       1  99.68%   69
tidy3d/plugins/design/parameter.py                               95       2  97.89%   39, 120
tidy3d/plugins/design/result.py                                 151       7  95.36%   102, 121, 149, 232, 274, 319, 329
tidy3d/plugins/dispersion/__init__.py                             5       0  100.00%
tidy3d/plugins/dispersion/fit.py                                271      13  95.20%   170, 420, 500, 508, 523, 530-533, 664-665, 739, 741
tidy3d/plugins/dispersion/fit_fast.py                            38       1  97.37%   149
tidy3d/plugins/dispersion/fit_web.py                              3       3  0.00%    3-7
tidy3d/plugins/dispersion/web.py                                109      23  78.90%   104, 229-232, 248-253, 278-294, 305-308, 351-355, 366-368
tidy3d/plugins/expressions/__init__.py                           20       0  100.00%
tidy3d/plugins/expressions/base.py                              133      49  63.16%   13, 42, 56, 59, 62, 87-89, 97-99, 102-103, 118-120, 128, 131-133, 136-138, 149-151, 154-156, 159-161, 169-171, 174-176, 179-181, 184-186, 189-191, 194-196, 199-201, 204, 207, 210, 213, 216, 219, 222, 225, 228
tidy3d/plugins/expressions/functions.py                          37       1  97.30%   49
tidy3d/plugins/expressions/metrics.py                            46       2  95.65%   50, 93
tidy3d/plugins/expressions/operators.py                          65       2  96.92%   33, 63
tidy3d/plugins/expressions/types.py                              15       4  73.33%   10-24
tidy3d/plugins/expressions/variables.py                          27       1  96.30%   97
tidy3d/plugins/invdes/__init__.py                                10       0  100.00%
tidy3d/plugins/invdes/base.py                                     4       0  100.00%
tidy3d/plugins/invdes/design.py                                 167      23  86.23%   58, 61, 73-76, 112-115, 184, 203-204, 206, 217-222, 227-233, 329-330
tidy3d/plugins/invdes/initialization.py                          57       7  87.72%   56, 96, 103-106, 108, 115, 122
tidy3d/plugins/invdes/optimizer.py                               97       4  95.88%   99, 176, 222, 295
tidy3d/plugins/invdes/penalty.py                                 24       1  95.83%   32
tidy3d/plugins/invdes/region.py                                 172      14  91.86%   82, 102, 186-187, 194-195, 207, 215, 218, 224, 230, 240, 283-284
tidy3d/plugins/invdes/result.py                                  54       0  100.00%
tidy3d/plugins/invdes/transformation.py                          26       2  92.31%   25, 80
tidy3d/plugins/invdes/utils.py                                   37       0  100.00%
tidy3d/plugins/invdes/validators.py                              35       6  82.86%   18-29
tidy3d/plugins/microwave/__init__.py                             10       0  100.00%
tidy3d/plugins/microwave/array_factor.py                        276       2  99.28%   178, 186
tidy3d/plugins/microwave/auto_path_integrals.py                  25       0  100.00%
tidy3d/plugins/microwave/custom_path_integrals.py               109      33  69.72%   232-304
tidy3d/plugins/microwave/impedance_calculator.py                 54       0  100.00%
tidy3d/plugins/microwave/lobe_measurer.py                       155       0  100.00%
tidy3d/plugins/microwave/path_integrals.py                      134       2  98.51%   40, 212
tidy3d/plugins/microwave/rf_material_library.py                  17       0  100.00%
tidy3d/plugins/microwave/rf_material_reference.py                 3       0  100.00%
tidy3d/plugins/microwave/viz.py                                  10       0  100.00%
tidy3d/plugins/microwave/models/__init__.py                       3       0  100.00%
tidy3d/plugins/microwave/models/coupled_microstrip.py            49       0  100.00%
tidy3d/plugins/microwave/models/microstrip.py                    64       0  100.00%
tidy3d/plugins/mode/__init__.py                                   3       0  100.00%
tidy3d/plugins/mode/mode_solver.py                                7       0  100.00%
tidy3d/plugins/mode/web.py                                        3       0  100.00%
tidy3d/plugins/polyslab/__init__.py                               3       0  100.00%
tidy3d/plugins/polyslab/polyslab.py                               7       0  100.00%
tidy3d/plugins/pytorch/__init__.py                                3       0  100.00%
tidy3d/plugins/pytorch/wrapper.py                                39       1  97.44%   66
tidy3d/plugins/resonance/__init__.py                              3       0  100.00%
tidy3d/plugins/resonance/resonance.py                           172      10  94.19%   110, 139, 184, 203-204, 218, 225, 248, 254, 281
tidy3d/plugins/smatrix/__init__.py                               11       0  100.00%
tidy3d/plugins/smatrix/smatrix.py                                 4       0  100.00%
tidy3d/plugins/smatrix/component_modelers/__init__.py             0       0  100.00%
tidy3d/plugins/smatrix/component_modelers/base.py               138      21  84.78%   162-166, 178-183, 194, 199, 221-224, 248, 306-310
tidy3d/plugins/smatrix/component_modelers/modal.py              156       1  99.36%   136
tidy3d/plugins/smatrix/component_modelers/terminal.py           275       0  100.00%
tidy3d/plugins/smatrix/data/__init__.py                           0       0  100.00%
tidy3d/plugins/smatrix/data/terminal.py                          19       4  78.95%   29-33, 56-60
tidy3d/plugins/smatrix/ports/__init__.py                          0       0  100.00%
tidy3d/plugins/smatrix/ports/base_lumped.py                      41       0  100.00%
tidy3d/plugins/smatrix/ports/base_terminal.py                    33       2  93.94%   49-53
tidy3d/plugins/smatrix/ports/coaxial_lumped.py                  165       0  100.00%
tidy3d/plugins/smatrix/ports/modal.py                            14       0  100.00%
tidy3d/plugins/smatrix/ports/rectangular_lumped.py              124       0  100.00%
tidy3d/plugins/smatrix/ports/wave.py                            109       2  98.17%   124, 182
tidy3d/plugins/waveguide/__init__.py                              3       0  100.00%
tidy3d/plugins/waveguide/rectangular_dielectric.py              369      80  78.32%   274, 327, 335, 338, 464-465, 613-635, 644-679, 682-700, 803, 808, 813, 849, 899, 935, 983, 1024, 1051-1090, 1142-1154
tidy3d/web/__init__.py                                           13       0  100.00%
tidy3d/web/environment.py                                         3       3  0.00%    3-7
tidy3d/web/api/__init__.py                                        0       0  100.00%
tidy3d/web/api/asynchronous.py                                   14       2  85.71%   67, 71
tidy3d/web/api/connect_util.py                                   44      18  59.09%   38-43, 50, 55-61, 66-72
tidy3d/web/api/container.py                                     336      36  89.29%   242, 290, 338, 451, 470-472, 622, 643, 734-738, 755, 758-763, 824-844, 865-866, 928-929, 975, 981-982
tidy3d/web/api/material_fitter.py                                63      20  68.25%   76-102, 106-107, 124-125
tidy3d/web/api/material_libray.py                                21       1  95.24%   32
tidy3d/web/api/mode.py                                          208      66  68.27%   125-128, 133, 141, 195-198, 220, 222-231, 234-246, 342, 359, 402-405, 488-490, 494, 523-581, 615-618, 629-630, 668
tidy3d/web/api/tidy3d_stub.py                                   116      45  61.21%   71-74, 85, 109, 145, 180-203, 219, 237-263
tidy3d/web/api/webapi.py                                        349      73  79.08%   335, 365, 395, 433-451, 494-499, 513-518, 539, 548, 569, 596-599, 619-620, 701-709, 813-818, 864, 885, 918, 921-922, 979, 987-989, 1002, 1004, 1008, 1062, 1099, 1116-1124
tidy3d/web/api/autograd/__init__.py                               0       0  100.00%
tidy3d/web/api/autograd/autograd.py                             429      70  83.68%   342, 581-595, 603-613, 731, 873, 929-949, 952, 1078-1082, 1116-1117, 1120-1121, 1123, 1131-1157, 1166-1180
tidy3d/web/api/autograd/utils.py                                 48       0  100.00%
tidy3d/web/cli/__init__.py                                        3       0  100.00%
tidy3d/web/cli/app.py                                            66      35  46.97%   34-39, 59, 71-114, 120
tidy3d/web/cli/constants.py                                       9       1  88.89%   13
tidy3d/web/cli/migrate.py                                        46      35  23.91%   21-72
tidy3d/web/cli/develop/__init__.py                                8       0  100.00%
tidy3d/web/cli/develop/documentation.py                          78      54  30.77%   64-115, 144-163, 179-182, 226-230, 330-340
tidy3d/web/cli/develop/index.py                                   5       0  100.00%
tidy3d/web/cli/develop/install.py                               156     129  17.31%   38-46, 60-63, 75-98, 110-123, 140-151, 163-173, 183-184, 204-258, 283-285, 303-358, 374-375, 395-406
tidy3d/web/cli/develop/packaging.py                              35      18  48.57%   52-79, 112-118
tidy3d/web/cli/develop/tests.py                                  17       6  64.71%   29-32, 65-66
tidy3d/web/cli/develop/utils.py                                  15       6  60.00%   47-49, 68-70
tidy3d/web/core/__init__.py                                       0       0  100.00%
tidy3d/web/core/account.py                                       20       1  95.00%   66
tidy3d/web/core/cache.py                                          3       0  100.00%
tidy3d/web/core/constants.py                                     23       0  100.00%
tidy3d/web/core/core_config.py                                   13       0  100.00%
tidy3d/web/core/environment.py                                   59       8  86.44%   17, 101-108, 141, 152, 163
tidy3d/web/core/exceptions.py                                     9       0  100.00%
tidy3d/web/core/file_util.py                                     40      14  65.00%   23-25, 36-38, 44-51, 61
tidy3d/web/core/http_util.py                                    105      15  85.71%   37, 64, 70, 89, 99, 136-139, 158-160, 170, 178-179
tidy3d/web/core/s3utils.py                                      121      75  38.02%   51-52, 57-58, 63, 75, 102-103, 113, 137-138, 148, 159-164, 203-211, 240-279, 305-358, 386-417
tidy3d/web/core/stub.py                                          14       0  100.00%
tidy3d/web/core/task_core.py                                    185      31  83.24%   78-80, 193-199, 241, 295, 307, 315, 319, 337, 352, 375, 377, 417, 451, 481, 484, 521, 534-535, 546-547, 578, 601, 632, 657-660, 670
tidy3d/web/core/task_info.py                                     98       0  100.00%
tidy3d/web/core/types.py                                         38       1  97.37%   69
TOTAL                                                         34483    3213  90.68%

Diff against develop

Filename                                                Stmts    Miss  Cover
----------------------------------------------------  -------  ------  --------
tidy3d/__init__.py                                         +2       0  +100.00%
tidy3d/components/mode_spec.py                             +2       0  +0.05%
tidy3d/components/simulation.py                           +23      +3  -0.09%
tidy3d/components/validators.py                            -8       0  -0.17%
tidy3d/components/data/monitor_data.py                    +19     +10  -0.66%
tidy3d/components/geometry/utils.py                       +29      +7  -2.84%
tidy3d/components/microwave/path_integral_factory.py      +20       0  +100.00%
tidy3d/components/microwave/path_spec.py                 +274     +44  +83.94%
tidy3d/components/microwave/path_spec_generator.py       +115       0  +100.00%
tidy3d/components/microwave/terminal_spec.py               +8       0  +100.00%
tidy3d/components/microwave/viz.py                        +12       0  +100.00%
tidy3d/components/mode/mode_solver.py                     +11      +6  -0.34%
tidy3d/components/mode/validators.py                      +11       0  +100.00%
tidy3d/plugins/microwave/custom_path_integrals.py         -26     +32  -29.54%
tidy3d/plugins/microwave/impedance_calculator.py           +6       0  +100.00%
tidy3d/plugins/microwave/path_integrals.py               -129      -2  +0.03%
tidy3d/plugins/microwave/viz.py                            -8       0  +100.00%
tidy3d/web/core/http_util.py                                0      -2  +1.90%
TOTAL                                                    +361     +98  -0.19%

Results for commit: c5f03f3

Minimum allowed coverage is 90%

♻️ This comment has been updated with latest results

Copy link
Contributor

github-actions bot commented May 26, 2025

badge

Changed Files Coverage

Filename                                                Stmts    Miss  Cover    Missing
----------------------------------------------------  -------  ------  -------  ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tidy3d/__init__.py                                         80       0  100.00%
tidy3d/components/mode_spec.py                             65       1  98.46%   221
tidy3d/components/simulation.py                          1782     112  93.71%   15-16, 140-141, 205, 213, 221, 362, 371, 407, 424, 439, 773-777, 825-829, 1048-1059, 1387, 1513-1514, 1551, 1557, 1598, 1603, 1615, 1688-1694, 1754, 2954, 3182, 3231, 3292, 3321, 3329, 3342, 3385, 3404, 3423-3432, 3458, 3466, 3527, 3589, 3616, 3695, 3819, 4161-4165, 4183, 4208, 4350, 4478-4482, 4499, 4669-4674, 4716, 4782-4783, 4844, 4849, 4853-4905, 4929, 4968-4970, 5008-5009, 5039, 5149, 5155-5166, 5200, 5221
tidy3d/components/validators.py                           212      10  95.28%   62, 129-136, 255, 271, 363
tidy3d/components/data/monitor_data.py                   1384      68  95.09%   118, 170, 180, 322, 447, 458, 492, 636-648, 659, 806, 852, 1185-1186, 1190-1191, 1205, 1375, 1636, 1755, 1772, 2024-2034, 2066-2067, 2083, 2092, 2106-2107, 2225, 2233-2244, 2303, 2533, 2612, 2625, 2748, 3111, 3230, 3252-3253, 3419, 3453, 3667-3668, 3775
tidy3d/components/geometry/utils.py                       213      13  93.90%   75, 80, 83-86, 114, 324, 342, 345-348, 513
tidy3d/components/microwave/path_integral_factory.py       20       0  100.00%
tidy3d/components/microwave/path_spec.py                  274      44  83.94%   107, 165-179, 269, 468-474, 503-526, 566-574, 701
tidy3d/components/microwave/path_spec_generator.py        115       0  100.00%
tidy3d/components/microwave/terminal_spec.py                8       0  100.00%
tidy3d/components/microwave/viz.py                         12       0  100.00%
tidy3d/components/mode/mode_solver.py                     900     243  73.00%   77-79, 108, 186-187, 197, 212, 258-260, 344-347, 378, 432, 457, 465-471, 476-547, 555-561, 567-605, 611-615, 649-731, 851-972, 977-984, 992-996, 1001-1034, 1045-1047, 1115, 1269-1281, 1311-1315, 1368-1380, 1397-1398, 1421-1422, 1472, 1529, 1603, 1721, 1736, 1741-1743, 1748-1754, 1762-1768, 1779, 1781, 1790, 1861, 2304, 2366-2368, 2531
tidy3d/components/mode/simulation.py                      128       6  95.31%   222-229
tidy3d/components/mode/validators.py                       11       0  100.00%
tidy3d/plugins/microwave/__init__.py                       10       0  100.00%
tidy3d/plugins/microwave/custom_path_integrals.py         109      33  69.72%   232-304
tidy3d/plugins/microwave/impedance_calculator.py           54       0  100.00%
tidy3d/plugins/microwave/path_integrals.py                134       2  98.51%   40, 212
tidy3d/plugins/microwave/viz.py                            10       0  100.00%
tidy3d/plugins/smatrix/ports/coaxial_lumped.py            165       0  100.00%
TOTAL                                                    5686     532  90.64%

Results for commit: c5f03f3

Minimum allowed coverage is 90%

♻️ This comment has been updated with latest results

@dmarek-flex dmarek-flex force-pushed the dmarek/add_terminal_mode_monitor branch from 3781b06 to c5f03f3 Compare May 28, 2025 01:49
Copy link
Contributor

@dbochkov-flexcompute dbochkov-flexcompute left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! Gave it a first pass, didn't spot anything major, just some minor comments/questions so far

@dmarek-flex dmarek-flex force-pushed the dmarek/add_terminal_mode_monitor branch 3 times, most recently from 3f3195e to 39318d6 Compare June 3, 2025 16:07
Copy link
Contributor

github-actions bot commented Jun 3, 2025

Diff Coverage

Diff: origin/develop...HEAD, staged and unstaged changes

  • tidy3d/init.py (100%)
  • tidy3d/components/data/data_array.py (100%)
  • tidy3d/components/data/monitor_data.py (50.0%): Missing lines 2067-2068
  • tidy3d/components/geometry/utils.py (77.4%): Missing lines 75,80,83-86,513
  • tidy3d/components/microwave/microwave_mode_spec.py (94.9%): Missing lines 53,60
  • tidy3d/components/microwave/path_integral_factory.py (61.1%): Missing lines 52,78,121-125,132-144,148
  • tidy3d/components/microwave/path_spec.py (83.9%): Missing lines 107,165,167-173,175-177,179,269,468-469,472-474,503,505-511,514,517,519-523,525-526,566-567,569-572,574,701
  • tidy3d/components/microwave/path_spec_generator.py (100%)
  • tidy3d/components/microwave/viz.py (100%)
  • tidy3d/components/mode/mode_solver.py (23.5%): Missing lines 505,1321,1327-1331,1334-1339
  • tidy3d/components/mode_spec.py (86.7%): Missing lines 264,270
  • tidy3d/components/simulation.py (100%)
  • tidy3d/plugins/microwave/custom_path_integrals.py (59.0%): Missing lines 234-235,240,242,245-247,250-257,260-263,266,268,271,274-276,278-279,281-282,285,288,291
  • tidy3d/plugins/microwave/impedance_calculator.py (100%)
  • tidy3d/plugins/microwave/path_integrals.py (100%)
  • tidy3d/plugins/smatrix/ports/coaxial_lumped.py (100%)

Summary

  • Total: 684 lines
  • Missing: 123 lines
  • Coverage: 82%

tidy3d/components/data/monitor_data.py

Lines 2063-2072

  2063             info["wg TE fraction"] = self.pol_fraction_waveguide["te"]
  2064             info["wg TM fraction"] = self.pol_fraction_waveguide["tm"]
  2065 
  2066         if self.Z0 is not None:
! 2067             info["Re(Z0)"] = self.Z0.real
! 2068             info["Im(Z0)"] = self.Z0.imag
  2069 
  2070         return xr.Dataset(data_vars=info)
  2071 
  2072     def to_dataframe(self) -> DataFrame:

tidy3d/components/geometry/utils.py

Lines 71-90

  71         Flat list of non-empty geometries matching the specified types.
  72     """
  73     # Handle single Shapely object by wrapping it in a list
  74     if isinstance(geoms, Shapely):
! 75         geoms = [geoms]
  76 
  77     flat = []
  78     for geom in geoms:
  79         if geom.is_empty:
! 80             continue
  81         if isinstance(geom, keep_types):
  82             flat.append(geom)
! 83         elif isinstance(geom, (MultiPolygon, MultiLineString, MultiPoint, GeometryCollection)):
! 84             flat.extend(flatten_shapely_geometries(geom.geoms, keep_types))
! 85         elif isinstance(geom, BaseGeometry) and hasattr(geom, "geoms"):
! 86             flat.extend(flatten_shapely_geometries(geom.geoms, keep_types))
  87     return flat
  88 
  89 
  90 def merging_geometries_on_plane(

Lines 509-517

  509         else:  # SnapType.Contract
  510             min_upper_bound_idx += snap_margin
  511             max_upper_bound_idx -= snap_margin
  512             if max_upper_bound_idx < min_upper_bound_idx:
! 513                 raise SetupError("The supplied 'snap_buffer' is too large for this contraction.")
  514             min_snap = get_upper_bound(interval_min, coords, min_upper_bound_idx, rel_tol=rtol)
  515             max_snap = get_lower_bound(interval_max, coords, max_upper_bound_idx, rel_tol=rtol)
  516         return (min_snap, max_snap)

tidy3d/components/microwave/microwave_mode_spec.py

Lines 49-57

  49     @property
  50     def num_voltage_specs(self) -> Optional[int]:
  51         """The number of voltage specifications supplied."""
  52         if type(self.voltage_spec) is tuple:
! 53             return len(self.voltage_spec)
  54         return None
  55 
  56     @property
  57     def num_current_specs(self) -> Optional[int]:

Lines 56-64

  56     @property
  57     def num_current_specs(self) -> Optional[int]:
  58         """The number of current specifications supplied."""
  59         if type(self.current_spec) is tuple:
! 60             return len(self.current_spec)
  61         return None
  62 
  63     @pd.validator("current_spec", always=True)
  64     def check_path_spec_combinations(cls, val, values):

tidy3d/components/microwave/path_integral_factory.py

Lines 48-56

  48         v_integral = VoltageIntegralAxisAligned(**path_spec.dict(exclude={"type"}))
  49     elif isinstance(path_spec, CustomVoltageIntegral2DSpec):
  50         v_integral = CustomVoltageIntegral2D(**path_spec.dict(exclude={"type"}))
  51     else:
! 52         raise ValidationError(f"Unsupported voltage path specification type: {type(path_spec)}")
  53     return v_integral
  54 
  55 
  56 def make_current_integral(path_spec: CurrentPathSpecTypes) -> CurrentIntegralTypes:

Lines 74-82

  74         i_integral = CustomCurrentIntegral2D(**path_spec.dict(exclude={"type"}))
  75     elif isinstance(path_spec, CompositeCurrentIntegralSpec):
  76         i_integral = CompositeCurrentIntegral(**path_spec.dict(exclude={"type"}))
  77     else:
! 78         raise ValidationError(f"Unsupported current path specification type: {type(path_spec)}")
  79     return i_integral
  80 
  81 
  82 def make_path_integrals(

Lines 117-129

  117                 sim.symmetry,
  118                 sim.bounding_box,
  119                 monitor.colocate,
  120             )
! 121             i_specs = (i_spec,) * monitor.mode_spec.num_modes
! 122         if v_specs is None:
! 123             v_specs = (None,) * monitor.mode_spec.num_modes
! 124         if i_specs is None:
! 125             i_specs = (None,) * monitor.mode_spec.num_modes
  126 
  127     except ValidationError as e:
  128         raise SetupError(
  129             f"Failed to auto-generate path specification for impedance calculation in monitor '{monitor.name}'."

Lines 128-149

  128         raise SetupError(
  129             f"Failed to auto-generate path specification for impedance calculation in monitor '{monitor.name}'."
  130         ) from e
  131 
! 132     try:
! 133         path_integrals = []
! 134         for v_spec, i_spec in zip(v_specs, i_specs):
! 135             v_integral = None
! 136             i_integral = None
! 137             if v_spec is not None:
! 138                 v_integral = make_voltage_integral(v_spec)
! 139             if i_spec is not None:
! 140                 i_integral = make_current_integral(i_spec)
! 141             path_integrals.append((v_integral, i_integral))
! 142         path_integrals = tuple(path_integrals)
! 143     except Exception as e:
! 144         raise SetupError(
  145             f"Failed to construct path integrals from the microwave mode specification for monitor '{monitor.name}'. "
  146             "Please create a github issue so that the problem can be investigated."
  147         ) from e
! 148     return path_integrals

tidy3d/components/microwave/path_spec.py

Lines 103-111

  103         """Axis for performing integration."""
  104         for index, value in enumerate(self.size):
  105             if value != 0:
  106                 return index
! 107         raise Tidy3dError("Failed to identify axis.")
  108 
  109     def _vertices_2D(self, axis: Axis) -> tuple[Coordinate2D, Coordinate2D]:
  110         """Returns the two vertices of this path in the plane defined by ``axis``."""
  111         min = self.bounds[0]

Lines 161-183

  161         -------
  162         VoltageIntegralAxisAlignedSpec
  163             The created path integral for computing voltage between the two terminals.
  164         """
! 165         axis_positions = Geometry.parse_two_xyz_kwargs(x=x, y=y, z=z)
  166         # Calculate center and size of the future box
! 167         midpoint = (plus_terminal + minus_terminal) / 2
! 168         length = np.abs(plus_terminal - minus_terminal)
! 169         center = [midpoint, midpoint, midpoint]
! 170         size = [length, length, length]
! 171         for axis, position in axis_positions:
! 172             size[axis] = 0
! 173             center[axis] = position
  174 
! 175         direction = "+"
! 176         if plus_terminal < minus_terminal:
! 177             direction = "-"
  178 
! 179         return VoltageIntegralAxisAlignedSpec(
  180             center=center,
  181             size=size,
  182             extrapolate_to_endpoints=extrapolate_to_endpoints,
  183             snap_path_to_grid=snap_path_to_grid,

Lines 265-273

  265         """Axis normal to loop"""
  266         for index, value in enumerate(self.size):
  267             if value == 0:
  268                 return index
! 269         raise Tidy3dError("Failed to identify axis.")
  270 
  271     def _to_path_integral_specs(
  272         self, h_horizontal=None, h_vertical=None
  273     ) -> tuple[AxisAlignedPathIntegralSpec, ...]:

Lines 464-478

  464 
  465     @staticmethod
  466     def _compute_dl_component(coord_array: xr.DataArray, closed_contour=False) -> np.array:
  467         """Computes the differential length element along the integration path."""
! 468         dl = np.gradient(coord_array)
! 469         if closed_contour:
  470             # If the contour is closed, we can use central difference on the starting/end point
  471             # which will be more accurate than the default forward/backward choice in np.gradient
! 472             grad_end = np.gradient([coord_array[-2], coord_array[0], coord_array[1]])
! 473             dl[0] = dl[-1] = grad_end[1]
! 474         return dl
  475 
  476     @classmethod
  477     def from_circular_path(
  478         cls, center: Coordinate, radius: float, num_points: int, normal_axis: Axis, clockwise: bool

Lines 499-530

  499         :class:`.CustomPathIntegral2DSpec`
  500             A path integral defined on a circular path.
  501         """
  502 
! 503         def generate_circle_coordinates(radius: float, num_points: int, clockwise: bool):
  504             """Helper for generating x,y vertices around a circle in the local coordinate frame."""
! 505             sign = 1.0
! 506             if clockwise:
! 507                 sign = -1.0
! 508             angles = np.linspace(0, sign * 2 * np.pi, num_points, endpoint=True)
! 509             xt = radius * np.cos(angles)
! 510             yt = radius * np.sin(angles)
! 511             return (xt, yt)
  512 
  513         # Get transverse axes
! 514         normal_center, trans_center = Geometry.pop_axis(center, normal_axis)
  515 
  516         # These x,y coordinates in the local coordinate frame
! 517         if normal_axis == 1:
  518             # Handle special case when y is the axis that is popped
! 519             clockwise = not clockwise
! 520         xt, yt = generate_circle_coordinates(radius, num_points, clockwise)
! 521         xt += trans_center[0]
! 522         yt += trans_center[1]
! 523         circle_vertices = np.column_stack((xt, yt))
  524         # Close the contour exactly
! 525         circle_vertices[-1, :] = circle_vertices[0, :]
! 526         return cls(axis=normal_axis, position=normal_center, vertices=circle_vertices)
  527 
  528     @cached_property
  529     def is_closed_contour(self) -> bool:
  530         """Returns ``true`` when the first vertex equals the last vertex."""

Lines 562-578

  562 
  563     @cached_property
  564     def sign(self) -> Direction:
  565         """Uses the ordering of the vertices to determine the direction of the current flow."""
! 566         linestr = shapely.LineString(coordinates=self.vertices)
! 567         is_ccw = shapely.is_ccw(linestr)
  568         # Invert statement when the vertices are given as (x, z)
! 569         if self.axis == 1:
! 570             is_ccw = not is_ccw
! 571         if is_ccw:
! 572             return "+"
  573         else:
! 574             return "-"
  575 
  576 
  577 class CustomVoltageIntegral2DSpec(CustomPathIntegral2DSpec):
  578     """Class for specfying the computation of voltage between two points defined by a custom path.

Lines 697-705

  697         linestr = shapely.LineString(coordinates=self.vertices)
  698         is_ccw = shapely.is_ccw(linestr)
  699         # Invert statement when the vertices are given as (x, z)
  700         if self.axis == 1:
! 701             is_ccw = not is_ccw
  702         if is_ccw:
  703             return "+"
  704         else:
  705             return "-"

tidy3d/components/mode/mode_solver.py

Lines 501-509

  501 
  502         mode_solver_data = self._filter_components(mode_solver_data)
  503         # Calculate and add the characteristic impedance
  504         if self.mode_spec.microwave_mode_spec is not None:
! 505             mode_solver_data = self._add_characteristic_impedance(mode_solver_data)
  506         return mode_solver_data
  507 
  508     @cached_property
  509     def bend_axis_3d(self) -> Axis:

Lines 1317-1325

  1317     def _add_characteristic_impedance(self, mode_solver_data: ModeSolverData):
  1318         """Calculate and add characteristic impedance to ``mode_solver_data`` with the path specifications.
  1319         If they were not supplied by the user, then create a specification automatically.
  1320         """
! 1321         path_integrals = make_path_integrals(
  1322             self.mode_spec.microwave_mode_spec,
  1323             self.to_monitor(name=MODE_MONITOR_NAME),
  1324             self.simulation,
  1325         )

Lines 1323-1343

  1323             self.to_monitor(name=MODE_MONITOR_NAME),
  1324             self.simulation,
  1325         )
  1326         # Need to operate on the full symmetry expanded fields
! 1327         mode_solver_data_expanded = mode_solver_data.symmetry_expanded_copy
! 1328         Z0_list = []
! 1329         for mode_index in range(self.mode_spec.num_modes):
! 1330             integral_pair = path_integrals[mode_index]
! 1331             impedance_calc = ImpedanceCalculator(
  1332                 voltage_integral=integral_pair[0], current_integral=integral_pair[1]
  1333             )
! 1334             single_mode_data = mode_solver_data_expanded._isel(mode_index=[mode_index])
! 1335             Z0 = impedance_calc.compute_impedance(single_mode_data)
! 1336             Z0_list.append(Z0)
! 1337         all_mode_Z0 = xr.concat(Z0_list, dim="mode_index")
! 1338         all_mode_Z0 = ImpedanceCalculator._set_data_array_attributes(all_mode_Z0)
! 1339         return mode_solver_data.updated_copy(Z0=all_mode_Z0)
  1340 
  1341     @cached_property
  1342     def data(self) -> ModeSolverData:
  1343         """:class:`.ModeSolverData` containing the field and effective index data.

tidy3d/components/mode_spec.py

Lines 260-268

  260             val.num_current_specs is None or val.num_current_specs == num_modes
  261         )
  262 
  263         if not valid_number_voltage_specs:
! 264             raise SetupError(
  265                 f"Given {val.num_voltage_specs} voltage specifications, but the number of modes requested is {num_modes}. "
  266                 "Please either ensure that the number of voltage specifications is equal to the "
  267                 "number of modes or leave this field as 'None' in the 'MicrowaveModeSpec'."
  268             )

Lines 266-274

  266                 "Please either ensure that the number of voltage specifications is equal to the "
  267                 "number of modes or leave this field as 'None' in the 'MicrowaveModeSpec'."
  268             )
  269         if not valid_number_current_specs:
! 270             raise SetupError(
  271                 f"Given {val.num_current_specs} current specifications, but the number of modes requested is {num_modes}. "
  272                 "Please either ensure that the number of voltage specifications is equal to the "
  273                 "number of modes or leave this field as 'None' in the 'MicrowaveModeSpec'."
  274             )

tidy3d/plugins/microwave/custom_path_integrals.py

Lines 230-295

  230     """Current integral comprising one or more disjoint paths"""
  231 
  232     def compute_current(self, em_field: MonitorDataTypes) -> IntegralResultTypes:
  233         """Compute current flowing in loop defined by the outer edge of a rectangle."""
! 234         if isinstance(em_field, FieldTimeData) and self.sum_spec == "split":
! 235             raise DataError(
  236                 "Only frequency domain field data is supported when using the 'split' sum_spec. "
  237                 "Either switch the sum_spec to 'sum' or supply frequency domain data."
  238             )
  239 
! 240         from tidy3d.components.microwave.path_integral_factory import make_current_integral
  241 
! 242         current_integrals = [make_current_integral(path_spec) for path_spec in self.path_specs]
  243 
  244         # Initialize arrays with first current term
! 245         first_term = current_integrals[0].compute_current(em_field)
! 246         current_in_phase = xr.zeros_like(first_term)
! 247         current_out_phase = xr.zeros_like(first_term)
  248 
  249         # Get reference phase from first non-zero current
! 250         phase_reference = None
! 251         for path in current_integrals:
! 252             term = path.compute_current(em_field)
! 253             if np.any(term.abs > 0):
! 254                 phase_reference = term.angle
! 255                 break
! 256         if phase_reference is None:
! 257             raise DataError("Cannot complete calculation of current. No non-zero current found.")
  258 
  259         # Accumulate currents based on phase comparison
! 260         for path in current_integrals:
! 261             term = path.compute_current(em_field)
! 262             if np.all(term.abs == 0):
! 263                 continue
  264 
  265             # Compare phase to reference
! 266             phase_diff = term.angle - phase_reference
  267             # Wrap phase difference to [-pi, pi]
! 268             phase_diff.values = np.mod(phase_diff.values + np.pi, 2 * np.pi) - np.pi
  269 
  270             # Check phase consistency across frequencies
! 271             self._check_phase_sign_consistency(phase_diff)
  272 
  273             # Add to in-phase or out-of-phase current
! 274             is_in_phase = phase_diff <= np.pi / 2
! 275             current_in_phase += xr.where(is_in_phase, term, 0)
! 276             current_out_phase += xr.where(~is_in_phase, term, 0)
  277 
! 278         current_in_phase = CurrentIntegralAxisAligned._set_data_array_attributes(current_in_phase)
! 279         current_out_phase = CurrentIntegralAxisAligned._set_data_array_attributes(current_out_phase)
  280 
! 281         if self.sum_spec == "sum":
! 282             return current_in_phase + current_out_phase
  283 
  284         # Check amplitude consistency across frequencies
! 285         self._check_phase_amplitude_consistency(current_in_phase, current_out_phase)
  286 
  287         # For split mode, return the larger magnitude current
! 288         current = xr.where(
  289             abs(current_in_phase) >= abs(current_out_phase), current_in_phase, current_out_phase
  290         )
! 291         return CurrentIntegralAxisAligned._set_data_array_attributes(current)
  292 
  293     def _check_phase_sign_consistency(
  294         self,
  295         phase_difference: Union[FreqDataArray, FreqModeDataArray],

@dmarek-flex dmarek-flex force-pushed the dmarek/add_terminal_mode_monitor branch from 39318d6 to 469d8ae Compare June 3, 2025 20:51
@dmarek-flex
Copy link
Contributor Author

Planning on changing the name to ImpedanceSpec, which makes more sense. A terminal would correspond with a single conductor, so doesn't really make sense here.

@dbochkov-flexcompute
Copy link
Contributor

Planning on changing the name to ImpedanceSpec, which makes more sense. A terminal would correspond with a single conductor, so doesn't really make sense here.

That does make more sense. Are there any other impedance specs that might be introduced in future? Just wondering if it would also make sense to expand it to ModeImpedanceSpec or LineImpedanceSpec to prevent any possible name collisions in future (field name impedance_spec doesn't need to be expanded though)

@dmarek-flex
Copy link
Contributor Author

That does make more sense. Are there any other impedance specs that might be introduced in future? Just wondering if it would also make sense to expand it to ModeImpedanceSpec or LineImpedanceSpec to prevent any possible name collisions in future (field name impedance_spec doesn't need to be expanded though)

Yea I think ModeImpedanceSpec would be a good expansion for it, that is its main purpose.

@yaugenst-flex
Copy link
Collaborator

@greptileai

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

20 files reviewed, 8 comments
Edit PR Review Bot Settings | Greptile

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

20 files reviewed, 3 comments
Edit PR Review Bot Settings | Greptile

@dmarek-flex dmarek-flex force-pushed the dmarek/add_terminal_mode_monitor branch from 48c29ea to 55f9529 Compare June 17, 2025 15:11
adding support for colocated fields

fixing bug for more complicated shapes

adding tests, fixing symmetric conditions, fixing for 2D structures

fix simulation validator for terminal spec

refactor by splitting the path integral specification away from the integral computation, now the path specification handles pretty much everything, except for the final integral computation and results preparation

rename auto patch spec to generator type name, fixed tests

add test for sim validation and improved doc strings

fix regression

fix python 3.9 tests

reorg of ModeData, the impedance is now calculated by the ModeSolver class avoiding the need for a duplicate terminal spec

add more descriptive warnings to composite current integral

renamed to MicrowaveModeSpec and allow for supplying manual path specifications for each mode
@dmarek-flex dmarek-flex force-pushed the dmarek/add_terminal_mode_monitor branch from 55f9529 to 8500a6a Compare June 18, 2025 14:01
@dmarek-flex
Copy link
Contributor Author

@dbochkov-flexcompute I finally settled on MicrowaveModeSpec, the reason being is that it really is a bit more than simply defining how impedance is calculated. It also defines how voltage/current should be related to the waveguide mode, which will be used in the WavePort. Although easy enough to change.

Also in the future, I can see how it might provide additional features/specification fields, like making linear combinations of modes to excite a specified conductor, instead of relying on the set of modes returned by the mode solver. Also combining modes to give a desired polarization direction that is based on the voltage/current paths.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.9 will go into version 2.9.*
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Automatic setup of path integrals
4 participants