femagtools 1.6.7__py3-none-any.whl → 1.7.0__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.
Files changed (50) hide show
  1. femagtools/__init__.py +2 -2
  2. femagtools/bch.py +1 -1
  3. femagtools/dxfsl/area.py +334 -332
  4. femagtools/dxfsl/areabuilder.py +131 -10
  5. femagtools/dxfsl/conv.py +27 -9
  6. femagtools/dxfsl/converter.py +390 -125
  7. femagtools/dxfsl/corner.py +3 -0
  8. femagtools/dxfsl/femparser.py +1 -1
  9. femagtools/dxfsl/fslrenderer.py +290 -246
  10. femagtools/dxfsl/functions.py +4 -2
  11. femagtools/dxfsl/geom.py +1120 -886
  12. femagtools/dxfsl/journal.py +53 -22
  13. femagtools/dxfsl/machine.py +250 -74
  14. femagtools/dxfsl/plotrenderer.py +34 -3
  15. femagtools/dxfsl/shape.py +380 -103
  16. femagtools/dxfsl/symmetry.py +679 -110
  17. femagtools/femag.py +27 -2
  18. femagtools/forcedens.py +65 -40
  19. femagtools/fsl.py +71 -28
  20. femagtools/losscoeffs.py +46 -0
  21. femagtools/machine/effloss.py +8 -1
  22. femagtools/machine/im.py +3 -1
  23. femagtools/machine/pm.py +12 -11
  24. femagtools/machine/sizing.py +14 -11
  25. femagtools/machine/sm.py +114 -33
  26. femagtools/machine/utils.py +38 -34
  27. femagtools/model.py +12 -2
  28. femagtools/moo/population.py +1 -1
  29. femagtools/parstudy.py +17 -1
  30. femagtools/plot/__init__.py +1 -1
  31. femagtools/plot/char.py +24 -7
  32. femagtools/plot/forcedens.py +56 -29
  33. femagtools/plot/mcv.py +4 -1
  34. femagtools/plot/phasor.py +6 -1
  35. femagtools/poc.py +17 -10
  36. femagtools/templates/cogg_calc.mako +7 -1
  37. femagtools/templates/displ_stator_rotor.mako +33 -0
  38. femagtools/templates/fieldcalc.mako +10 -16
  39. femagtools/templates/pm_sym_f_cur.mako +1 -1
  40. femagtools/tks.py +3 -9
  41. {femagtools-1.6.7.dist-info → femagtools-1.7.0.dist-info}/LICENSE +1 -0
  42. {femagtools-1.6.7.dist-info → femagtools-1.7.0.dist-info}/METADATA +7 -4
  43. {femagtools-1.6.7.dist-info → femagtools-1.7.0.dist-info}/RECORD +50 -49
  44. tests/engines/__init__.py +0 -20
  45. tests/geom/__init__.py +0 -20
  46. tests/moo/__init__.py +0 -20
  47. tests/test_model.py +8 -1
  48. {femagtools-1.6.7.dist-info → femagtools-1.7.0.dist-info}/WHEEL +0 -0
  49. {femagtools-1.6.7.dist-info → femagtools-1.7.0.dist-info}/entry_points.txt +0 -0
  50. {femagtools-1.6.7.dist-info → femagtools-1.7.0.dist-info}/top_level.txt +0 -0
femagtools/femag.py CHANGED
@@ -26,6 +26,7 @@ import femagtools.airgap as ag
26
26
  import femagtools.fsl
27
27
  import femagtools.config
28
28
  import femagtools.ecloss
29
+ import femagtools.forcedens
29
30
 
30
31
  from femagtools import ntib
31
32
 
@@ -248,6 +249,9 @@ class BaseFemag(object):
248
249
  def get_bch_file(self, modelname, offset=0):
249
250
  return self.get_result_file(modelname, 'B(AT)?CH', offset)
250
251
 
252
+ def get_plt_file(self, modelname, offset=0):
253
+ return self.get_result_file(modelname, 'PLT0', offset)
254
+
251
255
  def get_asm_file(self, modelname, offset=0):
252
256
  return self.get_result_file(modelname, 'ASM', offset)
253
257
 
@@ -317,6 +321,17 @@ class BaseFemag(object):
317
321
  result.read(f)
318
322
  return result
319
323
 
