paramrf 0.26.1__tar.gz → 0.26.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. {paramrf-0.26.1/paramrf.egg-info → paramrf-0.26.4}/PKG-INFO +1 -1
  2. {paramrf-0.26.1 → paramrf-0.26.4}/docs/api/index.rst +1 -1
  3. {paramrf-0.26.1 → paramrf-0.26.4/paramrf.egg-info}/PKG-INFO +1 -1
  4. {paramrf-0.26.1 → paramrf-0.26.4}/paramrf.egg-info/SOURCES.txt +5 -2
  5. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/__init__.py +1 -1
  6. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/base.py +58 -14
  7. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/ideal.py +1 -20
  8. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/lumped.py +175 -202
  9. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/sections.py +16 -101
  10. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/composite/interconnected.py +17 -1
  11. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/parameters.py +2 -2
  12. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/rf/__init__.py +10 -2
  13. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/rf/conversions.py +291 -42
  14. paramrf-0.26.4/pmrf/rf/mna.py +25 -0
  15. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/__init__.py +7 -0
  16. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/base.py +75 -1
  17. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/reduce.py +114 -2
  18. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/kron.py +15 -2
  19. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/mobius_terminator.py +5 -5
  20. paramrf-0.26.4/pmrf/simulate/solvers/modified_kron.py +82 -0
  21. {paramrf-0.26.1/pmrf → paramrf-0.26.4/pmrf/simulate}/topology.py +63 -2
  22. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/__init__.py +3 -1
  23. paramrf-0.26.4/pmrf/utils/debug.py +72 -0
  24. {paramrf-0.26.1 → paramrf-0.26.4}/pyproject.toml +1 -1
  25. {paramrf-0.26.1 → paramrf-0.26.4}/.github/workflows/docs.yml +0 -0
  26. {paramrf-0.26.1 → paramrf-0.26.4}/.github/workflows/draft-pdf.yml +0 -0
  27. {paramrf-0.26.1 → paramrf-0.26.4}/.github/workflows/publish.yml +0 -0
  28. {paramrf-0.26.1 → paramrf-0.26.4}/.github/workflows/tests.yml +0 -0
  29. {paramrf-0.26.1 → paramrf-0.26.4}/.gitignore +0 -0
  30. {paramrf-0.26.1 → paramrf-0.26.4}/CONTRIBUTING.md +0 -0
  31. {paramrf-0.26.1 → paramrf-0.26.4}/LICENSE +0 -0
  32. {paramrf-0.26.1 → paramrf-0.26.4}/README.rst +0 -0
  33. {paramrf-0.26.1 → paramrf-0.26.4}/assets/logo.png +0 -0
  34. {paramrf-0.26.1 → paramrf-0.26.4}/docs/Makefile +0 -0
  35. {paramrf-0.26.1 → paramrf-0.26.4}/docs/_static/custom.css +0 -0
  36. {paramrf-0.26.1 → paramrf-0.26.4}/docs/_templates/autosummary/class.rst +0 -0
  37. {paramrf-0.26.1 → paramrf-0.26.4}/docs/_templates/autosummary/function.rst +0 -0
  38. {paramrf-0.26.1 → paramrf-0.26.4}/docs/_templates/autosummary/module.rst +0 -0
  39. {paramrf-0.26.1 → paramrf-0.26.4}/docs/conf.py +0 -0
  40. {paramrf-0.26.1 → paramrf-0.26.4}/docs/core_concepts/core_primitives.rst +0 -0
  41. {paramrf-0.26.1 → paramrf-0.26.4}/docs/core_concepts/index.rst +0 -0
  42. {paramrf-0.26.1 → paramrf-0.26.4}/docs/core_concepts/jax_overview.rst +0 -0
  43. {paramrf-0.26.1 → paramrf-0.26.4}/docs/core_concepts/optimization_and_inference.rst +0 -0
  44. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/cascading_and_terminating.rst +0 -0
  45. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/circuit_clc.png +0 -0
  46. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/circuit_models.rst +0 -0
  47. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/custom_composite_models.rst +0 -0
  48. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/custom_parametric_models.rst +0 -0
  49. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/derivatives_and_sweeps.rst +0 -0
  50. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/index.rst +0 -0
  51. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/model_optimization.rst +0 -0
  52. {paramrf-0.26.1 → paramrf-0.26.4}/docs/examples/parameter_naming_and_model_manipulation.rst +0 -0
  53. {paramrf-0.26.1 → paramrf-0.26.4}/docs/index.rst +0 -0
  54. {paramrf-0.26.1 → paramrf-0.26.4}/docs/license.rst +0 -0
  55. {paramrf-0.26.1 → paramrf-0.26.4}/docs/make.bat +0 -0
  56. {paramrf-0.26.1 → paramrf-0.26.4}/docs/models/index.rst +0 -0
  57. {paramrf-0.26.1 → paramrf-0.26.4}/docs/skrf_comparison.rst +0 -0
  58. {paramrf-0.26.1 → paramrf-0.26.4}/docs/tutorials/1_cable_fitting.ipynb +0 -0
  59. {paramrf-0.26.1 → paramrf-0.26.4}/docs/tutorials/2_chip_inductor_fitting.ipynb +0 -0
  60. {paramrf-0.26.1 → paramrf-0.26.4}/docs/tutorials/data/CBN-1.5FT-SMSM.s2p +0 -0
  61. {paramrf-0.26.1 → paramrf-0.26.4}/docs/tutorials/data/on-chip-inductor.s2p +0 -0
  62. {paramrf-0.26.1 → paramrf-0.26.4}/docs/tutorials/index.rst +0 -0
  63. {paramrf-0.26.1 → paramrf-0.26.4}/paper/paper.bib +0 -0
  64. {paramrf-0.26.1 → paramrf-0.26.4}/paper/paper.md +0 -0
  65. {paramrf-0.26.1 → paramrf-0.26.4}/paper/rlc.png +0 -0
  66. {paramrf-0.26.1 → paramrf-0.26.4}/paramrf.egg-info/dependency_links.txt +0 -0
  67. {paramrf-0.26.1 → paramrf-0.26.4}/paramrf.egg-info/requires.txt +0 -0
  68. {paramrf-0.26.1 → paramrf-0.26.4}/paramrf.egg-info/top_level.txt +0 -0
  69. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/constraints.py +0 -0
  70. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/covariance_kernels.py +0 -0
  71. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/discrepancy_models.py +0 -0
  72. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/distributions.py +0 -0
  73. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/evaluators.py +0 -0
  74. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/fitting/__init__.py +0 -0
  75. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/fitting/minimize.py +0 -0
  76. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/fitting/result.py +0 -0
  77. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/fitting/routers.py +0 -0
  78. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/fitting/sample.py +0 -0
  79. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/frequency.py +0 -0
  80. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/__init__.py +0 -0
  81. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/base.py +0 -0
  82. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/result.py +0 -0
  83. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/sample.py +0 -0
  84. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/solvers/__init__.py +0 -0
  85. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/solvers/blackjax.py +0 -0
  86. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/infer/solvers/polychord.py +0 -0
  87. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/likelihoods.py +0 -0
  88. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/losses.py +0 -0
  89. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/math/__init__.py +0 -0
  90. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/math/aggregations.py +0 -0
  91. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/math/conversions.py +0 -0
  92. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/math/losses.py +0 -0
  93. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/math/misc.py +0 -0
  94. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/__init__.py +0 -0
  95. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/adapters/__init__.py +0 -0
  96. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/adapters/base.py +0 -0
  97. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/adapters/bridge.py +0 -0
  98. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/adapters/callable.py +0 -0
  99. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/adapters/static.py +0 -0
  100. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/__init__.py +0 -0
  101. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/lines/__init__.py +0 -0
  102. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/lines/nonuniform.py +0 -0
  103. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/components/lines/uniform.py +0 -0
  104. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/composite/__init__.py +0 -0
  105. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/composite/nodal.py +0 -0
  106. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/composite/topological.py +0 -0
  107. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/composite/transformed.py +0 -0
  108. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/composite/wrapped.py +0 -0
  109. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/surrogates/__init__.py +0 -0
  110. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/surrogates/expansion.py +0 -0
  111. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/models/surrogates/rational.py +0 -0
  112. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/network_collection.py +0 -0
  113. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/noise_models.py +0 -0
  114. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/__init__.py +0 -0
  115. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/base.py +0 -0
  116. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/minimize.py +0 -0
  117. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/result.py +0 -0
  118. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/solvers/__init__.py +0 -0
  119. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/solvers/jaxopt.py +0 -0
  120. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/solvers/optimistix.py +0 -0
  121. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/optimize/solvers/scipy.py +0 -0
  122. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/problem.py +0 -0
  123. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/serialization.py +0 -0
  124. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/cascade.py +0 -0
  125. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/result.py +0 -0
  126. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/__init__.py +0 -0
  127. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/hallbjorner.py +0 -0
  128. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/linear_fractional_terminator.py +0 -0
  129. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/redheffer.py +0 -0
  130. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/solvers/transfer_cascader.py +0 -0
  131. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/simulate/terminate.py +0 -0
  132. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/types.py +0 -0
  133. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/array.py +0 -0
  134. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/network.py +0 -0
  135. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/optix.py +0 -0
  136. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/random.py +0 -0
  137. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/rf.py +0 -0
  138. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/transforms.py +0 -0
  139. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/tree.py +0 -0
  140. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/utils/type.py +0 -0
  141. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/viz/__init__.py +0 -0
  142. {paramrf-0.26.1 → paramrf-0.26.4}/pmrf/viz/plots.py +0 -0
  143. {paramrf-0.26.1 → paramrf-0.26.4}/setup.cfg +0 -0
  144. {paramrf-0.26.1 → paramrf-0.26.4}/tests/__init__.py +0 -0
  145. {paramrf-0.26.1 → paramrf-0.26.4}/tests/data/10m_cable.s2p +0 -0
  146. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_autodiff.py +0 -0
  147. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_evaluators.py +0 -0
  148. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_fitting_minimize.py +0 -0
  149. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_fitting_routers.py +0 -0
  150. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_fitting_sample.py +0 -0
  151. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_frequency.py +0 -0
  152. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_infer_base.py +0 -0
  153. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_infer_sample.py +0 -0
  154. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_model.py +0 -0
  155. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_models/test_adapters.py +0 -0
  156. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_models/test_interconnected.py +0 -0
  157. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_models/test_lines.py +0 -0
  158. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_models/test_lumped.py +0 -0
  159. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_models/test_nodal.py +0 -0
  160. /paramrf-0.26.1/tests/test_models/test_topological.py → /paramrf-0.26.4/tests/test_models/test_sections.py +0 -0
  161. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_models/test_transformed.py +0 -0
  162. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_naming.py +0 -0
  163. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_optimize_base.py +0 -0
  164. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_optimize_minimize.py +0 -0
  165. {paramrf-0.26.1 → paramrf-0.26.4}/tests/test_transforms.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paramrf
