femagtools 1.8.1__py3-none-any.whl → 1.8.3__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/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:
|