femagtools 1.5.7__py3-none-any.whl → 1.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- femagtools/__init__.py +1 -1
- femagtools/bch.py +38 -3
- femagtools/dxfsl/area.py +2 -3
- femagtools/dxfsl/conv.py +6 -1
- femagtools/dxfsl/converter.py +10 -2
- femagtools/dxfsl/fslrenderer.py +1 -2
- femagtools/dxfsl/functions.py +24 -27
- femagtools/dxfsl/geom.py +210 -89
- femagtools/dxfsl/machine.py +43 -16
- femagtools/dxfsl/plotrenderer.py +2 -2
- femagtools/dxfsl/shape.py +84 -19
- femagtools/femag.py +21 -3
- femagtools/fsl.py +14 -2
- femagtools/machine/__init__.py +13 -33
- femagtools/machine/afpm.py +22 -21
- femagtools/machine/pm.py +22 -21
- femagtools/machine/sm.py +1 -1
- femagtools/machine/utils.py +112 -58
- femagtools/mcv.py +27 -1
- femagtools/model.py +4 -2
- femagtools/nc.py +7 -0
- femagtools/opt.py +1 -1
- femagtools/parstudy.py +5 -2
- femagtools/plot/__init__.py +1 -0
- femagtools/plot/bch.py +2 -0
- femagtools/plot/fieldlines.py +37 -0
- femagtools/templates/basic_modpar.mako +8 -0
- femagtools/templates/bertotti.mako +40 -0
- femagtools/templates/modified_steinmetz.mako +39 -0
- femagtools/ts.py +1 -1
- femagtools/utils.py +9 -3
- {femagtools-1.5.7.dist-info → femagtools-1.6.1.dist-info}/METADATA +1 -1
- {femagtools-1.5.7.dist-info → femagtools-1.6.1.dist-info}/RECORD +40 -37
- tests/test_bchreader.py +12 -1
- tests/test_femag.py +1 -1
- tests/test_fsl.py +1 -1
- {femagtools-1.5.7.dist-info → femagtools-1.6.1.dist-info}/LICENSE +0 -0
- {femagtools-1.5.7.dist-info → femagtools-1.6.1.dist-info}/WHEEL +0 -0
- {femagtools-1.5.7.dist-info → femagtools-1.6.1.dist-info}/entry_points.txt +0 -0
- {femagtools-1.5.7.dist-info → femagtools-1.6.1.dist-info}/top_level.txt +0 -0
femagtools/dxfsl/machine.py
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
from __future__ import print_function
|
6
6
|
import numpy as np
|
7
7
|
import logging
|
8
|
-
from .shape import Element, Circle, Arc, Line
|
8
|
+
from .shape import Element, Circle, Arc, Line
|
9
9
|
from .corner import Corner
|
10
10
|
from .functions import point, points_are_close, distance
|
11
11
|
from .functions import alpha_angle, normalise_angle, middle_angle, third_angle
|
@@ -14,6 +14,7 @@ from .functions import within_interval, part_of_circle
|
|
14
14
|
from .functions import less, less_equal, greater, greater_equal
|
15
15
|
logger = logging.getLogger('femagtools.geom')
|
16
16
|
|
17
|
+
|
17
18
|
#############################
|
18
19
|
# Machine #
|
19
20
|
#############################
|
@@ -170,7 +171,8 @@ class Machine(object):
|
|
170
171
|
|
171
172
|
def copy(self, startangle, endangle,
|
172
173
|
airgap=False, inside=True, split=False,
|
173
|
-
delete_appendices=False
|
174
|
+
delete_appendices=False,
|
175
|
+
concatenate_tiny_el=False):
|
174
176
|
if airgap and self.airgap_radius > 0.0:
|
175
177
|
if inside:
|
176
178
|
if self.airgap2_radius > 0.0:
|
@@ -181,7 +183,8 @@ class Machine(object):
|
|
181
183
|
startangle, endangle,
|
182
184
|
0.0, new_radius,
|
183
185
|
split=split,
|
184
|
-
delete_appendices=delete_appendices
|
186
|
+
delete_appendices=delete_appendices,
|
187
|
+
concatenate_tiny_el=concatenate_tiny_el)
|
185
188
|
else:
|
186
189
|
new_radius = self.radius
|
187
190
|
gap_radius = max(self.airgap_radius, self.airgap2_radius)
|
@@ -189,7 +192,8 @@ class Machine(object):
|
|
189
192
|
startangle, endangle,
|
190
193
|
gap_radius, self.radius+9999,
|
191
194
|
split=split,
|
192
|
-
delete_appendices=delete_appendices
|
195
|
+
delete_appendices=delete_appendices,
|
196
|
+
concatenate_tiny_el=concatenate_tiny_el)
|
193
197
|
|
194
198
|
circ = Circle(Element(center=self.center,
|
195
199
|
radius=self.airgap_radius))
|
@@ -200,7 +204,8 @@ class Machine(object):
|
|
200
204
|
startangle, endangle, 0.0,
|
201
205
|
self.radius+9999,
|
202
206
|
split=split,
|
203
|
-
delete_appendices=delete_appendices
|
207
|
+
delete_appendices=delete_appendices,
|
208
|
+
concatenate_tiny_el=concatenate_tiny_el)
|
204
209
|
|
205
210
|
if not np.isclose(normalise_angle(startangle),
|
206
211
|
normalise_angle(endangle), 0.0):
|
@@ -231,6 +236,7 @@ class Machine(object):
|
|
231
236
|
return clone.get_machine()
|
232
237
|
|
233
238
|
def copy_mirror(self, startangle, midangle, endangle):
|
239
|
+
logger.debug("begin of copy_mirror")
|
234
240
|
geom1 = self.geom.copy_shape(self.radius,
|
235
241
|
startangle,
|
236
242
|
midangle,
|
@@ -254,6 +260,7 @@ class Machine(object):
|
|
254
260
|
machine.mirror_geom = geom2
|
255
261
|
machine.mirror_startangle = midangle
|
256
262
|
machine.mirror_endangle = endangle
|
263
|
+
logger.debug("end of copy_mirror")
|
257
264
|
return machine
|
258
265
|
|
259
266
|
def has_mirrored_windings(self):
|
@@ -279,16 +286,16 @@ class Machine(object):
|
|
279
286
|
self.endangle += angle
|
280
287
|
|
281
288
|
def airgap(self, correct_airgap=0.0, correct_airgap2=0.0, atol=0.1):
|
282
|
-
logger.debug('locking for airgap')
|
289
|
+
logger.debug('begin airgap (locking for airgap)')
|
283
290
|
self.airgap_radius = 0.0
|
284
291
|
self.airgap2_radius = 0.0
|
285
292
|
|
286
293
|
if np.isclose(self.radius, 0.0):
|
287
|
-
logger.debug('no radius')
|
294
|
+
logger.debug('end airgap: no radius')
|
288
295
|
return False
|
289
296
|
|
290
297
|
if correct_airgap < 0:
|
291
|
-
logger.debug('no airgap')
|
298
|
+
logger.debug('end airgap: no airgap (%s)', correct_airgap)
|
292
299
|
return False # no airgap
|
293
300
|
|
294
301
|
self.airgaps = []
|
@@ -354,33 +361,39 @@ class Machine(object):
|
|
354
361
|
logger.error("No airgap with radius {} found"
|
355
362
|
.format(correct_airgap))
|
356
363
|
self.show_airgap_candidates(airgap_candidates, False)
|
364
|
+
logger.debug("end airgap: bad")
|
357
365
|
return True # bad exit
|
358
366
|
|
359
367
|
if correct_airgap2 > 0.0 and self.airgap2_radius == 0.0:
|
360
368
|
logger.error("No airgap2 with radius {} found"
|
361
369
|
.format(correct_airgap2))
|
362
370
|
self.show_airgap_candidates(airgap_candidates, False)
|
371
|
+
logger.debug("end airgap: bad")
|
363
372
|
return True # bad exit
|
364
373
|
|
365
374
|
if len(self.airgaps) == 0:
|
366
|
-
logger.debug('No airgap found')
|
375
|
+
logger.debug('end airgap: No airgap found')
|
367
376
|
return False # no airgaps found
|
368
377
|
|
369
378
|
if self.airgap_radius > 0.0:
|
379
|
+
logger.debug("end airgap: radius=%s", self.airgap_radius)
|
370
380
|
return False # correct airgap set
|
371
381
|
|
372
382
|
gaps = [c for b, c, d in airgap_candidates if b == 0]
|
373
383
|
|
374
384
|
if len(gaps) == 1: # one candidate without border intersection
|
375
385
|
self.airgap_radius = gaps[0].radius
|
386
|
+
logger.debug("end airgap: radius=%s", self.airgap_radius)
|
376
387
|
return False # ok
|
377
388
|
|
378
389
|
if len(airgap_candidates) == 1: # one candidate found
|
379
390
|
self.airgap_radius = airgap_candidates[0][1].radius
|
391
|
+
logger.debug("end airgap: radius=%s", self.airgap_radius)
|
380
392
|
return False # ok
|
381
393
|
|
382
394
|
self.airgap_radius = self.show_airgap_candidates(airgap_candidates,
|
383
395
|
True)
|
396
|
+
logger.debug("end airgap: radius=%s", self.airgap_radius)
|
384
397
|
return False # ok
|
385
398
|
|
386
399
|
def show_airgap_candidates(self, airgap_candidates, get_one):
|
@@ -451,7 +464,7 @@ class Machine(object):
|
|
451
464
|
self.geom.delete_circle((0.0, 0.0), first_dist)
|
452
465
|
|
453
466
|
def repair_hull(self):
|
454
|
-
logger.debug('repair_hull')
|
467
|
+
logger.debug('begin repair_hull(%s, %s)', self.startangle, self.endangle)
|
455
468
|
if self.is_full() and not self.has_airgap():
|
456
469
|
self.delete_center_circle()
|
457
470
|
|
@@ -468,18 +481,31 @@ class Machine(object):
|
|
468
481
|
logger.debug('end of repair_hull')
|
469
482
|
|
470
483
|
def repair_hull_geom(self, geom, startangle, endangle):
|
471
|
-
logger.debug('repair_hull_geom')
|
484
|
+
logger.debug('begin repair_hull_geom (%s, %s)', startangle, endangle)
|
472
485
|
|
486
|
+
rtol = 1e-4
|
487
|
+
atol = 1e-4
|
473
488
|
c_corner = Corner(self.center, self.center)
|
474
|
-
start_corners = geom.get_corner_list(self.center, startangle
|
475
|
-
|
489
|
+
start_c_added, start_corners = geom.get_corner_list(self.center, startangle,
|
490
|
+
rtol=rtol, atol=atol)
|
491
|
+
end_c_added, end_corners = geom.get_corner_list(self.center, endangle,
|
492
|
+
rtol=rtol, atol=atol)
|
493
|
+
if start_c_added or end_c_added:
|
494
|
+
rtol = 1e-3
|
495
|
+
atol = 1e-3
|
496
|
+
start_c_added, start_corners = geom.get_corner_list(self.center, startangle,
|
497
|
+
rtol=rtol, atol=atol)
|
498
|
+
end_c_added, end_corners = geom.get_corner_list(self.center, endangle,
|
499
|
+
rtol=rtol, atol=atol)
|
476
500
|
|
477
501
|
geom.repair_hull_line(self.center,
|
478
502
|
startangle, start_corners,
|
479
|
-
c_corner in end_corners
|
503
|
+
c_corner in end_corners,
|
504
|
+
rtol=rtol, atol=atol)
|
480
505
|
geom.repair_hull_line(self.center,
|
481
506
|
endangle, end_corners,
|
482
|
-
c_corner in start_corners
|
507
|
+
c_corner in start_corners,
|
508
|
+
rtol=rtol, atol=atol)
|
483
509
|
logger.debug('end of repair_hull_geom')
|
484
510
|
|
485
511
|
def create_stator_auxiliary_lines(self):
|
@@ -657,7 +683,8 @@ class Machine(object):
|
|
657
683
|
return None
|
658
684
|
|
659
685
|
machine_slice = self.copy(self.geom.symmetry_startangle(),
|
660
|
-
self.geom.symmetry_endangle()
|
686
|
+
self.geom.symmetry_endangle(),
|
687
|
+
concatenate_tiny_el=True)
|
661
688
|
machine_slice.clear_cut_lines()
|
662
689
|
machine_slice.repair_hull()
|
663
690
|
machine_slice.rotate_to(0.0)
|
femagtools/dxfsl/plotrenderer.py
CHANGED
@@ -287,7 +287,7 @@ class PlotRenderer(object):
|
|
287
287
|
|
288
288
|
self.ax.axis('scaled')
|
289
289
|
self.ax.set_aspect('equal')
|
290
|
-
|
290
|
+
|
291
291
|
self.ax.set_xlim(x_min, x_max)
|
292
292
|
self.ax.set_ylim(y_min, y_max)
|
293
293
|
|
@@ -360,7 +360,7 @@ class PlotRenderer(object):
|
|
360
360
|
area.render(self, 'red', with_nodes=True)
|
361
361
|
|
362
362
|
self.ax.axis('scaled')
|
363
|
-
self.ax.set_aspect('equal')
|
363
|
+
self.ax.set_aspect('equal')
|
364
364
|
pl.show()
|
365
365
|
|
366
366
|
def draw_slot(self, id, slot, ax):
|
femagtools/dxfsl/shape.py
CHANGED
@@ -9,7 +9,6 @@
|
|
9
9
|
"""
|
10
10
|
from __future__ import print_function
|
11
11
|
import numpy as np
|
12
|
-
import sys
|
13
12
|
import logging
|
14
13
|
from .functions import less_equal, greater_equal
|
15
14
|
from .functions import distance, line_m, line_n
|
@@ -18,7 +17,7 @@ from .functions import alpha_line, alpha_angle, alpha_triangle
|
|
18
17
|
from .functions import normalise_angle, min_angle, max_angle, get_angle_of_arc
|
19
18
|
from .functions import lines_intersect_point, nodes_are_equal
|
20
19
|
from .functions import is_angle_inside, intersect_point
|
21
|
-
from .functions import middle_angle
|
20
|
+
from .functions import middle_angle, middle_point_of_line
|
22
21
|
|
23
22
|
logger = logging.getLogger('femagtools.geom')
|
24
23
|
|
@@ -50,7 +49,10 @@ class Shape(object):
|
|
50
49
|
if hasattr(s, 'my_attrs'):
|
51
50
|
self.my_attrs = []
|
52
51
|
for a in s.my_attrs:
|
53
|
-
|
52
|
+
self.my_attrs.append(a)
|
53
|
+
|
54
|
+
def classname(self):
|
55
|
+
return "Shape"
|
54
56
|
|
55
57
|
def get_my_color(self):
|
56
58
|
if hasattr(self, 'my_color'):
|
@@ -209,8 +211,8 @@ class Shape(object):
|
|
209
211
|
d1 = distance(n, self.n1)
|
210
212
|
d2 = distance(n, self.n2)
|
211
213
|
if d1 == d2:
|
212
|
-
logger.
|
213
|
-
|
214
|
+
logger.warning("distances of %s and %s are equal (%s / %s)",
|
215
|
+
self.n1, self.n2, d1, d2)
|
214
216
|
raise ValueError('both nodes are equal in element')
|
215
217
|
|
216
218
|
if d1 < d2:
|
@@ -307,6 +309,9 @@ class Circle(Shape):
|
|
307
309
|
self.n1 = None
|
308
310
|
self.n2 = None
|
309
311
|
|
312
|
+
def classname(self):
|
313
|
+
return "Circle"
|
314
|
+
|
310
315
|
def render(self, renderer, color='blue', with_nodes=False):
|
311
316
|
tmp_color = self.get_my_color()
|
312
317
|
if not tmp_color:
|
@@ -544,6 +549,19 @@ class Circle(Shape):
|
|
544
549
|
assert(len(points) == 0)
|
545
550
|
return []
|
546
551
|
|
552
|
+
def cut_into_halves(self):
|
553
|
+
""" return two arcs
|
554
|
+
"""
|
555
|
+
a1 = Arc(Element(center=self.center,
|
556
|
+
radius=self.radius,
|
557
|
+
start_angle=0.0,
|
558
|
+
end_angle=180.0))
|
559
|
+
a2 = Arc(Element(center=self.center,
|
560
|
+
radius=self.radius,
|
561
|
+
start_angle=180.0,
|
562
|
+
end_angle=0.0))
|
563
|
+
return a1, a2
|
564
|
+
|
547
565
|
def get_angle_of_arc(self):
|
548
566
|
return np.pi*2.0
|
549
567
|
|
@@ -608,6 +626,9 @@ class Arc(Circle):
|
|
608
626
|
else:
|
609
627
|
self.rtheta = None
|
610
628
|
|
629
|
+
def classname(self):
|
630
|
+
return "Arc"
|
631
|
+
|
611
632
|
def render(self, renderer, color='blue', with_nodes=False):
|
612
633
|
tmp_color = self.get_my_color()
|
613
634
|
if not tmp_color:
|
@@ -715,8 +736,9 @@ class Arc(Circle):
|
|
715
736
|
points.append(e.p2)
|
716
737
|
|
717
738
|
elif e.is_point_inside(self.p2, rtol, atol):
|
718
|
-
if not points_are_close(self.p1, e.p1):
|
739
|
+
if not points_are_close(self.p1, e.p1, rtol=rtol, atol=atol):
|
719
740
|
logger.error("FATAL ERROR in overlapping_shape() of Arc")
|
741
|
+
|
720
742
|
raise ValueError('FATAL ERROR in overlapping_shape() of Arc')
|
721
743
|
points.append(e.p1)
|
722
744
|
points.append(self.p2)
|
@@ -808,6 +830,20 @@ class Arc(Circle):
|
|
808
830
|
assert(len(points_inside) == 0)
|
809
831
|
return []
|
810
832
|
|
833
|
+
def cut_into_halves(self):
|
834
|
+
""" return two arcs
|
835
|
+
"""
|
836
|
+
midangle = middle_angle(self.startangle, self.endangle)
|
837
|
+
a1 = Arc(Element(center=self.center,
|
838
|
+
radius=self.radius,
|
839
|
+
start_angle=self.startangle*180/np.pi,
|
840
|
+
end_angle=midangle*180/np.pi))
|
841
|
+
a2 = Arc(Element(center=self.center,
|
842
|
+
radius=self.radius,
|
843
|
+
start_angle=midangle*180/np.pi,
|
844
|
+
end_angle=self.endangle*180/np.pi))
|
845
|
+
return a1, a2
|
846
|
+
|
811
847
|
def concatenate_arc(self, n1, n2, el):
|
812
848
|
if not points_are_close(self.center, el.center):
|
813
849
|
return None
|
@@ -817,9 +853,11 @@ class Arc(Circle):
|
|
817
853
|
if np.isclose(self.startangle, el.endangle):
|
818
854
|
start_angle = el.startangle
|
819
855
|
end_angle = self.endangle
|
820
|
-
|
856
|
+
elif np.isclose(el.startangle, self.endangle):
|
821
857
|
start_angle = self.startangle
|
822
858
|
end_angle = el.endangle
|
859
|
+
else:
|
860
|
+
return None
|
823
861
|
|
824
862
|
logger.debug("concatenate_arc: start=%s, end=%s",
|
825
863
|
start_angle,
|
@@ -833,30 +871,30 @@ class Arc(Circle):
|
|
833
871
|
def is_point_inside(self, p, rtol=1e-03, atol=1e-03, include_end=False):
|
834
872
|
""" returns true if p is on arc
|
835
873
|
"""
|
836
|
-
logger.debug("is_point_inside: p=%s", p)
|
874
|
+
# logger.debug("is_point_inside: p=%s", p)
|
837
875
|
d = distance(p, self.center)
|
838
876
|
if not np.isclose(d, self.radius, rtol=rtol, atol=atol):
|
839
|
-
logger.debug(" <== RADIUS %s, DISTANCE %s",
|
840
|
-
|
877
|
+
# logger.debug(" <== RADIUS %s, DISTANCE %s",
|
878
|
+
# self.radius, d)
|
841
879
|
return False
|
842
880
|
if points_are_close(p, self.p1, rtol=rtol, atol=atol):
|
843
|
-
logger.debug(" <== CLOSE TO P1 %s: rtol=%s, atol=%s",
|
844
|
-
|
881
|
+
# logger.debug(" <== CLOSE TO P1 %s: rtol=%s, atol=%s",
|
882
|
+
# self.p1, rtol, atol)
|
845
883
|
return include_end
|
846
884
|
elif points_are_close(p, self.p2, rtol=rtol, atol=atol):
|
847
|
-
logger.debug(" <== CLOSE TO P2 %s: rtol=%s, atol=%s",
|
848
|
-
|
885
|
+
# logger.debug(" <== CLOSE TO P2 %s: rtol=%s, atol=%s",
|
886
|
+
# self.p2, rtol, atol)
|
849
887
|
return include_end
|
850
888
|
elif points_are_close(self.p1, self.p2, rtol=rtol, atol=atol):
|
851
|
-
logger.debug(" <== P1 AND P2 CLOSE TOGETHER")
|
889
|
+
# logger.debug(" <== P1 AND P2 CLOSE TOGETHER")
|
852
890
|
return False
|
853
891
|
|
854
892
|
alpha_p1 = alpha_line(self.center, self.p1)
|
855
893
|
alpha_p2 = alpha_line(self.center, self.p2)
|
856
894
|
alpha_p = alpha_line(self.center, p)
|
857
895
|
alpha_inside = is_angle_inside(alpha_p1, alpha_p2, alpha_p)
|
858
|
-
logger.debug("is_point_inside: %s (%s, %s ,%s)",
|
859
|
-
|
896
|
+
# logger.debug("is_point_inside: %s (%s, %s ,%s)",
|
897
|
+
# alpha_inside, alpha_p1, alpha_p2, alpha_p)
|
860
898
|
return alpha_inside
|
861
899
|
|
862
900
|
def is_angle_inside(self, alpha, rtol=1e-03, atol=1e-03,
|
@@ -889,7 +927,7 @@ class Arc(Circle):
|
|
889
927
|
self.startangle = np.arctan2(p1[1], p1[0])
|
890
928
|
self.endangle = np.arctan2(p2[1], p2[0])
|
891
929
|
if self.rtheta is not None:
|
892
|
-
self.rtheta = self.rtheta +
|
930
|
+
self.rtheta = self.rtheta + dest_alpha
|
893
931
|
return self
|
894
932
|
|
895
933
|
def minmax(self):
|
@@ -1054,6 +1092,9 @@ class Line(Shape):
|
|
1054
1092
|
self.n1 = None
|
1055
1093
|
self.n2 = None
|
1056
1094
|
|
1095
|
+
def classname(self):
|
1096
|
+
return "Line"
|
1097
|
+
|
1057
1098
|
def render(self, renderer, color='blue', with_nodes=False):
|
1058
1099
|
tmp_color = self.get_my_color()
|
1059
1100
|
if not tmp_color:
|
@@ -1186,9 +1227,29 @@ class Line(Shape):
|
|
1186
1227
|
return split_lines
|
1187
1228
|
return []
|
1188
1229
|
|
1230
|
+
def cut_into_halves(self):
|
1231
|
+
""" return two lines
|
1232
|
+
"""
|
1233
|
+
pm = middle_point_of_line(self.p1, self.p2)
|
1234
|
+
l1 = Line(Element(start=self.p1, end=pm))
|
1235
|
+
l2 = Line(Element(start=pm, end=self.p2))
|
1236
|
+
return l1, l2
|
1237
|
+
|
1189
1238
|
def concatenate_line(self, n1, n2, el):
|
1190
|
-
if np.isclose(self.m(999999.0), el.m(999999.0)):
|
1239
|
+
if not np.isclose(self.m(999999.0), el.m(999999.0)):
|
1240
|
+
return None
|
1241
|
+
|
1242
|
+
if n1 and n2:
|
1191
1243
|
return Line(Element(start=n1, end=n2))
|
1244
|
+
|
1245
|
+
if points_are_close(self.p1, el.p1):
|
1246
|
+
return Line(Element(start=self.p2, end=el.p2))
|
1247
|
+
if points_are_close(self.p1, el.p2):
|
1248
|
+
return Line(Element(start=self.p2, end=el.p1))
|
1249
|
+
if points_are_close(self.p2, el.p1):
|
1250
|
+
return Line(Element(start=self.p1, end=el.p2))
|
1251
|
+
if points_are_close(self.p2, el.p2):
|
1252
|
+
return Line(Element(start=self.p1, end=el.p1))
|
1192
1253
|
return None
|
1193
1254
|
|
1194
1255
|
def is_point_inside(self, point,
|
@@ -1293,6 +1354,9 @@ class Point(Shape):
|
|
1293
1354
|
def __init__(self, p):
|
1294
1355
|
self.p1 = p
|
1295
1356
|
|
1357
|
+
def classname(self):
|
1358
|
+
return "Point"
|
1359
|
+
|
1296
1360
|
def render(self, renderer):
|
1297
1361
|
renderer.point(self.p1)
|
1298
1362
|
|
@@ -1301,6 +1365,7 @@ class Point(Shape):
|
|
1301
1365
|
self.p1 = (n[0], n[1])
|
1302
1366
|
return self
|
1303
1367
|
|
1368
|
+
|
1304
1369
|
def is_Circle(e):
|
1305
1370
|
return isinstance(e, Circle) and not isinstance(e, Arc)
|
1306
1371
|
|
femagtools/femag.py
CHANGED
@@ -90,7 +90,7 @@ def get_shortCircuit_parameters(bch, nload):
|
|
90
90
|
fc_radius=bch.machine['fc_radius'],
|
91
91
|
lfe=bch.armatureLength/1e3,
|
92
92
|
pocfilename=bch.machine['pocfile'],
|
93
|
-
num_par_wdgs=bch.machine
|
93
|
+
num_par_wdgs=bch.machine.get('num_par_wdgs', 0),
|
94
94
|
calculationMode='shortcircuit')
|
95
95
|
except (KeyError, AttributeError, IndexError):
|
96
96
|
raise FemagError("missing pm/Rel-Sim results")
|
@@ -222,6 +222,12 @@ class BaseFemag(object):
|
|
222
222
|
pass
|
223
223
|
builder = femagtools.fsl.Builder(self.templatedirs)
|
224
224
|
if simulation:
|
225
|
+
if 'num_par_wdgs' not in simulation:
|
226
|
+
try:
|
227
|
+
num_par_wdgs = self.model.winding['num_par_wdgs']
|
228
|
+
simulation['num_par_wdgs'] = num_par_wdgs
|
229
|
+
except:
|
230
|
+
pass
|
225
231
|
set_magnet_properties(self.model, simulation, self.magnets)
|
226
232
|
return builder.create(self.model, simulation,
|
227
233
|
self.magnets, self.condMat)
|
@@ -351,6 +357,16 @@ class BaseFemag(object):
|
|
351
357
|
|
352
358
|
def readResult(self, simulation, bch=None):
|
353
359
|
if simulation:
|
360
|
+
if simulation['calculationMode'] == "fieldcalc":
|
361
|
+
nc = self.read_nc()
|
362
|
+
pmod = nc.poles_sim
|
363
|
+
r = {'airgap': ag.read(
|
364
|
+
os.path.join(self.workdir, 'bag.dat'), pmod=pmod)}
|
365
|
+
if 'plots' in simulation:
|
366
|
+
if 'field_lines' in simulation['plots']:
|
367
|
+
r['field_lines'] = os.path.join(
|
368
|
+
self.workdir, 'field.svg')
|
369
|
+
return r
|
354
370
|
if simulation['calculationMode'] == "pm_sym_loss":
|
355
371
|
return self.read_los(self.modelname)
|
356
372
|
|
@@ -365,7 +381,8 @@ class BaseFemag(object):
|
|
365
381
|
|
366
382
|
if simulation['calculationMode'] == 'therm-dynamic':
|
367
383
|
temp = [[float(n) for n in l.split()]
|
368
|
-
for l in (pathlib.Path(self.workdir) /
|
384
|
+
for l in (pathlib.Path(self.workdir) /
|
385
|
+
'temperature.dat').read_text().split('\n') if l]
|
369
386
|
ttemp = list(zip(*temp))
|
370
387
|
return {'t': ttemp[0], 'temperature': ttemp[1]}
|
371
388
|
|
@@ -383,9 +400,10 @@ class BaseFemag(object):
|
|
383
400
|
set_magnet_properties(self.model, simulation, self.magnets)
|
384
401
|
fslcmds = (builder.open_model(self.model) +
|
385
402
|
builder.create_shortcircuit(simulation))
|
403
|
+
fslfile = 'shortcicuit.fsl'
|
386
404
|
with open(os.path.join(self.workdir, fslfile), 'w') as f:
|
387
405
|
f.write('\n'.join(fslcmds))
|
388
|
-
self.run(fslfile
|
406
|
+
self.run(fslfile) #, options?
|
389
407
|
bchfile = self.get_bch_file(self.modelname)
|
390
408
|
if bchfile:
|
391
409
|
bchsc = femagtools.bch.Reader()
|
femagtools/fsl.py
CHANGED
@@ -484,7 +484,11 @@ class Builder:
|
|
484
484
|
if not hasattr(model, 'stator'):
|
485
485
|
setattr(model, 'stator', {})
|
486
486
|
model.stator['num_slots'] = conv.get('tot_num_slot')
|
487
|
-
model.stator
|
487
|
+
if model.stator.get('num_slots_gen', 0):
|
488
|
+
if model.stator['num_slots'] % model.stator['num_slots_gen'] > 0:
|
489
|
+
model.stator['num_slots_gen'] = conv.get('num_sl_gen')
|
490
|
+
else:
|
491
|
+
model.stator['num_slots_gen'] = conv.get('num_sl_gen')
|
488
492
|
if 'fsl_stator' in conv:
|
489
493
|
self.fsl_stator = True
|
490
494
|
model.stator['dxf'] = dict(fsl=conv['fsl_stator'])
|
@@ -633,15 +637,23 @@ class Builder:
|
|
633
637
|
|
634
638
|
def create_analysis(self, sim):
|
635
639
|
pfefunc = sim.get('loss_funct', '')
|
640
|
+
custom_fefunc = ['']
|
636
641
|
if pfefunc:
|
637
642
|
sim['loss_funct'] = 1 # 3?
|
643
|
+
if pfefunc == 'bertotti' or 'modified_steinmetz':
|
644
|
+
custom_fefunc = self.__render(sim['PVFE_FSL'], pfefunc)
|
645
|
+
else:
|
646
|
+
custom_fefunc = pfefunc.split('\n')
|
647
|
+
|
638
648
|
airgap_induc = (self.create_airgap_induc()
|
639
649
|
if sim.get('airgap_induc', 0) else [])
|
640
|
-
felosses =
|
650
|
+
felosses = custom_fefunc + self.create_fe_losses(sim)
|
641
651
|
fslcalc = (self.__render(sim, sim.get('calculationMode')) +
|
642
652
|
airgap_induc)
|
653
|
+
'''
|
643
654
|
if pfefunc:
|
644
655
|
sim['loss_funct'] = pfefunc
|
656
|
+
'''
|
645
657
|
|
646
658
|
if sim.get('calculationMode') in ('cogg_calc',
|
647
659
|
'ld_lq_fast',
|
femagtools/machine/__init__.py
CHANGED
@@ -14,13 +14,14 @@ import logging
|
|
14
14
|
logger = logging.getLogger(__name__)
|
15
15
|
|
16
16
|
|
17
|
-
def __scale_losses(losses,
|
17
|
+
def __scale_losses(losses, rlfe):
|
18
18
|
if losses:
|
19
|
-
l = {k:
|
19
|
+
l = {k: rlfe*np.array(losses[k]) for k in (
|
20
20
|
'styoke_hyst', 'styoke_eddy',
|
21
21
|
'stteeth_hyst', 'stteeth_eddy',
|
22
|
+
'styoke_excess', 'stteeth_excess', 'rotor_excess',
|
22
23
|
'rotor_hyst', 'rotor_eddy',
|
23
|
-
'magnet')}
|
24
|
+
'magnet') if k in losses}
|
24
25
|
l['speed'] = losses['speed']
|
25
26
|
return l
|
26
27
|
return {}
|
@@ -66,18 +67,15 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
66
67
|
|
67
68
|
psid = rwdg*rlfe*dqp['psid']
|
68
69
|
psiq = rwdg*rlfe*dqp['psiq']
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
except KeyError as e:
|
74
|
-
logger.warning(e)
|
75
|
-
losses = {}
|
70
|
+
losses = __scale_losses(dqp['losses'], rlfe)
|
71
|
+
losses['ef'] = dqpars[-1]['losses']['ef']
|
72
|
+
losses['eh'] = dqpars[-1]['losses']['ef']
|
73
|
+
|
76
74
|
if 'psidq' in eecpars:
|
77
75
|
machine = PmRelMachinePsidq(
|
78
76
|
eecpars['m'], eecpars['p'],
|
79
|
-
r1=eecpars
|
80
|
-
ls=eecpars
|
77
|
+
r1=eecpars.get('r1', 0)*rlfe*rwdg**2,
|
78
|
+
ls=eecpars.get('ls1', 0)*rwdg**2,
|
81
79
|
psid=psid,
|
82
80
|
psiq=psiq,
|
83
81
|
losses=losses,
|
@@ -90,8 +88,8 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
90
88
|
i1 = np.array(dqp['i1'])/rwdg
|
91
89
|
machine = PmRelMachineLdq(
|
92
90
|
eecpars['m'], eecpars['p'],
|
93
|
-
r1=eecpars
|
94
|
-
ls=eecpars
|
91
|
+
r1=eecpars.get('r1', 0)*rlfe*rwdg**2,
|
92
|
+
ls=eecpars.get('ls1', 0)*rwdg**2,
|
95
93
|
psid=psid,
|
96
94
|
psiq=psiq,
|
97
95
|
losses=losses,
|
@@ -103,7 +101,7 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
103
101
|
|
104
102
|
# must be an induction machine (TODO: check scaling)
|
105
103
|
pars = copy.deepcopy(eecpars)
|
106
|
-
pars['r1'] = rlfe*rwdg**2*pars
|
104
|
+
pars['r1'] = rlfe*rwdg**2*pars.get('r1', 0)
|
107
105
|
pars['lsigma1'] = rlfe*pars['lsigma1']
|
108
106
|
pars['lsigma2'] = rlfe*pars['lsigma2']
|
109
107
|
pars['psiref'] = rwdg*rlfe*pars['psiref']
|
@@ -119,24 +117,6 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
119
117
|
return InductionMachine(pars)
|
120
118
|
|
121
119
|
|
122
|
-
def __scale_losses(losses, rlfe):
|
123
|
-
if losses:
|
124
|
-
l = {k: rlfe*np.array(losses[k]) for k in (
|
125
|
-
'styoke_hyst', 'styoke_eddy',
|
126
|
-
'stteeth_hyst', 'stteeth_eddy',
|
127
|
-
'rotor_hyst', 'rotor_eddy',
|
128
|
-
'magnet')}
|
129
|
-
if 'styoke_exc' in losses:
|
130
|
-
l.update({k: rlfe*np.array(losses[k])
|
131
|
-
for k in (
|
132
|
-
'styoke_exc',
|
133
|
-
'stteeth_exc',
|
134
|
-
'rotor_exc')})
|
135
|
-
l['speed'] = losses['speed']
|
136
|
-
return l
|
137
|
-
return {}
|
138
|
-
|
139
|
-
|
140
120
|
def create(bch, r1, ls, lfe=1, wdg=1):
|
141
121
|
"""create PmRelMachine from BCH
|
142
122
|
|