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

@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

Comment on lines 2016 to 2020
from tidy3d.components.microwave.path_integral_factory import (
make_current_integral,
make_voltage_integral,
)
from tidy3d.plugins.microwave.impedance_calculator import ImpedanceCalculator
Copy link
Contributor

Choose a reason for hiding this comment

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

any reason not to import these at the top level?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

circular dependency 😢 Many of those popped up in this PR

Copy link
Collaborator

@daquinteroflex daquinteroflex Jun 2, 2025

Choose a reason for hiding this comment

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

Yes I think this is part of why we need to do the RF refactor soon on the lines of what we chatted back on Friday too.

@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/monitor_data.py (50.0%): Missing lines 2044-2045
  • tidy3d/components/geometry/utils.py (77.4%): Missing lines 75,80,83-86,513
  • tidy3d/components/microwave/path_integral_factory.py (85.0%): Missing lines 121-125,129
  • 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/terminal_spec.py (100%)
  • tidy3d/components/microwave/viz.py (100%)
  • tidy3d/components/mode/mode_solver.py (44.4%): Missing lines 488,1300,1303,1307-1308
  • tidy3d/components/mode_spec.py (100%)
  • tidy3d/components/simulation.py (100%)
  • tidy3d/plugins/microwave/custom_path_integrals.py (21.6%): Missing lines 233-234,239,241,244-246,249-256,259-262,265,267,270-275,281-283,285-286,291-296,301,304
  • tidy3d/plugins/microwave/impedance_calculator.py (100%)
  • tidy3d/plugins/microwave/path_integrals.py (100%)
  • tidy3d/plugins/smatrix/ports/coaxial_lumped.py (100%)

Summary

  • Total: 598 lines
  • Missing: 104 lines
  • Coverage: 82%

tidy3d/components/data/monitor_data.py

Lines 2040-2049

  2040             info["wg TE fraction"] = self.pol_fraction_waveguide["te"]
  2041             info["wg TM fraction"] = self.pol_fraction_waveguide["tm"]
  2042 
  2043         if self.Z0 is not None:
! 2044             info["Re(Z0)"] = self.Z0.real
! 2045             info["Im(Z0)"] = self.Z0.imag
  2046 
  2047         return xr.Dataset(data_vars=info)
  2048 
  2049     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/path_integral_factory.py

Lines 117-130

  117         raise SetupError(
  118             f"Failed to auto-generate path specification for impedance calculation in monitor '{monitor.name}'."
  119         ) from e
  120 
! 121     try:
! 122         v_integral = make_voltage_integral(v_spec)
! 123         i_integral = make_current_integral(i_spec)
! 124     except Exception as e:
! 125         raise SetupError(
  126             f"Failed to construct path integrals from the terminal specification for monitor '{monitor.name}'. "
  127             "Please create a github issue so that the problem can be investigated."
  128         ) from e
! 129     return (v_integral, i_integral)

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 484-492

  484 
  485         mode_solver_data = self._filter_components(mode_solver_data)
  486         # Calculate and add the characteristic impedance
  487         if self.mode_spec.terminal_spec is not None:
! 488             mode_solver_data = self._add_characteristic_impedance(mode_solver_data)
  489         return mode_solver_data
  490 
  491     @cached_property
  492     def bend_axis_3d(self) -> Axis:

Lines 1296-1312

  1296     def _add_characteristic_impedance(self, mode_solver_data: ModeSolverData):
  1297         """Calculate and add characteristic impedance to ``mode_solver_data`` with the path specifications.
  1298         If they were not supplied by the user, then create a specification automatically.
  1299         """
! 1300         v_integral, i_integral = make_path_integrals(
  1301             self.mode_spec.terminal_spec, self.to_monitor(name=MODE_MONITOR_NAME), self.simulation
  1302         )
! 1303         impedance_calc = ImpedanceCalculator(
  1304             voltage_integral=v_integral, current_integral=i_integral
  1305         )
  1306         # Need to operate on the full symmetry expanded fields
