Skip to content

Commit 0c51ceb

Browse files
authored
Merge pull request #109 from upb-lea/split_improvement
Split improvement
2 parents 0b68937 + 9242235 commit 0c51ceb

File tree

9 files changed

+234
-14
lines changed

9 files changed

+234
-14
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
- name: install femmt package
3838
run: |
3939
sudo apt-get update
40-
# Problem with OSError: libGLU.so.1: connot open shared object file
40+
# Problem with OSError: libGLU.so.1: cannot open shared object file
4141
# Answer here: https://stackoverflow.com/questions/55313610/importerror-libgl-so-1-cannot-open-shared-object-file-no-such-file-or-directo
4242
sudo apt install libsm6 libxext6 ffmpeg libfontconfig1 libxrender1 libglu1
4343
# Unzip used for onelab

.github/workflows/sphinx_render_docs.yml

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
name: "Sphinx: Render docs"
22

3-
on: push
3+
on:
4+
# Triggers the workflow on push or pull request events but only for the main branch
5+
push:
6+
branches: [ main]
7+
pull_request:
8+
branches: [ main ]
9+
# Allows you to run this workflow manually from the Actions tab
10+
workflow_dispatch:
411

512
jobs:
613
build:
@@ -13,15 +20,28 @@ jobs:
1320
with:
1421
python-version: '3.10'
1522
- uses: actions/checkout@v4
23+
- name: install femmt package
24+
run: |
25+
sudo apt-get update
26+
# Problem with OSError: libGLU.so.1: cannot open shared object file
27+
# Answer here: https://stackoverflow.com/questions/55313610/importerror-libgl-so-1-cannot-open-shared-object-file-no-such-file-or-directo
28+
sudo apt install libsm6 libxext6 ffmpeg libfontconfig1 libxrender1 libglu1
29+
# Unzip used for onelab
30+
sudo apt install unzip
31+
pip install --upgrade pip
32+
pip install opencv-python
33+
pip install -e .
1634
- name: Install sphinx and build documentation with sphinx
1735
run: |
1836
python --version
19-
pip install sphinx sphinx-multiversion sphinx_rtd_theme sphinxcontrib-email
20-
pip install -e .
21-
- name: Build HTML
22-
uses: ammaraskar/sphinx-action@master
23-
with:
24-
docs-folder: "docs/"
37+
pip install sphinx sphinx_rtd_theme sphinxcontrib-email
38+
cd docs
39+
make html
40+
# - name: Build HTML
41+
# uses: ammaraskar/sphinx-action@master
42+
# with:
43+
# docs-folder: "docs/"
44+
# pre-build-command: "pip install sphinx_rtd_theme sphinxcontrib-email"
2545
- name: Upload artifacts
2646
uses: actions/upload-artifact@v4
2747
with:

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77

88
## [Unreleased]
9-
9+
## [0.5.3] - 2024-05-23
10+
### Added
11+
- Conductor placing strategies
1012

1113
## [0.5.2] - 2024-04-30
1214
### Added
@@ -138,7 +140,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
138140
- add femmt/SolidComp.py
139141
- add femmt/CompRes.py
140142

141-
[Unreleased]: https://github.com/upb-lea/transistordatabase/compare/0.5.1...HEAD
143+
[Unreleased]: https://github.com/upb-lea/transistordatabase/compare/0.5.3...HEAD
144+
[0.5.3]: https://github.com/upb-lea/transistordatabase/compare/0.5.3...0.5.2
145+
[0.5.2]: https://github.com/upb-lea/transistordatabase/compare/0.5.2...0.5.1
142146
[0.5.1]: https://github.com/upb-lea/transistordatabase/compare/0.5.1...0.5.0
143147
[0.5.0]: https://github.com/upb-lea/transistordatabase/compare/0.5.0...0.4.0
144148
[0.4.0]: https://github.com/upb-lea/transistordatabase/compare/0.4.0...0.3.0

