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
2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.x'
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sphinx_render_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.12'
- uses: actions/checkout@v4
- name: install femmt package
run: |
Expand Down
464 changes: 218 additions & 246 deletions femmt/component.py

Large diffs are not rendered by default.

22 changes: 10 additions & 12 deletions femmt/data.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
"""Contains information about the file structure."""
# Python standard libraries
import os

import logging
import numpy as np
from typing import List

# Local libraries
from femmt.enumerations import ConductorType
from femmt.model import Conductor
from typing import Optional

logger = logging.getLogger(__name__)

class FileData:
"""Contains paths to every folder and file needed in femmt."""

def __init__(self, working_directory: str, electro_magnetic_folder_path: str = None, strands_coefficients_folder_path: str = None):
if working_directory is not None:
self.update_paths(working_directory, electro_magnetic_folder_path, strands_coefficients_folder_path)
self.onelab_folder_path: Optional[str] = None
self.onelab_folder_path: str | None = None

@staticmethod
def create_folders(*args: str) -> None:
Expand Down Expand Up @@ -57,10 +56,9 @@ def clean_folder_structure(folder_path: str):
for file in files:
file_path = os.path.join(root, file)
os.remove(file_path)
# print(f"remove {file_path}")
# print("All simulation results from previous simulations have been deleted successfully.")
# logger.info("All simulation results from previous simulations have been deleted successfully.")
except OSError:
print("Error occurred while deleting files and subdirectories.")
logger.warning("Error occurred while deleting files and subdirectories.")

def update_paths(self, working_directory: str, electro_magnetic_folder_path: str = None, strands_coefficients_folder_path: str = None) -> None:
"""Set the local path based on the given working directory.
Expand Down Expand Up @@ -123,16 +121,16 @@ class MeshData:
skin_mesh_factor: float
c_core: float
c_window: float
c_conductor = List[float]
c_center_conductor = List[float]
c_conductor = list[float]
c_center_conductor = list[float]
c_air_gaps: float

center_factor: float

mu0: float
core_w: float
window_w: float
windings: List["Conductor"] # This is written as string because it is a forward import
windings: list["Conductor"] # This is written as string because it is a forward import

frequency: float

Expand All @@ -153,7 +151,7 @@ def __init__(self, mesh_accuracy_core: float,
# The value 4 has good impact on the runtime and does not affect the simulation results too much.
self.center_factor = 4

def update_spatial_data(self, core_w: float, window_w: float, windings: List["Conductor"]):
def update_spatial_data(self, core_w: float, window_w: float, windings: list["Conductor"]):
"""
Update geometry data of the core of the magnetic component.

