femagtools 1.8.12__py3-none-any.whl → 1.8.14__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 +49 -15
- femagtools/ecloss.py +12 -4
- femagtools/femag.py +3 -2
- femagtools/fsl.py +28 -10
- femagtools/isa7.py +16 -0
- femagtools/job.py +15 -3
- femagtools/machine/__init__.py +8 -7
- femagtools/machine/effloss.py +37 -14
- femagtools/machine/im.py +51 -17
- femagtools/machine/pm.py +2 -1
- femagtools/machine/sizing.py +91 -5
- femagtools/machine/sm.py +69 -70
- femagtools/machine/utils.py +37 -14
- femagtools/multiproc.py +6 -0
- femagtools/parstudy.py +20 -14
- femagtools/plot/bch.py +1 -1
- femagtools/shortcircuit.py +15 -9
- femagtools/templates/FE-losses.mako +0 -3
- femagtools/templates/gen_hairpin_winding.mako +8 -6
- femagtools/templates/noloadflux-rot.mako +1 -0
- femagtools/ts.py +38 -7
- femagtools/utils.py +5 -1
- femagtools/zmq.py +4 -1
- {femagtools-1.8.12.dist-info → femagtools-1.8.14.dist-info}/METADATA +3 -2
- {femagtools-1.8.12.dist-info → femagtools-1.8.14.dist-info}/RECORD +32 -32
- {femagtools-1.8.12.dist-info → femagtools-1.8.14.dist-info}/WHEEL +1 -1
- tests/test_bchreader.py +5 -5
- tests/test_fsl.py +2 -2
- {femagtools-1.8.12.dist-info → femagtools-1.8.14.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.12.dist-info → femagtools-1.8.14.dist-info/licenses}/LICENSE +0 -0
- {femagtools-1.8.12.dist-info → femagtools-1.8.14.dist-info}/top_level.txt +0 -0
femagtools/machine/pm.py
CHANGED
femagtools/machine/sizing.py
CHANGED
@@ -546,9 +546,10 @@ def get_interior_magnet_dimensions(I1, N, kw, psi1, lfe, Da2, par):
|
|
546
546
|
iron_shape=Da2/2))
|
547
547
|
|
548
548
|
|
549
|
-
def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='
|
549
|
+
def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='statorRotor3'):
|
550
550
|
r = dict()
|
551
551
|
r['Da2'] = Da2
|
552
|
+
r['rotorfilfact']=0.95
|
552
553
|
if 'Q2' not in par:
|
553
554
|
r['num_slots'] = _rotor_slots(par['Q1'], par['p'])[0]
|
554
555
|
else:
|
@@ -568,13 +569,42 @@ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
|
|
568
569
|
taup = np.pi * Da2/(2*par['p'])
|
569
570
|
hyr = psi1/2/lfe*par['By']
|
570
571
|
r['Dy2'] = round(Da2 - 2*hr - 2*hyr, 4)
|
571
|
-
logger.info("Dy2 %f Da2 %f hys %f hr %f",
|
572
|
-
r['Dy2']*1e3, Da2*1e3, hyr*1e3, hr*1e3)
|
573
572
|
slotwidth = 1e-3
|
574
573
|
Q2 = r['num_slots']
|
575
574
|
r1 = wr/2-slotwidth
|
576
575
|
r2 = (Da2/2-hr-hs1)*np.tan(alfar)
|
577
|
-
|
576
|
+
|
577
|
+
logger.info("Dy2 %f Da2 %f hys %f hr %f",
|
578
|
+
r['Dy2']*1e3, Da2*1e3, hyr*1e3, hr*1e3)
|
579
|
+
|
580
|
+
# End-ring calculation
|
581
|
+
Ir_total_estimated = A * np.pi * Da2 / (2 * par['p'])
|
582
|
+
Ibar_eff = Ir_total_estimated / r['num_slots']
|
583
|
+
|
584
|
+
if r['num_slots'] > 0:
|
585
|
+
sin_term = np.sin(np.pi / r['num_slots'])
|
586
|
+
if sin_term != 0:
|
587
|
+
Iring_max_eff = Ibar_eff / (2 * sin_term)
|
588
|
+
else:
|
589
|
+
Iring_max_eff = 0
|
590
|
+
else:
|
591
|
+
Iring_max_eff = 0
|
592
|
+
if 'Jring' not in par:
|
593
|
+
par['Jring'] = par.get('J', 4e6)
|
594
|
+
|
595
|
+
Jring = par['Jring']
|
596
|
+
|
597
|
+
if 'kfilling_ring' not in par:
|
598
|
+
par['kfilling_ring'] = 1.0
|
599
|
+
|
600
|
+
kfilling_ring = par['kfilling_ring']
|
601
|
+
|
602
|
+
if Jring * kfilling_ring > 0:
|
603
|
+
Aring = Iring_max_eff / (Jring * kfilling_ring)
|
604
|
+
else:
|
605
|
+
Aring = 0
|
606
|
+
|
607
|
+
"""if rtype == 'statorRotor3':
|
578
608
|
r['statorRotor3'] = dict(
|
579
609
|
slot_width=slotwidth,
|
580
610
|
tooth_width=round(wt, 4),
|
@@ -587,6 +617,18 @@ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
|
|
587
617
|
wedge_width1=0,
|
588
618
|
wedge_width2=0,
|
589
619
|
middle_line=0)
|
620
|
+
r['statorRotor3'] = dict(
|
621
|
+
slot_width=1e-3,
|
622
|
+
tooth_width=0,
|
623
|
+
slot_height=0.0157,
|
624
|
+
slot_top_sh=0,
|
625
|
+
slot_h1=0.5e-3,
|
626
|
+
slot_h2=2.2e-3,
|
627
|
+
slot_r1=2.2e-3,
|
628
|
+
slot_r2=1e-3,
|
629
|
+
wedge_width1=1e-3,
|
630
|
+
wedge_width2=0,
|
631
|
+
middle_line=0)
|
590
632
|
elif rtype == 'rotorAsyn':
|
591
633
|
r['rotorAsyn'] = dict(
|
592
634
|
slot_bs2=0.1e-3,
|
@@ -610,8 +652,52 @@ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
|
|
610
652
|
slot_h2=0,
|
611
653
|
slot_r1=1e-3, # r1,
|
612
654
|
slot_r2=1e-3, # r2,
|
655
|
+
middle_line=0)"""
|
656
|
+
|
657
|
+
all_rotor_types = {}
|
658
|
+
|
659
|
+
all_rotor_types['statorRotor3'] = dict(
|
660
|
+
slot_width=slotwidth,
|
661
|
+
tooth_width=round(wt, 4),
|
662
|
+
slot_height=round(hr+r2, 4),
|
663
|
+
slot_top_sh=1.0,
|
664
|
+
slot_h1=round(hs1, 4),
|
665
|
+
slot_h2=round(hs1+r1, 4),
|
666
|
+
slot_r1=round(r1, 4),
|
667
|
+
slot_r2=round(r2),
|
668
|
+
wedge_width1=0,
|
669
|
+
wedge_width2=0,
|
613
670
|
middle_line=0)
|
614
671
|
|
672
|
+
all_rotor_types['rotorAsyn'] = dict(
|
673
|
+
slot_bs2=0.1e-3,
|
674
|
+
slot_hs2=0.5e-3,
|
675
|
+
slot_b32=0.0,
|
676
|
+
slot_h32=0.0,
|
677
|
+
slot_b42=0.0,
|
678
|
+
slot_h42=0.0,
|
679
|
+
slot_b52=round(wr, 4),
|
680
|
+
slot_b62=3e-3,
|
681
|
+
slot_h52=2.5e-3,
|
682
|
+
slot_h62=round(hr, 4),
|
683
|
+
slot_h72=2e-3
|
684
|
+
)
|
685
|
+
|
686
|
+
all_rotor_types['rotorKs2'] = dict(
|
687
|
+
slot_angle=round(2 * alfar * 180 / np.pi, 2),
|
688
|
+
slot_height=round(hr + r1 + r2, 4),
|
689
|
+
slot_topwidth=round(wr, 4),
|
690
|
+
slot_width=slotwidth,
|
691
|
+
slot_h1=hs1,
|
692
|
+
slot_h2=0,
|
693
|
+
slot_r1=1e-3, # r1,
|
694
|
+
slot_r2=1e-3, # r2,
|
695
|
+
middle_line=0
|
696
|
+
)
|
697
|
+
|
698
|
+
|
699
|
+
r.update(all_rotor_types)
|
700
|
+
r['Aring'] = Aring
|
615
701
|
return r
|
616
702
|
|
617
703
|
|
@@ -947,7 +1033,7 @@ def im(pnom: float, speed: float, p: int, **kwargs) -> dict:
|
|
947
1033
|
slots = []
|
948
1034
|
r = get_stator_dimensions(par, slots=slots)
|
949
1035
|
# rotor parameters
|
950
|
-
rtype = kwargs.get('rtype', '
|
1036
|
+
rtype = kwargs.get('rtype', 'statorRotor3')
|
951
1037
|
r['rotor'] = get_im_rotor_dimensions(
|
952
1038
|
par['cos_phi']*r['A'], r['Da2'], r['psi1'], r['lfe'],
|
953
1039
|
par, rtype=rtype)
|
femagtools/machine/sm.py
CHANGED
@@ -7,7 +7,7 @@ import pathlib
|
|
7
7
|
import numpy as np
|
8
8
|
import scipy.optimize as so
|
9
9
|
import scipy.interpolate as ip
|
10
|
-
from .utils import skin_resistance, wdg_resistance, betai1, iqd, KTH, create_wdg
|
10
|
+
from .utils import skin_resistance, wdg_resistance, betai1, iqd, KTH, create_wdg, loss_models
|
11
11
|
from .. import parstudy, windings
|
12
12
|
import femagtools.bch
|
13
13
|
|
@@ -42,14 +42,15 @@ def parident(workdir, engine, machine,
|
|
42
42
|
|
43
43
|
optional arguments:
|
44
44
|
num_cur_steps: number of current steps (default 5)
|
45
|
-
num_beta_steps: number of
|
46
|
-
num_exc_steps: number of excitation current (default
|
45
|
+
num_beta_steps: number of beta steps (default 13)
|
46
|
+
num_exc_steps: number of excitation current (default 6)
|
47
47
|
speed: rotor speed in 1/s (default 160/p)
|
48
48
|
i1_max: maximum current in A rms (default approx 3*i1nom)
|
49
49
|
beta_min: minimal current angle (default -180°)
|
50
50
|
beta_max: maximal current angle (default 0°)
|
51
51
|
num_move_steps: number of move steps
|
52
52
|
cmd: femag command (default None, platform executable)
|
53
|
+
feloss: jordan, steinmetz, modified steinmetz, bertotti
|
53
54
|
"""
|
54
55
|
cmd = kwargs.get('cmd', None)
|
55
56
|
|
@@ -79,9 +80,14 @@ def parident(workdir, engine, machine,
|
|
79
80
|
exc_logspace = True
|
80
81
|
ifmin, ifmax = ifnom/4, 1.4*ifnom
|
81
82
|
if exc_logspace:
|
82
|
-
excur = np.logspace(np.log(ifmin), np.log(ifmax),
|
83
|
+
"""excur = np.logspace(np.log(ifmin), np.log(ifmax),
|
83
84
|
kwargs.get("num_exc_steps", 6),
|
84
|
-
base=np.exp(1)).tolist()
|
85
|
+
base=np.exp(1)).tolist()"""
|
86
|
+
# create a data grid always contains ifnom
|
87
|
+
excur = np.logspace(np.log(ifmin), np.log(ifnom),
|
88
|
+
kwargs.get("num_exc_steps", 6) - 2,
|
89
|
+
base=np.exp(1), endpoint=False).tolist()
|
90
|
+
excur.extend([ifnom, ifmax])
|
85
91
|
else:
|
86
92
|
excur = np.linspace(ifmin, ifmax,
|
87
93
|
kwargs.get("num_exc_steps", 6))
|
@@ -120,6 +126,9 @@ def parident(workdir, engine, machine,
|
|
120
126
|
period_frac=kwargs.get('period_frac', 6),
|
121
127
|
speed=kwargs.get('speed', 50))
|
122
128
|
|
129
|
+
if kwargs.get("feloss", 0):
|
130
|
+
simulation["feloss"] = kwargs["feloss"]
|
131
|
+
machine["calc_fe_loss"] = loss_models[kwargs["feloss"].lower()]
|
123
132
|
###self.cleanup() # remove previously created files in workdir
|
124
133
|
results = parvar(parvardef, machine, simulation, engine)
|
125
134
|
b = results['f'][-1]
|
@@ -440,10 +449,6 @@ class SynchronousMachine(object):
|
|
440
449
|
|
441
450
|
with warnings.catch_warnings():
|
442
451
|
warnings.simplefilter("ignore")
|
443
|
-
def sqrtculoss(iqde):
|
444
|
-
pcu = self.culoss(iqde)
|
445
|
-
return pcu
|
446
|
-
|
447
452
|
res = so.minimize(
|
448
453
|
self.culoss, startvals, method='SLSQP', # trust-constr
|
449
454
|
bounds=self.bounds,
|
@@ -469,10 +474,6 @@ class SynchronousMachine(object):
|
|
469
474
|
|
470
475
|
with warnings.catch_warnings():
|
471
476
|
warnings.simplefilter("ignore")
|
472
|
-
def sqrtculoss(iqde):
|
473
|
-
pcu = self.culoss(iqde)
|
474
|
-
#logger.info("iqde %s --> pcu %f", iqde, pcu)
|
475
|
-
return pcu
|
476
477
|
res = so.minimize(
|
477
478
|
self.culoss, startvals, method='SLSQP', # trust-constr
|
478
479
|
bounds=self.bounds,
|
@@ -513,42 +514,38 @@ class SynchronousMachine(object):
|
|
513
514
|
with minimal losses at max voltage"""
|
514
515
|
iqde = self.iqd_tmech(torque, w1/2/np.pi/self.p)
|
515
516
|
if np.linalg.norm(
|
516
|
-
|
517
|
+
self.uqd(w1, *iqde)) <= u1max*np.sqrt(2):
|
517
518
|
if log:
|
518
519
|
log(iqde)
|
519
520
|
return (*iqde, torque)
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
521
|
+
beta, i1 = betai1(iqde[0], iqde[1])
|
522
|
+
iex = iqde[2]
|
523
|
+
def ubeta(b):
|
524
|
+
return np.sqrt(2)*u1max - np.linalg.norm(
|
525
|
+
self.uqd(w1, *iqd(b, i1), iex))
|
526
|
+
beta = -np.pi/4 if torque>0 else -3*np.pi/4
|
527
|
+
io = *iqd(beta, i1), iex
|
525
528
|
|
526
529
|
# logger.debug("--- torque %g io %s", torque, io)
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
{'type': 'eq',
|
542
|
-
'fun': lambda iqd: u1max*np.sqrt(2)
|
543
|
-
- np.linalg.norm(self.uqd(w1, *iqd))}])
|
544
|
-
#if res['success']:
|
545
|
-
if log:
|
546
|
-
log(res.x)
|
530
|
+
n = w1/2/np.pi/self.p
|
531
|
+
res = so.minimize(
|
532
|
+
self.culoss, io, method='SLSQP', # trust-constr
|
533
|
+
bounds=self.bounds,
|
534
|
+
constraints=[
|
535
|
+
{'type': 'eq',
|
536
|
+
'fun': lambda iqd: self.tmech_iqd(*iqd, n) - torque},
|
537
|
+
{'type': 'eq',
|
538
|
+
'fun': lambda iqd: np.linalg.norm(
|
539
|
+
self.uqd(w1, *iqd)) - u1max*np.sqrt(2)}])
|
540
|
+
|
541
|
+
if log:
|
542
|
+
log(res.x)
|
543
|
+
if res["success"]:
|
547
544
|
return *res.x, self.tmech_iqd(*res.x, n)
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
545
|
+
|
546
|
+
logger.warning("%s: w1=%f torque=%f, u1max=%f, io=%s",
|
547
|
+
res['message'], w1, torque, u1max, io)
|
548
|
+
raise ValueError(res['message'])
|
552
549
|
|
553
550
|
def iqd_torque_umax(self, torque, w1, u1max,
|
554
551
|
disp=False, maxiter=500, log=0, **kwargs):
|
@@ -559,33 +556,31 @@ class SynchronousMachine(object):
|
|
559
556
|
if log:
|
560
557
|
log(iqde)
|
561
558
|
return (*iqde, torque)
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
if log:
|
584
|
-
log(res.x)
|
559
|
+
beta, i1 = betai1(iqde[0], iqde[1])
|
560
|
+
iex = iqde[2]
|
561
|
+
def ubeta(b):
|
562
|
+
return np.sqrt(2)*u1max - np.linalg.norm(
|
563
|
+
self.uqd(w1, *iqd(b, i1), iex))
|
564
|
+
beta = -np.pi/4 if torque>0 else -3*np.pi/4
|
565
|
+
io = *iqd(beta, i1), iex
|
566
|
+
res = so.minimize(
|
567
|
+
self.culoss, io, method='SLSQP', # trust-constr
|
568
|
+
bounds=self.bounds,
|
569
|
+
#options={'disp': disp, 'maxiter': maxiter},
|
570
|
+
# jac=gradient_respecting_bounds(self.bounds, self.culoss),
|
571
|
+
constraints=[
|
572
|
+
{'type': 'eq',
|
573
|
+
'fun': lambda iqd: self.torque_iqd(*iqd) - torque},
|
574
|
+
{'type': 'eq',
|
575
|
+
'fun': lambda iqd: u1max*np.sqrt(2) - np.linalg.norm(
|
576
|
+
self.uqd(w1, *iqd))}])
|
577
|
+
if log:
|
578
|
+
log(res.x)
|
579
|
+
if res['success']:
|
585
580
|
return *res.x, self.torque_iqd(*res.x)
|
586
|
-
|
587
|
-
|
588
|
-
|
581
|
+
logger.warning("%s: w1=%f torque=%f, u1max=%f, io=%s",
|
582
|
+
res['message'], w1, torque, u1max, io)
|
583
|
+
raise ValueError(res['message'])
|
589
584
|
|
590
585
|
def w1_imax_umax(self, i1max, u1max):
|
591
586
|
"""return frequency w1 and shaft torque at voltage u1max and current i1max
|
@@ -669,10 +664,12 @@ class SynchronousMachine(object):
|
|
669
664
|
|
670
665
|
wmtab = []
|
671
666
|
dw = 0
|
667
|
+
wmMax = 2*np.pi*n
|
668
|
+
'''
|
672
669
|
wmMax = 5*wmType
|
673
670
|
if n > 0:
|
674
671
|
wmMax = min(wmMax, 2*np.pi*n)
|
675
|
-
|
672
|
+
'''
|
676
673
|
if wmType > wmMax:
|
677
674
|
wmrange = sorted([0, wmMax])
|
678
675
|
wmtab = np.linspace(0, wmMax, nsamples).tolist()
|
@@ -793,6 +790,7 @@ class SynchronousMachinePsidq(SynchronousMachine):
|
|
793
790
|
self.psiqf = ip.RegularGridInterpolator(
|
794
791
|
(exc, iqx, idx), psiq,
|
795
792
|
method='cubic', bounds_error=False, fill_value=None)
|
793
|
+
# excitation currents bounds should respect ifnom
|
796
794
|
self.bounds = [(min(iq), max(iq)),
|
797
795
|
(min(id), 0),
|
798
796
|
(iexc[0], iexc[-1])]
|
@@ -909,6 +907,7 @@ class SynchronousMachineLdq(SynchronousMachine):
|
|
909
907
|
method='cubic'
|
910
908
|
, bounds_error=False, fill_value=None)
|
911
909
|
i1max = np.sqrt(2)*(max(i1))
|
910
|
+
# excitation currents bounds should respect ifnom
|
912
911
|
self.bounds = [(np.cos(min(beta))*i1max, i1max),
|
913
912
|
(-i1max, 0),
|
914
913
|
(iexc[0], iexc[-1])]
|
femagtools/machine/utils.py
CHANGED
@@ -8,9 +8,16 @@ import numpy.linalg as la
|
|
8
8
|
import scipy.interpolate as ip
|
9
9
|
import logging
|
10
10
|
from .. import parstudy, windings
|
11
|
+
from ..model import MachineModel
|
11
12
|
|
12
13
|
logger = logging.getLogger(__name__)
|
13
14
|
|
15
|
+
loss_models = {
|
16
|
+
"modified steinmetz": 10,
|
17
|
+
"bertotti": 11,
|
18
|
+
"jordan": 1,
|
19
|
+
"steinmetz": 1
|
20
|
+
}
|
14
21
|
|
15
22
|
def K(d):
|
16
23
|
"""space phasor transformation matrix
|
@@ -369,6 +376,7 @@ def dqparident(workdir, engine, temp, machine,
|
|
369
376
|
period_frac: (int) fraction of rotating angle (default 6)
|
370
377
|
dqtype: (str) type of identification: 'ldq' (default), 'psidq'
|
371
378
|
cmd: femag executable
|
379
|
+
feloss: jordan, steinmetz, modified steinmetz, bertotti
|
372
380
|
"""
|
373
381
|
import pathlib
|
374
382
|
|
@@ -390,21 +398,25 @@ def dqparident(workdir, engine, temp, machine,
|
|
390
398
|
else:
|
391
399
|
fcu = 0.42
|
392
400
|
|
393
|
-
try:
|
394
|
-
from ..model import MachineModel
|
401
|
+
try:
|
395
402
|
wdg = create_wdg(machine)
|
396
|
-
Q1 = wdg.Q
|
397
|
-
model = MachineModel(machine)
|
398
|
-
Jmax = 30e6 # max current density in A/m2
|
399
|
-
Acu = fcu*model.slot_area() # approx. copper area of one slot
|
400
|
-
i1_max = round(g*Acu/wdg.l/N*Jmax/10)*10
|
401
403
|
except KeyError:
|
402
|
-
|
403
|
-
|
404
|
+
pass
|
405
|
+
model = MachineModel(machine)
|
406
|
+
if kwargs.get('i1_max', 0):
|
404
407
|
i1_max = kwargs['i1_max']
|
408
|
+
else:
|
409
|
+
try: # calc basic dimensions if not fsl or dxf model
|
410
|
+
Jmax = 30e6 # max current density in A/m2
|
411
|
+
Acu = fcu*model.slot_area() # approx. copper area of one slot
|
412
|
+
i1_max = round(g*Acu/wdg.l/N*Jmax/10)*10
|
413
|
+
except KeyError:
|
414
|
+
raise ValueError('i1_max missing')
|
405
415
|
|
406
416
|
# winding resistance
|
407
417
|
try:
|
418
|
+
da1 = machine['bore_diam']
|
419
|
+
hs = model.slot_height()
|
408
420
|
if 'wire_gauge' in machine[wdgk]:
|
409
421
|
aw = machine[wdgk]['wire_gauge']
|
410
422
|
elif 'dia_wire' in machine[wdgk]:
|
@@ -412,10 +424,10 @@ def dqparident(workdir, engine, temp, machine,
|
|
412
424
|
elif ('wire_width' in machine[wdgk]) and ('wire_height' in machine[wdgk]):
|
413
425
|
aw = machine[wdgk]['wire_width']*machine[wdgk]['wire_height']
|
414
426
|
else: # wire diameter from slot area
|
415
|
-
|
427
|
+
Q1 = wdg.Q
|
416
428
|
aw = 0.75 * fcu * np.pi*da1*hs/Q1/wdg.l/N
|
417
|
-
r1 = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
|
418
|
-
except (NameError, KeyError):
|
429
|
+
r1 = float(wdg_resistance(wdg, N, g, aw, da1, hs, lfe))
|
430
|
+
except (NameError, KeyError) as ex:
|
419
431
|
r1 = 0 # cannot calc winding resistance
|
420
432
|
|
421
433
|
n = len(temp)
|
@@ -483,6 +495,10 @@ def dqparident(workdir, engine, temp, machine,
|
|
483
495
|
speed=kwargs.get('speed', defspeed),
|
484
496
|
period_frac=period_frac)
|
485
497
|
|
498
|
+
if kwargs.get("feloss", 0):
|
499
|
+
simulation["feloss"] = kwargs["feloss"]
|
500
|
+
machine["calc_fe_loss"] = loss_models[kwargs["feloss"].lower()]
|
501
|
+
|
486
502
|
# TODO: cleanup() # remove previously created files in workdir
|
487
503
|
# start calculation
|
488
504
|
results = parvar(parvardef, machine, simulation, engine)
|
@@ -490,7 +506,6 @@ def dqparident(workdir, engine, temp, machine,
|
|
490
506
|
if 'poles' not in machine: # dxf model?
|
491
507
|
machine['poles'] = 2*results['f'][0]['machine']['p']
|
492
508
|
da1 = 2*results['f'][0]['machine']['fc_radius']
|
493
|
-
wdg = create_wdg(machine)
|
494
509
|
if 'bore_diam' in machine:
|
495
510
|
da1 = machine['bore_diam']
|
496
511
|
ls1 = 0
|
@@ -506,6 +521,14 @@ def dqparident(workdir, engine, temp, machine,
|
|
506
521
|
except KeyError:
|
507
522
|
rotor_mass = 0 # need femag classic > rel-9.3.x-48-gca42bbd0
|
508
523
|
|
524
|
+
if rotor_mass == 0:
|
525
|
+
try:
|
526
|
+
nc = parvar.femag.read_nc()
|
527
|
+
rotor_mass = float(sum(nc.get_mass()[1].values()))
|
528
|
+
logger.info("rotor mass from nc-file: %.1f kg", rotor_mass)
|
529
|
+
except StopIteration:
|
530
|
+
logger.warning("Could not read nc-file. Setting rotor_mass = 0!")
|
531
|
+
|
509
532
|
dq = []
|
510
533
|
if dqtype == 'ldq':
|
511
534
|
for i in range(0, len(results['f']), 2):
|
@@ -585,7 +608,7 @@ def dqparident(workdir, engine, temp, machine,
|
|
585
608
|
# diameter of wires
|
586
609
|
aw = fcu*asl/Q1/nlayers/N
|
587
610
|
hs = asl/(np.pi*da1/3)
|
588
|
-
dqpars['r1'] = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
|
611
|
+
dqpars['r1'] = float(wdg_resistance(wdg, N, g, aw, da1, hs, lfe))
|
589
612
|
|
590
613
|
if 'current_angles' in results['f'][0]:
|
591
614
|
dqpars['current_angles'] = results['f'][0]['current_angles']
|
femagtools/multiproc.py
CHANGED
@@ -235,6 +235,12 @@ class Engine:
|
|
235
235
|
self.pool = None # garbage collector deletes threads
|
236
236
|
return status
|
237
237
|
|
238
|
+
def read_nc(self):
|
239
|
+
"""return a generator object of nc list"""
|
240
|
+
for t in self.job.tasks:
|
241
|
+
yield t.read_nc()
|
242
|
+
return None
|
243
|
+
|
238
244
|
def stopThreads(self):
|
239
245
|
""" stop all running treads
|
240
246
|
"""
|
femagtools/parstudy.py
CHANGED
@@ -140,7 +140,8 @@ class ParameterStudy(object):
|
|
140
140
|
|
141
141
|
def __call__(self, opt, machine, simulation,
|
142
142
|
engine, bchMapper=None,
|
143
|
-
extra_files=[], num_samples=0
|
143
|
+
extra_files=[], num_samples=0,
|
144
|
+
data_model_created=False):
|
144
145
|
"""calculate objective vars for all decision vars
|
145
146
|
Args:
|
146
147
|
opt: variation parameter dict (decision_vars, objective_vars)
|
@@ -150,6 +151,8 @@ class ParameterStudy(object):
|
|
150
151
|
bchMapper: bch result transformation function
|
151
152
|
extra_files: list of additional input file names to be copied
|
152
153
|
num_samples: number of samples (ingored with Grid sampling)
|
154
|
+
data_model_created: model and complete data structur
|
155
|
+
was already created before calling this function
|
153
156
|
"""
|
154
157
|
|
155
158
|
self.stop = False # make sure the calculation will start. thomas.maier/OSWALD
|
@@ -174,7 +177,8 @@ class ParameterStudy(object):
|
|
174
177
|
workdir = pathlib.Path(self.femag.workdir)
|
175
178
|
for d in workdir.glob('[0-9]*'):
|
176
179
|
if re.search(r'^\d+$', d.name):
|
177
|
-
|
180
|
+
if not data_model_created:
|
181
|
+
shutil.rmtree(d)
|
178
182
|
|
179
183
|
extra_result_files = []
|
180
184
|
if simulation.get('airgap_induc', False):
|
@@ -198,8 +202,7 @@ class ParameterStudy(object):
|
|
198
202
|
|
199
203
|
prob = femagtools.moproblem.FemagMoProblem(decision_vars,
|
200
204
|
objective_vars)
|
201
|
-
|
202
|
-
if immutable_model:
|
205
|
+
if not data_model_created and immutable_model:
|
203
206
|
modelfiles = self.setup_model(builder, model, recsin=fea.recsin,
|
204
207
|
feloss=simulation.get('feloss', ''))
|
205
208
|
logger.info("Files %s", modelfiles+extra_files)
|
@@ -282,7 +285,7 @@ class ParameterStudy(object):
|
|
282
285
|
task = job.add_task(self.result_func)
|
283
286
|
for fn in extra_files:
|
284
287
|
task.add_file(fn)
|
285
|
-
if immutable_model:
|
288
|
+
if not data_model_created and immutable_model:
|
286
289
|
prob.prepare(x, [fea, self.femag.magnets])
|
287
290
|
for m in modelfiles:
|
288
291
|
task.add_file(m)
|
@@ -297,18 +300,21 @@ class ParameterStudy(object):
|
|
297
300
|
else:
|
298
301
|
prob.prepare(x, [model, fea, self.femag.magnets])
|
299
302
|
logger.info("prepare %s", x)
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
303
|
+
if not data_model_created:
|
304
|
+
for mc in self.femag.copy_magnetizing_curves(
|
305
|
+
model,
|
306
|
+
dir=task.directory,
|
307
|
+
recsin=fea.recsin,
|
308
|
+
feloss=feloss):
|
309
|
+
task.add_file(mc)
|
310
|
+
set_magnet_properties(model, fea, self.femag.magnets)
|
307
311
|
task.add_file(
|
308
312
|
'femag.fsl',
|
309
|
-
builder.create_model(model, self.femag.magnets) +
|
313
|
+
(builder.create_model(model, self.femag.magnets) if not data_model_created else []) +
|
310
314
|
builder.create_analysis(fea) +
|
311
|
-
['save_model("close")']
|
315
|
+
['save_model("close")'],
|
316
|
+
append=data_model_created # model already created, append fsl
|
317
|
+
)
|
312
318
|
|
313
319
|
if hasattr(fea, 'poc'):
|
314
320
|
task.add_file(fea.pocfilename,
|
femagtools/plot/bch.py
CHANGED
@@ -559,7 +559,7 @@ def demagnetization(demag, ax=0):
|
|
559
559
|
# arrowprops=dict(facecolor='green', edgecolor='green', shrink=0.05))
|
560
560
|
ax.set_ylim([rrmin, 1.01])
|
561
561
|
ax.set_ylabel('Rel. Remanence')
|
562
|
-
ax.set_xlabel(f'Phase Current / {unit}')
|
562
|
+
ax.set_xlabel(f'Phase Current / {unit} (peak)')
|
563
563
|
ax.grid()
|
564
564
|
|
565
565
|
|
femagtools/shortcircuit.py
CHANGED
@@ -112,12 +112,17 @@ def shortcircuit(femag, machine, bch, simulation, engine=0):
|
|
112
112
|
for d in bchsc.demag if 'H_max' in d],
|
113
113
|
'H_av': [d['H_av']
|
114
114
|
for d in bchsc.demag if 'H_av' in d]}
|
115
|
-
|
116
|
-
|
117
|
-
|
115
|
+
x1 = bchsc.demag[0]['current_1']
|
116
|
+
x2 = bchsc.demag[0]['current_2']
|
117
|
+
def func(phi):
|
118
|
+
return x2*np.cos(phi) - x1*np.cos(phi-2*np.pi/3)
|
119
|
+
phi=so.fsolve(func, 0)[0]
|
120
|
+
i1max = x1/np.cos(phi)
|
121
|
+
|
122
|
+
phirot = dd['displ'][0]/180*np.pi
|
118
123
|
bchsc.scData['demag'] = demag(
|
119
124
|
femag, machine, simulation,
|
120
|
-
i1max, phi, engine)
|
125
|
+
i1max, phirot, phi, engine)
|
121
126
|
bchsc.scData['demag'].update(dd)
|
122
127
|
scdata = bchsc.scData
|
123
128
|
#for w in bch.flux:
|
@@ -310,7 +315,7 @@ def shortcircuit_2phase(femag, machine, simulation, engine=0):
|
|
310
315
|
|
311
316
|
# rotor position at maximum current:
|
312
317
|
trot = min(iav[0], iap[0])
|
313
|
-
|
318
|
+
phirot = wm*trot + phi0
|
314
319
|
logger.info("phi %.1f")
|
315
320
|
|
316
321
|
scData = {
|
@@ -330,10 +335,10 @@ def shortcircuit_2phase(femag, machine, simulation, engine=0):
|
|
330
335
|
if simulation.get('sim_demagn', 0):
|
331
336
|
i1max = iap[1] if iap[1] > abs(iav[1]) else iav[1]
|
332
337
|
scData['demag'] = demag(femag, machine, simulation,
|
333
|
-
i1max,
|
338
|
+
i1max, phirot, 0, engine)
|
334
339
|
return scData
|
335
340
|
|
336
|
-
def demag(femag, machine, simulation, i1max, phi, engine=0):
|
341
|
+
def demag(femag, machine, simulation, i1max, phirot, phi, engine=0):
|
337
342
|
"""demag simulation using psi-torq-rem-rot"""
|
338
343
|
logger.info("Demagnetization processing")
|
339
344
|
i1min = simulation.get('i1min', abs(i1max/3))
|
@@ -345,7 +350,8 @@ def demag(femag, machine, simulation, i1max, phi, engine=0):
|
|
345
350
|
i1tab = b*(a+np.log(xtab))
|
346
351
|
|
347
352
|
if simulation.get('sc_type', 3) == 3:
|
348
|
-
curvec = [[
|
353
|
+
curvec = [[a*np.cos(phi), a*np.cos(phi-2*np.pi/3),
|
354
|
+
a*np.cos(phi+2*np.pi/3)] for a in i1tab]
|
349
355
|
else:
|
350
356
|
if i1max > 0:
|
351
357
|
curvec = [[a, -a, 0] for a in i1tab]
|
@@ -353,7 +359,7 @@ def demag(femag, machine, simulation, i1max, phi, engine=0):
|
|
353
359
|
curvec = [[-a, a, 0] for a in i1tab]
|
354
360
|
simulation.update({
|
355
361
|
'calculationMode': 'psi-torq-rem',
|
356
|
-
'phi':
|
362
|
+
'phi': phirot,
|
357
363
|
'magntemp': simulation['Tmag'],
|
358
364
|
'curvec': curvec})
|
359
365
|
_ = femag(machine, simulation)
|