3
- Version: 0.26.1
3
+ Version: 0.26.4
4
4
  Summary: Parametric radio frequency modeling
5
5
  Author-email: Gary Allen <gvcallen@gmail.com>
6
6
  Project-URL: homepage, https://github.com/gvcallen/paramrf
@@ -76,4 +76,4 @@ Utilities
76
76
  pmrf.unwrap
77
77
  pmrf.unwrap_self
78
78
  pmrf.as_fixed
79
- pmrf.as_free
79
+ pmrf.as_variable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paramrf
3
- Version: 0.26.1
3
+ Version: 0.26.4
4
4
  Summary: Parametric radio frequency modeling
5
5
  Author-email: Gary Allen <gvcallen@gmail.com>
6
6
  Project-URL: homepage, https://github.com/gvcallen/paramrf
@@ -60,7 +60,6 @@ pmrf/noise_models.py
60
60
  pmrf/parameters.py
61
61
  pmrf/problem.py
62
62
  pmrf/serialization.py
63
- pmrf/topology.py
64
63
  pmrf/types.py
65
64
  pmrf/fitting/__init__.py
66
65
  pmrf/fitting/minimize.py
@@ -112,21 +111,25 @@ pmrf/optimize/solvers/optimistix.py
112
111
  pmrf/optimize/solvers/scipy.py
