Coverage for src / tracekit / analyzers / power / conduction.py: 100%
63 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 23:04 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 23:04 +0000
1"""Conduction loss analysis for TraceKit.
3Provides conduction loss calculations for power semiconductor devices
4during their on-state.
7Example:
8 >>> from tracekit.analyzers.power.conduction import conduction_loss
9 >>> p_cond = conduction_loss(v_on_trace, i_d_trace, duty_cycle=0.5)
10 >>> print(f"Conduction loss: {p_cond:.2f} W")
11"""
13from __future__ import annotations
15from typing import TYPE_CHECKING
17import numpy as np
19from tracekit.core.exceptions import AnalysisError
21if TYPE_CHECKING:
22 from tracekit.core.types import WaveformTrace
25def conduction_loss(
26 voltage: WaveformTrace,
27 current: WaveformTrace,
28 duty_cycle: float | None = None,
29) -> float:
30 """Calculate conduction loss during on-state.
32 P_cond = V_on * I_on * D (steady state)
33 or
34 P_cond = mean(V(t) * I(t)) over on-state periods
36 Args:
37 voltage: On-state voltage trace (V_ds(on) or V_ce(sat)).
38 current: On-state current trace.
39 duty_cycle: Duty cycle (0 to 1). If None, calculates from waveforms.
41 Returns:
42 Conduction loss in Watts.
44 Example:
45 >>> p_cond = conduction_loss(v_on, i_d, duty_cycle=0.5)
46 >>> print(f"Conduction loss: {p_cond:.2f} W")
48 References:
49 Infineon Application Note AN-9010
50 """
51 v_data = voltage.data
52 i_data = current.data
54 # Ensure same length
55 min_len = min(len(v_data), len(i_data))
56 v_data = v_data[:min_len]
57 i_data = i_data[:min_len]
59 if duty_cycle is not None:
60 # Use average values and duty cycle
61 v_on = float(np.mean(v_data))
62 i_on = float(np.mean(i_data))
63 return v_on * i_on * duty_cycle
64 else:
65 # Calculate instantaneous power and average
66 power = v_data * i_data
67 return float(np.mean(power))
70def on_resistance(
71 voltage: WaveformTrace,
72 current: WaveformTrace,
73 *,
74 min_current: float | None = None,
75 min_current_fraction: float = 0.1,
76) -> float:
77 """Calculate on-state resistance (R_ds(on) or R_ce(sat)).
79 R_on = V_on / I_on
81 Args:
82 voltage: On-state voltage trace.
83 current: On-state current trace.
84 min_current: Minimum current threshold (to avoid division by small values).
85 If None, uses min_current_fraction * peak current.
86 min_current_fraction: Fraction of peak current to use as minimum threshold
87 when min_current is None (default: 0.1 = 10% of peak).
89 Returns:
90 On-state resistance in Ohms.
92 Example:
93 >>> r_on = on_resistance(v_ds, i_d)
94 >>> print(f"R_ds(on): {r_on*1e3:.2f} mOhm")
95 >>> # With tighter threshold (5% of peak):
96 >>> r_on = on_resistance(v_ds, i_d, min_current_fraction=0.05)
97 """
98 v_data = voltage.data
99 i_data = current.data
101 min_len = min(len(v_data), len(i_data))
102 v_data = v_data[:min_len]
103 i_data = i_data[:min_len]
105 # Filter by minimum current
106 if min_current is None:
107 min_current = min_current_fraction * np.max(np.abs(i_data))
109 mask = np.abs(i_data) >= min_current
110 if not np.any(mask):
111 return np.nan # type: ignore[no-any-return]
113 v_on = v_data[mask]
114 i_on = i_data[mask]
116 # Linear fit to get resistance
117 # V = I * R -> slope is R
118 coeffs = np.polyfit(i_on, v_on, 1)
119 return float(coeffs[0])
122def forward_voltage(
123 voltage: WaveformTrace,
124 current: WaveformTrace,
125 *,
126 current_threshold: float | None = None,
127 current_threshold_fraction: float = 0.1,
128 threshold_window: float = 0.1,
129) -> float:
130 """Calculate forward voltage drop (for diodes or IGBT V_ce(sat)).
132 Extracts the voltage at a reference current level.
134 Args:
135 voltage: Forward voltage trace.
136 current: Forward current trace.
137 current_threshold: Current level at which to measure Vf.
138 If None, uses current_threshold_fraction * peak current.
139 current_threshold_fraction: Fraction of peak current to use as threshold
140 when current_threshold is None (default: 0.1 = 10% of peak).
141 threshold_window: Tolerance window around the current threshold,
142 as a fraction of the threshold (default: 0.1 = +/-10% window).
143 Samples within this window are averaged to compute Vf.
145 Returns:
146 Forward voltage in Volts.
148 Example:
149 >>> vf = forward_voltage(v_f, i_f)
150 >>> print(f"Forward voltage: {vf:.2f} V")
151 >>> # With tighter window for more precise measurement:
152 >>> vf = forward_voltage(v_f, i_f, threshold_window=0.05)
153 """
154 v_data = voltage.data
155 i_data = current.data
157 min_len = min(len(v_data), len(i_data))
158 v_data = v_data[:min_len]
159 i_data = i_data[:min_len]
161 if current_threshold is None:
162 current_threshold = current_threshold_fraction * np.max(np.abs(i_data))
164 # Find samples near the current threshold
165 near_threshold = np.abs(i_data - current_threshold) < threshold_window * current_threshold
166 if not np.any(near_threshold):
167 # Interpolate
168 idx = np.argmin(np.abs(i_data - current_threshold))
169 return float(v_data[idx])
171 return float(np.mean(v_data[near_threshold]))
174def duty_cycle_weighted_loss(
175 losses: list[tuple[float, float]],
176) -> float:
177 """Calculate total loss from multiple operating points.
179 Useful for variable duty cycle or multi-mode operation.
181 Args:
182 losses: List of (loss_watts, duty_cycle) tuples.
184 Returns:
185 Total weighted average loss in Watts.
187 Raises:
188 AnalysisError: If total duty cycle exceeds 1.0
190 Example:
191 >>> # 10W at 30% duty, 5W at 50% duty
192 >>> total = duty_cycle_weighted_loss([(10, 0.3), (5, 0.5)])
193 """
194 total = 0.0
195 total_duty = 0.0
197 for loss, duty in losses:
198 total += loss * duty
199 total_duty += duty
201 if total_duty > 1.0:
202 raise AnalysisError(f"Total duty cycle exceeds 1.0: {total_duty}")
204 return total
207def temperature_derating(
208 r_on_25c: float,
209 temperature: float,
210 temp_coefficient: float = 0.004,
211) -> float:
212 """Calculate temperature-derated on-resistance.
214 R_on(T) = R_on(25C) * (1 + alpha * (T - 25))
216 Args:
217 r_on_25c: On-resistance at 25C in Ohms.
218 temperature: Operating temperature in Celsius.
219 temp_coefficient: Temperature coefficient (default 0.4%/C for Si MOSFET).
221 Returns:
222 Derated on-resistance in Ohms.
224 Example:
225 >>> r_on_100c = temperature_derating(0.010, 100) # 10mOhm at 25C
226 >>> print(f"R_on at 100C: {r_on_100c*1e3:.2f} mOhm")
227 """
228 return r_on_25c * (1 + temp_coefficient * (temperature - 25))
231def mosfet_conduction_loss(
232 i_rms: float,
233 r_ds_on: float,
234 temperature: float = 25.0,
235 temp_coefficient: float = 0.004,
236) -> float:
237 """Calculate MOSFET conduction loss.
239 P_cond = I_rms^2 * R_ds(on)
241 Args:
242 i_rms: RMS drain current in Amps.
243 r_ds_on: On-state resistance at 25C in Ohms.
244 temperature: Junction temperature in Celsius.
245 temp_coefficient: Temperature coefficient.
247 Returns:
248 Conduction loss in Watts.
250 Example:
251 >>> p = mosfet_conduction_loss(i_rms=10, r_ds_on=0.010, temperature=100)
252 """
253 r_on = temperature_derating(r_ds_on, temperature, temp_coefficient)
254 return i_rms**2 * r_on
257def diode_conduction_loss(
258 i_avg: float,
259 i_rms: float,
260 v_f: float,
261 r_d: float = 0.0,
262) -> float:
263 """Calculate diode conduction loss.
265 P_cond = V_f * I_avg + r_d * I_rms^2
267 Args:
268 i_avg: Average forward current in Amps.
269 i_rms: RMS forward current in Amps.
270 v_f: Forward voltage drop in Volts.
271 r_d: Dynamic resistance in Ohms.
273 Returns:
274 Conduction loss in Watts.
276 Example:
277 >>> p = diode_conduction_loss(i_avg=5, i_rms=7, v_f=0.7, r_d=0.01)
278 """
279 return v_f * i_avg + r_d * i_rms**2
282def igbt_conduction_loss(
283 i_c: float,
284 v_ce_sat: float,
285 r_c: float = 0.0,
286) -> float:
287 """Calculate IGBT conduction loss.
289 P_cond = V_ce(sat) * I_c + r_c * I_c^2
291 Args:
292 i_c: Collector current in Amps.
293 v_ce_sat: Collector-emitter saturation voltage in Volts.
294 r_c: Collector resistance in Ohms.
296 Returns:
297 Conduction loss in Watts.
299 Example:
300 >>> p = igbt_conduction_loss(i_c=50, v_ce_sat=2.0, r_c=0.01)
301 """
302 return v_ce_sat * i_c + r_c * i_c**2
305__all__ = [
306 "conduction_loss",
307 "diode_conduction_loss",
308 "duty_cycle_weighted_loss",
309 "forward_voltage",
310 "igbt_conduction_loss",
311 "mosfet_conduction_loss",
312 "on_resistance",
313 "temperature_derating",
314]