@@ -7,19 +7,35 @@ namespace SpiceSharpParser.CustomComponents.IdealDiodes
77 /// <summary>
88 /// LTspice-style idealized diode current law.
99 /// </summary>
10+ /// <remarks>
11+ /// This helper owns only the local current law for one ideal diode cell. The
12+ /// biasing behavior that calls it handles the external branch details such as
13+ /// series resistance and the parallel/series diode multipliers.
14+ ///
15+ /// The current law is built from straight-line regions in slope/intercept
16+ /// form: <c>I = g * V + b</c>. The off region uses <c>Roff</c> or simulator
17+ /// <c>Gmin</c>, the forward region uses <c>Ron</c> after <c>Vfwd</c>, and the
18+ /// optional reverse-breakdown region uses <c>Rrev</c> after <c>-Vrev</c>.
19+ /// Optional epsilon parameters replace abrupt knees with a finite-width
20+ /// conductance ramp so both current and conductance stay continuous for
21+ /// Newton iteration.
22+ /// </remarks>
1023 internal static class IdealDiodeEquation
1124 {
25+ /// <summary>
26+ /// Lowest fallback off-state conductance when neither <c>Roff</c> nor simulator <c>Gmin</c> supplies one.
27+ /// </summary>
1228 private const double MinimumConductance = 0.0 ;
1329
1430 /// <summary>
15- /// Evaluates the current and small-signal conductance for one ideal diode.
31+ /// Evaluates the current and small-signal conductance for one ideal diode cell .
1632 /// </summary>
17- /// <param name="parameters">The ideal diode parameters.</param>
18- /// <param name="biasingParameters">The simulation biasing parameters.</param>
19- /// <param name="voltage">The voltage across one diode.</param>
20- /// <param name="area">The diode area multiplier.</param>
21- /// <param name="current">The output current.</param>
22- /// <param name="conductance">The output conductance.</param>
33+ /// <param name="parameters">The effective model and instance parameters.</param>
34+ /// <param name="biasingParameters">The simulation biasing parameters, used for the default off conductance .</param>
35+ /// <param name="voltage">The local voltage across one diode cell .</param>
36+ /// <param name="area">The diode area multiplier applied after the local equation is evaluated .</param>
37+ /// <param name="current">The resulting local diode current, scaled by <paramref name="area" /> .</param>
38+ /// <param name="conductance">The resulting small-signal conductance, scaled by <paramref name="area" /> .</param>
2339 public static void Evaluate (
2440 IdealDiodeParameters parameters ,
2541 BiasingParameters biasingParameters ,
@@ -28,32 +44,51 @@ public static void Evaluate(
2844 out double current ,
2945 out double conductance )
3046 {
47+ // Work in conductance because the solver needs dI/dV. Each operating
48+ // region is represented as a line: current = slope * voltage + intercept.
3149 double onConductance = 1.0 / parameters . OnResistance ;
3250
51+ // LTspice's ideal diode can omit Roff. In that case the off-state
52+ // leakage follows the simulator's Gmin so the device still contributes
53+ // the same numerical shunt used elsewhere during biasing.
3354 double offConductance = parameters . OffResistance . Given
3455 ? 1.0 / parameters . OffResistance . Value
3556 : Math . Max ( biasingParameters . Gmin , MinimumConductance ) ;
3657
58+ // Vfwd defaults to zero. With a zero threshold, the forward on-line is
59+ // simply Ron through the origin.
3760 double forwardVoltage = parameters . ForwardVoltage . Given ? parameters . ForwardVoltage . Value : 0.0 ;
3861
62+ // Start from the off branch. Reverse breakdown and forward conduction
63+ // may overwrite this below when the voltage lies in their region.
3964 current = offConductance * voltage ;
4065 conductance = offConductance ;
4166
4267 if ( parameters . ReverseVoltage . Given )
4368 {
69+ // Reverse breakdown is a line through (-Vrev, 0):
70+ // current = Grev * voltage + Grev * Vrev.
71+ // If Rrev is omitted, LTspice-style behavior falls back to Ron.
4472 double reverseVoltage = Math . Abs ( parameters . ReverseVoltage . Value ) ;
4573 double reverseResistance = parameters . ReverseResistance . Given
4674 ? parameters . ReverseResistance . Value
4775 : parameters . OnResistance ;
4876 double reverseConductance = 1.0 / reverseResistance ;
4977 double reverseIntercept = reverseConductance * reverseVoltage ;
78+
79+ // Roff/Gmin can tilt the off-line, so the true intersection is not
80+ // always exactly -Vrev. Use the nominal knee only as a degenerate
81+ // fallback when the two lines are effectively parallel.
5082 double boundary = FindIntersection (
5183 reverseConductance ,
5284 reverseIntercept ,
5385 offConductance ,
5486 0.0 ,
5587 - reverseVoltage ) ;
5688
89+ // Evaluate the reverse-to-off knee. With revepsilon omitted or
90+ // zero this is a hard switch at the intersection; otherwise it is
91+ // smoothed symmetrically around the boundary.
5792 EvaluateTransition (
5893 voltage ,
5994 boundary ,
@@ -66,18 +101,29 @@ public static void Evaluate(
66101 out conductance ) ;
67102 }
68103
104+ // The forward on-line crosses zero current at Vfwd:
105+ // current = Gon * voltage - Gon * Vfwd.
69106 double onIntercept = - onConductance * forwardVoltage ;
107+
108+ // Find the voltage where the off-line and the forward on-line meet.
109+ // This is close to Vfwd when Roff is large, but computing it keeps the
110+ // model continuous for any legal Roff/Gmin.
70111 double forwardBoundary = FindIntersection (
71112 offConductance ,
72113 0.0 ,
73114 onConductance ,
74115 onIntercept ,
75116 forwardVoltage ) ;
76117
118+ // Avoid a second transition evaluation in the normal off region. For
119+ // smoothed knees, begin applying the blend at the start of the epsilon
120+ // band so the partial-conduction region is not missed.
77121 double forwardWidth = parameters . ForwardEpsilon . Given ? parameters . ForwardEpsilon . Value : 0.0 ;
78122 double forwardStart = forwardBoundary - ( Math . Max ( forwardWidth , 0.0 ) / 2.0 ) ;
79123 if ( voltage > forwardBoundary || ( forwardWidth > 0.0 && voltage >= forwardStart ) )
80124 {
125+ // Evaluate the off-to-forward knee using the same generic transition
126+ // helper used for reverse breakdown.
81127 EvaluateTransition (
82128 voltage ,
83129 forwardBoundary ,
@@ -90,12 +136,26 @@ public static void Evaluate(
90136 out conductance ) ;
91137 }
92138
139+ // Current limits are applied to the already-selected region. This keeps
140+ // the normal piecewise law simple and lets the limiter scale the local
141+ // derivative by the tanh derivative.
93142 ApplyCurrentLimits ( parameters , ref current , ref conductance ) ;
94143
144+ // Area behaves like parallel identical cells: both DC current and
145+ // small-signal conductance scale linearly.
95146 current *= area ;
96147 conductance *= area ;
97148 }
98149
150+ /// <summary>
151+ /// Finds where two line segments in <c>current = slope * voltage + intercept</c> form intersect.
152+ /// </summary>
153+ /// <param name="leftSlope">The slope of the first line.</param>
154+ /// <param name="leftIntercept">The intercept of the first line.</param>
155+ /// <param name="rightSlope">The slope of the second line.</param>
156+ /// <param name="rightIntercept">The intercept of the second line.</param>
157+ /// <param name="fallback">The voltage to use if the lines are nearly parallel.</param>
158+ /// <returns>The intersection voltage.</returns>
99159 private static double FindIntersection (
100160 double leftSlope ,
101161 double leftIntercept ,
@@ -104,11 +164,27 @@ private static double FindIntersection(
104164 double fallback )
105165 {
106166 double denominator = leftSlope - rightSlope ;
167+
168+ // Parallel or nearly parallel regions do not give a useful numerical
169+ // knee. The caller provides the physically meaningful nominal boundary.
107170 if ( Math . Abs ( denominator ) <= 1e-30 )
108171 return fallback ;
172+
109173 return ( rightIntercept - leftIntercept ) / denominator ;
110174 }
111175
176+ /// <summary>
177+ /// Evaluates either a hard or smoothed transition between two linear current regions.
178+ /// </summary>
179+ /// <param name="voltage">The voltage at which to evaluate the transition.</param>
180+ /// <param name="boundary">The intersection voltage of the two unsmoothed lines.</param>
181+ /// <param name="epsilon">The optional smoothing width around <paramref name="boundary" />.</param>
182+ /// <param name="leftSlope">The slope used below the transition.</param>
183+ /// <param name="leftIntercept">The intercept used below the transition.</param>
184+ /// <param name="rightSlope">The slope used above the transition.</param>
185+ /// <param name="rightIntercept">The intercept used above the transition.</param>
186+ /// <param name="current">The evaluated current.</param>
187+ /// <param name="conductance">The evaluated conductance.</param>
112188 private static void EvaluateTransition (
113189 double voltage ,
114190 double boundary ,
@@ -123,6 +199,8 @@ private static void EvaluateTransition(
123199 double width = epsilon . Given ? epsilon . Value : 0.0 ;
124200 if ( width <= 0.0 )
125201 {
202+ // No smoothing requested: choose the line on the active side of
203+ // the intersection and expose that line's slope as conductance.
126204 if ( voltage < boundary )
127205 {
128206 current = ( leftSlope * voltage ) + leftIntercept ;
@@ -137,8 +215,11 @@ private static void EvaluateTransition(
137215 return ;
138216 }
139217
218+ // The epsilon value is the full width of the blend, centered on the
219+ // natural intersection of the two lines.
140220 double start = boundary - ( width / 2.0 ) ;
141221 double end = boundary + ( width / 2.0 ) ;
222+
142223 if ( voltage <= start )
143224 {
144225 current = ( leftSlope * voltage ) + leftIntercept ;
@@ -153,6 +234,14 @@ private static void EvaluateTransition(
153234 return ;
154235 }
155236
237+ // Inside the smoothing band the conductance moves linearly from the
238+ // left slope to the right slope. Current is the integral of that ramp,
239+ // anchored to the left line at the start of the band:
240+ //
241+ // g(x) = leftSlope + slopeDelta * x / width
242+ // I(x) = I(start) + leftSlope * x + slopeDelta * x^2 / (2 * width)
243+ //
244+ // where x is the distance from the start of the smoothing band.
156245 double distance = voltage - start ;
157246 double slopeDelta = rightSlope - leftSlope ;
158247 current = ( leftSlope * start ) + leftIntercept
@@ -161,8 +250,17 @@ private static void EvaluateTransition(
161250 conductance = leftSlope + ( slopeDelta * distance / width ) ;
162251 }
163252
253+ /// <summary>
254+ /// Applies the optional forward or reverse current limiter to the selected current branch.
255+ /// </summary>
256+ /// <param name="parameters">The effective ideal diode parameters.</param>
257+ /// <param name="current">The current to limit in place.</param>
258+ /// <param name="conductance">The conductance to update in place with the limiter derivative.</param>
164259 private static void ApplyCurrentLimits ( IdealDiodeParameters parameters , ref double current , ref double conductance )
165260 {
261+ // Forward and reverse limits are independent. Select by the sign of the
262+ // already-computed current so leakage, breakdown, and smoothed knees all
263+ // feed into the same limiting law.
166264 if ( current > 0.0 && parameters . ForwardCurrentLimit . Given )
167265 {
168266 ApplyCurrentLimit ( parameters . ForwardCurrentLimit . Value , ref current , ref conductance ) ;
@@ -173,12 +271,21 @@ private static void ApplyCurrentLimits(IdealDiodeParameters parameters, ref doub
173271 }
174272 }
175273
274+ /// <summary>
275+ /// Smoothly compresses current toward a symmetric magnitude limit.
276+ /// </summary>
277+ /// <param name="limit">The positive or negative limit magnitude.</param>
278+ /// <param name="current">The current to limit in place.</param>
279+ /// <param name="conductance">The conductance to update in place with the limiter derivative.</param>
176280 private static void ApplyCurrentLimit ( double limit , ref double current , ref double conductance )
177281 {
178282 limit = Math . Abs ( limit ) ;
179283 if ( limit <= 0.0 )
180284 return ;
181285
286+ // I_limited = limit * tanh(I_raw / limit)
287+ // dI_limited/dV = dI_raw/dV * (1 - tanh(I_raw / limit)^2)
288+ // This gives a soft saturation without a derivative discontinuity.
182289 double normalized = current / limit ;
183290 double limited = Math . Tanh ( normalized ) ;
184291 current = limit * limited ;
0 commit comments