Skip to content

Commit 636e282

Browse files
authored
Merge pull request #162 from upb-lea/planar_transformer
Planar transformer
2 parents be2e564 + 5ce2908 commit 636e282

17 files changed

Lines changed: 1678 additions & 51 deletions

docs/wordlist

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,17 @@ xlsxwriter
523523
Thermoset
524524
Polyphenylene
525525

526+
# Time domain Visualization
527+
VisualizationMode
528+
nopopup
529+
ShowScale
530+
PositionY
531+
PositionX
532+
AutoPosition
533+
534+
# Winding scheme
535+
HorizontalRightward
536+
526537
# mdb
527538
mdb
528539
CoreMaterial

femmt/component.py

Lines changed: 150 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import csv
44
import fileinput
55
import os
6+
import sys
67
import gmsh
78
import json
89
import warnings
@@ -51,7 +52,7 @@ class MagneticComponent:
5152

5253
def __init__(self, simulation_type: SimulationType = SimulationType.FreqDomain,
5354
component_type: ComponentType = ComponentType.Inductor, working_directory: str = None,
54-
clean_previous_results: bool = True, onelab_verbosity: Verbosity = 1, is_gui: bool = False,
55+
clean_previous_results: bool = True, onelab_verbosity: Verbosity = 1, visualization_mode: VisualizationMode = 1, is_gui: bool = False,
5556
simulation_name: str | None = None, wwr_enabled=True):
5657
"""
5758
Initialize the magnetic component.
@@ -63,6 +64,8 @@ def __init__(self, simulation_type: SimulationType = SimulationType.FreqDomain,
6364
:type component_type: ComponentType
6465
:param working_directory: Sets the working directory
6566
:type working_directory: string
67+
:param visualization_mode: Sets the visualization mode. it is only used in the time domain simulation.
68+
:type visualization_mode: VisualizationMode
6669
:param is_gui: Asks at first startup for onelab-path. Distinction between GUI and command line.
6770
Defaults to 'False' in command-line-mode.
6871
:type is_gui: bool
@@ -97,6 +100,9 @@ def __init__(self, simulation_type: SimulationType = SimulationType.FreqDomain,
97100

98101
self.wwr_enabled = wwr_enabled
99102

103+
# visulization
104+
self.visualization_mode = visualization_mode
105+
100106
logger.info(f"\n"
101107
f"Initialized a new Magnetic Component of type {component_type.name}\n"
102108
f"--- --- --- ---")
@@ -1298,9 +1304,19 @@ def simulate(self):
12981304
self.onelab_client.runSubClient("myGetDP", getdp_filepath + " " + solver_freq + " -msh " + \
12991305
self.file_data.e_m_mesh_file + " -solve Analysis -v2 " + verbose + to_file_str)
13001306
if self.simulation_type == SimulationType.TimeDomain:
1301-
# the two commands work but some changes should be done in fields_time.pro
1302-
self.onelab_client.runSubClient("myGetDP", getdp_filepath + " " + solver_time + " -msh " + self.file_data.e_m_mesh_file + \
1303-
" -solve Analysis -pos Map_local " + verbose + to_file_str)
1307+
if self.visualization_mode == VisualizationMode.Stream:
1308+
# 1) Start GUI
1309+
if not gmsh.isInitialized():
1310+
gmsh.initialize()
1311+
if '-nopopup' not in sys.argv:
1312+
gmsh.fltk.initialize()
1313+
gmsh.onelab.run("myGetDP", getdp_filepath + " " + solver_time + " -msh " + self.file_data.e_m_mesh_file + \
1314+
" -solve Analysis -pos Map_local " + verbose + to_file_str)
1315+
gmsh.fltk.run()
1316+
else:
1317+
# the two commands work but some changes should be done in fields_time.pro
1318+
self.onelab_client.runSubClient("myGetDP", getdp_filepath + " " + solver_time + " -msh " + self.file_data.e_m_mesh_file + \
1319+
" -solve Analysis -pos Map_local " + verbose + to_file_str)
13041320
# self.onelab_client.runSubClient("myGetDP", getdp_filepath + " " + solver + " -msh " + self.file_data.e_m_mesh_file +
13051321
# " -solve Analysis -v2 " + verbose) # freeing solutions
13061322
if self.simulation_type == SimulationType.ElectroStatic:
@@ -1453,7 +1469,6 @@ def time_domain_simulation(self, current_period_vec: list[list[float]], time_per
14531469
:param rolling_avg_window_size: how many data points used in each calculation of the average
14541470
:param benchmark: Benchmark simulation (stop time). Defaults to False.
14551471
:type benchmark: bool
1456-
14571472
"""
14581473
self.check_create_empty_material_log()
14591474

