|
23 | 23 | from jinja2 import Environment, FileSystemLoader |
24 | 24 | from bson.objectid import ObjectId |
25 | 25 | from bson import json_util |
| 26 | +import pandas as pd |
| 27 | +from sklearn.model_selection import train_test_split |
26 | 28 |
|
27 | 29 | # Local libraries |
28 | 30 | from transistordatabase.constants import * |
@@ -404,6 +406,16 @@ def update_wp(self, t_j: float, v_g: float, i_channel: float, switch_or_diode: s |
404 | 406 | # working point, calculate q_oss |
405 | 407 | self.wp.graph_v_qoss = self.calc_v_qoss() |
406 | 408 |
|
| 409 | + # check if lists are not empty |
| 410 | + if self.switch.e_on_meas: |
| 411 | + self.wp.e_on_meas_fit = self.calc_e_on_off_switch_loss_fit_parameters("on") |
| 412 | + else: |
| 413 | + self.wp.e_on_meas_fit = None |
| 414 | + if self.switch.e_off_meas: |
| 415 | + self.wp.e_off_meas_fit = self.calc_e_on_off_switch_loss_fit_parameters("off") |
| 416 | + else: |
| 417 | + self.wp.e_off_meas_fit = None |
| 418 | + |
407 | 419 | def init_loss_matrices(self): |
408 | 420 | """Experimental.""" |
409 | 421 | self.init_switch_channel_matrix() |
@@ -2146,14 +2158,16 @@ class WP: |
2146 | 2158 | diode_r_channel: float | None |
2147 | 2159 | switch_channel: float | None |
2148 | 2160 | diode_channel: float | None |
2149 | | - e_on: npt.NDArray[np.float64] | None #: Units: Row 1: A; Row 2: J |
2150 | | - e_off: npt.NDArray[np.float64] | None #: Units: Row 1: A; Row 2: J |
2151 | | - e_rr: npt.NDArray[np.float64] | None #: Units: Row 1: A; Row 2: J |
| 2161 | + e_on: SwitchEnergyData | None |
| 2162 | + e_off: SwitchEnergyData | None |
| 2163 | + e_rr: SwitchEnergyData | None |
2152 | 2164 | v_switching_ref: float | None #: Unit: V |
2153 | 2165 | graph_v_coss: npt.NDArray[np.float64] | None #: Units: Row 1: V; Row 2: F |
2154 | 2166 | graph_v_eoss: npt.NDArray[np.float64] | None #: Units: Row 1: V; Row 2: J |
2155 | 2167 | graph_v_qoss: npt.NDArray[np.float64] | None #: Units: Row 1: V; Row 2: C |
2156 | 2168 | parallel_transistors: float | None #: Unit: Number |
| 2169 | + e_on_meas_fit: None |
| 2170 | + e_off_meas_fit: None |
2157 | 2171 |
|
2158 | 2172 | def __init__(self): |
2159 | 2173 | self.switch_v_channel = None |
@@ -2652,6 +2666,128 @@ def compare_measurement_datasheet(self): |
2652 | 2666 | plt.grid() |
2653 | 2667 | plt.show() |
2654 | 2668 |
|
| 2669 | + @staticmethod |
| 2670 | + def fit_function(input_params: tuple, a_current: np.float64, b_current: np.float64, c_current: np.float64, voltage_factor: np.float64, |
| 2671 | + voltage_exponent: np.float64, ct_0: np.float64, ct_1: np.float64, ct_2: np.float64): |
| 2672 | + """ |
| 2673 | + Fit the switching loss curves. |
| 2674 | +
|
| 2675 | + :param input_params: (current, voltage, temperature) as tuple |
| 2676 | + :type input_params: tuple |
| 2677 | + :param a_current: constant offset factor to fit |
| 2678 | + :type a_current: np.float64 |
| 2679 | + :param b_current: linear factor to fit |
| 2680 | + :type b_current: np.float64 |
| 2681 | + :param c_current: quadratic factor to fit |
| 2682 | + :type c_current: np.float64 |
| 2683 | + :param voltage_factor: linear voltage factor to fit |
| 2684 | + :type voltage_factor: np.float64 |
| 2685 | + :param voltage_exponent: voltage exponent factor to fit |
| 2686 | + :type voltage_exponent: np.float64 |
| 2687 | + :param ct_0: constant temperature factor |
| 2688 | + :type ct_0: np.float64 |
| 2689 | + :param ct_1: linear temperature factor |
| 2690 | + :type ct_1: np.float64 |
| 2691 | + :param ct_2: quadratic temperature factor |
| 2692 | + :type ct_2: np.float64 |
| 2693 | + """ |
| 2694 | + current, voltage, t_j = input_params |
| 2695 | + |
| 2696 | + loss_current = a_current + b_current * current + c_current * current ** 2 |
| 2697 | + |
| 2698 | + loss_voltage_current = loss_current * ((voltage * voltage_factor) ** voltage_exponent) |
| 2699 | + |
| 2700 | + loss_voltage_current_temperature = loss_voltage_current * (ct_0 + t_j * (ct_1 + ct_2 ** 2)) |
| 2701 | + |
| 2702 | + return loss_voltage_current_temperature |
| 2703 | + |
| 2704 | + def calc_e_on_off_switch_loss_fit_parameters(self, on_off_key: str): |
| 2705 | + """ |
| 2706 | + Fit the parameters for the turn-on and the turn-off losses. |
| 2707 | +
|
| 2708 | + :param on_off_key: "on" or "off" |
| 2709 | + :type on_off_key: str |
| 2710 | + """ |
| 2711 | + df = pd.DataFrame() |
| 2712 | + |
| 2713 | + if on_off_key == "on": |
| 2714 | + for lossfile in self.switch.e_on_meas: |
| 2715 | + voltage = np.full_like(lossfile.graph_i_e[0], lossfile.v_supply) |
| 2716 | + temperature = np.full_like(lossfile.graph_i_e[0], lossfile.t_j) |
| 2717 | + df_local = pd.DataFrame({"current": lossfile.graph_i_e[0], "energy": lossfile.graph_i_e[1], "voltage": voltage, "temperature": temperature}) |
| 2718 | + df = pd.concat([df, df_local], axis=0) |
| 2719 | + elif on_off_key == "off": |
| 2720 | + for lossfile in self.switch.e_off_meas: |
| 2721 | + voltage = np.full_like(lossfile.graph_i_e[0], lossfile.v_supply) |
| 2722 | + temperature = np.full_like(lossfile.graph_i_e[0], lossfile.t_j) |
| 2723 | + df_local = pd.DataFrame({"current": lossfile.graph_i_e[0], "energy": lossfile.graph_i_e[1], "voltage": voltage, "temperature": temperature}) |
| 2724 | + df = pd.concat([df, df_local], axis=0) |
| 2725 | + else: |
| 2726 | + raise ValueError("on_off_key must be 'on' or 'off'.") |
| 2727 | + |
| 2728 | + df_to_split = df.copy().drop(columns=["energy"]) |
| 2729 | + |
| 2730 | + X_train, X_test, y_train, y_test = train_test_split( |
| 2731 | + df_to_split, df["energy"], test_size=0.3, random_state=42) |
| 2732 | + popt, pcov = curve_fit(self.fit_function, (X_train["current"], X_train["voltage"], X_train["temperature"]), y_train, maxfev=int(1e6)) |
| 2733 | + |
| 2734 | + a_current, b_current, c_current, voltage_factor, voltage_exponent, ct_0, ct_1, ct_2 = popt |
| 2735 | + |
| 2736 | + fitted_switching_factors = SwitchingLossFitFactors( |
| 2737 | + a_current=a_current, |
| 2738 | + b_current=b_current, |
| 2739 | + c_current=c_current, |
| 2740 | + voltage_factor=voltage_factor, |
| 2741 | + voltage_exponent=voltage_exponent, |
| 2742 | + ct_0=ct_0, |
| 2743 | + ct_1=ct_1, |
| 2744 | + ct_2=ct_2, |
| 2745 | + temperature_min=df["temperature"].min(), |
| 2746 | + temperature_max=df["temperature"].max(), |
| 2747 | + voltage_min=df["voltage"].min(), |
| 2748 | + voltage_max=df["voltage"].max(), |
| 2749 | + current_min=df["current"].min(), |
| 2750 | + current_max=df["current"].max() |
| 2751 | + ) |
| 2752 | + |
| 2753 | + return fitted_switching_factors |
| 2754 | + |
| 2755 | + def generate_energy_loss_curve_from_fit_factors(self, on_off_key: str, voltage: np.float64, temperature: np.float64): |
| 2756 | + """ |
| 2757 | + Generate the turn-on/off loss over current from the fit factors. |
| 2758 | +
|
| 2759 | + :param on_off_key: "on" or "off" |
| 2760 | + :type on_off_key: str |
| 2761 | + :param voltage: Voltage in V |
| 2762 | + :type voltage: np.float64 |
| 2763 | + :param temperature: Temperature in °C |
| 2764 | + :type temperature: np.float64 |
| 2765 | + """ |
| 2766 | + if on_off_key == "on": |
| 2767 | + current_vec = np.linspace(0, self.wp.e_on_meas_fit.current_max) |
| 2768 | + energy_vec = self.fit_function( |
| 2769 | + (current_vec, voltage, temperature), self.wp.e_on_meas_fit.a_current, self.wp.e_on_meas_fit.b_current, |
| 2770 | + self.wp.e_on_meas_fit.c_current, self.wp.e_on_meas_fit.voltage_factor, self.wp.e_on_meas_fit.voltage_exponent, self.wp.e_on_meas_fit.ct_0, |
| 2771 | + self.wp.e_on_meas_fit.ct_1, self.wp.e_on_meas_fit.ct_2) |
| 2772 | + elif on_off_key == "off": |
| 2773 | + current_vec = np.linspace(0, self.wp.e_off_meas_fit.current_max) |
| 2774 | + energy_vec = self.fit_function( |
| 2775 | + (current_vec, voltage, temperature), self.wp.e_off_meas_fit.a_current, self.wp.e_off_meas_fit.b_current, |
| 2776 | + self.wp.e_off_meas_fit.c_current, self.wp.e_off_meas_fit.voltage_factor, self.wp.e_off_meas_fit.voltage_exponent, self.wp.e_off_meas_fit.ct_0, |
| 2777 | + self.wp.e_off_meas_fit.ct_1, self.wp.e_off_meas_fit.ct_2) |
| 2778 | + else: |
| 2779 | + raise ValueError("on_off_key must be 'on' or 'off'.") |
| 2780 | + |
| 2781 | + # correct data with the energy in c_oss |
| 2782 | + energy_in_capacitance_at_dpt_voltage = np.interp(voltage, self.graph_v_eoss[0], self.graph_v_ecoss[1]) |
| 2783 | + |
| 2784 | + if on_off_key == "on": |
| 2785 | + energy_vec_corrected = energy_vec + energy_in_capacitance_at_dpt_voltage |
| 2786 | + elif on_off_key == "off": |
| 2787 | + energy_vec_corrected = energy_vec - energy_in_capacitance_at_dpt_voltage |
| 2788 | + |
| 2789 | + return current_vec, energy_vec, energy_vec_corrected |
| 2790 | + |
2655 | 2791 |
|
2656 | 2792 | def attach_units(trans: dict, devices: dict): |
2657 | 2793 | """ |
|
0 commit comments