324
+ def read_forcedens(self, modelname=None, offset=0):
325
+ "read most recent PLT0 file and return result"
326
+ if not modelname:
327
+ modelname = self._get_modelname_from_log()
328
+
329
+ pltfile = self.get_plt_file(modelname, offset)
330
+ if pltfile:
331
+ logger.info("Read PLT0 {}".format(pltfile))
332
+ return femagtools.forcedens.read(pltfile)
333
+ return None
334
+
320
335
  def read_isa(self, modelname=None):
321
336
  "read most recent I7/ISA7 file and return result"
322
337
  if not modelname:
@@ -555,6 +570,13 @@ class Femag(BaseFemag):
555
570
  simulation)))
556
571
  if simulation:
557
572
  stateofproblem = simulation.get('stateofproblem', 'mag_static')
573
+ if 'eccentricity' in simulation:
574
+ #assert (self.model.stator['num_slots']
575
+ # == self.model.stator['num_slots_gen'])
576
+ for k in ('bore_diam', 'airgap'):
577
+ if k not in simulation['eccentricity']:
578
+ simulation['eccentricity'][k] = self.model.get(k)
579
+
558
580
  if 'poc' in simulation:
559
581
  with open(os.path.join(self.workdir,
560
582
  simulation['pocfilename']), 'w') as f:
@@ -579,8 +601,9 @@ class Femag(BaseFemag):
579
601
  self.run(fslfile, options, fsl_args, stateofproblem=stateofproblem)
580
602
  if simulation:
581
603
  return self.readResult(simulation)
582
- return dict(status='ok', message=self.modelname)
583
604
 
605
+ return {'status': 'ok', 'message': self.modelname,
606
+ 'model': self.model.props()}
584
607
 
585
608
  class FemagTask(threading.Thread):
586
609
  def __init__(self, port, args, workdir, logdir):
@@ -1096,7 +1119,9 @@ class ZmqFemag(BaseFemag):
1096
1119
  raise FemagError(r['message'])
1097
1120
 
1098
1121
  if not simulation:
1099
- return [r, dict(name=machine['name'])]
1122
+ model = self.model.props()
1123
+ model['name'] = machine['name']
1124
+ return [r, model]
1100
1125
 
1101
1126
  result_file = r['result_file'][0]
1102
1127
  if simulation['calculationMode'] == "pm_sym_loss":
femagtools/forcedens.py CHANGED
@@ -51,6 +51,22 @@ def _readSections(f):
51
51
  yield section
52
52
 
53
53
 
54
+ def _complete(positions):
55
+ """return FN and FT Matrix completed to full model"""
56
+ ntiles = int(360/positions[0]['X'][-1])
57
+ try:
58
+ FN = np.tile(
59
+ np.array([p['FN'][:-1] for p in positions[:-1]]), (1, ntiles))
60
+ FT = np.tile(
61
+ np.array([p['FT'][:-1] for p in positions[:-1]]), (1, ntiles))
62
+ except AttributeError:
63
+ return [[], []]
64
+
65
+ rotangle = positions[-1]['position'] - positions[0]['position']
66
+ mtiles = int(round(360/rotangle))
67
+ return np.tile(FN, (mtiles, 1)).T, np.tile(FT, (mtiles, 1)).T
68
+
69
+
54
70
  class ForceDensity(object):
55
71
 
56
72
  def __init__(self):
@@ -109,7 +125,9 @@ class ForceDensity(object):
109
125
  rec = l.split()[1:]
110
126
  if len(rec) == len(labels):
111
127
  m.append([float(x) for x in rec])
112
- d.update({k: v for k, v in zip(labels, list(zip(*m)))})
128
+ logging.debug("POSITION %s len %d: %s", d['position'],
129
+ len(m), ','.join(labels))
130
+ d.update(dict(zip(labels, list(zip(*m)))))
113
131
 
114
132
  self.positions.append(d)
115
133
 
@@ -129,36 +147,33 @@ class ForceDensity(object):
129
147
  self.__read_date_and_title(s)
130
148
  elif s[0].startswith('POSITION'):
131
149
  self.__read_position(s)
