Coverage for src / tracekit / analyzers / power / efficiency.py: 100%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 23:04 +0000

1"""Power efficiency calculations for TraceKit. 

2 

3Provides efficiency calculations for power converters and systems. 

4 

5 

6Example: 

7 >>> from tracekit.analyzers.power.efficiency import efficiency 

8 >>> eta = efficiency(v_in, i_in, v_out, i_out) 

9 >>> print(f"Efficiency: {eta*100:.1f}%") 

10""" 

11 

12from __future__ import annotations 

13 

14from typing import TYPE_CHECKING 

15 

16import numpy as np 

17 

18from tracekit.analyzers.power.basic import average_power, instantaneous_power 

19 

20if TYPE_CHECKING: 

21 from numpy.typing import NDArray 

22 

23 from tracekit.core.types import WaveformTrace 

24 

25 

26def efficiency( 

27 v_in: WaveformTrace, 

28 i_in: WaveformTrace, 

29 v_out: WaveformTrace, 

30 i_out: WaveformTrace, 

31) -> float: 

32 """Calculate power conversion efficiency. 

33 

34 eta = P_out / P_in * 100% 

35 

36 Args: 

37 v_in: Input voltage trace. 

38 i_in: Input current trace. 

39 v_out: Output voltage trace. 

40 i_out: Output current trace. 

41 

42 Returns: 

43 Efficiency as a ratio (0 to 1). 

44 

45 Example: 

46 >>> eta = efficiency(v_in, i_in, v_out, i_out) 

47 >>> print(f"Efficiency: {eta*100:.1f}%") 

48 """ 

49 p_in = average_power(voltage=v_in, current=i_in) 

50 p_out = average_power(voltage=v_out, current=i_out) 

51 

52 if p_in <= 0: 

53 return 0.0 

54 

55 return p_out / p_in 

56 

57 

58def power_conversion_efficiency( 

59 p_in: float, 

60 p_out: float, 

61) -> float: 

62 """Calculate efficiency from power values. 

63 

64 Args: 

65 p_in: Input power in Watts. 

66 p_out: Output power in Watts. 

67 

68 Returns: 

69 Efficiency as a ratio (0 to 1). 

70 

71 Example: 

72 >>> eta = power_conversion_efficiency(p_in=100, p_out=90) 

73 >>> print(f"Efficiency: {eta*100:.1f}%") 

74 """ 

75 if p_in <= 0: 

76 return 0.0 

77 return p_out / p_in 

78 

79 

80def multi_output_efficiency( 

81 v_in: WaveformTrace, 

82 i_in: WaveformTrace, 

83 outputs: list[tuple[WaveformTrace, WaveformTrace]], 

84) -> dict[str, float]: 

85 """Calculate efficiency for multi-output power supply. 

86 

87 Args: 

88 v_in: Input voltage trace. 

89 i_in: Input current trace. 

90 outputs: List of (v_out, i_out) trace tuples for each output. 

91 

92 Returns: 

93 Dictionary with: 

94 - total_efficiency: Overall efficiency 

95 - output_N_efficiency: Per-output efficiency (contribution) 

96 - output_N_power: Per-output power 

97 - total_output_power: Sum of all output powers 

98 - input_power: Input power 

99 - losses: Power losses (P_in - P_out_total) 

100 

101 Example: 

102 >>> outputs = [(v1, i1), (v2, i2), (v3, i3)] 

103 >>> result = multi_output_efficiency(v_in, i_in, outputs) 

104 >>> print(f"Total efficiency: {result['total_efficiency']*100:.1f}%") 

105 """ 

106 p_in = average_power(voltage=v_in, current=i_in) 

107 

108 result = { 

109 "input_power": p_in, 

110 } 

111 

112 total_output = 0.0 

113 for idx, (v_out, i_out) in enumerate(outputs): 

114 p_out = average_power(voltage=v_out, current=i_out) 

115 result[f"output_{idx + 1}_power"] = p_out 

116 result[f"output_{idx + 1}_efficiency"] = p_out / p_in if p_in > 0 else 0.0 

117 total_output += p_out 

118 

119 result["total_output_power"] = total_output 

120 result["total_efficiency"] = total_output / p_in if p_in > 0 else 0.0 

121 result["losses"] = p_in - total_output 

122 

123 return result 

124 

125 

126def efficiency_vs_load( 

127 v_in: WaveformTrace, 

128 i_in: WaveformTrace, 

129 v_out: WaveformTrace, 

130 i_out: WaveformTrace, 

131 *, 

132 n_points: int = 100, 

133) -> dict[str, NDArray[np.float64]]: 

134 """Calculate efficiency across the load range. 

135 

136 Segments the waveforms and calculates efficiency at each load level. 

137 

138 Args: 

139 v_in: Input voltage trace. 

140 i_in: Input current trace. 

141 v_out: Output voltage trace. 

142 i_out: Output current trace. 

143 n_points: Number of load points to evaluate. 

144 

145 Returns: 

146 Dictionary with: 

147 - load_percent: Load levels as percentage of max 

148 - efficiency: Efficiency at each load level 

149 - output_power: Output power at each load level 

150 - input_power: Input power at each load level 

151 

152 Example: 

153 >>> result = efficiency_vs_load(v_in, i_in, v_out, i_out) 

154 >>> plt.plot(result['load_percent'], result['efficiency'] * 100) 

155 """ 

156 # Calculate instantaneous power 

157 p_out_trace = instantaneous_power(v_out, i_out) 

158 p_in_trace = instantaneous_power(v_in, i_in) 

159 

