Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ docs/_templates/

# FEMMT: generated files
femmt/electro_magnetic/Parameter.pro
femmt/electro_magnetic/Electrostatic_Parameter.pro
femmt/electro_magnetic/postquantities.pro
femmt/electro_magnetic/Strands_Coefficients/PreParameter.pro
femmt/electro_magnetic/core_materials_temp.pro
Expand Down
187 changes: 120 additions & 67 deletions femmt/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,10 @@ def write_simulation_parameters_to_pro_files(self):
logger.info("Write simulation parameters to .pro files (file communication).")

# Write initialization parameters for simulation in 'Parameter.pro' file
self.write_electro_magnetic_parameter_pro()
if self.simulation_type == SimulationType.ElectroStatic:
self.write_electro_static_parameter_pro()
else:
self.write_electro_magnetic_parameter_pro()

# Write postprocessing parameters in 'postquantities.pro' file
self.write_electro_magnetic_post_pro()
Expand Down Expand Up @@ -3458,20 +3461,10 @@ def write_electro_magnetic_parameter_pro(self):
text_file.write("Flag_Time_Domain = 1;\n")
text_file.write("Flag_Freq_Domain = 0;\n")
text_file.write("Flag_Static = 0;\n")
if self.simulation_type == SimulationType.ElectroStatic:
text_file.write("Flag_Static = 1;\n")
text_file.write("Flag_Freq_Domain = 0;\n")
text_file.write("Flag_Time_Domain = 0;\n")

# Airgap number
if self.simulation_type == SimulationType.ElectroStatic:
text_file.write("n_airgaps = {};\n".format(len(self.air_gaps.midpoints)))

# Frequency
if not self.simulation_type == SimulationType.ElectroStatic:
text_file.write("Freq = %s;\n" % self.frequency)
text_file.write(f"delta = {self.delta};\n")

# Frequency
text_file.write("Freq = %s;\n" % self.frequency)
text_file.write(f"delta = {self.delta};\n")
# time domain parameters
if self.simulation_type == SimulationType.TimeDomain:
text_file.write(f"T = {self.time_period};\n")
Expand Down Expand Up @@ -3503,8 +3496,6 @@ def write_electro_magnetic_parameter_pro(self):
winding_number=winding_number)

if self.windings[winding_number].parallel:
if self.simulation_type == SimulationType.ElectroStatic:
raise Exception("Parallel winding are not considered yet for electrostatic simulation")
text_file.write(f"NbrCond_{winding_number + 1} = 1;\n")
text_file.write(f"AreaCell_{winding_number + 1} = {self.windings[winding_number].a_cell * turns};\n")
else:
Expand Down Expand Up @@ -3551,32 +3542,6 @@ def write_electro_magnetic_parameter_pro(self):
text_file.write(f"Val_EE_{winding_number + 1} = {self.current_density[winding_number]};\n")
raise NotImplementedError

if self.simulation_type == SimulationType.ElectroStatic:
if self.voltage is not None:
text_file.write("Flag_voltage = 1;\n")
text_file.write("Flag_charge = 0;\n")
turns = self.voltage[winding_number]
for turn_index, voltage_value in enumerate(turns):
text_file.write(f"Voltage_{winding_number + 1}_{turn_index + 1} = {voltage_value};\n")
elif self.charge is not None:
text_file.write("Flag_charge = 1;\n")
text_file.write("Flag_voltage = 0;\n")
turns = self.charge[winding_number]
for turn_index, charge_value in enumerate(turns):
text_file.write(f"Charge_{winding_number + 1}_{turn_index + 1} = {charge_value};\n")

if self.v_core is not None:
text_file.write("v_core = {};\n".format(self.v_core))
text_file.write("Flag_excite_core = 1;\n")
else:
text_file.write("Flag_excite_core = 0;\n")

if self.v_ground_out_boundary == 0:
text_file.write("Flag_ground_OutBoundary = 1;\n")
text_file.write("v_ground_OutBoundary = 0;\n")
else:
text_file.write("Flag_ground_OutBoundary = 0;\n")

logger.info(f"Cell surface area: {self.windings[winding_number].a_cell}, "
f"Reduced frequency: {self.red_freq[winding_number]}")

Expand All @@ -3601,35 +3566,123 @@ def write_electro_magnetic_parameter_pro(self):
text_file.write("er_layer_insulation = 1.0;\n")
text_file.write(f"er_bobbin = {self.insulation.er_bobbin};\n")