Expand All @@ -162,7 +160,7 @@ def update_spatial_data(self, core_w: float, window_w: float, windings: List["Co
:param window_w: window width
:type window_w: float
:param windings: list of windings
:type windings: List["Conductor"]
:type windings: list["Conductor"]
"""
self.core_w = core_w
self.window_w = window_w
Expand Down
52 changes: 18 additions & 34 deletions femmt/drawing.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""Draw structures inside Onelab."""
# Python standard libraries
import numpy as np
from logging import Logger
from typing import List
import logging

# Local libraries
from femmt.enumerations import *
from femmt.data import MeshData
from femmt.model import Core, WindingWindow, AirGaps, StrayPath, Insulation

logger = logging.getLogger(__name__)

class TwoDaxiSymmetric:
"""
Expand All @@ -18,28 +18,27 @@ class TwoDaxiSymmetric:
"""

core: Core
winding_windows: List[WindingWindow]
winding_windows: list[WindingWindow]
air_gaps: AirGaps
stray_path: StrayPath
insulation: Insulation
component_type: ComponentType
mesh_data: MeshData
number_of_windings: int
verbosity: Verbosity
logger: Logger

# List of points which represent the model
# Every List is a List of 4 Points: x, y, z, mesh_factor
p_outer: np.ndarray
p_region_bound: np.ndarray
p_window: np.ndarray
p_air_gaps: np.ndarray
# p_conductor: List[List[float]]
p_iso_core: List[List[float]]
p_iso_pri_sec: List[List[float]]
# p_conductor: list[list[float]]
p_iso_core: list[list[float]]
p_iso_pri_sec: list[list[float]]

def __init__(self, core: Core, mesh_data: MeshData, air_gaps: AirGaps, winding_windows: List[WindingWindow],
stray_path: StrayPath, insulation: Insulation, component_type: ComponentType, number_of_windings: int, verbosity: Verbosity, logger: Logger):
def __init__(self, core: Core, mesh_data: MeshData, air_gaps: AirGaps, winding_windows: list[WindingWindow],
stray_path: StrayPath, insulation: Insulation, component_type: ComponentType, number_of_windings: int, verbosity: Verbosity):
self.core = core
self.mesh_data = mesh_data
self.winding_windows = winding_windows
Expand All @@ -49,7 +48,6 @@ def __init__(self, core: Core, mesh_data: MeshData, air_gaps: AirGaps, winding_w
self.insulation = insulation
self.number_of_windings = number_of_windings
self.verbosity = verbosity
self.logger = logger

# -- Arrays for geometry data --
# TODO Is the zero initialization necessary?
Expand All @@ -71,16 +69,6 @@ def __init__(self, core: Core, mesh_data: MeshData, air_gaps: AirGaps, winding_w
self.r_inner = core.r_inner
self.r_outer = core.r_outer

def femmt_print(self, text: str):
"""
Print text to terminal or to log-file, dependent on the current verbosity.

:param text: text to print
:type text: str
"""
if not self.verbosity == Verbosity.Silent:
self.logger.info(text)

def draw_outer(self):
"""Draws the outer points of the main core (single core)."""
# Outer Core
Expand All @@ -96,7 +84,6 @@ def draw_outer(self):
def draw_single_window(self):
"""Draw a single window."""
# At this point both windows (in a cut) are modeled
# print(f"win: c_window: {self.component.mesh.c_window}")
self.p_window[0] = [-self.r_inner,
-self.core.window_h / 2,
0,
Expand Down Expand Up @@ -277,9 +264,6 @@ def draw_conductors(self, draw_top_down: bool = True):
right_bound = virtual_winding_window.right_bound

# Check, if virtual winding window fits in physical window
# print(f"{bot_bound = }\n", f"{top_bound = }\n", f"{left_bound = }\n", f"{right_bound = }\n")
# print(f"{winding_window.max_bot_bound = }\n", f"{winding_window.max_top_bound = }\n", f"{winding_window.max_left_bound = }\n",
# f"{winding_window.max_right_bound = }\n")
if bot_bound < winding_window.max_bot_bound or top_bound > winding_window.max_top_bound or \
left_bound < winding_window.max_left_bound or right_bound > winding_window.max_right_bound:
# Set valid to False, so that geometry is to be neglected in geometry sweep
Expand Down Expand Up @@ -309,9 +293,9 @@ def draw_conductors(self, draw_top_down: bool = True):

"""
if winding0.conductor_radius != winding1.conductor_radius:
print("For bifilar winding scheme both conductors must be of the same radius!")
logger.warning("For bifilar winding scheme both conductors must be of the same radius!")
else:
print("Bifilar winding scheme is applied")
logger.info("Bifilar winding scheme is applied")

raise Exception("Bifilar winding scheme is not implemented yet.")

Expand Down Expand Up @@ -1304,8 +1288,8 @@ def check_number_of_drawn_conductors(self):
drawn_number_of_turns += int(self.p_conductor[winding.winding_number].shape[0] / 5) # rectangular conductors

if drawn_number_of_turns != needed_number_of_turns:
self.femmt_print(f"{drawn_number_of_turns=}")
self.femmt_print(f"{needed_number_of_turns=}")
logger.info(f"{drawn_number_of_turns=}")
logger.info(f"{needed_number_of_turns=}")
raise Exception("Winding mismatch. Probably too many turns that do not fit in the winding window")

def draw_region_single(self):
Expand Down Expand Up @@ -1347,7 +1331,7 @@ def draw_insulations(self):
if self.component_type == ComponentType.IntegratedTransformer:
# TODO: insulations implement for integrated_transformers
# TODO Change back to warnings?
self.femmt_print("Insulations are not set because they are not implemented for integrated transformers.")
logger.info("Insulations are not set because they are not implemented for integrated transformers.")
else:
window_h = self.core.window_h
iso = self.insulation
Expand Down Expand Up @@ -1587,18 +1571,18 @@ def draw_model(self):
# self.draw_region_stacked()

@staticmethod
def get_center_of_rect(p1: List[float], p2: List[float], p3: List[float], p4: List[float]):
def get_center_of_rect(p1: list[float], p2: list[float], p3: list[float], p4: list[float]):
"""
Get center point of a rectangular conductor.

:param p1: Point 1 as a x,y-List
:type p1: List[float]
:type p1: list[float]
:param p2: Point 1 as a x,y-List
:type p2: List[float]
:type p2: list[float]
:param p3: Point 1 as a x,y-List
:type p3: List[float]
:type p3: list[float]
:param p4: Point 1 as a x,y-List
:type p4: List[float]
:type p4: list[float]
"""
x_list = [p1[0], p2[0], p3[0], p4[0]]
y_list = [p1[1], p2[1], p3[1], p4[1]]
Expand Down
47 changes: 23 additions & 24 deletions femmt/dtos.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Data transfer objects (DTOs) used by this toolbox."""
from dataclasses import dataclass
from femmt.enumerations import WindingTag
from typing import Optional, List


@dataclass
Expand Down Expand Up @@ -29,10 +28,10 @@ class ConductorRow:
"""Defines the conductors in one row."""

number_of_conds_per_winding: int
number_of_conds_per_row: Optional[int]
row_height: Optional[float]
number_of_conds_per_row: int | None
row_height: float | None
winding_tag: WindingTag
number_of_rows: Optional[int]
number_of_rows: int | None
additional_bobbin: float


Expand All @@ -55,20 +54,20 @@ class ThreeWindingIsolation:
class CenterTappedGroup:
"""Definitions for the center tapped group. A group is made of several primary and secondary rows."""

primary_number_of_rows: Optional[int]
secondary_number_of_rows: Optional[int]
primary_rest: Optional[int]
secondary_rest: Optional[int]
stack: List[WindingTag]
primary_number_of_rows: int | None
secondary_number_of_rows: int | None
primary_rest: int | None
secondary_rest: int | None
stack: list[WindingTag]


@dataclass
class ConductorStack:
"""Definitions for the conductor stack."""

number_of_groups: int
number_of_single_rows: Optional[int]
order: List[int]
number_of_single_rows: int | None
order: list[int]


@dataclass
Expand Down Expand Up @@ -106,16 +105,16 @@ class TransformerInductance:
class ThreeWindingTransformerInductance:
"""Inductance definitions for a three-winding transformer."""

M_12: Optional[float]
M_13: Optional[float]
M_23: Optional[float]
L_s1: Optional[float]
L_s2: Optional[float]
L_s3: Optional[float]
L_h: Optional[float]
n_12: Optional[float]
n_13: Optional[float]
n_23: Optional[float]
L_s12: Optional[float]
L_s13: Optional[float]
L_s23: Optional[float]
M_12: float | None
M_13: float | None
M_23: float | None
L_s1: float | None
L_s2: float | None
L_s3: float | None
L_h: float | None
n_12: float | None
n_13: float | None
n_23: float | None
L_s12: float | None
L_s13: float | None
L_s23: float | None
5 changes: 2 additions & 3 deletions femmt/examples/advanced_inductor_air_gap_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
import matplotlib.pyplot as plt
import femmt as fmt
import os
from typing import Optional

def basic_example_sweep(onelab_folder: Optional[str] = None, show_visual_outputs: bool = True, is_test: bool = False):
def basic_example_sweep(onelab_folder: str | None = None, show_visual_outputs: bool = True, is_test: bool = False):
"""
Advanced example to demonstrate an air gap sweep for an inductor.

Expand Down Expand Up @@ -39,7 +38,7 @@ def basic_example_sweep(onelab_folder: Optional[str] = None, show_visual_outputs

working_directories.append(directory)

geo = fmt.MagneticComponent(component_type=fmt.ComponentType.Inductor, working_directory=directory, verbosity=fmt.Verbosity.Silent, is_gui=is_test)
geo = fmt.MagneticComponent(component_type=fmt.ComponentType.Inductor, working_directory=directory, is_gui=is_test)

# This line is for automated pytest running on GitHub only. Please ignore this line!
if onelab_folder is not None:
Expand Down
3 changes: 1 addition & 2 deletions femmt/examples/advanced_inductor_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ def advanced_example_inductor_sweep(onelab_folder: str = None, show_visual_outpu
os.mkdir(working_directory)

# 1. chose simulation type
geo = fmt.MagneticComponent(component_type=fmt.ComponentType.Inductor, working_directory=working_directory,
verbosity=fmt.Verbosity.Silent, is_gui=is_test)
geo = fmt.MagneticComponent(component_type=fmt.ComponentType.Inductor, working_directory=working_directory, is_gui=is_test)

# This line is for automated pytest running on GitHub only. Please ignore this line!
if onelab_folder is not None:
Expand Down
7 changes: 5 additions & 2 deletions femmt/examples/basic_inductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
"""
import femmt as fmt
import os
import logging

# configure logging to show femmt terminal output
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)

def basic_example_inductor(onelab_folder: str = None, show_visual_outputs: bool = True, is_test: bool = False):
def basic_example_inductor(onelab_folder: str = None, show_visual_outputs: bool = False, is_test: bool = False):
"""
Run the example code for the inductor.

Expand Down Expand Up @@ -100,7 +103,7 @@ def example_thermal_simulation(show_thermal_visual_outputs: bool = True, flag_in

# 1. chose simulation type
geo = fmt.MagneticComponent(simulation_type=fmt.SimulationType.FreqDomain, component_type=fmt.ComponentType.Inductor, working_directory=working_directory,
verbosity=fmt.Verbosity.ToConsole, is_gui=is_test)
is_gui=is_test)

# This line is for automated pytest running on GitHub only. Please ignore this line!
if onelab_folder is not None:
Expand Down
5 changes: 4 additions & 1 deletion femmt/examples/basic_inductor_excitation_sweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
"""
import femmt as fmt
import os
import logging

# configure logging to show femmt terminal output
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)

example_results_folder = os.path.join(os.path.dirname(__file__), "example_results")
if not os.path.exists(example_results_folder):
Expand Down Expand Up @@ -42,7 +45,7 @@ def basic_example_inductor_excitation_sweep(onelab_folder: str = None, show_visu

# 1. chose simulation type
geo = fmt.MagneticComponent(component_type=fmt.ComponentType.Inductor, working_directory=working_directory,
verbosity=fmt.Verbosity.Silent, is_gui=is_test)
is_gui=is_test)

# This line is for automated pytest running on GitHub only. Please ignore this line!
if onelab_folder is not None:
Expand Down
Loading