33import csv
44import fileinput
55import os
6+ import sys
67import gmsh
78import json
89import 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 ---\n Visualize 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