if not self.simulation_type == SimulationType.ElectroStatic:
# Core Material
text_file.write(f"mur = {self.core.material.mu_r_abs};\n") # mur is predefined to a fixed value
if self.core.material.permeability_type == PermeabilityType.FromData:
text_file.write("Flag_Permeability_From_Data = 1;\n") # mur is predefined to a fixed value
# Core Material
text_file.write(f"mur = {self.core.material.mu_r_abs};\n") # mur is predefined to a fixed value
if self.core.material.permeability_type == PermeabilityType.FromData:
text_file.write("Flag_Permeability_From_Data = 1;\n") # mur is predefined to a fixed value
else:
text_file.write("Flag_Permeability_From_Data = 0;\n") # mur is predefined to a fixed value
if self.core.material.permeability_type == PermeabilityType.FixedLossAngle:
# Real part of complex permeability
text_file.write(f"mur_real = {self.core.material.mu_r_abs * np.cos(np.deg2rad(self.core.material.phi_mu_deg))};\n")
# Imaginary part of complex permeability
text_file.write(
f"mur_imag = {self.core.material.mu_r_abs * np.sin(np.deg2rad(self.core.material.phi_mu_deg))};\n")
# loss angle for complex representation of hysteresis loss
text_file.write("Flag_Fixed_Loss_Angle = 1;\n")
else:
text_file.write(
"Flag_Fixed_Loss_Angle = 0;\n") # loss angle for complex representation of hysteresis loss

# Complex (equivalent) conductivity
self.core.material.complex_conductance = \
self.core.material.dc_conductivity + complex(0, 1) * 2 * np.pi * self.frequency * self.core.material.complex_permittivity
if self.core.material.complex_conductance != 0 and self.core.material.complex_conductance is not None:
text_file.write("Flag_Conducting_Core = 1;\n")
text_file.write(f"sigma_core_real = {self.core.material.complex_conductance.real};\n")
text_file.write(f"sigma_core_imag = {self.core.material.complex_conductance.imag};\n")
else:
text_file.write("Flag_Conducting_Core = 0;\n")

text_file.close()

def write_electro_static_parameter_pro(self):
"""
Write the needed parameters to the "Electrostatic_Parameter.pro" file.

This file is generated by python and is read by gmsh to hand over some parameters.
"""
text_file = open(os.path.join(self.file_data.electro_magnetic_folder_path, "Electrostatic_Parameter.pro"), "w")

# component types
if self.component_type == ComponentType.Inductor:
text_file.write("Number_of_Windings = {};\n".format(len(self.windings)))

if self.component_type == ComponentType.Transformer:
text_file.write("Number_of_Windings = {};\n".format(len(self.windings)))

if self.component_type == ComponentType.IntegratedTransformer:
text_file.write("Number_of_Windings = {};\n".format(len(self.windings)))

text_file.write("Flag_Static = 1;\n")

# Airgap number
text_file.write("n_airgaps = {};\n".format(len(self.air_gaps.midpoints)))

# Conductor specific definitions
for winding_number in range(len(self.windings)):
# -- Geometry --
# Core Parts
text_file.write(f"nCoreParts = {len(self.mesh.plane_surface_core)};\n")

turns = ff.get_number_of_turns_of_winding(winding_windows=self.winding_windows, windings=self.windings,
winding_number=winding_number)

if self.windings[winding_number].parallel:
raise Exception("Parallel winding are not considered yet for electrostatic simulation")
else:
text_file.write("Flag_Permeability_From_Data = 0;\n") # mur is predefined to a fixed value
if self.core.material.permeability_type == PermeabilityType.FixedLossAngle:
# Real part of complex permeability
text_file.write(f"mur_real = {self.core.material.mu_r_abs * np.cos(np.deg2rad(self.core.material.phi_mu_deg))};\n")
# Imaginary part of complex permeability
text_file.write(
f"mur_imag = {self.core.material.mu_r_abs * np.sin(np.deg2rad(self.core.material.phi_mu_deg))};\n")
# loss angle for complex representation of hysteresis loss
text_file.write("Flag_Fixed_Loss_Angle = 1;\n")
text_file.write(f"NbrCond_{winding_number + 1} = {turns};\n")
text_file.write(f"AreaCell_{winding_number + 1} = {self.windings[winding_number].a_cell};\n")