113
112
  pmrf/rf/__init__.py
114
113
  pmrf/rf/conversions.py
114
+ pmrf/rf/mna.py
115
115
  pmrf/simulate/__init__.py
116
116
  pmrf/simulate/base.py
117
117
  pmrf/simulate/cascade.py
118
118
  pmrf/simulate/reduce.py
119
119
  pmrf/simulate/result.py
120
120
  pmrf/simulate/terminate.py
121
+ pmrf/simulate/topology.py
121
122
  pmrf/simulate/solvers/__init__.py
122
123
  pmrf/simulate/solvers/hallbjorner.py
123
124
  pmrf/simulate/solvers/kron.py
124
125
  pmrf/simulate/solvers/linear_fractional_terminator.py
125
126
  pmrf/simulate/solvers/mobius_terminator.py
127
+ pmrf/simulate/solvers/modified_kron.py
126
128
  pmrf/simulate/solvers/redheffer.py
127
129
  pmrf/simulate/solvers/transfer_cascader.py
128
130
  pmrf/utils/__init__.py
129
131
  pmrf/utils/array.py
132
+ pmrf/utils/debug.py
130
133
  pmrf/utils/network.py
131
134
  pmrf/utils/optix.py
132
135
  pmrf/utils/random.py
@@ -156,5 +159,5 @@ tests/test_models/test_interconnected.py
156
159
  tests/test_models/test_lines.py