@@ -1480,7 +1495,10 @@ def time_domain_simulation(self, current_period_vec: list[list[float]], time_per
14801495
self.calculate_average_files()
14811496
self.calculate_and_write_time_domain_log() # TODO: reuse center tapped
14821497
if show_fem_simulation_results:
1483-
self.visualize()
1498+
if self.visualization_mode == VisualizationMode.Final:
1499+
self.visualize()
1500+
elif self.visualization_mode == VisualizationMode.Post:
1501+
self.live_visualization_2d()
14841502
if show_rolling_average:
14851503
self.get_rolling_average(window_size=rolling_avg_window_size)
14861504

@@ -1496,7 +1514,10 @@ def time_domain_simulation(self, current_period_vec: list[list[float]], time_per
14961514
self.calculate_and_write_time_domain_log() # TODO: reuse center tapped
14971515

14981516
if show_fem_simulation_results:
1499-
self.visualize()
1517+
if self.visualization_mode == VisualizationMode.Final:
1518+
self.visualize()
1519+
elif self.visualization_mode == VisualizationMode.Post:
1520+
self.live_visualization_2d()
15001521
if show_rolling_average:
15011522
self.get_rolling_average(window_size=rolling_avg_window_size)
15021523

@@ -3460,6 +3481,15 @@ def write_electro_magnetic_parameter_pro(self):
34603481
text_file.write("Flag_Time_Domain = 1;\n")
34613482
text_file.write("Flag_Freq_Domain = 0;\n")
34623483
text_file.write("Flag_Static = 0;\n")
3484+
# Needed for visualization
3485+
if self.visualization_mode == VisualizationMode.Stream:
3486+
text_file.write("Flag_Stream_Visualization = 1;\n")
3487+
else:
3488+
text_file.write("Flag_Stream_Visualization = 0;\n")
3489+
if self.simulation_type == SimulationType.ElectroStatic:
3490+
text_file.write("Flag_Static = 1;\n")
3491+
text_file.write("Flag_Freq_Domain = 0;\n")
3492+
text_file.write("Flag_Time_Domain = 0;\n")
34633493

34643494
# Frequency
34653495
text_file.write("Freq = %s;\n" % self.frequency)
@@ -4758,6 +4788,119 @@ def visualize(self):
47584788
gmsh.fltk.run()
47594789
# gmsh.finalize()
47604790

4791+
def live_visualization_2d(self):
4792+
"""
4793+
Show live visualization of the time-domain simulation results.
4794+
4795+
- a post simulation viewer
4796+
- allows to open ".pos"-files in gmsh
4797+
- For example current density, ohmic losses or the magnetic field density can be visualized
4798+
"""
4799+
logger.info("\n---\nVisualize fields in GMSH front end:\n")
4800+
gmsh.initialize()
4801+
epsilon = 1e-9
4802+
4803+
# merge view files
4804+
gmsh.merge(os.path.join(self.file_data.e_m_fields_folder_path, 'j2F_density.pos'))
4805+
gmsh.merge(os.path.join(self.file_data.e_m_fields_folder_path, 'j2H_density.pos'))
4806+
gmsh.merge(os.path.join(self.file_data.e_m_fields_folder_path, 'Magb.pos'))
4807+
# to show the magnetic field in space and time step, merge b_grid, if it is existed
4808+
b_grid = os.path.join(self.file_data.e_m_fields_folder_path, "b_grid.pos")
4809+
has_b_grid = os.path.exists(b_grid)
4810+
if has_b_grid:
4811+
gmsh.merge(b_grid)
4812+
4813+
# Show the GUI:
4814+
if '-nopopup' not in sys.argv:
4815+
gmsh.fltk.initialize()
4816+
4817+
v = gmsh.view.getTags()
4818+
if len(v) == 0:
4819+
logger.info("No .pos views found in:", self.file_data.e_m_fields_folder_path)
4820+
gmsh.finalize()
4821+
return
4822+
# We set some options for each post-processing view:
4823+
# Mesh
4824+
gmsh.option.setNumber("Mesh.SurfaceEdges", 0)
4825+
4826+
if any(self.windings[i].conductor_type != ConductorType.RoundLitz for i in range(len(self.windings))):
4827+
# Ohmic losses (weighted effective value of current density)
4828+
# gmsh.open(os.path.join(self.file_data.e_m_fields_folder_path, "j2F_density.pos"))
4829+
gmsh.option.setNumber(f"View[{v[0]}].ScaleType", 2)
4830+
gmsh.option.setNumber(f"View[{v[0]}].RangeType", 3)
4831+
gmsh.option.setNumber(f"View[{v[0]}].SaturateValues", 1)
4832+
gmsh.option.setNumber(f"View[{v[0]}].CustomMin", gmsh.option.getNumber(f"View[{v[0]}].Min") + epsilon)
4833+
gmsh.option.setNumber(f"View[{v[0]}].CustomMax", gmsh.option.getNumber(f"View[{v[0]}].Max"))
4834+
gmsh.option.setNumber(f"View[{v[0]}].ColormapNumber", 1)
4835+
gmsh.option.setNumber(f"View[{v[0]}].IntervalsType", 2)
4836+
gmsh.option.setNumber(f"View[{v[0]}].NbIso", 40)
4837+
gmsh.option.setNumber(f"View[{v[0]}].ShowTime", 4)
4838+
gmsh.option.setNumber(f"View[{v[0]}].TimeStep", 1)
4839+
gmsh.option.setNumber(f"View[{v[0]}].Time", 1)
4840+
gmsh.option.setNumber(f"View[{v[0]}].NbTimeStep", 1)
4841+
4842+
if any(self.windings[i].conductor_type == ConductorType.RoundLitz for i in range(len(self.windings))):
4843+
# Ohmic losses (weighted effective value of current density)
4844+
# gmsh.open(os.path.join(self.file_data.e_m_fields_folder_path, "j2H_density.pos"))
4845+
gmsh.option.setNumber(f"View[{v[1]}].ScaleType", 2)
4846+
gmsh.option.setNumber(f"View[{v[1]}].RangeType", 3)
4847+
gmsh.option.setNumber(f"View[{v[1]}].SaturateValues", 1)
4848+
gmsh.option.setNumber(f"View[{v[1]}].CustomMin",
4849+
gmsh.option.getNumber(f"View[{v[1]}].Min") + epsilon)
4850+
gmsh.option.setNumber(f"View[{v[1]}].CustomMax", gmsh.option.getNumber(f"View[{v[1]}].Max"))
4851+
gmsh.option.setNumber(f"View[{v[1]}].ColormapNumber", 1)
4852+
gmsh.option.setNumber(f"View[{v[1]}].IntervalsType", 2)
4853+
gmsh.option.setNumber(f"View[{v[1]}].NbIso", 40)
4854+
gmsh.option.setNumber(f"View[{v[1]}].ShowTime", 4)
4855+
gmsh.option.setNumber(f"View[{v[1]}].TimeStep", 1)
4856+
gmsh.option.setNumber(f"View[{v[1]}].Time", 1)
4857+
gmsh.option.setNumber(f"View[{v[1]}].NbTimeStep", 1)
4858+
4859+
# Magnetic flux density
4860+
# gmsh.open(os.path.join(self.file_data.e_m_fields_folder_path, "Magb.pos"))
4861+
gmsh.option.setNumber(f"View[{v[2]}].ScaleType", 1)
4862+
gmsh.option.setNumber(f"View[{v[2]}].RangeType", 3)
4863+
gmsh.option.setNumber(f"View[{v[2]}].CustomMin", gmsh.option.getNumber(f"View[{v[2]}].Min") + epsilon)
4864+
gmsh.option.setNumber(f"View[{v[2]}].CustomMax", gmsh.option.getNumber(f"View[{v[2]}].Max"))
4865+
gmsh.option.setNumber(f"View[{v[2]}].ColormapNumber", 1)
4866+
gmsh.option.setNumber(f"View[{v[2]}].IntervalsType", 2)
4867+
gmsh.option.setNumber(f"View[{v[2]}].ShowTime", 4)
4868+
gmsh.option.setNumber(f"View[{v[2]}].NbIso", 40)
4869+
gmsh.option.setNumber(f"View[{v[2]}].TimeStep", 1)
4870+
gmsh.option.setNumber(f"View[{v[2]}].Time", 1)
4871+
gmsh.option.setNumber(f"View[{v[2]}].NbTimeStep", 1)
4872+
4873+
if has_b_grid:
4874+
bgrid_view = v[-1] # last merged
4875+
gmsh.view.option.setString(bgrid_view, "Name", "B_field...")
4876+
gmsh.view.option.setNumber(bgrid_view, "Axes", 1)
4877+
gmsh.view.option.setNumber(bgrid_view, "IntervalsType", 2)
4878+
gmsh.view.option.setNumber(bgrid_view, "Type", 2) # graph mode
4879+
gmsh.view.option.setNumber(bgrid_view, "AutoPosition", 0)
4880+
gmsh.view.option.setNumber(bgrid_view, "PositionX", 50)
4881+
gmsh.view.option.setNumber(bgrid_view, "PositionY", 50)
4882+
gmsh.view.option.setNumber(bgrid_view, "Width", 350)
4883+
gmsh.view.option.setNumber(bgrid_view, "Height", 250)
4884+
4885+
# Configure views for 2D
4886+
for vt in v:
4887+
gmsh.view.option.setNumber(vt, "IntervalsType", 2) # color shading
4888+
gmsh.view.option.setNumber(vt, "ShowScale", 1) # colorbar
4889+
gmsh.view.option.setNumber(vt, "Visible", 1) # ensure visible
4890+
4891+
# Animate through time steps
4892+
frames = int(gmsh.view.option.getNumber(v[0], "NbTimeStep"))
4893+
t = 0
4894+
while gmsh.fltk.isAvailable():
4895+
for vt in v:
4896+
gmsh.view.option.setNumber(vt, "TimeStep", t)
4897+
gmsh.graphics.draw()
4898+
time.sleep(0.2) # seconds per frame
4899+
t = (t + 1) % frames
4900+
if '-nopopup' not in sys.argv:
4901+
gmsh.fltk.run()
4902+
gmsh.finalize()
4903+
47614904
def get_loss_data(self, last_n_values: int, loss_type: str = 'litz_loss'):
47624905
"""
47634906
Return the last n values from the chosen loss type logged in the result folder.

0 commit comments

Comments
 (0)