132
-
133
- def _prepare(self):
134
- """return FN and FT Matrix"""
135
- ntiles = int(360/self.positions[0]['X'][-1])
136
- try:
137
- FN = np.tile(
138
- np.array([p['FN'][:-1] for p in self.positions[:-1]]).T,
139
- (ntiles, 1))
140
- FT = np.tile(
141
- np.array([p['FT'][:-1] for p in self.positions[:-1]]).T,
142
- (ntiles, 1))
143
- except AttributeError:
144
- return []
145
- return [FN, FT]
150
+ pos = [p['position'] for p in self.positions]
151
+ labels = self.positions[0].keys()
152
+ fn = [p['FN'] for p in self.positions]
153
+ fnmax = max([max(x) for x in fn])
154
+ fnmin = min([min(x) for x in fn])
155
+ flen = set([len(x) for x in fn])
156
+ if len(flen) > 1:
157
+ logging.warning("inhomogenous samples: %s", flen)
158
+ logging.info("%s shape %s: pos %s, FN %s %s",
159
+ filename, (len(pos), min(list(flen))),
160
+ (min(pos), max(pos)),
161
+ (fnmin, fnmax),
162
+ ','.join(labels))
163
+ self.FN, self.FT = _complete(self.positions)
146
164
 
147
165
  def fft(self):
148
166
  """return 2D FFT of the Force Density"""
149
- fdens = self._prepare()
150
- FN, FT= fdens[0], fdens[1]
151
- N = FN.size
152
- dim = FN.shape[0]//2
167
+ N = self.FN.size
168
+ dim = self.FN.shape[0]//2
153
169
  # Dimension [Position °] X [Time Steps s]
154
- FN_MAG = np.fft.fft2(FN)[0:dim, :]/N
155
- FT_MAG = np.fft.fft2(FT)[0:dim, :]/N
170
+ FN_MAG = np.fft.fft2(self.FN)[0:dim, :]/N
171
+ FT_MAG = np.fft.fft2(self.FT)[0:dim, :]/N
156
172
 
157
- return dict(fn_harm=dict(amplitude=np.abs(FN_MAG),
158
- phase=np.angle(FN_MAG)),
159
- ft_harm=dict(amplitude=np.abs(FT_MAG),
160
- phase=np.angle(FT_MAG))
161
- )
173
+ return {'fn_harm': {'amplitude': np.abs(FN_MAG),
174
+ 'phase': np.angle(FN_MAG)},
175
+ 'ft_harm': {'amplitude': np.abs(FT_MAG),
176
+ 'phase': np.angle(FT_MAG)}}
162
177
 
163
178
  def items(self):