157
160
  tests/test_models/test_lumped.py
158
161
  tests/test_models/test_nodal.py
159
- tests/test_models/test_topological.py
162
+ tests/test_models/test_sections.py
160
163
  tests/test_models/test_transformed.py
@@ -27,7 +27,7 @@ except PackageNotFoundError:
27
27
  # Re-exports
28
28
  from pmrf.models import Model as Model
29
29
  from pmrf.frequency import Frequency as Frequency
30
- from pmrf.topology import Topology as Topology
30
+ from pmrf.simulate.topology import Topology as Topology
31
31
  from pmrf.problem import Problem as Problem
32
32
 
33
33
  #: The canonical type hint for a float, or a numpy or JAX array.
@@ -3,6 +3,7 @@ Base class for RF models.
3
3
  """
4
4
 
5
5
  from typing import Any, Callable, Self, TypeVar, Union
6
+ from functools import cached_property
6
7
  import dataclasses
7
8
 
8
9
  import jax
@@ -12,12 +13,12 @@ import equinox as eqx
12
13
  import skrf
13
14
  import parax as prx
14
15
 
16
+ from pmrf.rf.mna import MNAStamp
15
17
  from pmrf.utils.optix import focus, Lens
16
18
  from pmrf.parameters import Param
17
19
  from pmrf.frequency import Frequency
18
20
  from pmrf.rf import (
19
- s2s, a2s, s2a, s2y, y2s, s2z, z2s,
20
- y2z, z2y, a2y, y2a, a2z, z2a,
21
+ a2s, s2a, s2y, y2s, s2z, z2s, y2z, z2y, a2y, y2a, a2z, z2a, s2mna, y2mna, z2mna, a2mna,
21
22
  )
22
23
  from pmrf.math import CONVERSION_LOOKUP
23
24
  from pmrf.utils.type import is_overridden
@@ -25,7 +26,7 @@ from pmrf.utils import field, unwrap, unwrap_self
25
26
 
26
27
  T = TypeVar('T')
27
28
 
28
- PRIMARY_DOMAINS = ('s', 'a', 'y', 'z')
29
+ PRIMARY_DOMAINS = ('s', 'a', 'y', 'z', 'mna')
29
30
  PRIMARY_METHODS = PRIMARY_DOMAINS + ('build', 'primary_matrix')
30
31
  HUB_Z0 = 50.0 + 0.0j
31
32
 
@@ -64,6 +65,7 @@ class Model(eqx.Module):
64
65
  :meth:`a` ABCD parameter matrix.
65
66
  :meth:`z` Impedance (Z) parameter matrix.
66
67
  :meth:`y` Admittance (Y) parameter matrix.
68
+ :meth:`mna` Modified Nodal Analysis (MNA) stamp matrices.
67
69
  :meth:`build` Build the model. Can be overridden for advanced models.
68
70
  :meth:`primary_matrix` Return the primary matrix. Can be overridden for dynamic dispatch.
69
71
  :attr:`primary_domain` The domain of the primary matrix as a string (e.g. ``"s"``, ``"a"``).
@@ -181,7 +183,7 @@ class Model(eqx.Module):
181
183
 
182
184
  # ---- Introspection properties --------------------------------------------------------
183
185
 
184
- @property
186
+ @cached_property
185
187
  def number_of_ports(self) -> int:
186
188
  """Number of ports.
