diff --git a/femmt/functions.py b/femmt/functions.py
index c37358a4..2fb50280 100644
--- a/femmt/functions.py
+++ b/femmt/functions.py
@@ -800,7 +800,13 @@ def fft(period_vector_t_i: npt.ArrayLike, sample_factor: int = 1000, plot: str =
def plot_fourier_coefficients(frequency_list, amplitude_list, phi_rad_list, sample_factor: int = 1000,
figure_directory: str = None):
"""Plot fourier coefficients in a visual figure."""
- time_period = 1 / min(frequency_list)
+ # dc and ac handling
+ nonzero_frequencies = [f for f in frequency_list if f != 0]
+ if nonzero_frequencies:
+ time_period = 1 / min(nonzero_frequencies)
+ else:
+ time_period = 1
+ # time_period = 1 / min(frequency_list)
t_interp = np.linspace(0, time_period, sample_factor)
reconstructed_signal = 0
@@ -830,6 +836,7 @@ def plot_fourier_coefficients(frequency_list, amplitude_list, phi_rad_list, samp
plt.tight_layout()
if figure_directory is not None:
plt.savefig(figure_directory, bbox_inches="tight")
+ plt.close('all') # close the figures to remove the warning when you run many figures
# plt.show()
@@ -1064,43 +1071,73 @@ def visualize_simulation_results(simulation_result_file_path: str, store_figure_
"""Visualize the simulation results by a figure."""
with open(simulation_result_file_path, "r") as fd:
loaded_results_dict = json.loads(fd.read())
- # core eddy and hysteresis losses
- loss_core_eddy_current = loaded_results_dict["total_losses"]["eddy_core"]
- loss_core_hysteresis = loaded_results_dict["total_losses"]["hyst_core_fundamental_freq"]
- # Inductances and winding losses
- windings_inductance = []
- windings_loss = []
+
+ # Initialize accumulators for cumulative losses and inductances
+ cumulative_core_hysteresis = 0
+ cumulative_core_eddy = 0
+ cumulative_losses = []
+ cumulative_inductances = []
windings_labels = []
- # Dynamically for 3 windings
- for i in range(1, 3):
- winding_key = f"winding{i}"
- if winding_key in loaded_results_dict["single_sweeps"][0]:
- windings_inductance.append(loaded_results_dict["single_sweeps"][0][winding_key]["flux_over_current"][0])
- windings_loss.append(loaded_results_dict["total_losses"][winding_key]["total"])
- windings_labels.append(f"Winding {i}")
-
- # Plotting results
- fig, ax = plt.subplots()
- bar_width = 0.35
- # Plot core losses
- ax.bar(0, loss_core_hysteresis, width=bar_width, label='Core Hysteresis Loss')
- ax.bar(0, loss_core_eddy_current, bottom=loss_core_hysteresis, width=bar_width, label='Core Eddy Current Loss')
+ for index, sweep in enumerate(loaded_results_dict["single_sweeps"]):
+ freq = sweep['f']
+ loss_core_eddy_current = sweep.get("core_eddy_losses", 0)
+ loss_core_hysteresis = sweep.get("core_hyst_losses", 0)
+
+ # Accumulate core losses
+ cumulative_core_hysteresis += loss_core_hysteresis
+ cumulative_core_eddy += loss_core_eddy_current
+
+ # Plotting for each frequency
+ fig, ax = plt.subplots()
+ ax.bar(0, loss_core_hysteresis, width=0.35, label='Core Hysteresis Loss')
+ ax.bar(0, loss_core_eddy_current, bottom=loss_core_hysteresis, width=0.35, label='Core Eddy Current Loss')
+
+ for i in range(1, 4):
+ winding_key = f"winding{i}"
+ if winding_key in sweep:
+ inductance = sweep[winding_key].get("flux_over_current", [0])[0]
+ loss = sweep[winding_key].get("winding_losses", 0)
+
+ if len(cumulative_losses) < i:
+ cumulative_losses.append(loss)
+ cumulative_inductances.append(inductance)
+ windings_labels.append(f"Winding {i}")
+ else:
+ cumulative_losses[i - 1] += loss
+ cumulative_inductances[i - 1] += inductance
+
+ # Plot for current frequency
+ ax.bar(i, loss, width=0.35, label=f'{windings_labels[i - 1]} Loss at {freq} Hz')
+
+ ax.set_ylabel('Losses in W')
+ ax.set_title(f'Loss Distribution at {freq} Hz')
+ ax.legend()
+ plt.grid(True)
+
+ # Save plot for the current frequency
+ base_path, ext = os.path.splitext(store_figure_file_path)
+ filename = f"{base_path}_{index}{ext}"
+ plt.savefig(filename, bbox_inches="tight")
+ plt.close(fig)
+
+ # Plot cumulative results for core and windings
+ fig, ax = plt.subplots()
+ ax.bar(0, cumulative_core_hysteresis, width=0.35, label='Cumulative Core Hysteresis Loss')
+ ax.bar(0, cumulative_core_eddy, bottom=cumulative_core_hysteresis, width=0.35, label='Cumulative Core Eddy Current Loss')
- # Plot winding inductances and losses
- for index, (inductance, loss) in enumerate(zip(windings_inductance, windings_loss), start=1):
- ax.bar(index, loss, width=bar_width, label=f'{windings_labels[index - 1]} Loss')
- print(f"{windings_labels[index - 1]} Inductance: {inductance} H")
- print(f"{windings_labels[index - 1]} Loss: {loss} W")
+ for index, loss in enumerate(cumulative_losses):
+ ax.bar(index + 1, loss, width=0.35, label=f'{windings_labels[index]} Cumulative Loss')
- ax.set_ylabel('Losses in W')
- ax.set_title('Loss Distribution in Magnetic Components')
- ax.set_xticks(list(range(len(windings_labels) + 1)))
+ ax.set_ylabel('total Losses in W')
+ ax.set_title('Loss Distribution in Magnetic Components for all frequencies')
+ ax.set_xticks(range(len(windings_labels) + 1))
ax.set_xticklabels(['Core'] + windings_labels)
ax.legend()
-
plt.grid(True)
- plt.savefig(store_figure_file_path, bbox_inches="tight")
+ base_path, ext = os.path.splitext(store_figure_file_path)
+ cumulative_filename = f"{base_path}_total_freq{ext}"
+ plt.savefig(cumulative_filename, bbox_inches="tight")
if show_plot:
plt.show()
diff --git a/femmt/model.py b/femmt/model.py
index 02a9d18d..5ec936d4 100644
--- a/femmt/model.py
+++ b/femmt/model.py
@@ -562,9 +562,19 @@ def add_air_gap(self, leg_position: AirGapLegPosition, height: float, position_v
elif self.method == AirGapMethod.Percent:
if position_value > 100 or position_value < 0:
raise Exception("AirGap position values for the percent method need to be between 0 and 100.")
+ # Calculate the maximum and minimum position in percent considering the winding window height and air gap length
+ max_position = 100 - (height / self.core.window_h) * 51
+ min_position = (height / self.core.window_h) * 51
+
+ # Adjust the position value if it exceeds the bounds of 0 to 100 percent
+ if position_value > max_position:
+ position_value = max_position
+ elif position_value < min_position:
+ position_value = min_position
+
position = position_value / 100 * self.core.window_h - self.core.window_h / 2
- # When the position is above the winding window it needs to be adjusted
+ # # When the position is above the winding window it needs to be adjusted
if position + height / 2 > self.core.window_h / 2:
position -= (position + height / 2) - self.core.window_h / 2
elif position - height / 2 < -self.core.window_h / 2:
diff --git a/gui/femmt_gui.py b/gui/femmt_gui.py
index 6090ddac..031139d9 100644
--- a/gui/femmt_gui.py
+++ b/gui/femmt_gui.py
@@ -2753,7 +2753,16 @@ def md_get_frequency_lists(self) -> List:
winding2_frequency_list = []
winding2_amplitude_list = []
winding2_phi_rad_list = []
+ # case to handle dc in exitation sweeb
+ if self.md_dc_checkBox.isChecked():
+ winding1_frequency_list.append(0) # DC frequency is 0 Hz
+ winding1_amplitude_list.append(comma_str_to_point_float(self.md_winding1_idc_lineEdit.text()))
+ winding1_phi_rad_list.append(0) # DC phase is typically 0
+ if self.md_simulation_type_comboBox.currentText() != self.translation_dict['inductor']:
+ winding2_frequency_list.append(0) # DC frequency is 0 Hz
+ winding2_amplitude_list.append(comma_str_to_point_float(self.md_winding2_idc_lineEdit.text()))
+ winding2_phi_rad_list.append(0) # DC phase is typically 0
if self.md_fk1_checkBox.isChecked():
winding1_frequency_list.append(1 * comma_str_to_point_float(self.md_base_frequency_lineEdit.text()))
winding1_amplitude_list.append(comma_str_to_point_float(self.md_winding1_ik1_lineEdit.text()))
@@ -3240,47 +3249,46 @@ def md_action_run_simulation(self) -> None:
# Read back results
# -----------------------------------------------
- self.md_simulation_QLabel.setText('simulation fertig.')
-
- # loaded_results_dict = fmt.visualize_simulation_results(geo.file_data.femm_results_log_path, './results.png', show_plot=False)
- loaded_results_dict = fmt.visualize_simulation_results(geo.file_data.e_m_results_log_path,
- geo.file_data.results_em_simulation, show_plot=False)
- # pixmap = QPixmap("./results.png")
- pixmap = QPixmap(geo.file_data.results_em_simulation)
- self.md_loss_plot_label.setPixmap(pixmap)
- self.md_loss_plot_label.setMask(pixmap.mask())
- self.md_loss_plot_label.show()
-
- # inductance = loaded_results_dict["single_sweeps"][0]["winding1"]["flux_over_current"][0]
- loss_core_eddy_current = loaded_results_dict["total_losses"]["eddy_core"]
- loss_core_hysteresis = loaded_results_dict["total_losses"]["hyst_core_fundamental_freq"]
- # loss_winding_1 = loaded_results_dict["total_losses"]["winding1"]["total"]
- self.md_loss_core_hysteresis_label.setText(f"Core Hysteresis loss: {loss_core_hysteresis} W")
- self.md_loss_core_eddy_current_label.setText(f"Core Eddy Current loss: {loss_core_eddy_current} W")
- # self.md_loss_winding1_label.setText(f"Winding 1 loss: {loss_winding_1} W")
- # self.md_inductance_label.setText(f"Primary Inductance: {inductance} H")
- inductances = []
- windings_loss = []
- for i in range(1, 3): # for 3 windings
- winding_key = f"winding{i}"
- if winding_key in loaded_results_dict["single_sweeps"][0]:
- inductances.append(loaded_results_dict["single_sweeps"][0][winding_key]["flux_over_current"][0])
- windings_loss.append(loaded_results_dict["total_losses"][winding_key]["total"])
-
- # show them just for 2 windings in GUI
- if self.md_simulation_type_comboBox.currentText() == self.translation_dict['inductor']:
- self.md_loss_winding1_label.setText(f"Winding 1 loss: {windings_loss[0]} W")
- self.md_inductance1_label.setText(f"Primary Inductance: {inductances[0]} H")
- elif self.md_simulation_type_comboBox.currentText() == self.translation_dict['transformer']:
- self.md_loss_winding1_label.setText(f"Winding 1 loss: {windings_loss[0]} W")
- self.md_loss_winding2_label.setText(f"Winding 2 loss: {windings_loss[1]} W")
- self.md_inductance1_label.setText(f"Primary Inductance: {inductances[0]} H")
- self.md_inductance2_label.setText(f"Secondary Inductance: {inductances[1]} H")
-
- # log_path = geo.e_m_results_log_path
- # simulation_results = str(fmt.read_results_log(log_path))
- # print(simulation_results)
- # self.md_simulation_output_textBrowser.setText(simulation_results)
+ self.md_simulation_QLabel.setText('simulation complete.')
+ loaded_results_dict = fmt.visualize_simulation_results(geo.file_data.e_m_results_log_path, geo.file_data.results_em_simulation, show_plot=False)
+
+ for index, sweep in enumerate(loaded_results_dict["single_sweeps"]):
+ # Frequency-specific losses
+ freq_label = getattr(self, f'md_freq{index + 1}')
+ # loss_plot_label = getattr(self, f'md_loss_plot_label1')
+ hysteresis_label = getattr(self, f'md_loss_core_hysteresis_label{index + 1}')
+ eddy_current_label = getattr(self, f'md_loss_core_eddy_current_label{index + 1}')
+ winding1_loss_label = getattr(self, f'md_loss_winding1_label{index + 1}')
+ inductance1_label = getattr(self, f'md_inductance1_label{index + 1}')
+
+ if self.md_simulation_type_comboBox.currentText() == self.translation_dict['transformer']:
+ winding2_loss_label = getattr(self, f'md_loss_winding2_label{index + 1}')
+ inductance2_label = getattr(self, f'md_inductance2_label{index + 1}')
+
+ # Update frequency label
+ freq_label.setText(f"Frequency: {sweep['f']} Hz")
+
+ # image for loss plot
+ base_path, ext = os.path.splitext(geo.file_data.results_em_simulation)
+ cumulative_filename = f"{base_path}_total_freq{ext}"
+ pixmap = QPixmap(cumulative_filename)
+ if not pixmap.isNull():
+ # to show more than one figure in the future:
+ # loss_plot_label.setPixmap(pixmap)
+ # loss_plot_label.show()
+ # just for shown one figure:
+ self.md_loss_plot_label1.setPixmap(pixmap)
+ self.md_loss_plot_label1.show()
+
+ # loss labels
+ hysteresis_label.setText(f"Core Hysteresis loss: {sweep.get('core_hyst_losses', 0)} W")
+ eddy_current_label.setText(f"Core Eddy Current loss: {sweep.get('core_eddy_losses', 0)} W")
+ winding1_loss_label.setText(f"Winding 1 loss: {sweep['winding1'].get('winding_losses', 0)} W")
+ inductance1_label.setText(f"Primary Inductance: {sweep['winding1'].get('flux_over_current', [0])[0]} H")
+ # transformer case
+ if self.md_simulation_type_comboBox.currentText() == self.translation_dict['transformer']:
+ winding2_loss_label.setText(f"Winding 2 loss: {sweep['winding2'].get('winding_losses', 0)} W")
+ inductance2_label.setText(f"Secondary Inductance: {sweep['winding2'].get('flux_over_current', [0])[0]} H")
def inductancecalc(self):
"""Calculate inductance from given geometries."""
diff --git a/gui/femmt_gui.ui b/gui/femmt_gui.ui
index b1ec0549..7ad82d03 100644
--- a/gui/femmt_gui.ui
+++ b/gui/femmt_gui.ui
@@ -6,8 +6,8 @@
0
0
- 1656
- 1076
+ 1545
+ 1177
@@ -37,7 +37,7 @@
0
0
- 1619
+ 1508
1280
@@ -45,7 +45,7 @@
-
- 1
+ 2
@@ -61,7 +61,7 @@
- 3
+ 2
@@ -1836,47 +1836,690 @@
Simulation Text Output
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
+
+
-
+
+
+ false
+
+
+
+ 0
+ 0
+ 641
+ 1137
+
+
+
+
-
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1887,12 +2530,37 @@
Simulation Visual Output
-
- -
-
-
-
+
+
-
+
+
+ false
+
+
+
+ 0
+ 0
+ 619
+ 1137
+
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
@@ -3854,7 +4522,7 @@
0
0
- 635
+ 98
518
@@ -4026,7 +4694,7 @@
0
0
- 468
+ 98
518
@@ -4081,8 +4749,8 @@
0
0
- 1551
- 1172
+ 268
+ 253
@@ -4212,8 +4880,8 @@
0
0
- 1511
- 807
+ 98
+ 518
@@ -4283,7 +4951,7 @@
0
0
- 1514
+ 1403
2036
@@ -8776,7 +9444,7 @@
-
- T
+ Hz
@@ -8786,7 +9454,7 @@
-
- T
+ Hz
@@ -8796,7 +9464,7 @@
-
- T
+ Hz
@@ -8806,7 +9474,7 @@
-
- T
+ Hz
@@ -8816,7 +9484,7 @@
-
- T
+ Hz
@@ -8825,6 +9493,9 @@
Qt::Horizontal
+
+ QSizePolicy::Expanding
+
40
@@ -8872,7 +9543,7 @@
0
0
- 1514
+ 1403
1024
@@ -8926,15 +9597,18 @@
-
+
+ false
+
- true
+ false
0
0
- 1531
+ 1329
967
@@ -9483,7 +10157,7 @@
0
0
- 1656
+ 1545
21