164
179
  return [(k, getattr(self, k)) for k in ('version',
@@ -210,9 +225,13 @@ def readall(workdir='.'):
210
225
 
211
226
 
212
227
  if __name__ == "__main__":
213
- import matplotlib.pyplot as pl
228
+ import matplotlib.pyplot as plt
214
229
  import femagtools.plot
215
230
  import sys
231
+
232
+ logging.basicConfig(level=logging.INFO,
233
+ format='%(asctime)s %(message)s')
234
+
216
235
  if len(sys.argv) == 2:
217
236
  filename = sys.argv[1]
218
237
  else:
@@ -221,16 +240,22 @@ if __name__ == "__main__":
221
240
  fdens = read(filename)
222
241
 
223
242
  # Show the results
224
- print(fdens.version)
225
-
226
- title = '{}, Rotor position {}'.format(
227
- fdens.title, fdens.positions[0]['position'])
228
- pos = fdens.positions[0]['X']
229
- FT_FN = (fdens.positions[0]['FT'],
230
- fdens.positions[0]['FN'])
231
- femagtools.plot.forcedens(title, pos, FT_FN)
232
- pl.show()
233
-
234
- title = 'Force Density Harmonics'
235
- femagtools.plot.forcedens_fft(title, fdens)
236
- pl.show()
243
+ #print(fdens.version)
244
+
245
+ #title = '{}, Rotor position {}'.format(
246
+ # fdens.title, fdens.positions[0]['position'])
247
+ #pos = fdens.positions[0]['X']
248
+ #FT_FN = (fdens.positions[0]['FT'],
249
+ # fdens.positions[0]['FN'])
250
+ #femagtools.plot.forcedens(title, pos, FT_FN)
251
+ #pl.show()
252
+
253
+ #femagtools.plot.forcedens_surface(fdens)
254
+ #pl.show()
255
+
256
+ title = 'Radial Force Density'
257
+ fig, ax = plt.subplots()
258
+ fig.suptitle(title)
259
+ femagtools.plot.forcedens_contour(fdens, ax=ax)
260
+ plt.tight_layout()
261
+ plt.show()
femagtools/fsl.py CHANGED
@@ -7,7 +7,7 @@ import mako.lookup
7
7
  import os
8
8
  import re
9
9
  import sys
10
- import math
10
+ import numpy as np
11
11
  import femagtools.windings
12
12
  from femagtools.poc import Poc
13
13
  from . import __version__
@@ -73,7 +73,7 @@ class Builder:
73
73
 
74
74
  g = femagtools.gmsh.Gmsh(model.stator['mshfile']['name'])
75
75
  phi = g.get_section_angles()
76
- model.stator['num_slots'] = round(math.pi/(phi[1]-phi[0]))
76
+ model.stator['num_slots'] = round(np.pi/(phi[1]-phi[0]))
77
77
  r = g.get_section_radius()
78
78
  model.set_value('outer_diam', 2*r[1])
79
79
  model.set_value('bore_diam', 2*r[0])
@@ -92,7 +92,7 @@ class Builder:
92
92
  self.fsl_stator = True
93
93
  return
94
94
 
95
- if templ == 'statorFsl' or templ == 'dxf':
95
+ if templ in ('statorFsl', 'dxf'):
96
96
  self.fsl_stator = True
97
97
 
98
98
  if templ != 'dxffile':
@@ -111,10 +111,13 @@ class Builder:
111
111
  conv = convert(model.stator['dxffile']['name'], **params)
112
112
 
113
113
  model.stator['num_slots'] = conv.get('tot_num_slot')
114
+ model.set_value('poles', conv.get('num_poles'))
114
115
  self.set_diameter_parameter(model, conv)
115
116
  if model.get('dy1'):
116
117
  model.set_value('outer_diam', model.get('dy1'))
117
118
  model.set_value('bore_diam', model.get('da1'))
119
+ if 'agndst' in conv:
120
+ model.set_value('agndst', model.get('agndst'))
118
121
 
119
122
  model.stator['dxf'] = dict(fsl=conv['fsl'])
120
123
  self.fsl_stator = True
@@ -126,6 +129,8 @@ class Builder:
126
129
  model.set_value(v, conv[v])
127
130
  except KeyError:
128
131
  pass
132
+ if conv.keys() >= {'da1', 'da2'}:
133
+ model.set_value('airgap', abs(model.da1 - model.da2)/2)
129
134
 
130
135
  def prepare_diameter(self, model):
131
136
  dy1 = model.get('dy1', 0.0)
@@ -144,10 +149,11 @@ class Builder:
144
149
  def render_stator(self, model):
145
150
  templ = model.statortype()
146
151
  if templ == 'dxf':
147
- return [
148
- 'agndst = {}'.format(model.agndst*1e3),
149
- 'ndt(agndst)'
150
- ] + model.stator['dxf']['fsl']
152
+ agndst = []
153
+ if model.get('agndst', 0) > 0:
154
+ agndst = ['agndst = {}'.format(model.agndst*1e3)]
155
+ return (agndst + ['ndt(agndst)']
156
+ + model.stator['dxf']['fsl'])
151
157
  if templ == 'statorFsl':
152
158
  # obsolete
153
159
  if 'parameter' in model.stator['statorFsl']:
@@ -190,8 +196,8 @@ class Builder:
190
196
  if fslcode:
191
197
  if self.fsl_stator:
192
198
  return (['agndst = {}'.format(model.agndst*1e3),
193
- 'alfa = 2*math.pi*m.num_sl_gen/m.tot_num_slot',
194
- 'num_agnodes = math.floor(m.fc_radius*alfa/agndst + 0.5)'] +
199
+ 'alfa = 2*np.pi*m.num_sl_gen/m.tot_num_slot',
200
+ 'num_agnodes = np.floor(m.fc_radius*alfa/agndst + 0.5)'] +
195
201
  fslcode)
196
202
  if hasattr(model, 'num_agnodes'):
197
203
  return fslcode
@@ -276,7 +282,7 @@ class Builder:
276
282
  g = femagtools.gmsh.Gmsh(model.magnet['mshfile']['name'])
277
283
  r = g.get_section_radius()
278
284
  phi = g.get_section_angles()
279
- p = round(math.pi/(phi[1]-phi[0]))
285
+ p = round(np.pi/(phi[1]-phi[0]))
280
286
  model.set_value('poles', p)
281
287
  model.set_value('inner_diam', 2*r[0])
282
288
  ag = (model.get('bore_diam') - 2*r[1])/2
@@ -298,7 +304,7 @@ class Builder:
298
304
  return
299
305
 
300
306
  if templ == 'dxf':
301
- # reuse dxfsl model
307
+ # reuse dxfsl model
302
308
  self.fsl_magnet = True
303
309
  if templ != 'dxffile':
304
310
  return
@@ -330,7 +336,7 @@ class Builder:
330
336
  if fslcode:
331
337
  return fslcode
332
338
 
333
- logger.error('File {}.fsl not found'.format(templ))
339
+ logger.error('File %s.fsl not found', templ)
334
340
  return []
335
341
 
336
342
  def create_connect_models(self, model):
@@ -346,7 +352,7 @@ class Builder:
346
352
  '-- thermal properties in airgap',
347
353
  'ag_cond = 0.063',
348
354
  'thcap = 1007',
349
- 'beta = math.pi*m.npols_gen/m.num_poles + m.zeroangl/180*math.pi',
355
+ 'beta = np.pi*m.npols_gen/m.num_poles + m.zeroangl/180*np.pi',
350
356
  'xai, yai = pr2c((da1+da2)/4, beta)',
351
357
  'def_mat_therm(xai,yai,"cyan",1.19,ag_cond,thcap,1)',
352
358
  'xai, yai = pr2c((da1+da2)/4-ag/4, beta)',
@@ -434,13 +440,11 @@ class Builder:
434
440
  logger.info("Leakage type %s", k)
435
441
  if 'wiredia' not in model.winding[k[0]]:
436
442
  if 'wire_gauge' in model.winding:
437
- import numpy as np
438
443
  d = 2*np.sqrt(model.winding['wire_gauge']/np.pi)
439
444
  model.winding[k[0]]['wiredia'] = d
440
445
  elif 'dia_wire' in model.winding:
441
446
  model.winding[k[0]]['wiredia'] = model.winding['dia_wire']
442
447
  elif 'wire_width' in model.winding:
443
- import numpy as np
444
448
  w = model.winding['wire_width']
445
449
  h = model.winding['wire_height']
446
450
  d = 2*np.sqrt(h*w/np.pi)
@@ -462,10 +466,10 @@ class Builder:
462
466
  logger.error('Name of dxf-file expected')
463
467
  return []
464
468
 
465
- if dxfname.split('.')[-1] not in ('dxf'):
469
+ if dxfname.split('.')[-1] not in ('dxf', 'svg'): # add svg support
466
470
  dxfname += '.dxf'
467
471
  if not os.path.isfile(dxfname):
468
- logger.error('File {} not found'.format(dxfname))
472
+ logger.error('File "%s" not found', dxfname)
469
473
  raise ValueError(f'File {dxfname} not found')
470
474
 
471
475
  params = {}
@@ -474,7 +478,7 @@ class Builder:
474
478
  params['write_fsl'] = True
475
479
  params['airgap'] = model.dxffile.get('airgap', 0.0)
476
480
  params['nodedist'] = model.dxffile.get('nodedist', 1)
477
-
481
+ params['full_model'] = model.dxffile.get('full_model', False)
478
482
  conv = convert(dxfname, **params)
479
483
 
480
484
  model.set_value('poles', conv.get('num_poles'))
@@ -482,16 +486,20 @@ class Builder:
482
486
  model.set_value('bore_diam', conv.get('da1') * 1e-3)
483
487
  model.set_value('inner_diam', conv.get('dy2') * 1e-3)
484
488
  model.set_value('airgap', (conv.get('da1') - conv.get('da2'))/2/1e3)
485
- model.set_value('agndst', conv.get('agndst')*1e-3)
489
+ model.set_value('external_rotor', conv.get('external_rotor'))
486
490
 
487
491
  if not hasattr(model, 'stator'):
488
492
  setattr(model, 'stator', {})
489
493
  model.stator['num_slots'] = conv.get('tot_num_slot')
490
- if model.stator.get('num_slots_gen', 0):
491
- if model.stator['num_slots'] % model.stator['num_slots_gen'] > 0:
492
- model.stator['num_slots_gen'] = conv.get('num_sl_gen')
494
+ if params['full_model']:
495
+ model.stator['num_slots_gen'] = model.stator['num_slots']
493
496
  else:
494
- model.stator['num_slots_gen'] = conv.get('num_sl_gen')
497
+ if model.stator.get('num_slots_gen', 0):
498
+ if model.stator['num_slots'] % model.stator['num_slots_gen'] > 0:
499
+ model.stator['num_slots_gen'] = conv.get('num_sl_gen')
500
+ else:
501
+ model.stator['num_slots_gen'] = conv.get('num_sl_gen')
502
+
495
503
  if 'fsl_stator' in conv:
496
504
  self.fsl_stator = True
497
505
  model.stator['dxf'] = dict(fsl=conv['fsl_stator'])
@@ -523,12 +531,27 @@ class Builder:
523
531
  logger.info("create new model '%s'", model.name)
524
532
  if model.is_dxffile():
525
533
  self.prepare_model_with_dxf(model)
534
+ if model.get('num_agnodes', 0) == 0:
535
+ from femagtools.dxfsl.fslrenderer import agndst
536
+ ag = model.get('airgap')
537
+ model.set_value(
538
+ 'agndst',
539
+ agndst(model.get('bore_diam'),
540
+ model.get('bore_diam') - 2*ag,
541
+ model.stator.get('num_slots'),
542
+ model.get('poles'),
543
+ model.stator.get('nodedist') or 1.0))
544
+
545
+ logger.info(" num poles %d num slots %d outer diameter %.4f m",
546
+ model.poles, model.stator['num_slots'],
547
+ model.outer_diam)
548
+
526
549
  else:
527
550
  self.prepare_stator(model)
528
551
  if hasattr(model, 'magnet'):
529
552
  self.prepare_magnet(model)
530
553
  self.prepare_diameter(model)
531
- if self.fsl_stator:
554
+ if self.fsl_stator and model.get('num_agnodes', 0) == 0:
532
555
  from femagtools.dxfsl.fslrenderer import agndst
533
556
  ag = model.get('airgap')
534
557
  model.set_value(
@@ -638,6 +661,11 @@ class Builder:
638
661
  except:
639
662
  return []
640
663
 
664
+ def create_displ_stator_rotor(self, exc):
665
+ if exc.keys() >= {'type', 'bore_diam', 'airgap', 'ecc'}:
666
+ return self.__render(exc, 'displ_stator_rotor')
667
+ return []
668
+
641
669
  def create_analysis(self, sim):
642
670
  pfefunc = sim.get('loss_funct', '')
643
671
  custom_fefunc = ['']
@@ -650,9 +678,23 @@ class Builder:
650
678
 
651
679
  airgap_induc = (self.create_airgap_induc()
652
680
  if sim.get('airgap_induc', 0) else [])
681
+ displ_stator_rotor = self.create_displ_stator_rotor(
682
+ sim.get('eccentricity', {}))
683
+ revert_displ = []
684
+ if displ_stator_rotor:
685
+ sim['eval_force'] = 1
686
+ sim['period_frac'] = 1
687
+ if sim['eccentricity']['type'] == 'static':
688
+ sim['explicit_mode'] = 1
689
+ sim['range_phi'] = 180
690
+ sim['eccentricity']['ecc'] = -sim['eccentricity']['ecc']
691
+ revert_displ = self.create_displ_stator_rotor(
692
+ sim['eccentricity'])
693
+
653
694
  felosses = custom_fefunc + self.create_fe_losses(sim)
654
- fslcalc = (self.__render(sim, sim.get('calculationMode')) +
655
- airgap_induc)
695
+ fslcalc = (displ_stator_rotor
696
+ + self.__render(sim, sim.get('calculationMode'))
697
+ + airgap_induc + revert_displ)
656
698
  '''
657
699
  if pfefunc:
658
700
  sim['loss_funct'] = pfefunc
@@ -687,8 +729,8 @@ class Builder:
687
729
  def create(self, model, sim, magnets=None, condMat=[]):
688
730
  "create model and analysis function"
689
731
  try:
690
- sim['lfe'] = model.get('lfe')
691
732
  num_poles = model.get('poles')
733
+ sim['lfe'] = model.get('lfe')
692
734
  except AttributeError:
693
735
  pass
694
736
  try:
@@ -713,10 +755,11 @@ class Builder:
713
755
  try:
714
756
  poc = Poc(2*360/num_poles)
715
757
  sim['poc'] = poc
716
- sim['pocfilename'] = poc.filename()
758
+ sim['pocfilename'] = poc.filename(model.get('name'))
717
759
  except UnboundLocalError:
718
760
  logger.warning("unknown number of poles")
719
761
  pass
762
+ logger.info("Poc file %s", sim.get('pocfilename', '<undefined>'))
720
763
 
721
764
  if 'phi_start' not in sim:
722
765
  sim['phi_start'] = 0.0
femagtools/losscoeffs.py CHANGED
@@ -83,6 +83,52 @@ def fitjordan(f, B, losses, Bo, fo):
83
83
  return fitp
84
84
 
85
85
 
86
+ def fit_bertotti(f, B, losses):
87
+ """fit coeffs of
88
+ losses(f,B)=(ch*f + ch*f**2)*B**2 + ce*f**1.5*B**1.5
89
+ returns (ch, cw, ce)
90
+
91
+ Args:
92
+ f: list of n strictly increasing frequency values
93
+ B: list of n or list of list of induction values (nxm)
94
+ losses: list of list of fe loss values (nxm)
95
+ """
96
+ i0 = 0
97
+ if np.isclose(f[i0], 0):
98
+ i0 = 1
99
+ v = []
100
+ # collect losses and flux density along the frequency
101
+ for k in range(len(losses[i0])):
102
+ y = []
103
+ bb = []
104
+ if isinstance(B[0], tuple) or isinstance(B[0], list):
105
+ for fx, bx, p in zip(f[i0:], B[i0:], losses[i0:]):
106
+ if k < len(p):
107
+ y.append(p[k]/fx)
108
+ bb.append(bx[k])
109
+ else:
110
+ for fx, p in zip(f[i0:], losses[i0:]):
111
+ if k < len(p):
112
+ y.append(p[k]/fx)
113
+ bb.append(B[k])
114
+ j = len(y)
115
+ if j > 2:
116
+ v.append(np.array((f[i0:j+i0], bb, y)).T.tolist())
117
+
118
+ def wbert(f, b, ch, cw, cx):
119
+ return (ch + cw*f)*b**2 + cx*f**0.5*b**1.5
120
+
121
+ z = np.array([b for a in v for b in a]).T
122
+ fbx = z[0:2]
123
+ y = z[2]
124
+ fitp, _ = so.curve_fit(
125
+ lambda x, ch, cw, ce: wbert(
126
+ x[0], x[1], ch, cw, ce),
127
+ fbx, y,
128
+ bounds=[(0, 0, 0),
129
+ (np.inf, np.inf, np.inf)])
130
+ return fitp
131
+
86
132
  def fit_bertotti0(f, B, losses, generate=False):
87
133
  """fit coeffs of
88
134
  losses(f,B)=(ch*f + ch*f**2)*B**2 + ce*f**1.5*B**1.5
@@ -309,13 +309,20 @@ def efficiency_losses_map(eecpars, u1, T, temp, n, npoints=(60, 40),
309
309
  r['plcu1'].append(m.m*np.abs(i1)**2*m.rstat(w1))
310
310
  r['plcu2'].append(m.m*np.abs(i2)**2*m.rrot(w1-m.p*wm))
311
311
 
312
- if isinstance(m, (PmRelMachine, SynchronousMachine)):
312
+ if isinstance(m, PmRelMachine):
313
313
  plfe1 = m.kpfe*m.iqd_plfe1(*iqd, f1)
314
314
  plfe2 = m.kpfe*m.iqd_plfe2(iqd[0], iqd[1], f1)
315
315
  plmag = m.kpmag*m.iqd_plmag(iqd[0], iqd[1], f1)
316
316
  plcu1 = m.iqd_plcu1(iqd[0], iqd[1], 2*np.pi*f1)
317
317
  plcu2 = m.iqd_plcu2(*iqd)
318
318
  tfric = m.tfric
319
+ elif isinstance(m, SynchronousMachine):
320
+ plfe1 = m.kpfe*m.iqd_plfe1(*iqd, f1)
321
+ plfe2 = m.kpfe*m.iqd_plfe2(*iqd, f1)
322
+ plmag = np.zeros_like(plfe2)
323
+ plcu1 = m.iqd_plcu1(iqd[0], iqd[1], 2*np.pi*f1)
324
+ plcu2 = m.iqd_plcu2(*iqd)
325
+ tfric = m.tfric
319
326
  else:
320
327
  plfe1 = np.array(r['plfe1'])
321
328
  plfe2 = np.zeros(ntmesh.shape[1])
femagtools/machine/im.py CHANGED
@@ -230,7 +230,7 @@ class InductionMachine(Component):
230
230
 
231
231
  def i1(self, w1, psi, wm):
232
232
  """stator current"""
233
- imag = complex(self.imag(psi))
233
+ imag = self.imag(psi)+0j
234
234
  if abs(w1) > 0:
235
235
  imag += w1*psi/self.rfe(w1, psi)*1j
236
236
  return self.i2(w1, psi, wm) + imag
@@ -577,6 +577,8 @@ class InductionMachine(Component):
577
577
  r['n'].append(wm/2/np.pi)
578
578
  r['s'].append(float((w1 - self.p * wm) / w1))
579
579
  r['sk'].append(self.sk(w1, np.abs(u1)/w1))
580
+ # add n_type to result dict
581
+ r['n_type'] = wmType/2/np.pi
580
582
  # except ValueError as ex:
581
583
  # break
582
584
  r['plfw'] = [self.pfric(n) for n in r['n']]
femagtools/machine/pm.py CHANGED
@@ -268,9 +268,11 @@ class PmRelMachine(object):
268
268
  u -- the maximum voltage (RMS)
269
269
  iq, id -- the d-q currents"""
270
270
  w10 = np.sqrt(2)*u/la.norm(self.psi(iq, id))
271
- return so.fsolve(
272
- lambda w1: la.norm(self.uqd(w1, iq, id))-u*np.sqrt(2),
273
- w10)[0]
271
+ with warnings.catch_warnings():
272
+ warnings.simplefilter("ignore")
273
+ return so.fsolve(
274
+ lambda w1: la.norm(self.uqd(w1, iq, id))-u*np.sqrt(2),
275
+ w10)[0]
274
276
 
275
277
  def w1_imax_umax(self, i1max, u1max):
276
278
  """return frequency w1 and torque at voltage u1max and current i1max
@@ -584,7 +586,7 @@ class PmRelMachine(object):
584
586
  i1max=i1max)
585
587
  return iq, id, tq
586
588
  except ValueError as e:
587
- logger.warning(e)
589
+ logger.debug(e)
588
590
 
589
591
  if with_tmech:
590
592
  tq = self.tmech_iqd(iq, id, w1/2/np.pi/self.p)
@@ -798,8 +800,8 @@ class PmRelMachine(object):
798
800
  k, wx, wl, wu, 100*(wu-wl)/wl)
799
801
 
800
802
  self.check_extrapolation = True
801
- w1max = min(w1max, self.w1_umax(
802
- u1max, *iqd(-np.pi/2, abs(i1max))))
803
+ w1max = min(w1max, np.floor(self.w1_umax(
804
+ u1max, *iqd(-np.pi/2, abs(i1max)))))
803
805
  return [w/2/np.pi/self.p for w in (w1type, w1max)] # ['MTPA']
804
806
 
805
807
 
@@ -907,6 +909,8 @@ class PmRelMachine(object):
907
909
  with_mtpa -- (optional) use mtpa if True (default) in const speed range, set id=0 if false
908
910
  with_tmech -- (optional) use friction and windage losses if True (default)
909
911
  with_torque_corr -- (optional) T is corrected if out of range
912
+ Optional arguments:
913
+ i1max: max. phase current (RMS)
910
914
  """
911
915
  r = dict(id=[], iq=[], uq=[], ud=[], u1=[], i1=[], T=[],
912
916
  beta=[], gamma=[], phi=[], cosphi=[], pmech=[], n=[])
@@ -1396,11 +1400,8 @@ class PmRelMachinePsidq(PmRelMachine):
1396
1400
  'rotor_excess']
1397
1401
  self._set_losspar(pfe)
1398
1402
  self._losses = {k: ip.RectBivariateSpline(
1399
- iq, id, np.array(pfe[k])).ev for k in (
1400
- 'styoke_hyst', 'stteeth_hyst',
1401
- 'styoke_eddy', 'stteeth_eddy',
1402
- 'rotor_hyst', 'rotor_eddy',
1403
- 'magnet')}
1403
+ iq, id, np.array(pfe[k])).ev for k in self.losskeys
1404
+ if k in pfe}
1404
1405
  except KeyError as e:
1405
1406
  logger.warning("loss map missing: %s", e)
1406
1407
  pass