-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathdrawing.py
More file actions
2034 lines (1838 loc) · 127 KB
/
drawing.py
File metadata and controls
2034 lines (1838 loc) · 127 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""Draw structures inside Onelab."""
# Python standard libraries
import numpy as np
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:
"""
Create the model of the magnetic component.
This is done by creating lists of points which will be later added to the gmsh file.
"""
core: Core
winding_windows: list[WindingWindow]
air_gaps: AirGaps
stray_path: StrayPath
insulation: Insulation
component_type: ComponentType
simulation_type: SimulationType
mesh_data: MeshData
number_of_windings: int
verbosity: Verbosity
# 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]]
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
self.air_gaps = air_gaps
self.simulation_type = SimulationType
self.component_type = component_type
self.stray_path = stray_path
self.insulation = insulation
self.number_of_windings = number_of_windings
self.verbosity = verbosity
# -- Arrays for geometry data --
# TODO Is the zero initialization necessary?
self.p_outer = np.zeros((4, 4))
self.p_region_bound = np.zeros((4, 4))
if self.core.core_type == CoreType.Single:
self.p_window = np.zeros((4 * core.number_core_windows, 4)) # TODO: why is this done for both sides?
if self.core.core_type == CoreType.Stacked:
self.p_window_top = np.zeros((4, 4)) # TODO: just for right side? make it the same as for single core geometry
self.p_window_bot = np.zeros((4, 4)) # TODO: just for right side? make it the same as for single core geometry
self.p_air_gaps = np.zeros((4 * air_gaps.number, 4))
self.p_conductor = []
self.p_iso_conductor = []
self.p_iso_core = []
self.p_iso_pri_sec = []
self.p_iso_layer = []
for i in range(number_of_windings):
self.p_conductor.insert(i, [])
self.p_iso_conductor.insert(i, [])
self.r_inner = core.r_inner
self.r_outer = core.r_outer
def draw_outer(self):
"""Draws the outer points of the main core (single core)."""
# Outer Core
# (A_zyl=2pi*r*h => h=0.5r=0.25core_w <=> ensure A_zyl=A_core on the tiniest point)
self.p_outer[0][:] = [-self.r_outer, -self.core.core_h / 2, 0, self.mesh_data.c_core]
self.p_outer[1][:] = [self.r_outer, -self.core.core_h / 2, 0, self.mesh_data.c_core]
self.p_outer[2][:] = [-self.r_outer, self.core.core_h / 2, 0, self.mesh_data.c_core]
self.p_outer[3][:] = [self.r_outer, self.core.core_h / 2, 0, self.mesh_data.c_core]
def draw_single_window(self):
"""Draw a single window."""
# At this point both windows (in a cut) are modeled
self.p_window[0] = [-self.r_inner,
-self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[1] = [-self.core.core_inner_diameter / 2,
-self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[2] = [-self.r_inner,
self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[3] = [-self.core.core_inner_diameter / 2,
self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[4] = [self.core.core_inner_diameter / 2,
-self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[5] = [self.r_inner,
-self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[6] = [self.core.core_inner_diameter / 2,
self.core.window_h / 2,
0,
self.mesh_data.c_window]
self.p_window[7] = [self.r_inner,
self.core.window_h / 2,
0,
self.mesh_data.c_window]
def draw_stacked_windows(self):
"""Draw stacked windows."""
self.p_window_bot[0] = [self.core.core_inner_diameter / 2,
-self.core.window_h_bot / 2,
0,
self.mesh_data.c_window]
self.p_window_bot[1] = [self.r_inner,
-self.core.window_h_bot / 2,
0,
self.mesh_data.c_window]
self.p_window_bot[2] = [self.core.core_inner_diameter / 2,
self.core.window_h_bot / 2,
0,
self.mesh_data.c_window]
self.p_window_bot[3] = [self.r_inner,
self.core.window_h_bot / 2,
0,
self.mesh_data.c_window]
self.p_window_top[0] = [self.core.core_inner_diameter / 2,
self.core.window_h_bot / 2 + self.core.core_thickness,
0,
self.mesh_data.c_window]
self.p_window_top[1] = [self.r_inner,
self.core.window_h_bot / 2 + self.core.core_thickness,
0,
self.mesh_data.c_window]
self.p_window_top[2] = [self.core.core_inner_diameter / 2,
self.core.window_h_bot / 2 + self.core.core_thickness + self.core.window_h_top,
0,
self.mesh_data.c_window]
self.p_window_top[3] = [self.r_inner,
self.core.window_h_bot / 2 + self.core.core_thickness + self.core.window_h_top,
0,
self.mesh_data.c_window]
def draw_air_gaps(self):
"""Draw air gaps."""
# Air gaps
# "air_gaps" is a list with [position_tag, air_gap_position, air_gap_h, c_air_gap]
# - position_tag: specifies the "leg" with air gaps
# - air_gap_position: specifies the coordinate of the air gap's center point along the specified leg
# - air_gap_h: height/length of the air gap
# - c_air_gap: mesh accuracy factor
# at this point the 4 corner points of each air gap are generated out of "air_gaps"
for i in range(0, self.air_gaps.number):
# # Left leg (-1)
# if self.component.air_gaps.midpoints[i][0] == -1:
# self.p_air_gaps[i * 4] = [-(self.component.core.core_w + self.component.core.window_w),
# self.component.air_gaps.midpoints[i][1] -
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# self.p_air_gaps[i * 4 + 1] = [-(self.component.core.core_w / 2 + self.component.core.window_w),
# self.component.air_gaps.midpoints[i][1] -
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# self.p_air_gaps[i * 4 + 2] = [-(self.component.core.core_w + self.component.core.window_w),
# self.component.air_gaps.midpoints[i][1] +
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# self.p_air_gaps[i * 4 + 3] = [-(self.component.core.core_w / 2 + self.component.core.window_w),
# self.component.air_gaps.midpoints[i][1] +
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
#
# # Right leg (+1)
# if self.component.air_gaps.midpoints[i][0] == 1:
# self.p_air_gaps[i * 4] = [self.component.core.core_w / 2 + self.component.core.window_w,
# self.component.air_gaps.midpoints[i][1] -
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# self.p_air_gaps[i * 4 + 1] = [self.component.core.core_w + self.component.core.window_w,
# self.component.air_gaps.midpoints[i][1] -
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# self.p_air_gaps[i * 4 + 2] = [self.component.core.core_w / 2 + self.component.core.window_w,
# self.component.air_gaps.midpoints[i][1] +
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# self.p_air_gaps[i * 4 + 3] = [self.component.core.core_w + self.component.core.window_w,
# self.component.air_gaps.midpoints[i][1] +
# self.component.air_gaps.midpoints[i][2] / 2, 0, self.component.air_gaps.midpoints[i][3]]
# Center leg (0)
if self.air_gaps.midpoints[i][0] == 0:
# The center points are transformed each into 4 corner points
air_gap_y_position = self.air_gaps.midpoints[i][1]
air_gap_height = self.air_gaps.midpoints[i][2]
air_gap_length_top = self.core.core_inner_diameter / 2
air_gap_length_bot = self.core.core_inner_diameter / 2
# Check for stray_paths in integrated transformers
if self.component_type == ComponentType.IntegratedTransformer:
if self.stray_path.start_index == i:
# Stray path is above current air_gap
air_gap_length_top = self.stray_path.length
elif self.stray_path.start_index + 1 == i:
# Stray path is below current air_gap
air_gap_length_bot = self.stray_path.length
# Bottom left
self.p_air_gaps[i * 4 + 0] = [0, air_gap_y_position - air_gap_height / 2, 0, self.mesh_data.c_air_gaps]
# Bottom right
self.p_air_gaps[i * 4 + 1] = [air_gap_length_bot, air_gap_y_position - air_gap_height / 2, 0, self.mesh_data.c_air_gaps]
# Top left
self.p_air_gaps[i * 4 + 2] = [0, air_gap_y_position + air_gap_height / 2, 0, self.mesh_data.c_air_gaps]
# Top right
self.p_air_gaps[i * 4 + 3] = [air_gap_length_top, air_gap_y_position + air_gap_height / 2, 0, self.mesh_data.c_air_gaps]
# In order to close the air gap when a stray_path is added, additional points need to be added
if self.component_type == ComponentType.IntegratedTransformer:
top_point = [self.core.core_inner_diameter / 2,
self.air_gaps.midpoints[self.stray_path.start_index + 1][1] - self.air_gaps.midpoints[self.stray_path.start_index + 1][2] / 2,
0, self.mesh_data.c_air_gaps]
bot_point = [self.core.core_inner_diameter / 2,
self.air_gaps.midpoints[self.stray_path.start_index][1] + self.air_gaps.midpoints[self.stray_path.start_index][2] / 2,
0, self.mesh_data.c_air_gaps]
self.p_close_air_gaps = [top_point, bot_point]
def draw_conductors(self, draw_top_down: bool = True):
"""
Draw every conductor type based on the virtual_winding_window bounds.
:param draw_top_down: True to draw from top to bottom
:type draw_top_down: bool
"""
for winding_window in self.winding_windows:
for virtual_winding_window in winding_window.virtual_winding_windows:
# Get bounds from virtual winding window
bot_bound = virtual_winding_window.bot_bound
top_bound = virtual_winding_window.top_bound
left_bound = virtual_winding_window.left_bound
right_bound = virtual_winding_window.right_bound
# Check, if virtual winding window fits in physical window
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
# self.valid = False
raise Exception("Winding does not fit into winding window!")
else:
# Check the possible WindingTypes and draw accordingly
if virtual_winding_window.winding_type == WindingType.TwoInterleaved:
# Two windings in the virtual winding window
windings = virtual_winding_window.windings
winding0 = windings[0]
winding1 = windings[1]
turns = virtual_winding_window.turns
turns0 = turns[0]
turns1 = turns[1]
winding_numbers = [winding0.winding_number, winding1.winding_number]
# Now check for every winding scheme which is possible for interleaved winding type
if virtual_winding_window.winding_scheme == InterleavedWindingScheme.Bifilar:
"""
- Bifilar interleaving means a uniform winding scheme of two conductors (prim. and sec.)
- Can only be used for conductors of identical radius (in terms of litz radius for
stranded wires)
- Excess windings are placed below the bifilar ones
"""
if winding0.conductor_radius != winding1.conductor_radius:
logger.warning("For bifilar winding scheme both conductors must be of the same radius!")
else:
logger.info("Bifilar winding scheme is applied")
raise Exception("Bifilar winding scheme is not implemented yet.")
if virtual_winding_window.winding_scheme == InterleavedWindingScheme.VerticalAlternating:
"""
- Vertical interleaving means a winding scheme where the two conductors are alternating
in vertical (y-)direction
- This is practically uncommon
- If the turns ratio is != 1, the scheme always begins with the "higher-turns-number's"
conductor
"""
raise Exception("Vertical winding scheme is not implemented yet.")
if virtual_winding_window.winding_scheme == InterleavedWindingScheme.HorizontalAlternating:
"""
- Horizontal interleaving means a winding scheme where the two conductors are alternating in
horizontal (x-)direction (Tonnenwicklung)
- This is practically most common
- If the turns ratio is != 1, the scheme always begins with the "higher-turns-number's"
conductor
"""
# assume 2 winding transformer and dedicated stray path:
if draw_top_down:
# This will draw from top bound down
# Initialize the list, that counts the already placed conductors
N_completed = [0, 0]
# Initialize the starting conductor
if turns0 >= turns1:
# Primary starts first
col_cond = 0
else:
# Secondary starts fist
col_cond = 1
# Get winding number of current winding
winding_number = winding_numbers[col_cond]
# Initialize the x and y coordinate
x = left_bound + windings[col_cond].conductor_radius
y = top_bound - windings[col_cond].conductor_radius
top_window_iso_counter = 0
# Continue placing as long as not all conductors have been placed
while (turns0 - N_completed[0] != 0) or (turns1 - N_completed[1] != 0):
if turns[col_cond] - N_completed[col_cond] != 0:
# is this winding not already finished?
if x < right_bound - windings[col_cond].conductor_radius:
while y > bot_bound + windings[col_cond].conductor_radius and \
N_completed[col_cond] < turns[col_cond]:
self.p_conductor[winding_number].append([
x,
y,
0,
self.mesh_data.c_center_conductor[winding_number]])
self.p_conductor[winding_number].append([
x - windings[col_cond].conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number]])
self.p_conductor[winding_number].append([
x,
y + windings[col_cond].conductor_radius,
0,
self.mesh_data.c_conductor[winding_number]])
self.p_conductor[winding_number].append([
x + windings[col_cond].conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number]])
self.p_conductor[winding_number].append([
x,
y - windings[col_cond].conductor_radius,
0,
self.mesh_data.c_conductor[winding_number]])
N_completed[col_cond] += 1
# one from top to bot
y -= windings[col_cond].conductor_radius * 2 + self.insulation.cond_cond[col_cond][col_cond]
# Check whether one winding is "finished" and only the other winding must be placed...
if N_completed[(col_cond + 1) % 2] == turns[(col_cond + 1) % 2]:
x += windings[col_cond].conductor_radius + windings[col_cond].conductor_radius + \
self.insulation.cond_cond[col_cond][(col_cond + 1) % 2]
else:
x += windings[col_cond].conductor_radius + windings[(col_cond + 1) % 2].conductor_radius + \
self.insulation.cond_cond[col_cond][(col_cond + 1) % 2]
# TODO: only works for two windings
col_cond = (col_cond + 1) % 2
# Reset y
winding_number = winding_numbers[col_cond]
y = top_bound - windings[col_cond].conductor_radius
top_window_iso_counter += 1
else:
break
else:
# is this winding already finished? - continue with the other one
col_cond = (col_cond + 1) % 2
# Correct the reset of y and correct x displacement
x += windings[col_cond].conductor_radius - windings[(col_cond + 1) % 2].conductor_radius - \
self.insulation.cond_cond[col_cond][(col_cond + 1) % 2] + self.insulation.cond_cond[col_cond][col_cond]
y = top_bound - windings[col_cond].conductor_radius
top_window_iso_counter -= 1
else:
# This will draw from bottom bound up.
# Initialize the list, that counts the already placed conductors
N_completed = [0, 0]
# Initialize the starting conductor
if turns0 >= turns1:
col_cond = 0
else:
col_cond = 1
# Get winding number of current winding
winding_number = winding_numbers[col_cond]
# Initialize the x and y coordinate
x = left_bound + windings[col_cond].conductor_radius
y = bot_bound + windings[col_cond].conductor_radius
# Continue placing as long as not all conductors have been placed
while (turns0 - N_completed[0] != 0) or \
(turns1 - N_completed[1] != 0):
if turns[col_cond] - N_completed[col_cond] != 0:
# is this winding not already finished?
if x < right_bound - windings[col_cond].conductor_radius:
while y < top_bound - windings[col_cond].conductor_radius and \
N_completed[col_cond] < turns[col_cond]:
self.p_conductor[winding_number].append([
x,
y,
0,
self.mesh_data.c_center_conductor[winding_number]])
self.p_conductor[winding_number].append([
x - windings[col_cond].conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number]])
self.p_conductor[winding_number].append([
x,
y + windings[col_cond].conductor_radius,
0,
self.mesh_data.c_conductor[winding_number]])
self.p_conductor[winding_number].append([
x + windings[col_cond].conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number]])
self.p_conductor[winding_number].append([
x,
y - windings[col_cond].conductor_radius,
0,
self.mesh_data.c_conductor[winding_number]])
N_completed[col_cond] += 1
# one from bot to top
y += windings[col_cond].conductor_radius * 2 + self.insulation.cond_cond[col_cond][col_cond]
x += windings[col_cond].conductor_radius + windings[(col_cond + 1) % 2].conductor_radius + \
self.insulation.cond_cond[col_cond][(col_cond + 1) % 2]
# Reset y
col_cond = (col_cond + 1) % 2
y = bot_bound + windings[col_cond].conductor_radius
else:
break
else:
# is this winding already finished? - continue with the other one
col_cond = (col_cond + 1) % 2
winding_number = winding_numbers[col_cond]
# Correct the reset of y and correct x displacement
x += windings[col_cond].conductor_radius - windings[(col_cond + 1) % 2].conductor_radius - \
self.insulation.cond_cond[col_cond][(col_cond + 1) % 2] + self.insulation.cond_cond[col_cond][col_cond]
y = bot_bound + windings[col_cond].conductor_radius
if virtual_winding_window.winding_scheme == InterleavedWindingScheme.VerticalStacked:
winding_number0, winding_number1 = winding_numbers
y = bot_bound + winding0.conductor_radius
# Initialization
x = left_bound + winding0.conductor_radius
i = 0
# First winding from bottom to top
if winding0.conductor_arrangement == ConductorArrangement.Square:
while y < top_bound - winding0.conductor_radius and \
i < turns0:
while x < right_bound - winding0.conductor_radius and \
i < turns0:
self.p_conductor[winding_number0].append([
x,
y,
0,
self.mesh_data.c_center_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x - winding0.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x,
y + winding0.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x + winding0.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x,
y - winding0.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number0]])
i += 1
x += winding0.conductor_radius * 2 + self.insulation.cond_cond[winding_number0][winding_number0] # from left to right
y += winding0.conductor_radius * 2 + self.insulation.cond_cond[winding_number0][winding_number0] # one step from bot to top
x = left_bound + winding0.conductor_radius # always the same
elif winding0.conductor_arrangement == ConductorArrangement.Hexagonal:
base_line = True
while y < top_bound - winding0.conductor_radius and \
i < turns0:
while x < right_bound - winding0.conductor_radius and \
i < turns0:
self.p_conductor[winding_number0].append([
x,
y,
0,
self.mesh_data.c_center_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x - winding0.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x,
y + winding0.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x + winding0.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number0]])
self.p_conductor[winding_number0].append([
x,
y - winding0.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number0]])
i += 1
x += 2 * np.cos(np.pi / 6) * (winding0.conductor_radius + \
self.insulation.cond_cond[winding_number0][winding_number0] / 2)
# depending on what line, hexa scheme starts shifted
# reset y to "new" bottom
base_line = (not base_line)
if base_line:
y -= (winding0.conductor_radius + self.insulation.cond_cond[winding_number0][winding_number0] / 2)
else:
y += (winding0.conductor_radius + self.insulation.cond_cond[winding_number0][winding_number0] / 2)
# Undo last base_line reset
if base_line:
y += (winding0.conductor_radius + self.insulation.cond_cond[winding_number0][winding_number0] / 2)
else:
y -= (winding0.conductor_radius + self.insulation.cond_cond[winding_number0][winding_number0] / 2)
base_line = True
x = left_bound + winding0.conductor_radius
y += winding0.conductor_radius * 2 + self.insulation.cond_cond[winding_number0][winding_number0]
elif winding0.conductor_arrangement == ConductorArrangement.SquareFullWidth:
raise Exception("ConductorArrangement SquareFullWidth is not implemented for interleaved and vertical stacked")
else:
raise Exception(f"Unknown conductor_arrangement {winding0.conductor_arrangement}")
# Second winding from top to bottom
y = top_bound - winding1.conductor_radius
i = 0
if winding1.conductor_arrangement == ConductorArrangement.Square:
while y > bot_bound + winding1.conductor_radius and i < \
turns1:
while x < right_bound - winding1.conductor_radius and i < \
turns1:
self.p_conductor[winding_number1].append([
x,
y,
0,
self.mesh_data.c_center_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x - winding1.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x,
y + winding1.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x + winding1.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x,
y - winding1.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number1]])
i += 1
x += winding1.conductor_radius * 2 + self.insulation.cond_cond[winding_number1][winding_number1] # from left to right
# one step from bot to top
y += -(winding1.conductor_radius * 2) - self.insulation.cond_cond[winding_number1][winding_number1]
x = left_bound + winding1.conductor_radius # always the same
elif winding1.conductor_arrangement == ConductorArrangement.Hexagonal:
base_line = True
while y > bot_bound + winding1.conductor_radius and \
i < turns1:
while x < right_bound - winding1.conductor_radius and \
i < turns1:
self.p_conductor[winding_number1].append([
x,
y,
0,
self.mesh_data.c_center_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x - winding1.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x,
y + winding1.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x + winding1.conductor_radius,
y,
0,
self.mesh_data.c_conductor[winding_number1]])
self.p_conductor[winding_number1].append([
x,
y - winding1.conductor_radius,
0,
self.mesh_data.c_conductor[winding_number1]])
i += 1
x += 2 * np.cos(np.pi / 6) * \
(winding1.conductor_radius + self.insulation.cond_cond[winding_number1][winding_number1] / 2)
# depending on what line, hexa scheme starts shifted
# reset y to "new" bottom
base_line = (not base_line)
if base_line:
y += (winding1.conductor_radius + self.insulation.cond_cond[winding_number1][winding_number1])
else:
y -= (winding1.conductor_radius + self.insulation.cond_cond[winding_number1][winding_number1])
# Undo last base_line reset
if base_line:
y -= (winding1.conductor_radius + self.insulation.cond_cond[winding_number1][winding_number1])
else:
y += (winding1.conductor_radius + self.insulation.cond_cond[winding_number1][winding_number1])
base_line = True
x = left_bound + winding1.conductor_radius
# from top to bottom
y += -(winding1.conductor_radius * 2) - self.insulation.cond_cond[winding_number1][winding_number1]
else:
raise Exception(f"Unknown conductor_arrangement {winding1.conductor_arrangement.name}")
elif virtual_winding_window.winding_type == WindingType.Single:
# One winding in the virtual winding window
winding = virtual_winding_window.windings[0]
turns = sum(virtual_winding_window.turns)
# TODO: find another solution for this (turns = ...) (is needed in mesh.py for air_stacked) see set_winding in model
conductor_type = winding.conductor_type
winding_scheme = virtual_winding_window.winding_scheme
alignment = virtual_winding_window.alignment
placing_strategy = virtual_winding_window.placing_strategy
foil_vertical_placing_strategy = virtual_winding_window.foil_vertical_placing_strategy
foil_horizontal_placing_strategy = virtual_winding_window.foil_horizontal_placing_strategy
zigzag = virtual_winding_window.zigzag
num = winding.winding_number
# Check if the coil is round or rectangular
if conductor_type == ConductorType.RectangularSolid:
# Now check for each possible winding scheme
if winding_scheme == WindingScheme.Full:
# Full window conductor
self.p_conductor[num].append([
left_bound,
bot_bound,
0,
self.mesh_data.c_conductor[num]])
self.p_conductor[num].append([
right_bound,
bot_bound,
0,
self.mesh_data.c_conductor[num]])
self.p_conductor[num].append([
left_bound,
top_bound,
0,
self.mesh_data.c_conductor[num]])
self.p_conductor[num].append([
right_bound,
top_bound,
0,
self.mesh_data.c_conductor[num]])
center_point = self.get_center_of_rect([left_bound, bot_bound], [right_bound, bot_bound], [left_bound, top_bound],
[right_bound, top_bound])
self.p_conductor[num].append([center_point[0], center_point[1], 0, self.mesh_data.c_center_conductor[num]])
elif winding_scheme == WindingScheme.FoilVertical:
# TODO Add check if turns do not fit in winding window
# Foil conductors where each conductor is very high and the conductors are expanding in the x-direction
if virtual_winding_window.wrap_para == WrapParaType.FixedThickness:
# Wrap defined number of turns and chosen thickness
winding.a_cell = winding.thickness * (top_bound - bot_bound)
for i in range(turns):
# Starting point of x depending on the distribution way if it is from left to right or vice versa.
if foil_vertical_placing_strategy == FoilVerticalDistribution.HorizontalRightward:
x_start = left_bound + i * winding.thickness + i * self.insulation.cond_cond[num][num]
x_move = left_bound + (i + 1) * winding.thickness + i * self.insulation.cond_cond[num][num]
if x_move > right_bound:
break
elif foil_vertical_placing_strategy == FoilVerticalDistribution.HorizontalLeftward:
x_start = right_bound - i * winding.thickness - i * self.insulation.cond_cond[num][num]
x_move = right_bound - (i + 1) * winding.thickness - i * self.insulation.cond_cond[num][num]
if x_move < left_bound:
break
# Foil
self.p_conductor[num].extend([
[x_start, bot_bound, 0, self.mesh_data.c_conductor[num]],
[x_move, bot_bound, 0, self.mesh_data.c_conductor[num]],
[x_start, top_bound, 0, self.mesh_data.c_conductor[num]],
[x_move, top_bound, 0, self.mesh_data.c_conductor[num]]
])
# Find the center point of each turn
center_point = self.get_center_of_rect(self.p_conductor[num][-4], self.p_conductor[num][-3],
self.p_conductor[num][-2], self.p_conductor[num][-1])
self.p_conductor[num].append([center_point[0], center_point[1], 0, self.mesh_data.c_center_conductor[num]])
# Interpolate type is where the foils will have a dynamic thickness
elif virtual_winding_window.wrap_para == WrapParaType.Interpolate:
# Fill the allowed space in the Winding Window with a chosen number of turns
# we need first to calculate the area of every turn
# Find the wrap turn space
winding.thickness = (right_bound - left_bound - (turns - 1) * self.insulation.cond_cond[num][num]) / turns
window_height = top_bound - bot_bound
winding.a_cell = winding.thickness * window_height
# Update MeshData with the new thickness
self.mesh_data.update_data(frequency=self.mesh_data.frequency, skin_mesh_factor=self.mesh_data.skin_mesh_factor)
for i in range(turns):
if foil_vertical_placing_strategy == FoilVerticalDistribution.HorizontalRightward:
# Generate interpolated positions for each turn, starting from the left and moving right
x_interpol = np.linspace(left_bound, right_bound + self.insulation.cond_cond[num][num], turns + 1)
x_start = x_interpol[i]
x_move = x_interpol[i + 1] - self.insulation.cond_cond[num][num]
elif foil_vertical_placing_strategy == FoilVerticalDistribution.HorizontalLeftward:
# Generate interpolated positions for each turn, starting from the right and moving left
x_interpol = np.linspace(right_bound, left_bound - self.insulation.cond_cond[num][num], turns + 1)
x_start = x_interpol[i]
x_move = x_interpol[i + 1] + self.insulation.cond_cond[num][num]
# Foil
self.p_conductor[num].extend([
[x_start, bot_bound, 0, self.mesh_data.c_conductor[num]],
[x_move, bot_bound, 0, self.mesh_data.c_conductor[num]],
[x_start, top_bound, 0, self.mesh_data.c_conductor[num]],
[x_move, top_bound, 0, self.mesh_data.c_conductor[num]]
])
# Find the center point of each turn
center_point = self.get_center_of_rect(self.p_conductor[num][-4], self.p_conductor[num][-3],
self.p_conductor[num][-2], self.p_conductor[num][-1])
self.p_conductor[num].append([center_point[0], center_point[1], 0, self.mesh_data.c_center_conductor[num]])
else:
raise Exception(f"Unknown wrap para type {virtual_winding_window.wrap_para}")
# Apply alignment
if alignment == Align.ToEdges:
pass
if alignment == Align.CenterOnVerticalAxis:
raise ValueError("FoilVertical Conductors can not be centered on vertical axis as they are very high")
if alignment == Align.CenterOnHorizontalAxis:
min_x = min(point[0] for point in self.p_conductor[num])
max_x = max(point[0] for point in self.p_conductor[num])
occupied_width = max_x - min_x
occupied_midpoint_x = min_x + (occupied_width / 2)
window_midpoint_x = (left_bound + right_bound) / 2
adjustment_x = window_midpoint_x - occupied_midpoint_x
for i, _ in enumerate(self.p_conductor[num]):
self.p_conductor[num][i][0] += adjustment_x
# Foil conductors where each conductor is very long and the conductors are expanding the y-direction
elif winding_scheme == WindingScheme.FoilHorizontal:
# the user can choose the thickness
if virtual_winding_window.wrap_para == WrapParaType.FixedThickness:
# Find the turn space
winding.a_cell = winding.thickness * (right_bound - left_bound)
for i in range(turns):
# Starting point of y depending on the distribution way if it is from left to right or vice versa.
if foil_horizontal_placing_strategy == FoilHorizontalDistribution.VerticalUpward:
y_start = bot_bound + i * winding.thickness + i * self.insulation.cond_cond[num][num]
y_move = bot_bound + (i + 1) * winding.thickness + i * self.insulation.cond_cond[num][num]
if round(y_move, 6) > round(top_bound, 6):
break
elif foil_horizontal_placing_strategy == FoilHorizontalDistribution.VerticalDownward:
y_start = top_bound - i * winding.thickness - i * self.insulation.cond_cond[num][num]
y_move = top_bound - (i + 1) * winding.thickness - i * self.insulation.cond_cond[num][num]
if round(y_move) < round(bot_bound):
break
# Foil
self.p_conductor[num].extend([
[left_bound, y_start, 0, self.mesh_data.c_conductor[num]],
[right_bound, y_start, 0, self.mesh_data.c_conductor[num]],
[left_bound, y_move, 0, self.mesh_data.c_conductor[num]],
[right_bound, y_move, 0, self.mesh_data.c_conductor[num]]
])
# Find the center point of each turn
center_point = self.get_center_of_rect(self.p_conductor[num][-4], self.p_conductor[num][-3],
self.p_conductor[num][-2], self.p_conductor[num][-1])
self.p_conductor[num].append([center_point[0], center_point[1], 0, self.mesh_data.c_center_conductor[num]])
# Interpolate type is where the foils will have a dynamic thickness
elif virtual_winding_window.wrap_para == WrapParaType.Interpolate:
# Turn space
winding.thickness = (top_bound - bot_bound - (turns - 1) * self.insulation.cond_cond[num][num]) / turns
window_width = right_bound - left_bound
winding.a_cell = winding.thickness * window_width
# Update MeshData with the new thickness
self.mesh_data.update_data(frequency=self.mesh_data.frequency, skin_mesh_factor=self.mesh_data.skin_mesh_factor)
for i in range(turns):
# Placing Foil horizontal rectangular conductors from bot to top
if foil_horizontal_placing_strategy == FoilHorizontalDistribution.VerticalUpward:
# Generate interpolated positions for each turn, starting from the bottom and moving top
y_interpol = np.linspace(bot_bound, top_bound + self.insulation.cond_cond[num][num], turns + 1)
y_start = y_interpol[i]
y_move = y_interpol[i + 1] - self.insulation.cond_cond[num][num]
elif foil_horizontal_placing_strategy == FoilHorizontalDistribution.VerticalDownward:
y_interpol = np.linspace(top_bound, bot_bound - self.insulation.cond_cond[num][num], turns + 1)
y_start = y_interpol[i]
y_move = y_interpol[i + 1] + self.insulation.cond_cond[num][num]
# Foil
self.p_conductor[num].extend([
[left_bound, y_start, 0, self.mesh_data.c_conductor[num]],
[right_bound, y_start, 0, self.mesh_data.c_conductor[num]],
[left_bound, y_move, 0, self.mesh_data.c_conductor[num]],
[right_bound, y_move, 0, self.mesh_data.c_conductor[num]]
])
# Find the center point of each turn
center_point = self.get_center_of_rect(self.p_conductor[num][-4], self.p_conductor[num][-3],
self.p_conductor[num][-2], self.p_conductor[num][-1])
self.p_conductor[num].append([center_point[0], center_point[1], 0, self.mesh_data.c_center_conductor[num]])
# Apply alignment
if alignment == Align.ToEdges:
pass
if alignment == Align.CenterOnVerticalAxis:
min_y = min(point[1] for point in self.p_conductor[num])
max_y = max(point[1] for point in self.p_conductor[num])
occupied_height = max_y - min_y
occupied_midpoint_y = min_y + occupied_height / 2
window_midpoint_y = (bot_bound + top_bound) / 2
adjustment_y = window_midpoint_y - occupied_midpoint_y
for i, _ in enumerate(self.p_conductor[num]):
self.p_conductor[num][i][1] += adjustment_y
if alignment == Align.CenterOnHorizontalAxis:
raise ValueError("FoilHorizontal Conductors can not be centered on horizontal axis as they are long")
else:
raise Exception(f"Winding scheme {winding_scheme.name} is not implemented.")
elif conductor_type == ConductorType.RoundSolid or conductor_type == ConductorType.RoundLitz:
# Since round conductors have no winding scheme check for each conductor_arrangement.
conductor_arrangement = winding.conductor_arrangement
if conductor_arrangement == ConductorArrangement.Square:
# Define initial conditions based on the placement strategy.
# 16 cases will be handled here, 8 cases with consistent direction, and 8 cases with Zigzag movement.
vertical_first = placing_strategy in [
ConductorDistribution.VerticalUpward_HorizontalRightward,
ConductorDistribution.VerticalUpward_HorizontalLeftward,
ConductorDistribution.VerticalDownward_HorizontalRightward,
ConductorDistribution.VerticalDownward_HorizontalLeftward]
upward_movement = placing_strategy in [
ConductorDistribution.VerticalUpward_HorizontalRightward,
ConductorDistribution.VerticalUpward_HorizontalLeftward,
ConductorDistribution.HorizontalRightward_VerticalUpward,
ConductorDistribution.HorizontalLeftward_VerticalUpward]
rightward_movement = placing_strategy in [
ConductorDistribution.VerticalUpward_HorizontalRightward,
ConductorDistribution.VerticalDownward_HorizontalRightward,
ConductorDistribution.HorizontalRightward_VerticalUpward,
ConductorDistribution.HorizontalRightward_VerticalDownward]
# define a delta insulation
insulation_delta = self.mesh_data.c_window / self.insulation.max_aspect_ratio
thickness_of_the_insulation_layer = self.insulation.thickness_of_insulation
# Set the starting position and step size based on initial conditions
if vertical_first:
if upward_movement:
start_y = bot_bound + winding.conductor_radius + self.insulation.turn_ins[num] # Start from the bottom
step_y = winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num]
else:
start_y = top_bound - winding.conductor_radius - self.insulation.turn_ins[num] # Start from the top
step_y = -(winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num])
if rightward_movement:
# Moving right after completing a column
start_x = left_bound + winding.conductor_radius + self.insulation.turn_ins[num]
step_x = winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer
# For insulation between layer
start_x_layer = left_bound + 2 * winding.conductor_radius + 2 * self.insulation.turn_ins[num]
step_x_layer = winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer
else:
# Moving left after completing a column
start_x = right_bound - winding.conductor_radius - self.insulation.turn_ins[num]
step_x = -(winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer)
# For insulation between layer
start_x_layer = right_bound - 2 * winding.conductor_radius - 2 * self.insulation.turn_ins[num]
step_x_layer = -(winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer)
# Determine if the first movement is horizontally (rightward or leftward)
else:
if rightward_movement:
start_x = left_bound + winding.conductor_radius + self.insulation.turn_ins[num] # start from the left
step_x = winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num]
else:
start_x = right_bound - winding.conductor_radius - self.insulation.turn_ins[num] # Start from the right
step_x = -(winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num])
if upward_movement:
start_y = bot_bound + winding.conductor_radius + self.insulation.turn_ins[num] # Moving top after completing a raw
step_y = winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer
# For insulation between layer
start_y_layer = bot_bound + 2 * winding.conductor_radius + 2 * self.insulation.turn_ins[num]
step_y_layer = winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer
else:
start_y = top_bound - winding.conductor_radius - self.insulation.turn_ins[num] # Moving bottom after completing a raw
step_y = -(winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer)
# For insulation between layer
start_y_layer = top_bound - 2 * winding.conductor_radius - 2 * self.insulation.turn_ins[num]
step_y_layer = -(winding.conductor_radius * 2 + 2 * self.insulation.turn_ins[num] + thickness_of_the_insulation_layer)
# Loop and place conductors accordingly
x = start_x
y = start_y
x_l = start_x_layer if vertical_first else None
y_l = start_y_layer if not vertical_first else None
i = 0
counter_layer = 0
# Vertically movement
if vertical_first:
while i < turns and left_bound + winding.conductor_radius <= x <= right_bound - winding.conductor_radius:
while i < turns and bot_bound + winding.conductor_radius <= y <= top_bound - winding.conductor_radius:
y_last = y
# drawing conductor
self.p_conductor[num].append(
[x, y, 0, self.mesh_data.c_center_conductor[num]])
self.p_conductor[num].append(
[x - winding.conductor_radius, y, 0, self.mesh_data.c_conductor[num]])
self.p_conductor[num].append(
[x, y + winding.conductor_radius, 0, self.mesh_data.c_conductor[num]])
self.p_conductor[num].append(
[x + winding.conductor_radius, y, 0, self.mesh_data.c_conductor[num]])
self.p_conductor[num].append(
[x, y - winding.conductor_radius, 0, self.mesh_data.c_conductor[num]])
# if the number of layer is less than len(insulation.cond_air_cond), then count it as zero