README.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Stable features
2929
* `Center tapped transformer </femmt/examples/basic_transformer_center_tapped.py>`__
3030
* `Magnetic shunt </femmt/examples/basic_transformer_integrated.py>`__ (transformer with integrated inductor)
3131
* `Stacked core </femmt/examples/basic_transformer_stacked.py>`__ (transformer with integrated inductor)
32-
* Round litz wire
32+
* Round litz wire, implemented according to `Niyomsatian et al.: Frequency-domain homogenization for litz-wire bundles in finite element calculations <https://ieeexplore.ieee.org/document/9007233>`__
3333
* Round and rectangular solid wires
3434
* Different winding schemes (hexagonal, left/right, top/down, ...)
3535
* Parallel connection of solid wires
@@ -45,7 +45,7 @@ Stable features
4545
* Implemented using `ONELAB <https://onelab.info/>`__
4646
* Current excitation
4747
* Frequency domain solver
48-
* Litz wire loss model for proximity and skin effect (Niyomsatian et al.: Frequency-domain homogenization for impedance characterization of litz-wire transformers in 2-D finite element models)
48+
* Litz wire loss model for proximity and skin effect (`Niyomsatian et al.: Frequency-domain homogenization for impedance characterization of litz-wire transformers in 2-D finite element models <https://ieeexplore.ieee.org/document/7695378>`__)
4949
* Core loss calculation for real materials (data from material database)
5050
* Amplitude dependent loss angle (Local resolution of complex permeability)
5151
* Equivalent permittivity data for eddy current calculations
@@ -98,6 +98,13 @@ Documentation
9898
-------------------
9999
Please have a look at the `documentation <https://upb-lea.github.io/FEM_Magnetics_Toolbox/intro.html>`__. You will find tutorials and a function description.
100100

101+
Literature
102+
-------------------
103+
104+
* `An Open-Source FEM Magnetics Toolbox for Power Electronic Magnetic Components <https://ieeexplore.ieee.org/document/9862128>`__
105+
106+
* `An Open-Source FEM Magnetic Toolbox for Calculating Electric and Thermal Behavior of Power Electronic Magnetic Components <https://ieeexplore.ieee.org/document/9907554>`__
107+
101108
Installation
102109
---------------
103110

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
author = 'LEA-UPB'
2424

2525
# The full version, including alpha/beta/rc tags
26-
release = '0.5.2'
26+
release = '0.5.3'
2727

