femagtools 1.8.1__py3-none-any.whl → 1.8.3__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- femagtools/__init__.py +1 -1
- femagtools/dxfsl/area.py +110 -1
- femagtools/dxfsl/areabuilder.py +93 -45
- femagtools/dxfsl/conv.py +5 -0
- femagtools/dxfsl/converter.py +85 -27
- femagtools/dxfsl/fslrenderer.py +5 -4
- femagtools/dxfsl/functions.py +14 -6
- femagtools/dxfsl/geom.py +135 -149
- femagtools/dxfsl/journal.py +1 -1
- femagtools/dxfsl/machine.py +161 -9
- femagtools/dxfsl/shape.py +46 -1
- femagtools/dxfsl/svgparser.py +1 -1
- femagtools/dxfsl/symmetry.py +143 -38
- femagtools/femag.py +64 -61
- femagtools/fsl.py +15 -12
- femagtools/isa7.py +3 -2
- femagtools/machine/__init__.py +5 -4
- femagtools/machine/afpm.py +79 -33
- femagtools/machine/effloss.py +29 -18
- femagtools/machine/sizing.py +192 -13
- femagtools/machine/sm.py +34 -36
- femagtools/machine/utils.py +2 -2
- femagtools/mcv.py +58 -29
- femagtools/model.py +4 -3
- femagtools/multiproc.py +79 -80
- femagtools/parstudy.py +11 -5
- femagtools/plot/nc.py +2 -2
- femagtools/semi_fea.py +108 -0
- femagtools/templates/basic_modpar.mako +0 -3
- femagtools/templates/fe-contr.mako +18 -18
- femagtools/templates/ld_lq_fast.mako +3 -0
- femagtools/templates/mult_cal_fast.mako +3 -0
- femagtools/templates/pm_sym_f_cur.mako +4 -1
- femagtools/templates/pm_sym_fast.mako +3 -0
- femagtools/templates/pm_sym_loss.mako +3 -0
- femagtools/templates/psd_psq_fast.mako +3 -0
- femagtools/templates/torq_calc.mako +3 -0
- femagtools/tks.py +23 -20
- femagtools/zmq.py +213 -0
- {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/METADATA +3 -3
- {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/RECORD +49 -47
- {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/WHEEL +1 -1
- tests/test_afpm.py +15 -6
- tests/test_femag.py +1 -1
- tests/test_fsl.py +4 -4
- tests/test_mcv.py +21 -15
- {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/LICENSE +0 -0
- {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/entry_points.txt +0 -0
- {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/top_level.txt +0 -0
femagtools/femag.py
CHANGED
@@ -27,6 +27,7 @@ import femagtools.fsl
|
|
27
27
|
import femagtools.config
|
28
28
|
import femagtools.ecloss
|
29
29
|
import femagtools.forcedens
|
30
|
+
import femagtools.zmq
|
30
31
|
|
31
32
|
from femagtools import ntib
|
32
33
|
|
@@ -180,15 +181,22 @@ class BaseFemag(object):
|
|
180
181
|
def copy_winding_file(self, name, wdg):
|
181
182
|
wdg.write(name, self.workdir)
|
182
183
|
|
183
|
-
def copy_magnetizing_curves(self, model, dir=None, recsin=''):
|
184
|
+
def copy_magnetizing_curves(self, model, dir=None, recsin='', feloss=''):
|
184
185
|
"""extract mc names from model and write files into workdir or dir if given
|
185
186
|
|
186
187
|
Return:
|
187
188
|
list of extracted mc names (:obj:`list`)
|
188
189
|
"""
|
189
190
|
dest = dir if dir else self.workdir
|
191
|
+
if isinstance(feloss, (int, float)):
|
192
|
+
try:
|
193
|
+
feloss = {1: 'jordan', 11: 'bertotti'}[int(feloss)]
|
194
|
+
except KeyError:
|
195
|
+
feloss = ''
|
190
196
|
return [self.magnetizingCurves.writefile(m[0], dest,
|
191
|
-
fillfac=m[1],
|
197
|
+
fillfac=m[1],
|
198
|
+
recsin=recsin,
|
199
|
+
feloss=feloss)
|
192
200
|
for m in model.set_magcurves(
|
193
201
|
self.magnetizingCurves, self.magnets)]
|
194
202
|
|
@@ -212,9 +220,11 @@ class BaseFemag(object):
|
|
212
220
|
self.model = femagtools.model.MachineModel(machine)
|
213
221
|
self.modelname = self.model.name
|
214
222
|
recsin = ''
|
223
|
+
feloss = ''
|
215
224
|
if simulation:
|
216
225
|
recsin = simulation.get('recsin', '')
|
217
|
-
|
226
|
+
feloss = simulation.get('calc_fe_loss', '')
|
227
|
+
self.copy_magnetizing_curves(self.model, recsin=recsin, feloss=feloss)
|
218
228
|
try:
|
219
229
|
if 'wdgdef' in self.model.winding:
|
220
230
|
self.model.winding['wdgfile'] = self.create_wdg_def(
|
@@ -429,12 +439,15 @@ class BaseFemag(object):
|
|
429
439
|
"""
|
430
440
|
Read the modelname from the Femag Log file
|
431
441
|
"""
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
442
|
+
try:
|
443
|
+
with open(os.path.join(self.workdir, 'FEMAG-FSL.log')) as f:
|
444
|
+
for l in f:
|
445
|
+
if l.startswith('New model') or l.startswith('Load model'):
|
446
|
+
return l.split('"')[1]
|
447
|
+
except FileNotFoundError:
|
448
|
+
pass
|
436
449
|
|
437
|
-
|
450
|
+
return list(pathlib.Path(self.workdir).glob('*.PROT'))[0].stem
|
438
451
|
|
439
452
|
def readResult(self, simulation, bch=None):
|
440
453
|
if simulation:
|
@@ -715,55 +728,6 @@ class FemagTask(threading.Thread):
|
|
715
728
|
self.returncode = self.proc.wait()
|
716
729
|
|
717
730
|
|
718
|
-
class SubscriberTask(threading.Thread):
|
719
|
-
def __init__(self, port, host, notify):
|
720
|
-
threading.Thread.__init__(self)
|
721
|
-
context = zmq.Context.instance()
|
722
|
-
self.subscriber = context.socket(zmq.SUB)
|
723
|
-
if not host:
|
724
|
-
host = 'localhost'
|
725
|
-
self.subscriber.connect(f'tcp://{host}:{port}')
|
726
|
-
self.subscriber.setsockopt(zmq.SUBSCRIBE, b'')
|
727
|
-
self.controller = zmq.Context.instance().socket(zmq.PULL)
|
728
|
-
self.controller_url = 'inproc://publisher'
|
729
|
-
self.controller.bind(self.controller_url)
|
730
|
-
self.poller = zmq.Poller()
|
731
|
-
self.poller.register(self.subscriber, zmq.POLLIN)
|
732
|
-
self.poller.register(self.controller, zmq.POLLIN)
|
733
|
-
self.logger = logger
|
734
|
-
self.notify = notify
|
735
|
-
|
736
|
-
def stop(self):
|
737
|
-
socket = zmq.Context.instance().socket(zmq.PUSH)
|
738
|
-
socket.connect(self.controller_url)
|
739
|
-
socket.send(b"quit")
|
740
|
-
socket.close()
|
741
|
-
|
742
|
-
def run(self):
|
743
|
-
self.logger.info("subscriber is ready")
|
744
|
-
while True:
|
745
|
-
socks = dict(self.poller.poll())
|
746
|
-
if socks.get(self.subscriber) == zmq.POLLIN:
|
747
|
-
try:
|
748
|
-
response = self.subscriber.recv_multipart()
|
749
|
-
# Sometimes femag send messages with only len = 1. These messages must be ignored
|
750
|
-
if len(response) < 2:
|
751
|
-
continue
|
752
|
-
self.notify([s.decode('latin1') for s in response])
|
753
|
-
|
754
|
-
except Exception:
|
755
|
-
self.logger.error(
|
756
|
-
"error in subscription message processing", exc_info=True)
|
757
|
-
|
758
|
-
if socks.get(self.controller) == zmq.POLLIN:
|
759
|
-
req = self.controller.recv()
|
760
|
-
self.logger.info("subscriber %s", req)
|
761
|
-
break
|
762
|
-
self.subscriber.close()
|
763
|
-
self.controller.close()
|
764
|
-
self.logger.debug("subscriber stopped")
|
765
|
-
|
766
|
-
|
767
731
|
class ZmqFemag(BaseFemag):
|
768
732
|
"""Invoke and control execution of FEMAG with ZeroMQ
|
769
733
|
|
@@ -838,8 +802,8 @@ class ZmqFemag(BaseFemag):
|
|
838
802
|
"""attaches a notify function"""
|
839
803
|
logger.info("Subscribe on '%s' port %d", self.femaghost, self.port+1)
|
840
804
|
if self.subscriber is None:
|
841
|
-
self.subscriber = SubscriberTask(
|
842
|
-
self.port+1, self.femaghost, notify)
|
805
|
+
self.subscriber = femagtools.zmq.SubscriberTask(
|
806
|
+
port=self.port+1, host=self.femaghost, notify=notify)
|
843
807
|
self.subscriber.start()
|
844
808
|
else:
|
845
809
|
# reattach?
|
@@ -1149,6 +1113,38 @@ class ZmqFemag(BaseFemag):
|
|
1149
1113
|
logger.warning(response[0])
|
1150
1114
|
return [s.decode('latin1') for s in response]
|
1151
1115
|
|
1116
|
+
def airgap_flux_density(self, pmod):
|
1117
|
+
# try to read bag.dat
|
1118
|
+
agr = self.getfile("bag.dat")
|
1119
|
+
status = json.loads(agr[0])['status']
|
1120
|
+
if status == 'ok':
|
1121
|
+
datfile = os.path.join(self.workdir, 'bag.dat')
|
1122
|
+
with open(datfile, 'wb') as bagfile:
|
1123
|
+
bagfile.write(agr[1])
|
1124
|
+
agi = ag.read(datfile, pmod)
|
1125
|
+
else:
|
1126
|
+
import numpy as np
|
1127
|
+
# try to read model file (TODO download with getfile)
|
1128
|
+
nc = self.read_nc()
|
1129
|
+
ag_elmnts = nc.airgap_center_elements
|
1130
|
+
logger.info("Airgap elements %d scale_factor %f",
|
1131
|
+
len(ag_elmnts), nc.scale_factor())
|
1132
|
+
if len(ag_elmnts) < 1:
|
1133
|
+
raise ValueError("Missing airgap elements")
|
1134
|
+
scf = 360/nc.scale_factor()/ag_elmnts[-1].center[0]
|
1135
|
+
pos = np.array([e.center[0]*scf for e in ag_elmnts])
|
1136
|
+
bxy = np.array([e.flux_density() for e in ag_elmnts]).T
|
1137
|
+
if np.max(bxy[0]) > np.max(bxy[1]):
|
1138
|
+
agi = ag.fft(pos, bxy[0], pmod)
|
1139
|
+
else:
|
1140
|
+
agi = ag.fft(pos, bxy[1], pmod)
|
1141
|
+
return dict(Bamp=agi['Bamp'],
|
1142
|
+
phi0=agi['phi0'],
|
1143
|
+
angle=agi['pos'],
|
1144
|
+
angle_fft=agi['pos'],
|
1145
|
+
B=agi['B'],
|
1146
|
+
B_fft=agi['B_fft'])
|
1147
|
+
|
1152
1148
|
def interrupt(self):
|
1153
1149
|
"""send push message to control port to stop current calculation"""
|
1154
1150
|
context = zmq.Context.instance()
|
@@ -1165,7 +1161,7 @@ class ZmqFemag(BaseFemag):
|
|
1165
1161
|
wdg.write(name, self.workdir)
|
1166
1162
|
self.upload(os.path.join(self.workdir, name+'.WID'))
|
1167
1163
|
|
1168
|
-
def copy_magnetizing_curves(self, model, dir=None, recsin=''):
|
1164
|
+
def copy_magnetizing_curves(self, model, dir=None, recsin='', feloss=''):
|
1169
1165
|
"""extract mc names from model and write files into workdir or dir if given
|
1170
1166
|
and upload to Femag
|
1171
1167
|
|
@@ -1175,9 +1171,16 @@ class ZmqFemag(BaseFemag):
|
|
1175
1171
|
dest = dir if dir else self.workdir
|
1176
1172
|
mc_names = [m for m in model.set_magcurves(
|
1177
1173
|
self.magnetizingCurves, self.magnets)]
|
1174
|
+
if isinstance(feloss, (int, float)):
|
1175
|
+
try:
|
1176
|
+
feloss = {1: 'jordan', 11: 'bertotti'}[int(feloss)]
|
1177
|
+
except KeyError:
|
1178
|
+
feloss = ''
|
1178
1179
|
for m in mc_names:
|
1179
1180
|
f = self.magnetizingCurves.writefile(m[0], dest,
|
1180
|
-
fillfac=m[1],
|
1181
|
+
fillfac=m[1],
|
1182
|
+
recsin=recsin,
|
1183
|
+
feloss=feloss)
|
1181
1184
|
self.upload(os.path.join(dest, f))
|
1182
1185
|
return mc_names
|
1183
1186
|
|
femagtools/fsl.py
CHANGED
@@ -158,13 +158,13 @@ class Builder:
|
|
158
158
|
if templ == 'statorFsl':
|
159
159
|
# obsolete
|
160
160
|
th_props = [' ']
|
161
|
-
try:
|
161
|
+
try:
|
162
162
|
th_props = [f'stator_density = {model.stator["density"]}',
|
163
163
|
f'stator_thcond = {model.stator["thcond"]}',
|
164
164
|
f'stator_thcap = {model.stator["thcap"]}',
|
165
165
|
]
|
166
|
-
except:
|
167
|
-
pass
|
166
|
+
except:
|
167
|
+
pass
|
168
168
|
if 'parameter' in model.stator['statorFsl']:
|
169
169
|
return self.render_template(
|
170
170
|
model.stator['statorFsl']['content_template'],
|
@@ -226,20 +226,20 @@ class Builder:
|
|
226
226
|
self.fsl_rotor = True
|
227
227
|
# obsolete
|
228
228
|
th_props = [' ']
|
229
|
-
try:
|
229
|
+
try:
|
230
230
|
logger.info(model.magnet)
|
231
231
|
th_props = [f'rotor_density = {model["magnet"]["density"]}',
|
232
232
|
f'rotor_thcond = {model["magnet"]["thcond"]}',
|
233
233
|
f'rotor_thcap = {model["magnet"]["thcap"]}'
|
234
234
|
]
|
235
|
-
except:
|
236
|
-
pass
|
235
|
+
except:
|
236
|
+
pass
|
237
237
|
if 'parameter' in model.magnet['magnetFsl']:
|
238
238
|
return mcv + self.render_template(
|
239
239
|
model.magnet['magnetFsl']['content_template'],
|
240
240
|
model.magnet['magnetFsl']['parameter']) + th_props
|
241
241
|
elif model.magnet['magnetFsl'].get('content'):
|
242
|
-
return mcv + model.magnet['magnetFsl']['content'].split('\n')
|
242
|
+
return mcv + model.magnet['magnetFsl']['content'].split('\n')
|
243
243
|
if isinstance(model.magnet['magnetFsl']
|
244
244
|
['content_template'], str):
|
245
245
|
with open(model.magnet['magnetFsl']
|
@@ -248,7 +248,7 @@ class Builder:
|
|
248
248
|
else:
|
249
249
|
templ = model.magnet['magnetFsl']['content_template']
|
250
250
|
return mcv + self.render_template(
|
251
|
-
'\n'.join(templ),
|
251
|
+
'\n'.join(templ),
|
252
252
|
model.magnet['magnetFsl'])
|
253
253
|
|
254
254
|
templ = model.magnettype()
|
@@ -364,8 +364,8 @@ class Builder:
|
|
364
364
|
"""return connect_model if rotating machine and incomplete model
|
365
365
|
(Note: femag bug with connect model)"
|
366
366
|
"""
|
367
|
-
if (model.
|
368
|
-
model.
|
367
|
+
if (model.connect_full or (
|
368
|
+
model.get('move_action', 0) == 0 and
|
369
369
|
model.stator['num_slots'] > model.stator['num_slots_gen'])):
|
370
370
|
fslcmds = ['pre_models("connect_models")\n']
|
371
371
|
if 'thcond' in model.stator:
|
@@ -556,7 +556,7 @@ class Builder:
|
|
556
556
|
if 'fsl_rotor' in conv:
|
557
557
|
self.fsl_rotor = True
|
558
558
|
th_props = ['']
|
559
|
-
logger.
|
559
|
+
logger.debug(model['magnet'])
|
560
560
|
if hasattr(model, 'magnet'):
|
561
561
|
if model['magnet'].get('thcond', 0):
|
562
562
|
th_props = [f'rotor_density = {model["magnet"]["density"]}',
|
@@ -730,7 +730,7 @@ class Builder:
|
|
730
730
|
custom_fefunc = ['']
|
731
731
|
if pfefunc:
|
732
732
|
sim['loss_funct'] = 1 # 3?
|
733
|
-
if pfefunc
|
733
|
+
if pfefunc in ('bertotti', 'modified_steinmetz'):
|
734
734
|
custom_fefunc = self.__render(sim['PVFE_FSL'], pfefunc)
|
735
735
|
else:
|
736
736
|
custom_fefunc = pfefunc.split('\n')
|
@@ -750,6 +750,9 @@ class Builder:
|
|
750
750
|
revert_displ = self.create_displ_stator_rotor(
|
751
751
|
sim['eccentricity'])
|
752
752
|
|
753
|
+
if sim.get('calculationMode') == 'pm_sym_f_cur':
|
754
|
+
if 'nload_ex_cur' in sim.keys(): # convert obsolete key
|
755
|
+
sim['noload_ex_cur'] = sim.pop('nload_ex_cur')
|
753
756
|
felosses = custom_fefunc + self.create_fe_losses(sim)
|
754
757
|
fslcalc = (displ_stator_rotor
|
755
758
|
+ self.__render(sim, sim.get('calculationMode'))
|
femagtools/isa7.py
CHANGED
@@ -815,8 +815,9 @@ class Isa7(object):
|
|
815
815
|
airgap_positions.append(axy[0][0])
|
816
816
|
self.airgap_center_elements.append(se.elements)
|
817
817
|
|
818
|
-
if
|
819
|
-
|
818
|
+
if self.airgap_center_elements:
|
819
|
+
# TODO check airgap center
|
820
|
+
self.airgap_center_elements = self.airgap_center_elements[-1]
|
820
821
|
if horiz:
|
821
822
|
airgap_positions.append(np.min(nxy[:, 1]))
|
822
823
|
else:
|
femagtools/machine/__init__.py
CHANGED
@@ -68,8 +68,9 @@ def create_from_eecpars(temp, eecpars, lfe=1, wdg=1):
|
|
68
68
|
psid = rwdg*rlfe*dqp['psid']
|
69
69
|
psiq = rwdg*rlfe*dqp['psiq']
|
70
70
|
losses = __scale_losses(dqp['losses'], rlfe)
|
71
|
-
losses['ef'] = dqpars[-1]['losses']
|
72
|
-
losses['
|
71
|
+
losses['ef'] = dqpars[-1]['losses'].get('ef', [2.0, 2.0])
|
72
|
+
losses['hf'] = dqpars[-1]['losses'].get('hf', [1.0, 1.0])
|
73
|
+
# TODO handle bertotti excess loss factor
|
73
74
|
|
74
75
|
if 'psidq' in eecpars:
|
75
76
|
machine = PmRelMachinePsidq(
|
@@ -140,7 +141,7 @@ def create(bch, r1, ls, lfe=1, wdg=1):
|
|
140
141
|
try:
|
141
142
|
losses = __scale_losses(bch.psidq['losses'], rlfe)
|
142
143
|
losses['ef'] = bch.lossPar.get('ef', [2.0, 2.0])
|
143
|
-
losses['
|
144
|
+
losses['hf'] = bch.lossPar.get('hf', [1.0, 1.0])
|
144
145
|
except KeyError:
|
145
146
|
losses = {}
|
146
147
|
if 'ex_current' in bch.machine:
|
@@ -162,7 +163,7 @@ def create(bch, r1, ls, lfe=1, wdg=1):
|
|
162
163
|
try:
|
163
164
|
losses = __scale_losses(bch.ldq['losses'], rlfe)
|
164
165
|
losses['ef'] = bch.lossPar.get('ef', [2.0, 2.0])
|
165
|
-
losses['
|
166
|
+
losses['hf'] = bch.lossPar.get('hf', [1.0, 1.0])
|
166
167
|
except KeyError:
|
167
168
|
losses = {}
|
168
169
|
if 'ex_current' in bch.machine:
|
femagtools/machine/afpm.py
CHANGED
@@ -25,6 +25,30 @@ AFM_TYPES = (
|
|
25
25
|
"S2R1_all" # 2 stator, 1 rotor, all simulated
|
26
26
|
)
|
27
27
|
|
28
|
+
def num_agnodes(Q, p, pw, ag):
|
29
|
+
"""return total number of nodes in airgap per pole
|
30
|
+
Args:
|
31
|
+
Q: (int) number of slots
|
32
|
+
p: (int) number of pole pairs
|
33
|
+
pw: (float) pole width
|
34
|
+
ag: (float) airgap height
|
35
|
+
"""
|
36
|
+
num_nodes = np.arange(6, 120, 6)
|
37
|
+
i = np.argmin(np.abs(pw/num_nodes - ag/2))
|
38
|
+
nag = num_nodes[i-1]
|
39
|
+
if p*nag % Q:
|
40
|
+
lcm = np.lcm(Q, 2*p)//p
|
41
|
+
nmin, nmax = 1, num_nodes[-1]//lcm
|
42
|
+
num_nodes = np.array(
|
43
|
+
[i*lcm for i in range(nmin, nmax) if i*lcm % 6 == 0])
|
44
|
+
i = np.argmin(np.abs(pw/num_nodes - ag/2))
|
45
|
+
if i > 0:
|
46
|
+
nag = num_nodes[i-1]
|
47
|
+
else:
|
48
|
+
nag = num_nodes[i]
|
49
|
+
# TODO nodedist 0.5, 2, 4, 6
|
50
|
+
return nag
|
51
|
+
|
28
52
|
def _integrate(radius, pos, val):
|
29
53
|
interp = RegularGridInterpolator((radius, pos), val)
|
30
54
|
def func(x, y):
|
@@ -141,11 +165,8 @@ def parident(workdir, engine, temp, machine,
|
|
141
165
|
|
142
166
|
if "num_agnodes" not in machine:
|
143
167
|
for pw in pole_width:
|
144
|
-
machine['num_agnodes'] =
|
145
|
-
|
146
|
-
#if machine['num_agnodes'] < nper:
|
147
|
-
# machine['num_agnodes'] = 8*round(pw/machine['airgap']/4)
|
148
|
-
|
168
|
+
machine['num_agnodes'] = num_agnodes(Q1, p//2, pw,
|
169
|
+
machine['airgap'])
|
149
170
|
nlparvardef = {
|
150
171
|
"decision_vars": [
|
151
172
|
{"values": pole_width,
|
@@ -165,13 +186,13 @@ def parident(workdir, engine, temp, machine,
|
|
165
186
|
parvardef = {
|
166
187
|
"decision_vars": [
|
167
188
|
# {"values": sorted(2*temp), "name": "magn_temp"},
|
168
|
-
{"steps": num_beta_steps, "bounds": [beta_min, 0],
|
189
|
+
{"steps": num_beta_steps, "bounds": [beta_min, 0],
|
190
|
+
"name": "angl_i_up"},
|
169
191
|
{"steps": num_cur_steps,
|
170
192
|
"bounds": [i1_max/num_cur_steps, i1_max], "name": "current"}
|
171
193
|
]
|
172
194
|
}
|
173
195
|
|
174
|
-
|
175
196
|
ldq = []
|
176
197
|
for magtemp in temp:
|
177
198
|
nlcalc = dict(
|
@@ -189,7 +210,8 @@ def parident(workdir, engine, temp, machine,
|
|
189
210
|
|
190
211
|
nlresults = pstudy(nlparvardef, machine, nlcalc, engine)
|
191
212
|
if nlresults['status'].count('C') != len(nlresults['status']):
|
192
|
-
raise ValueError(
|
213
|
+
raise ValueError(
|
214
|
+
f"Noload simulation failed {nlresults['status']}")
|
193
215
|
else:
|
194
216
|
nlresults = {"x": [], "f": []}
|
195
217
|
i = 0
|
@@ -210,7 +232,7 @@ def parident(workdir, engine, temp, machine,
|
|
210
232
|
nlresults['f'].append({k: v for k, v in r.items()})
|
211
233
|
i = i + 1
|
212
234
|
nlresults.update(process(lfe, pole_width, machine, nlresults['f']))
|
213
|
-
|
235
|
+
weights = nlresults['weights']
|
214
236
|
current_angles = nlresults['f'][0]['current_angles']
|
215
237
|
results = []
|
216
238
|
i = 0
|
@@ -325,7 +347,7 @@ def parident(workdir, engine, temp, machine,
|
|
325
347
|
(-1, num_beta_steps)),
|
326
348
|
axis=1).T.tolist()})
|
327
349
|
ldq.append({'temperature': magtemp,
|
328
|
-
'i1':i1, 'beta':beta,
|
350
|
+
'i1': i1, 'beta': beta,
|
329
351
|
'psid': psid.tolist(), 'psiq': psiq.tolist(),
|
330
352
|
'ld': ld, 'lq': lq,
|
331
353
|
'torque': torque.tolist(),
|
@@ -334,7 +356,8 @@ def parident(workdir, engine, temp, machine,
|
|
334
356
|
#iq, id = femagtools.machine.utils.iqd(*np.meshgrid(beta, i1))
|
335
357
|
|
336
358
|
return {'m': machine[wdgk]['num_phases'],
|
337
|
-
'p': machine['poles']//2,
|
359
|
+
'p': machine['poles']//2, 'weights': weights,
|
360
|
+
'rotor_mass': sum(weights[1]), "kfric_b": 1,
|
338
361
|
'ls1': 0, 'r1': r1, 'ldq': ldq}
|
339
362
|
|
340
363
|
|
@@ -362,7 +385,7 @@ def process(lfe, pole_width, machine, bch):
|
|
362
385
|
num_slots = machine['stator']['num_slots']
|
363
386
|
mmod = model.MachineModel(machine)
|
364
387
|
slots_gen = mmod.stator['num_slots_gen']
|
365
|
-
scale_factor =_get_scale_factor(model_type, num_slots, slots_gen)
|
388
|
+
scale_factor = _get_scale_factor(model_type, num_slots, slots_gen)
|
366
389
|
endpos = [2*pw*1e3 for pw in pole_width]
|
367
390
|
displ = [[d for d in r['linearForce'][0]['displ']
|
368
391
|
if d < e*(1+1/len(r['linearForce'][0]['displ']))]
|
@@ -373,11 +396,14 @@ def process(lfe, pole_width, machine, bch):
|
|
373
396
|
currents = [bch[0]['flux'][k][0]['current_k'][:n]
|
374
397
|
for k in bch[0]['flux']]
|
375
398
|
if len(pole_width) > 1:
|
399
|
+
# check homogenity:
|
400
|
+
if np.diff([len(d) for d in displ]).any():
|
401
|
+
raise ValueError(
|
402
|
+
f"inhomogenous number of steps: {[len(d) for d in displ]}")
|
403
|
+
lfx = [r['linearForce'][0]['force_x'] for r in bch]
|
376
404
|
torque = _integrate(radius, rotpos[0], np.array(
|
377
405
|
[r*scale_factor*np.array(fx[:-1])/l
|
378
|
-
for l, r, fx in zip(lfe, radius,
|
379
|
-
[r['linearForce'][0]['force_x']
|
380
|
-
for r in bch])]))
|
406
|
+
for l, r, fx in zip(lfe, radius, lfx)]))
|
381
407
|
|
382
408
|
voltage = {k: [scale_factor * np.array(ux[:-1])/l
|
383
409
|
for l, ux in zip(lfe, [r['flux'][k][0]['voltage_dpsi']
|
@@ -385,6 +411,13 @@ def process(lfe, pole_width, machine, bch):
|
|
385
411
|
for k in bch[0]['flux']}
|
386
412
|
emf = [_integrate(radius, rotpos[0], np.array(voltage[k]))
|
387
413
|
for k in voltage]
|
414
|
+
|
415
|
+
fluxxy = {k: [scale_factor * np.array(flx[:-1])/l
|
416
|
+
for l, flx in zip(lfe, [r['flux'][k][0]['flux_k']
|
417
|
+
for r in bch])]
|
418
|
+
for k in bch[0]['flux']}
|
419
|
+
flux = [_integrate(radius, rotpos[0], np.array(fluxxy[k]))
|
420
|
+
for k in fluxxy]
|
388
421
|
else:
|
389
422
|
r = radius[0]
|
390
423
|
torque = [r*scale_factor*fx
|
@@ -393,6 +426,7 @@ def process(lfe, pole_width, machine, bch):
|
|
393
426
|
for ux in bch[0]['flux'][k][0]['voltage_dpsi'][:-1]]
|
394
427
|
for k in bch[0]['flux']}
|
395
428
|
emf = [voltage[k][:n] for k in voltage]
|
429
|
+
flux = [fluxxy[k][:n] for k in fluxxy]
|
396
430
|
|
397
431
|
pos = (rotpos[0]/np.pi*180)
|
398
432
|
emffft = utils.fft(pos, emf[0])
|
@@ -442,10 +476,20 @@ def process(lfe, pole_width, machine, bch):
|
|
442
476
|
mmod.outer_diam, mmod.inner_diam)
|
443
477
|
i1 = np.mean([np.max(c) for c in currents])/np.sqrt(2)
|
444
478
|
plcu = mmod.winding['num_phases']*i1**2*r1
|
479
|
+
weights = np.array([[0, 0, 0], [0, 0, 0]])
|
480
|
+
try:
|
481
|
+
for b in bch:
|
482
|
+
weights = weights + b['weights']
|
483
|
+
weights *= scale_factor
|
484
|
+
except KeyError as exc:
|
485
|
+
#logger.warning("missing key %s", exc)
|
486
|
+
pass
|
445
487
|
|
446
488
|
return {
|
489
|
+
'weights': weights.tolist(),
|
447
490
|
'pos': pos.tolist(), 'r1': r1,
|
448
491
|
'torque': torque,
|
492
|
+
'flux_k': flux,
|
449
493
|
'emf': emf,
|
450
494
|
'emf_amp': emffft['a'], 'emf_angle': emffft['alfa0'],
|
451
495
|
'freq': freq,
|
@@ -563,10 +607,10 @@ class AFPM:
|
|
563
607
|
condMat: conductor material
|
564
608
|
"""
|
565
609
|
def __init__(self, workdir, magnetizingCurves='.', magnetMat='',
|
566
|
-
condMat=''):
|
610
|
+
condMat='', cmd=None):
|
567
611
|
self.parstudy = parstudy.List(
|
568
612
|
workdir, condMat=condMat, magnets=magnetMat,
|
569
|
-
magnetizingCurves=magnetizingCurves)
|
613
|
+
magnetizingCurves=magnetizingCurves, cmd=cmd)
|
570
614
|
|
571
615
|
def __call__(self, engine, machine, simulation, num_slices):
|
572
616
|
""" run FE simulation
|
@@ -603,14 +647,12 @@ class AFPM:
|
|
603
647
|
for pw in pole_width]
|
604
648
|
|
605
649
|
if "num_agnodes" not in machine:
|
650
|
+
Q1 = machine['stator']['num_slots']
|
651
|
+
p = machine['poles']
|
606
652
|
for pw in pole_width:
|
607
|
-
machine['num_agnodes'] =
|
608
|
-
|
609
|
-
|
610
|
-
#nper = np.lcm(Q, p)
|
611
|
-
#if machine['num_agnodes'] < nper:
|
612
|
-
# machine['num_agnodes'] = 8*round(pw/machine['airgap']/4)
|
613
|
-
|
653
|
+
machine['num_agnodes'] = num_agnodes(Q1, p//2, pw,
|
654
|
+
machine['airgap'])
|
655
|
+
logger.info("Num agnodes/pole %d", machine['num_agnodes'])
|
614
656
|
parvardef = {
|
615
657
|
"decision_vars": [
|
616
658
|
{"values": pole_width,
|
@@ -622,21 +664,24 @@ class AFPM:
|
|
622
664
|
}
|
623
665
|
machine['pole_width'] = np.pi * machine['inner_diam']/machine['poles']
|
624
666
|
machine['lfe'] = machine['outer_diam'] - machine['inner_diam']
|
625
|
-
|
667
|
+
simulation['skew_displ'] = (simulation.get('skew_angle', 0)/180 * np.pi
|
668
|
+
* machine['inner_diam'])
|
626
669
|
nlresults = {}
|
627
|
-
if (simulation['calculationMode'] != 'cogg_calc'
|
628
|
-
'poc' not in simulation):
|
670
|
+
if (simulation['calculationMode'] != 'cogg_calc'
|
671
|
+
and 'poc' not in simulation):
|
629
672
|
nlcalc = dict(
|
630
673
|
calculationMode="cogg_calc",
|
631
674
|
magn_temp=simulation.get('magn_temp', 20),
|
632
675
|
num_move_steps=60,
|
676
|
+
skew_linear=0,
|
677
|
+
skew_steps=0,
|
633
678
|
poc=poc.Poc(machine['pole_width']),
|
634
679
|
speed=0)
|
635
680
|
logging.info("Noload simulation")
|
636
681
|
nlresults = self.parstudy(parvardef,
|
637
682
|
machine, nlcalc, engine)
|
638
683
|
if nlresults['status'].count('C') != len(nlresults['status']):
|
639
|
-
raise ValueError('Noload simulation failed
|
684
|
+
raise ValueError(f'Noload simulation failed {nlresults["status"]}')
|
640
685
|
nlresults.update(process(lfe, pole_width, machine, nlresults['f']))
|
641
686
|
|
642
687
|
current_angles = nlresults['f'][0]['current_angles']
|
@@ -645,14 +690,15 @@ class AFPM:
|
|
645
690
|
parameters={
|
646
691
|
'phi_voltage_winding': current_angles})
|
647
692
|
logger.info("Current angles: %s", current_angles)
|
648
|
-
elif (simulation['calculationMode'] == 'cogg_calc'
|
649
|
-
'poc' not in simulation):
|
693
|
+
elif (simulation['calculationMode'] == 'cogg_calc'
|
694
|
+
and 'poc' not in simulation):
|
650
695
|
simulation['poc'] = poc.Poc(machine['pole_width'])
|
651
696
|
|
652
697
|
lresults = self.parstudy(
|
653
|
-
parvardef,
|
654
|
-
|
655
|
-
|
698
|
+
parvardef, machine, simulation, engine)
|
699
|
+
if lresults['status'].count('C') != len(lresults['status']):
|
700
|
+
raise ValueError(
|
701
|
+
f'{simulation["calculationMode"]} failed {lresults["status"]}')
|
656
702
|
|
657
703
|
results = process(lfe, pole_width, machine, lresults['f'])
|
658
704
|
if nlresults:
|