# relative permittivity
# Turn insulation is not implemented yet for RectangularSolid
# Or if the turn insulation is not drawn
if self.windings[winding_number].conductor_type == ConductorType.RectangularSolid or not self.insulation.turn_ins:
text_file.write(f"er_turns_insulation_{winding_number + 1} = 1.0;\n")
else:
text_file.write(
"Flag_Fixed_Loss_Angle = 0;\n") # loss angle for complex representation of hysteresis loss

# Complex (equivalent) conductivity
self.core.material.complex_conductance = \
self.core.material.dc_conductivity + complex(0, 1) * 2 * np.pi * self.frequency * self.core.material.complex_permittivity
if self.core.material.complex_conductance != 0 and self.core.material.complex_conductance is not None:
text_file.write("Flag_Conducting_Core = 1;\n")
text_file.write(f"sigma_core_real = {self.core.material.complex_conductance.real};\n")
text_file.write(f"sigma_core_imag = {self.core.material.complex_conductance.imag};\n")
text_file.write(f"er_turns_insulation_{winding_number + 1} = {self.insulation.er_turn_insulation[winding_number]};\n")

# For stranded Conductors:
# text_file.write(f"NbrstrandedCond = {self.turns};\n") # redundant
if self.windings[winding_number].conductor_type == ConductorType.RoundLitz:
raise Exception("RoundLitz conductor type not yet implemented for electrostatic simulation")

# could be needed in future
text_file.write(f"Rc_{winding_number + 1} = {self.windings[winding_number].conductor_radius};\n")

if self.voltage is not None:
text_file.write("Flag_voltage = 1;\n")
text_file.write("Flag_charge = 0;\n")
turns = self.voltage[winding_number]
for turn_index, voltage_value in enumerate(turns):
text_file.write(f"Voltage_{winding_number + 1}_{turn_index + 1} = {voltage_value};\n")
elif self.charge is not None:
text_file.write("Flag_charge = 1;\n")
text_file.write("Flag_voltage = 0;\n")
turns = self.charge[winding_number]
for turn_index, charge_value in enumerate(turns):
text_file.write(f"Charge_{winding_number + 1}_{turn_index + 1} = {charge_value};\n")

if self.v_core is not None:
text_file.write("v_core = {};\n".format(self.v_core))
text_file.write("Flag_excite_core = 1;\n")
else:
text_file.write("Flag_Conducting_Core = 0;\n")
text_file.write("Flag_excite_core = 0;\n")

if self.v_ground_out_boundary == 0:
text_file.write("Flag_ground_OutBoundary = 1;\n")
text_file.write("v_ground_OutBoundary = 0;\n")
else:
text_file.write("Flag_ground_OutBoundary = 0;\n")

# Nature Constants
text_file.write(f"e0 = {epsilon_0};\n")
text_file.write(f"er_core= {self.core.material.eps_r};\n")
if self.insulation.er_layer_insulation is not None:
text_file.write(f"er_layer_insulation = {self.insulation.er_layer_insulation};\n")
else:
text_file.write("er_layer_insulation = 1.0;\n")
text_file.write(f"er_bobbin = {self.insulation.er_bobbin};\n")
text_file.close()