! 1307         Z0 = impedance_calc.compute_impedance(mode_solver_data.symmetry_expanded_copy)
! 1308         return mode_solver_data.updated_copy(Z0=Z0)
  1309 
  1310     @cached_property
  1311     def data(self) -> ModeSolverData:
  1312         """:class:`.ModeSolverData` containing the field and effective index data.

tidy3d/plugins/microwave/custom_path_integrals.py

Lines 229-279

  229     """Current integral comprising one or more disjoint paths"""
  230 
  231     def compute_current(self, em_field: MonitorDataTypes) -> IntegralResultTypes:
  232         """Compute current flowing in loop defined by the outer edge of a rectangle."""
! 233         if isinstance(em_field, FieldTimeData) and self.sum_spec == "split":
! 234             raise DataError(
  235                 "Only frequency domain field data is supported when using the 'split' sum_spec. "
  236                 "Either switch the sum_spec to 'sum' or supply frequency domain data."
  237             )
  238 
! 239         from tidy3d.components.microwave.path_integral_factory import make_current_integral
  240 
! 241         current_integrals = [make_current_integral(path_spec) for path_spec in self.path_specs]
  242 
  243         # Initialize arrays with first current term
! 244         first_term = current_integrals[0].compute_current(em_field)
! 245         current_in_phase = xr.zeros_like(first_term)
! 246         current_out_phase = xr.zeros_like(first_term)
  247 
  248         # Get reference phase from first non-zero current
! 249         phase_reference = None
! 250         for path in current_integrals:
! 251             term = path.compute_current(em_field)
! 252             if np.any(abs(term) > 0):
! 253                 phase_reference = np.angle(term)
! 254                 break
! 255         if phase_reference is None:
! 256             raise DataError("Cannot complete calculation of current. No non-zero current found.")
  257 
  258         # Accumulate currents based on phase comparison
! 259         for path in current_integrals:
! 260             term = path.compute_current(em_field)
! 261             if np.all(abs(term) == 0):
! 262                 continue
  263 
  264             # Compare phase to reference
! 265             phase_diff = np.angle(term) - phase_reference
  266             # Wrap phase difference to [-pi, pi]
! 267             phase_diff = np.mod(phase_diff + np.pi, 2 * np.pi) - np.pi
  268 
  269             # Check phase consistency across frequencies
! 270             freq_axis = term.get_axis_num("f")
! 271             all_in_phase = np.all(abs(phase_diff) <= np.pi / 2, axis=freq_axis)
! 272             all_out_of_phase = np.all(abs(phase_diff) > np.pi / 2, axis=freq_axis)
! 273             consistent_phase = np.logical_or(all_in_phase, all_out_of_phase)
! 274             if not np.all(consistent_phase) and self.sum_spec == "split":
! 275                 log.warning(
  276                     "Phase alignment of current is not consistent across frequencies. "
  277                     "The current calculation may be inaccurate."
  278                 )

Lines 277-305

  277                     "The current calculation may be inaccurate."
  278                 )
  279 
  280             # Add to in-phase or out-of-phase current
! 281             is_in_phase = abs(phase_diff) <= np.pi / 2
! 282             current_in_phase += xr.where(is_in_phase, term, 0)
! 283             current_out_phase += xr.where(~is_in_phase, term, 0)
  284 
! 285         if self.sum_spec == "sum":
! 286             return CurrentIntegralAxisAligned._set_data_array_attributes(
  287                 current_in_phase + current_out_phase
  288             )
  289 
  290         # For split mode, return the larger magnitude current
! 291         freq_axis = current_in_phase.get_axis_num("f")
! 292         in_all_larger = np.all(abs(current_in_phase) >= abs(current_out_phase), axis=freq_axis)
! 293         in_all_smaller = np.all(abs(current_in_phase) < abs(current_out_phase), axis=freq_axis)
! 294         consistent_max_current = np.logical_or(in_all_larger, in_all_smaller)
! 295         if not np.all(consistent_max_current) and self.sum_spec == "split":
! 296             log.warning(
  297                 "There is not a consistently larger current across frequencies between the in "
  298                 "and out of phase components. The current calculation may be inaccurate."
  299             )
  300 
! 301         current = xr.where(
  302             abs(current_in_phase) >= abs(current_out_phase), current_in_phase, current_out_phase
  303         )
! 304         return CurrentIntegralAxisAligned._set_data_array_attributes(current)

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

rebase on ruff changes

reorg of ModeData, the impedance is now calculated by the ModeSolver class avoiding the need for a duplicate terminal spec
@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.

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
3 participants