160 p_out_data = p_out_trace.data 

161 p_in_data = p_in_trace.data[: len(p_out_data)] 

162 

163 # Sort by output power to get load curve 

164 sort_idx = np.argsort(p_out_data) 

165 p_out_sorted = p_out_data[sort_idx] 

166 p_in_sorted = p_in_data[sort_idx] 

167 

168 # Divide into bins 

169 bin_size = len(p_out_sorted) // n_points 

170 bin_size = max(bin_size, 1) 

171 

172 load_pct = [] 

173 efficiency_vals = [] 

174 p_out_vals = [] 

175 p_in_vals = [] 

176 

177 max_p_out = np.max(p_out_data) 

178 

179 for i in range(0, len(p_out_sorted), bin_size): 

180 bin_p_out = np.mean(p_out_sorted[i : i + bin_size]) 

181 bin_p_in = np.mean(p_in_sorted[i : i + bin_size]) 

182 

183 load_pct.append(bin_p_out / max_p_out * 100 if max_p_out > 0 else 0) 

184 p_out_vals.append(bin_p_out) 

185 p_in_vals.append(bin_p_in) 

186 efficiency_vals.append(bin_p_out / bin_p_in if bin_p_in > 0 else 0) 

187 

188 return { 

189 "load_percent": np.array(load_pct), 

190 "efficiency": np.array(efficiency_vals), 

191 "output_power": np.array(p_out_vals), 

192 "input_power": np.array(p_in_vals), 

193 } 

194 

195 

196def loss_breakdown( 

197 v_in: WaveformTrace, 

198 i_in: WaveformTrace, 

199 v_out: WaveformTrace, 

200 i_out: WaveformTrace, 

201 *, 

202 switching_loss: float = 0.0, 

203 conduction_loss: float = 0.0, 

204 magnetic_loss: float = 0.0, 

205 gate_drive_loss: float = 0.0, 

206) -> dict[str, float]: 

207 """Break down power losses by category. 

208 

209 Args: 

210 v_in: Input voltage trace. 

211 i_in: Input current trace. 

212 v_out: Output voltage trace. 

213 i_out: Output current trace. 

214 switching_loss: Known switching losses in Watts. 

215 conduction_loss: Known conduction losses in Watts. 

216 magnetic_loss: Known magnetic (core/copper) losses in Watts. 

217 gate_drive_loss: Known gate drive losses in Watts. 

218 

219 Returns: 

220 Dictionary with loss breakdown. 

221 

222 Example: 

223 >>> result = loss_breakdown(v_in, i_in, v_out, i_out, 

224 ... switching_loss=2.0, conduction_loss=1.5, magnetic_loss=0.5) 

225 >>> print(f"Other losses: {result['other_loss']:.2f} W") 

226 """ 

227 p_in = average_power(voltage=v_in, current=i_in) 

228 p_out = average_power(voltage=v_out, current=i_out) 

229 total_loss = p_in - p_out 

230 

231 known_losses = switching_loss + conduction_loss + magnetic_loss + gate_drive_loss 

232 other_loss = total_loss - known_losses 

233 

234 eta = p_out / p_in if p_in > 0 else 0.0 

235 

236 return { 

237 "input_power": p_in, 

238 "output_power": p_out, 

239 "efficiency": eta, 

240 "total_loss": total_loss, 

241 "switching_loss": switching_loss, 

242 "conduction_loss": conduction_loss, 

243 "magnetic_loss": magnetic_loss, 

244 "gate_drive_loss": gate_drive_loss, 

245 "other_loss": max(0, other_loss), # Clamp to non-negative 

246 "switching_loss_percent": switching_loss / total_loss * 100 if total_loss > 0 else 0, 

247 "conduction_loss_percent": conduction_loss / total_loss * 100 if total_loss > 0 else 0, 

248 "magnetic_loss_percent": magnetic_loss / total_loss * 100 if total_loss > 0 else 0, 

249 } 

250 

251 

252def thermal_efficiency( 

253 p_in: float, 

254 p_out: float, 

255 ambient_temp: float, 

256 case_temp: float, 

257 thermal_resistance: float, 

258) -> dict[str, float]: 

259 """Calculate thermal-related efficiency metrics. 

260 

261 Args: 

262 p_in: Input power in Watts. 

263 p_out: Output power in Watts. 

264 ambient_temp: Ambient temperature in Celsius. 

265 case_temp: Case/junction temperature in Celsius. 

266 thermal_resistance: Thermal resistance (Rth_j-a) in C/W. 

267 

268 Returns: 

269 Dictionary with thermal analysis. 

270 

271 Example: 

272 >>> result = thermal_efficiency(100, 90, 25, 65, 2.5) 

273 >>> print(f"Estimated losses: {result['estimated_losses']:.1f} W") 

274 """ 

275 losses = p_in - p_out 

276 eta = p_out / p_in if p_in > 0 else 0.0 

277 

278 # Estimate losses from thermal measurement 

279 thermal_losses = (case_temp - ambient_temp) / thermal_resistance 

280 

281 return { 

282 "efficiency": eta, 

283 "electrical_losses": losses, 

284 "thermal_estimated_losses": thermal_losses, 

285 "temperature_rise": case_temp - ambient_temp, 

286 "loss_discrepancy": abs(losses - thermal_losses), 

287 } 

288 

289 

290__all__ = [ 

291 "efficiency", 

292 "efficiency_vs_load", 

293 "loss_breakdown", 

294 "multi_output_efficiency", 

295 "power_conversion_efficiency", 

296 "thermal_efficiency", 

297]