def write_electro_magnetic_post_pro(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ----------------------
Include "Parameter.pro";
Include "Electrostatic_Parameter.pro";
Include "postquantities.pro";
// All Variables - remove or create in python
// ----------------------
Expand Down Expand Up @@ -180,7 +180,7 @@ Function {
SurfCore[] = SurfaceArea[]{CORE_PN} ;
// Materials
er_air = 1;
er_core = 100000;
er_core = er_core;
//er_cond_insulation = 3;
For n In {1:n_windings}
epsilon[#{Cond_Insulation~{n}}] = e0 * er_turns_insulation~{n};
Expand Down
6 changes: 1 addition & 5 deletions femmt/examples/basic_inductor_electrostatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ def basic_example_inductor_electrostatic(onelab_folder: str = None, show_visual_
window_h=core_db["window_h"],
core_h=core_db["core_h"])

core_material = fmt.ImportedComplexCoreMaterial(material=fmt.Material.N49,
temperature=45,
permeability_datasource=fmt.DataSource.TDK_MDT,
permittivity_datasource=fmt.DataSource.LEA_MTB,
mdb_verbosity=fmt.Verbosity.Silent)
core_material = fmt.ElectrostaticCoreMaterial(eps_r=100e3)

core = fmt.Core(material=core_material,
core_type=fmt.CoreType.Single,
Expand Down
6 changes: 1 addition & 5 deletions femmt/examples/basic_inductor_foil_vertical_electrostatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ def basic_example_inductor_foil_vertical_electrostatic(onelab_folder: str = None
window_h=core_db["window_h"],
core_h=core_db["core_h"])

core_material = fmt.ImportedComplexCoreMaterial(material=fmt.Material.N49,
temperature=45,
permeability_datasource=fmt.DataSource.TDK_MDT,
permittivity_datasource=fmt.DataSource.LEA_MTB,
mdb_verbosity=fmt.Verbosity.Silent)
core_material = fmt.ElectrostaticCoreMaterial(eps_r=100e3)

core = fmt.Core(material=core_material,
core_type=fmt.CoreType.Single,
Expand Down
4 changes: 2 additions & 2 deletions femmt/examples/basic_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@ def example_thermal_simulation(show_thermal_visual_outputs: bool = True, flag_in
winding1 = fmt.Conductor(0, fmt.ConductorMaterial.Copper)
winding1.set_solid_round_conductor(0.0011, fmt.ConductorArrangement.Square)

# winding1 = fmt.Conductor(0, fmt.Conductivity.Copper)
# winding1 = fmt.Conductor(0, fmt.ConductorMaterial.Copper)
# winding1.set_litz_round_conductor(0.0011, 50, 0.00011, None, fmt.ConductorArrangement.Square)

# winding2 = fmt.Conductor(1, fmt.Conductivity.Copper)
# winding2 = fmt.Conductor(1, fmt.ConductorMaterial.Copper)
# winding2.set_solid_round_conductor(0.0011, fmt.ConductorArrangement.Square)

winding2 = fmt.Conductor(1, fmt.ConductorMaterial.Copper)
Expand Down
16 changes: 2 additions & 14 deletions femmt/examples/basic_transformer_electrostatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,13 @@ def basic_example_transformer_electrostatic(onelab_folder: str = None, show_visu
geo.file_data.onelab_folder_path = onelab_folder

# 2. set core parameters
# core_dimensions = fmt.dtos.SingleCoreDimensions(core_inner_diameter=0.02, window_w=0.01, window_h=0.03,
# core_h=0.02)
# core = fmt.Core(core_dimensions=core_dimensions, mu_r_abs=3100, phi_mu_deg=12, sigma=1.2,
# permeability_datasource=fmt.MaterialDataSource.Custom,
# permittivity_datasource=fmt.MaterialDataSource.Custom,
# detailed_core_model=False)
# geo.set_core(core)
core_db = fmt.core_database()["PQ 40/40"]
core_dimensions = fmt.dtos.SingleCoreDimensions(core_inner_diameter=core_db["core_inner_diameter"],
window_w=core_db["window_w"],
window_h=core_db["window_h"],
core_h=core_db["core_h"])
# core_material = fmt.ImportedComplexCoreMaterial(material=fmt.Material.N49,
# temperature=45,
# permeability_datasource=fmt.DataSource.TDK_MDT,
# permittivity_datasource=fmt.DataSource.LEA_MTB,
# mdb_verbosity=fmt.Verbosity.Silent)

core = fmt.Core(material=None,
core_material = fmt.ElectrostaticCoreMaterial(eps_r=100e3)
core = fmt.Core(material=core_material,
core_type=fmt.CoreType.Single,
core_dimensions=core_dimensions,
detailed_core_model=False)
Expand Down
25 changes: 24 additions & 1 deletion femmt/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,29 @@ def to_dict(self) -> Dict[str, Any]:
"permittivity_datatype": MeasurementDataType.ComplexPermittivity
}

class ElectrostaticCoreMaterial:
"""Defines material properties for electrostatic simulations."""

def __init__(self, eps_r: float):
"""
Define the dielectric constant of the core.

:param eps_r: Relative permittivity of the core material.
:type eps_r: float
"""
self.eps_r = eps_r

def to_dict(self):
"""Return a dictionary representation of the core material.

Useful for serialization or logging.

:return: Dictionary of core material parameters.
:rtype: dict
"""
return {
"eps_r": self.eps_r
}

class Core:
"""Combines geometry and material properties of a magnetic core.
Expand All @@ -500,7 +523,7 @@ class Core:
"""

def __init__(self,
material: ImportedComplexCoreMaterial | LinearComplexCoreMaterial,
material: ImportedComplexCoreMaterial | LinearComplexCoreMaterial | ElectrostaticCoreMaterial,
core_type: CoreType = CoreType.Single,
core_dimensions: Optional[object] = None,
detailed_core_model: bool = False):
Expand Down
Loading