2828
# -- General configuration ---------------------------------------------------
2929

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Example how to use the split winding method. Run this file, to see the different winding orders."""
2+
import femmt as fmt
3+
import os
4+
5+
def run_transformer_vvw_split_examples(num_windings, onelab_folder: str = None, show_visual_outputs: bool = True, is_test: bool = False):
6+
"""Run the example code for the transformer."""
7+
example_results_folder = os.path.join(os.path.dirname(__file__), "example_results")
8+
if not os.path.exists(example_results_folder):
9+
os.mkdir(example_results_folder)
10+
11+
def setup_simulation(working_directory, horizontal_split_factors, vertical_split_factors):
12+
geo = fmt.MagneticComponent(component_type=fmt.ComponentType.Transformer, working_directory=working_directory,
13+
verbosity=fmt.Verbosity.Silent, is_gui=is_test)
14+
15+
# This line is for automated pytest running on GitHub only. Please ignore this line!
16+
if onelab_folder is not None:
17+
geo.file_data.onelab_folder_path = onelab_folder
18+
19+
core_dimensions = fmt.dtos.SingleCoreDimensions(window_h=16.1e-3, window_w=(22.5 - 12) / 2 * 1e-3,
20+
core_inner_diameter=12e-3, core_h=22e-3)
21+
core = fmt.Core(core_dimensions=core_dimensions, material=fmt.Material.N95, temperature=60, frequency=100000,
22+
permeability_datasource=fmt.MaterialDataSource.Measurement,
23+
permeability_datatype=fmt.MeasurementDataType.ComplexPermeability,
24+
permeability_measurement_setup=fmt.MeasurementSetup.LEA_LK,
25+
permittivity_datasource=fmt.MaterialDataSource.Measurement,
26+
permittivity_datatype=fmt.MeasurementDataType.ComplexPermittivity,
27+
permittivity_measurement_setup=fmt.MeasurementSetup.LEA_LK)
28+
geo.set_core(core)
29+
30+
air_gaps = fmt.AirGaps(fmt.AirGapMethod.Percent, core)
31+
air_gaps.add_air_gap(fmt.AirGapLegPosition.CenterLeg, 0.00016, 50)
32+
geo.set_air_gaps(air_gaps)
33+
34+
insulation = fmt.Insulation()
35+
insulation.add_core_insulations(0.0008, 0.0008, 0.0001, 0.00001)
36+
iso_self = 0.0001
37+
iso_against = 0.0002
38+
insulation.add_winding_insulations(
39+
[[iso_self, iso_against, iso_against, iso_against, iso_against, iso_against, iso_against],
40+
[iso_against, iso_self, iso_against, iso_against, iso_against, iso_against, iso_against],
41+
[iso_against, iso_against, iso_self, iso_against, iso_against, iso_against, iso_against],
42+
[iso_against, iso_against, iso_against, iso_self, iso_against, iso_against, iso_against],
43+
[iso_against, iso_against, iso_against, iso_against, iso_self, iso_against, iso_against],
44+
[iso_against, iso_against, iso_against, iso_against, iso_against, iso_self, iso_against],
45+
[iso_against, iso_against, iso_against, iso_against, iso_against, iso_against, iso_self]])
46+
geo.set_insulation(insulation)
47+
48+
winding_window = fmt.WindingWindow(core, insulation)
49+
50+
cells = winding_window.flexible_split(
51+
horizontal_split_factors=horizontal_split_factors,
52+
vertical_split_factors=vertical_split_factors
53+
)
54+
55+
windings = []
56+
for i in range(num_windings):
57+
winding = fmt.Conductor(i, fmt.Conductivity.Copper)
58+
winding.set_litz_round_conductor(0.85e-3 / 2, 40, 0.1e-3 / 2, None, fmt.ConductorArrangement.Square)
59+
windings.append(winding)
60+
61+
for i in range(num_windings):
62+
cells[i].set_winding(windings[i], 7 - i, fmt.WindingType.Single, fmt.Align.ToEdges,
63+
fmt.ConductorDistribution.VerticalUpward_HorizontalRightward)
64+
65+
geo.set_winding_windows([winding_window])
66+
67+
geo.create_model(freq=100000, pre_visualize_geometry=show_visual_outputs)
68+
69+
return geo
70+
71+
if num_windings == 2:
72+
# Run with vertical split
73+
working_directory = os.path.join(example_results_folder, "2-windings-vertical-split-only")
74+
setup_simulation(working_directory, horizontal_split_factors=[], vertical_split_factors=[[0.5]])
75+
76+
# Run with horizontal split
77+
working_directory = os.path.join(example_results_folder, "2-windings-horizontal-split-only")
78+
setup_simulation(working_directory, horizontal_split_factors=[0.5], vertical_split_factors=[])
79+
80+
elif num_windings == 3:
81+
working_directory = os.path.join(example_results_folder, "3-windings-vertical-split-only")
82+
setup_simulation(working_directory, horizontal_split_factors=[], vertical_split_factors=[[0.33, 0.66]])
83+
84+
working_directory = os.path.join(example_results_folder, "3-windings-horizontal-split-only")
85+
setup_simulation(working_directory, horizontal_split_factors=[0.33, 0.66], vertical_split_factors=[])
86+
87+
elif num_windings == 5:
88+
working_directory = os.path.join(example_results_folder, "5-windings")
89+
setup_simulation(working_directory, horizontal_split_factors=[0.48, 0.75], vertical_split_factors=[[0.5], [0.5], None])
90+
91+
elif num_windings == 6:
92+
working_directory = os.path.join(example_results_folder, "6-windings")
93+
setup_simulation(working_directory, horizontal_split_factors=[0.48, 0.75], vertical_split_factors=[[0.5], [0.5], [0.5]])
94+
95+
else:
96+
raise ValueError("Unsupported number of windings")
97+
98+
99+
if __name__ == "__main__":
100+
# Run simulations for different numbers of windings
101+
for num_windings in [2, 3, 5, 6]:
102+
print(f"Running simulation for {num_windings} windings")
103+
run_transformer_vvw_split_examples(num_windings, show_visual_outputs=True)

femmt/model.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,83 @@ def NCellsSplit(self, split_distance: float = 0, horizontal_split_factors: List[
11661166
# we return the combined list of all virtual winding windows.
11671167
return self.virtual_winding_windows
11681168

1169+
def flexible_split(self, split_distance: float = 0,
1170+
horizontal_split_factors: Optional[List[float]] = None,
1171+
vertical_split_factors: Optional[List[List[float]]] = None) -> List[VirtualWindingWindow]:
1172+
"""
1173+
Flexible split function to divide a window into sections based on provided horizontal and vertical split factors.
1174+
1175+
:param split_distance: Distance between split sections.
1176+
:param horizontal_split_factors: Relative positions for horizontal splits (0-1 range).
1177+
:param vertical_split_factors: Nested list of relative positions for vertical splits (0-1 range).
1178+
Each sublist corresponds to the vertical splits for each horizontal section.
1179+
:return: List of VirtualWindingWindow instances.
1180+
"""
1181+
if horizontal_split_factors is None:
1182+
horizontal_split_factors = []
1183+
1184+
if vertical_split_factors is None:
1185+
vertical_split_factors = [[]]
1186+
1187+
if self.stray_path is not None and self.air_gaps is not None and self.air_gaps.number > self.stray_path.start_index:
1188+
air_gap_1_position = self.air_gaps.midpoints[self.stray_path.start_index][1]
1189+
air_gap_2_position = self.air_gaps.midpoints[self.stray_path.start_index + 1][1]
1190+
max_pos = max(air_gap_2_position, air_gap_1_position)
1191+
min_pos = min(air_gap_2_position, air_gap_1_position)
1192+
distance = max_pos - min_pos # TODO: this is set in accordance to the midpoint of the air gap:
1193+
# TODO: should be changed to the core-cond isolation
1194+
horizontal_splits = min_pos + distance / 2
1195+
vertical_splits = self.max_left_bound + (self.max_right_bound - self.max_left_bound) * vertical_split_factors
1196+
split_distance = distance # here, the distance between the two vwws is set automatically
1197+
else:
1198+
1199+
horizontal_splits = np.array(horizontal_split_factors)
1200+
horizontal_splits = np.sort(np.clip(horizontal_splits, 0, 1))
1201+
horizontal_splits = self.max_bot_bound + (self.max_top_bound - self.max_bot_bound) * horizontal_splits
1202+
horizontal_splits = np.concatenate(([self.max_bot_bound], horizontal_splits, [self.max_top_bound]))
1203+
1204+
cells = []
1205+
1206+
if len(horizontal_split_factors) == 0 and any(vertical_split_factors): # Only vertical splits
1207+
for i in range(len(vertical_split_factors)):
1208+
vertical_splits = np.array(vertical_split_factors[i]) if vertical_split_factors[i] else []
1209+
vertical_splits = np.sort(np.clip(vertical_splits, 0, 1))
1210+
vertical_splits = self.max_left_bound + (self.max_right_bound - self.max_left_bound) * vertical_splits
1211+
vertical_splits = np.concatenate(([self.max_left_bound], vertical_splits, [self.max_right_bound]))
1212+
1213+
for j in range(len(vertical_splits) - 1):
1214+
cells.append(VirtualWindingWindow(
1215+
bot_bound=self.max_bot_bound,
1216+
top_bound=self.max_top_bound,
1217+
left_bound=vertical_splits[j],
1218+
right_bound=vertical_splits[j + 1]
1219+
))
1220+
elif len(vertical_split_factors) == 0 and len(horizontal_split_factors) > 0: # Only horizontal splits
1221+
for i in range(len(horizontal_splits) - 1):
1222+
cells.append(VirtualWindingWindow(
1223+
bot_bound=horizontal_splits[i],
1224+
top_bound=horizontal_splits[i + 1],
1225+
left_bound=self.max_left_bound,
1226+
right_bound=self.max_right_bound
1227+
))
1228+
else: # Both horizontal and vertical splits or no splits
1229+
for i in range(len(horizontal_splits) - 1):
1230+
vertical_splits = np.array(vertical_split_factors[i]) if vertical_split_factors[i] else []
1231+
vertical_splits = np.sort(np.clip(vertical_splits, 0, 1))
1232+
vertical_splits = self.max_left_bound + (self.max_right_bound - self.max_left_bound) * vertical_splits
1233+
vertical_splits = np.concatenate(([self.max_left_bound], vertical_splits, [self.max_right_bound]))
1234+
1235+
for j in range(len(vertical_splits) - 1):
1236+
cells.append(VirtualWindingWindow(
1237+
bot_bound=horizontal_splits[i],
1238+
top_bound=horizontal_splits[i + 1],
1239+
left_bound=vertical_splits[j],
1240+
right_bound=vertical_splits[j + 1]
1241+
))
1242+
1243+
self.virtual_winding_windows = cells
1244+
return self.virtual_winding_windows
1245+
11691246
def split_with_stack(self, stack: ConductorStack):
11701247
"""
11711248
Split the winding window according to a ConductorStack dataclass.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"Documentation": "https://upb-lea.github.io/FEM_Magnetics_Toolbox/main/intro.html",
6767
"Source Code": "https://github.com/upb-lea/FEM_Magnetics_Toolbox",
6868
},
69-
version='0.5.2',
69+
version='0.5.3',
7070
zip_safe=False,
7171
data_files=[('', ['CHANGELOG.md'])]
7272
)

tests/integration/test_femmt.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import femmt.examples.component_study.transformer_component_study
2424
import femmt.examples.basic_transformer_excitation_sweep
2525
import femmt.examples.basic_inductor_excitation_sweep
26+
import femmt.examples.basic_split_windings
2627
import materialdatabase as mdb
2728

2829

@@ -2442,3 +2443,11 @@ def test_transformer_excitation_sweep(temp_folder):
24422443
femmt.examples.basic_transformer_excitation_sweep.basic_example_transformer_excitation_sweep(onelab_folder=onelab_folder,
24432444
show_visual_outputs=False,
24442445
is_test=True)
2446+
2447+
def test_split_windings(temp_folder):
2448+
"""Integration test to the basic example file."""
2449+
temp_folder_path, onelab_folder = temp_folder
2450+
for num_windings in [2, 3, 5, 6]:
2451+
print(f"Running simulation for {num_windings} windings")
2452+
femmt.examples.basic_split_windings.run_transformer_vvw_split_examples(num_windings, onelab_folder=onelab_folder,
2453+
show_visual_outputs=False, is_test=True)

0 commit comments

Comments
 (0)