187
189
 
@@ -189,11 +191,14 @@ class Model(eqx.Module):
189
191
  -------
190
192
  int
191
193
  """
194
+ if is_overridden(type(self), Model, 'build'):
195
+ return self.build().number_of_ports
196
+
192
197
  freq = Frequency(1, 2, 2)
193
198
  eval = jax.eval_shape(lambda: self.s(freq))
194
199
  return eval.shape[1]
195
200
 
196
- @property
201
+ @cached_property
197
202
  def nports(self) -> int:
198
203
  """Alias of :attr:`number_of_ports`."""
199
204
  return self.number_of_ports
@@ -490,11 +495,11 @@ class Model(eqx.Module):
490
495
  kwargs = {'z0': HUB_Z0} if primary_domain == 's' else {}
491
496
  val = self.primary_matrix(freq, **kwargs)
492
497
 
493
- # Return or Convert
498
+ # Return direct
494
499
  if primary_domain == 'a':
495
500
  return val
496
501
 
497
- # Convert via S parameters (Hub strategy)
502
+ # Convert with priority s, z, y
498
503
  if primary_domain == 's':
499
504
  return s2a(val, z0=HUB_Z0)
500
505
  elif primary_domain == 'z':
@@ -529,11 +534,11 @@ class Model(eqx.Module):
529
534
  kwargs = {'z0': HUB_Z0} if primary_domain == 's' else {}
530
535
  val = self.primary_matrix(freq, **kwargs)
531
536
 
532
- # Return or Convert
537
+ # Return direct
533
538
  if primary_domain == 'z':
534
539
  return val
535
540
 
536
- # Convert via S parameters (Hub strategy)
541
+ # Convert with priority s, a, y
537
542
  if primary_domain == 's':
538
543
  return s2z(val, z0=HUB_Z0)
539
544
  elif primary_domain == 'a':
@@ -568,11 +573,11 @@ class Model(eqx.Module):
568
573
  kwargs = {'z0': HUB_Z0} if primary_domain == 's' else {}
569
574
  val = self.primary_matrix(freq, **kwargs)
570
575
 
571
- # Return or Convert
576
+ # Return direct
572
577
  if primary_domain == 'y':
573
578
  return val
574
579
 
575
- # Convert via S parameters (Hub strategy)
580
+ # Convert with priority s, a, z
576
581
  if primary_domain == 's':
577
582
  return s2y(val, HUB_Z0)
578
583
  elif primary_domain == 'a':
@@ -581,6 +586,43 @@ class Model(eqx.Module):
581
586
  return z2y(val)
582
587
  else:
583
588
  raise NotImplementedError(f"Conversion from '{primary_domain}' to 'y' is not implemented.")
589
+
590
+ @eqx.filter_jit
591
+ @unwrap_self
592
+ def mna(self, freq: Frequency) -> MNAStamp:
593
+ """
594
+ (experimental) Modified Nodal Analysis (MNA) stamp.
595
+
596
+ Can be overridden in sub-classes.
597
+
598
+ If the model does not explicitly define an MNA stamp, this automatically
599
+ delegates to the appropriate conversion utility (`s2mna`, `z2mna`, etc.).
600
+ Explicitly defined Y-matrices are prioritized to maximize matrix sparsity,
601
+ while other domains fall back to auxiliary variables to guarantee stability.
602
+ """
603
+ if is_overridden(type(self), Model, 'build'):
604
+ return self.build().mna(freq)
605
+
606
+ primary_domain = self.primary_domain
607
+
608
+ if primary_domain == 'mna':
609
+ return self.primary_matrix(freq)
610
+
611
+ # We prioritize y, z and a for sparsity, assuming the caller
612
+ # has created a numerically stable implementation.
613
+ if primary_domain == 'y' or is_overridden(type(self), Model, 'y'):
614
+ return y2mna(self.y(freq))
615
+
616
+ if primary_domain == 'z' or is_overridden(type(self), Model, 'z'):
617
+ return z2mna(self.z(freq))
618
+
619
+ if primary_domain == 'a' or is_overridden(type(self), Model, 'a'):
620
+ return a2mna(self.a(freq))
621
+
622
+ if primary_domain == 's' or is_overridden(type(self), Model, 's'):
623
+ return s2mna(self.s(freq, z0=HUB_Z0), z0=HUB_Z0)
624
+
625
+ return y2mna(self.y(freq))
584
626
 
585
627
  # ---- Magic methods and copying --------------------------------------------------
586
628
 
@@ -669,9 +711,11 @@ class Model(eqx.Module):
669
711
  methods like `.get()` and `.set()` to retrieve values
670
712
  or an updated model.
671
713
 
672
- Warning: Note that updates are "surgical", so the value
673
- is replaced as-is i.e. without any automatic converters.
674
- When replacing parameters, ensure to pass in a fully
714
+ WARNING: All updates made by this method are "surgical".
715
+ In order words, values are replaced *as-is* without any converters
716
+ or verification applied (a new instance is still returned).
717
+ Any invariants must therefore be enforced or checked manually.
718
+ For example, when replacing parameters, ensure to pass in a fully
675
719
  constructed parameter and not a float.
676
720
 
677
721
  Examples
@@ -9,7 +9,7 @@ import equinox as eqx
9
9
 
10
10
  from pmrf.models import Model
11
11
  from pmrf.frequency import Frequency
12
- from pmrf.utils import field
12
+ from pmrf.utils import field, error_if
13
13
  from pmrf.rf import renormalize_s
14
14
  from pmrf.types import ArrayLike, Param
15
15
  from pmrf.parameters import param
@@ -37,25 +37,6 @@ class Load(Model):
37
37
  repeat(freq.npoints, 0)
38
38
  return s
39
39
 
40
- def y(self, freq: Frequency) -> jnp.ndarray:
41
- gamma_arr = jnp.asarray(self.gamma)
42
-
43
- is_invalid = jnp.any(gamma_arr == -1.0)
44
-
45
- gamma_safe = eqx.error_if(
46
- gamma_arr,
47
- is_invalid,
48
- "Y-matrix is singular and undefined for ideal short (-1.0) loads."
49
- )
50
-
51
- y_val = (1.0 - gamma_safe) / (1.0 + gamma_safe)
52
-
53
- y = jnp.asarray(y_val).reshape(-1, 1, 1) * \
54
- jnp.eye(self.nports, dtype=jnp.complex128).reshape((-1, self.nports, self.nports)).\
55
- repeat(freq.npoints, 0)
56
-
57
- return y
58
-
59
40
 
60
41
  class Short(Model):
61
42
  """