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.
Files changed (49) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/dxfsl/area.py +110 -1
  3. femagtools/dxfsl/areabuilder.py +93 -45
  4. femagtools/dxfsl/conv.py +5 -0
  5. femagtools/dxfsl/converter.py +85 -27
  6. femagtools/dxfsl/fslrenderer.py +5 -4
  7. femagtools/dxfsl/functions.py +14 -6
  8. femagtools/dxfsl/geom.py +135 -149
  9. femagtools/dxfsl/journal.py +1 -1
  10. femagtools/dxfsl/machine.py +161 -9
  11. femagtools/dxfsl/shape.py +46 -1
  12. femagtools/dxfsl/svgparser.py +1 -1
  13. femagtools/dxfsl/symmetry.py +143 -38
  14. femagtools/femag.py +64 -61
  15. femagtools/fsl.py +15 -12
  16. femagtools/isa7.py +3 -2
  17. femagtools/machine/__init__.py +5 -4
  18. femagtools/machine/afpm.py +79 -33
  19. femagtools/machine/effloss.py +29 -18
  20. femagtools/machine/sizing.py +192 -13
  21. femagtools/machine/sm.py +34 -36
  22. femagtools/machine/utils.py +2 -2
  23. femagtools/mcv.py +58 -29
  24. femagtools/model.py +4 -3
  25. femagtools/multiproc.py +79 -80
  26. femagtools/parstudy.py +11 -5
  27. femagtools/plot/nc.py +2 -2
  28. femagtools/semi_fea.py +108 -0
  29. femagtools/templates/basic_modpar.mako +0 -3
  30. femagtools/templates/fe-contr.mako +18 -18
  31. femagtools/templates/ld_lq_fast.mako +3 -0
  32. femagtools/templates/mult_cal_fast.mako +3 -0
  33. femagtools/templates/pm_sym_f_cur.mako +4 -1
  34. femagtools/templates/pm_sym_fast.mako +3 -0
  35. femagtools/templates/pm_sym_loss.mako +3 -0
  36. femagtools/templates/psd_psq_fast.mako +3 -0
  37. femagtools/templates/torq_calc.mako +3 -0
  38. femagtools/tks.py +23 -20
  39. femagtools/zmq.py +213 -0
  40. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/METADATA +3 -3
  41. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/RECORD +49 -47
  42. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/WHEEL +1 -1
  43. tests/test_afpm.py +15 -6
  44. tests/test_femag.py +1 -1
  45. tests/test_fsl.py +4 -4
  46. tests/test_mcv.py +21 -15
  47. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/LICENSE +0 -0
  48. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/entry_points.txt +0 -0
  49. {femagtools-1.8.1.dist-info → femagtools-1.8.3.dist-info}/top_level.txt +0 -0
femagtools/mcv.py CHANGED
@@ -8,6 +8,7 @@ import sys
8
8
  import copy
9
9
  import logging
10
10
  import os.path
11
+ import pathlib
11
12
  import struct
12
13
  import math
13
14
  import numpy as np
@@ -292,7 +293,12 @@ class Mcv(object):
292
293
  self.MC1_CW_FREQ_FACTOR = 0.0
293
294
  self.MC1_INDUCTION_FACTOR = 0.0
294
295
  self.MC1_INDUCTION_BETA_FACTOR = 0.0
295
-
296
+ self.jordan = {}
297
+ # {'ch': 0, 'cw': 0, 'ch_freq':0, 'cw_freq':0}
298
+ self.steinmetz = {}
299
+ # {'ch': 0, 'cw': 0, 'ch_freq':0, 'cw_freq':0}
300
+ self.bertotti = {}
301
+ # {'ch': 0, 'cw': 0, 'ce':0, 'ch_freq':0, 'cw_freq':0}
296
302
  self.MC1_FE_SPEZ_WEIGTH = 7.65
297
303
  self.MC1_FE_SAT_MAGNETIZATION = 2.15
298
304
 
@@ -335,9 +341,8 @@ class Mcv(object):
335
341
  self.setData(data)
336
342
 
337
343
  self.mc1_curves = len(self.curve)
338
- if self.mc1_type == MAGCRV and self.mc1_curves > 1:
339
- self.mc1_type = ORIENT_CRV
340
- if self.mc1_type in (ORIENT_CRV, ORIENT_PM_CRV):
344
+ if (self.mc1_type in (ORIENT_CRV, ORIENT_PM_CRV)
345
+ or self.mc1_curves > 1):
341
346
  self.version_mc_curve = self.ORIENTED_VERSION_MC_CURVE
342
347
  elif self.mc1_type == DEMCRV_BR:
343
348
  self.version_mc_curve = self.PARAMETER_PM_CURVE
@@ -355,6 +360,9 @@ class Mcv(object):
355
360
  for k in wtrans:
356
361
  if wtrans[k] in data.keys():
357
362
  self.__setattr__(k, data[wtrans[k]])
363
+ for k in ('bertotti', 'jordan', 'steinmetz'):
364
+ if k in data:
365
+ self.__setattr__(k, data[k])
358
366
  self.curve = data['curve']
359
367
  try:
360
368
  self.mc1_angle = [c['angle'] for c in data['curve']]
@@ -364,6 +372,9 @@ class Mcv(object):
364
372
  self.losses = data['losses']
365
373
  except Exception:
366
374
  pass
375
+ # assume jordan iron loss parameters
376
+ for k in self.jordan:
377
+ self.jordan[k] = getattr(self, transl[k])
367
378
  return
368
379
 
369
380
  def rtrimValueList(self, vlist):
@@ -471,13 +482,25 @@ class Writer(Mcv):
471
482
  for c in curve]
472
483
  return curve
473
484
 
474
- def writeBinaryFile(self, fillfac=None, recsin=''):
485
+ def writeBinaryFile(self, fillfac=None, recsin='', feloss=''):
475
486
  """write binary file after conversion if requested.
476
487
  arguments:
477
488
  fillfac: (float) fill actor
478
489
  recsin: (str) either 'flux' or 'cur'
490
+ feloss: (str) iron loss method (bertotti, jordan)
479
491
  """
480
492
  curve = self._prepare(fillfac, recsin)
493
+ try:
494
+ if feloss.lower() == 'bertotti':
495
+ for k in self.bertotti:
496
+ setattr(self, transl[k], self.bertotti[k])
497
+ del self.losses
498
+ else:
499
+ for k in self.jordan:
500
+ setattr(self, transl[k], self.jordan[k])
501
+ except AttributeError as e:
502
+ logger.warning("%s", e)
503
+ pass
481
504
  mc1_type = self.mc1_type
482
505
  mc1_recalc = self.mc1_recalc
483
506
  mc1_fillfac = self.mc1_fillfac
@@ -557,6 +580,7 @@ class Writer(Mcv):
557
580
 
558
581
  try:
559
582
  if not (self.mc1_ch_factor or self.mc1_cw_factor) and self.losses:
583
+ # fit loss parameters
560
584
  pfe = self.losses['pfe']
561
585
  f = self.losses['f']
562
586
  B = self.losses['B']
@@ -599,7 +623,8 @@ class Writer(Mcv):
599
623
  return
600
624
 
601
625
  try:
602
- nfreq = len([1 for x in self.losses['f'] if x > 0])
626
+ freq = [x for x in self.losses['f'] if x > 0]
627
+ nfreq = len(freq)
603
628
  nind = len(self.losses['B'])
604
629
  if nind < 1 or nfreq < 1:
605
630
  return
@@ -632,26 +657,28 @@ class Writer(Mcv):
632
657
  self.writeBlock([nfreq, nind])
633
658
  self.writeBlock([float(b) for b in B] + [0.0]*(M_LOSS_INDUCT - nind))
634
659
 
660
+ nrec = 0
635
661
  for f, p in zip(self.losses['f'], pfe):
636
662
  if f > 0:
637
663
  y = np.array(p)
638
664
  losses = [float(x) for x in y[y != np.array(None)]]
639
- if len(losses) == nind:
640
- pl = p
665
+ nloss = len(losses)
666
+ if nloss == nind:
667
+ pl = list(p)
641
668
  else:
642
- n = len(losses)
643
669
  cw, alfa, beta = lc.fitsteinmetz(
644
- f, B[:n], losses, Bo, fo)
670
+ f, B[:nloss], losses, Bo, fo)
645
671
  pl = losses + [lc.pfe_steinmetz(
646
672
  f, b, cw, alfa, beta,
647
673
  self.losses['fo'],
648
674
  self.losses['Bo'])
649
- for b in B[n:]]
675
+ for b in B[nloss:]]
650
676
  logger.debug("%s", pl)
651
677
  self.writeBlock(pl +
652
678
  [0.0]*(M_LOSS_INDUCT - len(pl)))
653
679
  self.writeBlock(float(f))
654
- for m in range(M_LOSS_FREQ - len(pfe)):
680
+ nrec += 1
681
+ for m in range(M_LOSS_FREQ - nrec):
655
682
  self.writeBlock([0.0]*M_LOSS_INDUCT)
656
683
  self.writeBlock(0.0)
657
684
 
@@ -663,21 +690,21 @@ class Writer(Mcv):
663
690
  except Exception as e:
664
691
  logger.error("Exception %s", e, exc_info=True)
665
692
 
666
- def writeMcv(self, filename, fillfac=None, recsin=''):
693
+ def writeMcv(self, filename, fillfac=None, recsin='', feloss='Jordan'):
667
694
  # windows needs this strip to remove '\r'
668
- filename = filename.strip()
669
- self.name = os.path.splitext(filename)[0]
695
+ filename = pathlib.Path(filename)
696
+ self.name = filename.stem
670
697
 
671
- if filename.upper().endswith('.MCV') or \
672
- filename.upper().endswith('.MC'):
698
+ if filename.suffix.upper() in ('.MCV', '.MC'):
673
699
  binary = True
674
- self.fp = open(filename, "wb")
700
+ self.fp = filename.open(mode="wb")
675
701
  else:
676
702
  binary = False
677
- self.fp = open(filename, "wb")
678
- logger.info("Write File %s, binary format", filename)
703
+ self.fp = filename.open(mode="w")
704
+ logger.info("Write File %s, binary format (feloss '%s')",
705
+ filename, feloss)
679
706
 
680
- self.writeBinaryFile(fillfac, recsin)
707
+ self.writeBinaryFile(fillfac, recsin, feloss)
681
708
  self.fp.close()
682
709
 
683
710
 
@@ -738,17 +765,16 @@ class Reader(Mcv):
738
765
 
739
766
  def readMcv(self, filename):
740
767
  # intens bug : windows needs this strip to remove '\r'
741
- filename = filename.strip()
768
+ filename = pathlib.Path(filename)
742
769
 
743
- if filename.upper().endswith('.MCV') or \
744
- filename.upper().endswith('.MC'):
770
+ if filename.suffix in ('.MCV', '.MC'):
745
771
  binary = True
746
- self.fp = open(filename, "rb")
772
+ self.fp = filename.open(mode="rb")
747
773
  else:
748
774
  binary = False
749
- self.fp = open(filename, "r")
775
+ self.fp = filename.open(mode="r")
750
776
 
751
- self.name = os.path.splitext(os.path.basename(filename))[0]
777
+ self.name = filename.stem
752
778
  # read curve version (INTEGER)
753
779
  if binary:
754
780
  self.version_mc_curve = self.readBlock(int)
@@ -904,6 +930,8 @@ class Reader(Mcv):
904
930
  if self.MC1_INDUCTION_FACTOR > 2.0:
905
931
  self.MC1_INDUCTION_FACTOR = 2.0
906
932
 
933
+ # TODO: handle self.mc1_ce_factor, self.mc1_induction_beta_factor
934
+
907
935
  self.losses = {}
908
936
  try:
909
937
  (nfreq, njind) = self.readBlock([int, int])
@@ -1086,13 +1114,14 @@ class MagnetizingCurve(object):
1086
1114
  repls.items(), name)
1087
1115
 
1088
1116
  def writefile(self, name, directory='.',
1089
- fillfac=None, recsin=''):
1117
+ fillfac=None, recsin='', feloss='jordan'):
1090
1118
  """find magnetic curve by name or id and write binary file
1091
1119
  Arguments:
1092
1120
  name: key of mcv dict (name or id)
1093
1121
  directory: destination directory (must be writable)
1094
1122
  fillfac: (float) new fill factor (curves will be recalulated if not None or 0)
1095
1123
  recsin: (str) either 'flux' or 'cur' recalculates for eddy current calculation (dynamic simulation)
1124
+ feloss: (str) iron loss calc method ('jordan', 'bertotti', 'steinmetz')
1096
1125
 
1097
1126
  returns filename if found else None
1098
1127
  """
@@ -1126,7 +1155,7 @@ class MagnetizingCurve(object):
1126
1155
  filename = ''.join((bname, ext))
1127
1156
  writer = Writer(mcv)
1128
1157
  writer.writeMcv(os.path.join(directory, filename),
1129
- fillfac=fillfac, recsin=recsin)
1158
+ fillfac=fillfac, recsin=recsin, feloss=feloss)
1130
1159
  return filename
1131
1160
 
1132
1161
  def fitLossCoeffs(self):
femagtools/model.py CHANGED
@@ -136,14 +136,15 @@ class MachineModel(Model):
136
136
  name = 'DRAFT'
137
137
  if isinstance(parameters, str):
138
138
  name = parameters
139
+ self.connect_full = False # no matter
139
140
  else:
140
141
  if 'name' in parameters:
141
142
  name = parameters['name']
142
143
  if 'windings' in parameters:
143
144
  self.winding = self.windings
144
145
 
145
- # connect model even for complete model (see fsl connect_models)
146
- self.connect_full = True
146
+ # connect model even for complete model (see fsl connect_models)
147
+ self.connect_full = parameters.get('afmtype', '') == ''
147
148
  # must sanitize name to prevent femag complaints
148
149
  self.name = ''.join([n
149
150
  for n in name.strip()
@@ -410,7 +411,7 @@ class MachineModel(Model):
410
411
  missing += m
411
412
  names += n
412
413
  if missing:
413
- raise MCerror("MC pars missing: {}".format(
414
+ raise MCerror("Material properties missing: {}".format(
414
415
  ', '.join(set(missing))))
415
416
  return set(names)
416
417
 
femagtools/multiproc.py CHANGED
@@ -12,6 +12,8 @@ import pathlib
12
12
  import logging
13
13
  from .job import Job
14
14
  import femagtools.config as cfg
15
+ import femagtools.zmq
16
+ from femagtools.zmq import SubscriberTask
15
17
  try:
16
18
  from subprocess import DEVNULL
17
19
  except ImportError:
@@ -19,86 +21,40 @@ except ImportError:
19
21
 
20
22
  logger = logging.getLogger(__name__)
21
23
 
22
- numpat = re.compile(r'([+-]?\d+(?:\.\d+)?(?:[eE][+-]\d+)?)\s*')
23
-
24
24
  class LicenseError(Exception):
25
25
  pass
26
26
 
27
- class ProtFile:
28
- def __init__(self, dirname, num_cur_steps):
29
- self.size = 0
30
- self.looplen = 0
31
- self.cur_steps = [1, num_cur_steps]
32
- self.n = 0
33
- self.num_loops = 0
34
- self.dirname = dirname
35
- self.name = 'samples'
36
-
37
- def percent(self):
38
- if self.looplen > 0:
39
- return min(100 * self.n / self.looplen, 100)
40
- return 0
41
-
42
- def update(self):
43
- p = list(pathlib.Path(self.dirname).glob('*.PROT'))
44
- if p:
45
- buf = ''
46
- if self.size < p[0].stat().st_size:
47
- with p[0].open() as fp:
48
- fp.seek(self.size)
49
- buf = fp.read()
50
- return self.append(buf)
51
- return ''
52
-
53
- def append(self, buf):
54
- self.size += len(buf)
55
- for line in [l.strip() for l in buf.split('\n') if l]:
56
- if line.startswith('Loop'):
57
- self.n = 0
58
- try:
59
- cur_steps = self.cur_steps[self.num_loops]
60
- except IndexError:
61
- cur_steps = 1
62
- x0, x1, dx, nbeta = [float(f)
63
- for f in re.findall(numpat, line)][:4]
64
- move_steps = round((x1-x0)/dx+1)
65
- beta_steps = int(nbeta)
66
- self.looplen = cur_steps*beta_steps*move_steps
67
- self.num_loops += 1
68
- elif (line.startswith('Cur') or
69
- line.startswith('Id')):
70
- self.n += 1
71
- elif line.startswith('Number movesteps Fe-Losses'):
72
- return ''
73
- elif line.startswith('begin'):
74
- self.name = line.split()[1].strip()
75
-
76
- return f'{self.percent():3.1f}%' # {self.n}/{self.looplen}'
77
-
78
-
79
27
  class ProgressLogger(threading.Thread):
80
- def __init__(self, dirs, num_cur_steps, timestep):
28
+ def __init__(self, dirs, num_cur_steps, timestep, notify):
81
29
  threading.Thread.__init__(self)
82
- self.dirs = dirs
83
- self.num_cur_steps = num_cur_steps
30
+ self.protfiles = [femagtools.zmq.ProtFile(d, num_cur_steps)
31
+ for d in dirs]
32
+ self.numTot = len(dirs)
84
33
  self.running = False
85
34
  self.timestep = timestep
35
+ self.notify = notify
86
36
 
87
37
  def run(self):
88
38
  self.running = True
89
- protfiles = [ProtFile(d, self.num_cur_steps)
90
- for d in self.dirs]
91
39
  while self.running:
92
- time.sleep(self.timestep)
93
- logmsg = [p.update() for p in protfiles]
40
+ if self.timestep > 0:
41
+ time.sleep(self.timestep)
42
+ logmsg = [p.update() for p in self.protfiles]
94
43
  summary = [l # f'<{i}> {l}'
95
44
  for i, l in enumerate(logmsg)
96
45
  if l]
97
46
  if summary:
98
- labels = set([p.name for p in protfiles])
47
+ labels = set([p.name for p in self.protfiles])
99
48
  logger.info('%s: %s',
100
49
  ', '.join(labels),
101
50
  ', '.join(summary))
51
+ if self.notify:
52
+ numOf = f"{summary.count('100.0%')} of {self.numTot}"
53
+ percent = sum([float(i[:-1])
54
+ for i in summary]) / self.numTot
55
+ self.notify(
56
+ ["progress_logger",
57
+ f"{self.numTot}:{numOf}:{percent}:{' '.join(summary)}"])
102
58
  else:
103
59
  logger.info('collecting FE losses ...')
104
60
  return
@@ -107,7 +63,7 @@ class ProgressLogger(threading.Thread):
107
63
  self.running = False
108
64
 
109
65
 
110
- def run_femag(cmd, workdir, fslfile):
66
+ def run_femag(cmd, workdir, fslfile, port):
111
67
  """Start the femag command as subprocess.
112
68
 
113
69
  :internal:
@@ -120,7 +76,8 @@ def run_femag(cmd, workdir, fslfile):
120
76
  with open(os.path.join(workdir, "femag.out"), "wb") as out, \
121
77
  open(os.path.join(workdir, "femag.err"), "wb") as err:
122
78
  try:
123
- proc = subprocess.Popen(cmd + ['-b', fslfile],
79
+ args = ['-b', str(port), fslfile] if port else ['-b', fslfile]
80
+ proc = subprocess.Popen(cmd + args,
124
81
  shell=False,
125
82
  stdin=DEVNULL,
126
83
  stdout=out,
@@ -163,11 +120,16 @@ class Engine:
163
120
  cmd: the program (executable image) to be run
164
121
  (femag dc is used if None)
165
122
  process_count: number of processes (cpu_count() if None)
166
- progress_timestep: time step in seconds for progress log messages if > 0)
123
+ timestep: time step in seconds for progress log messages if > 0)
167
124
  """
168
125
 
169
126
  def __init__(self, **kwargs):
170
127
  self.process_count = kwargs.get('process_count', None)
128
+ self.notify = kwargs.get('notify', None)
129
+ # cogg_calc mode, subscribe xyplot
130
+ self.calc_mode = kwargs.get('calc_mode')
131
+ self.port = kwargs.get('port', 0)
132
+ self.curve_label = kwargs.get('curve_label')
171
133
  cmd = kwargs.get('cmd', '')
172
134
  if cmd:
173
135
  self.cmd = [cmd]
@@ -175,7 +137,10 @@ class Engine:
175
137
  self.cmd.append('-m')
176
138
 
177
139
  self.progressLogger = 0
178
- self.progress_timestep = kwargs.get('timestep', 5)
140
+ self.progress_timestep = kwargs.get('timestep', -1)
141
+ self.subscriber = None
142
+ self.job = None
143
+ self.tasks = []
179
144
 
180
145
  def create_job(self, workdir):
181
146
  """Create a FEMAG :py:class:`Job`
@@ -209,20 +174,41 @@ class Engine:
209
174
  t.cmd = [cfg.get_executable(
210
175
  t.stateofproblem)] + args
211
176
 
212
- self.pool = multiprocessing.Pool(self.process_count)
213
- self.tasks = [self.pool.apply_async(run_femag,
214
- args=(t.cmd,
215
- t.directory,
216
- t.fsl_file))
217
- for t in self.job.tasks]
177
+ num_proc = self.process_count
178
+ if not num_proc and multiprocessing.cpu_count() > 1:
179
+ num_proc = min(multiprocessing.cpu_count()-1, len(self.job.tasks))
180
+ self.pool = multiprocessing.Pool(num_proc)
181
+ if self.port:
182
+ header = [b'progress']
183
+ if self.calc_mode == 'cogg_calc':
184
+ header +=[b'xyplot']
185
+ self.subscriber = [SubscriberTask(port=self.port + i * 5,
186
+ host='127.0.0.1',
187
+ notify=self.notify,
188
+ header=header,
189
+ curve_label=self.curve_label,
190
+ num_cur_steps=self.job.num_cur_steps,
191
+ timestep=self.progress_timestep
192
+ )
193
+ for i, t in enumerate(self.job.tasks)]
194
+ [s.start() for s in self.subscriber]
195
+ self.tasks = [self.pool.apply_async(
196
+ run_femag, args=(t.cmd, t.directory, t.fsl_file, self.port + i * 5))
197
+ for i, t in enumerate(self.job.tasks)]
198
+ else:
199
+ self.tasks = [self.pool.apply_async(
200
+ run_femag, args=(t.cmd, t.directory, t.fsl_file, 0))
201
+ for t in self.job.tasks]
218
202
  self.pool.close()
219
203
 
220
- if (self.progress_timestep and
221
- self.job.num_cur_steps):
204
+ # only works on linux
205
+ if (self.progress_timestep and not self.port and
206
+ self.job.num_cur_steps):
222
207
  self.progressLogger = ProgressLogger(
223
208
  [t.directory for t in self.job.tasks],
224
209
  num_cur_steps=self.job.num_cur_steps,
225
- timestep=self.progress_timestep)
210
+ timestep=self.progress_timestep,
211
+ notify=self.notify)
226
212
  self.progressLogger.start()
227
213
  return len(self.tasks)
228
214
 
@@ -244,17 +230,30 @@ class Engine:
244
230
  if t.errmsg:
245
231
  logger.error(t.errmsg)
246
232
  status.append(t.status)
233
+ self.stopThreads()
234
+ self.pool = None # garbage collector deletes threads
235
+ return status
236
+
237
+ def stopThreads(self):
238
+ """ stop all running treads
239
+ """
247
240
  if self.progressLogger:
248
241
  self.progressLogger.stop()
249
- return status
242
+ if self.port and self.subscriber:
243
+ [s.stop() for s in self.subscriber]
244
+ SubscriberTask.clear()
245
+ self.subscriber = None
250
246
 
251
247
  def terminate(self):
248
+ """ terminate all
249
+ """
252
250
  logger.info("terminate Engine")
253
- if self.progressLogger:
254
- self.progressLogger.stop()
251
+ self.stopThreads()
252
+
255
253
  # terminate pool
256
254
  try:
257
- self.pool.terminate()
258
- self.pool.close()
255
+ if self.pool:
256
+ self.pool.terminate()
257
+ self.pool = None # garbage collector deletes threads
259
258
  except AttributeError as e:
260
259
  logger.warn("%s", e)
femagtools/parstudy.py CHANGED
@@ -105,10 +105,10 @@ class ParameterStudy(object):
105
105
  raise ValueError("directory {} is not empty".format(dirname))
106
106
  self.reportdir = dirname
107
107
 
108
- def setup_model(self, builder, model, recsin=''):
108
+ def setup_model(self, builder, model, recsin='', feloss=''):
109
109
  """builds model in current workdir and returns its filenames"""
110
110
  # get and write mag curves
111
- mc_files = self.femag.copy_magnetizing_curves(model, recsin=recsin)
111
+ mc_files = self.femag.copy_magnetizing_curves(model, recsin=recsin, feloss=feloss)
112
112
 
113
113
  if model.is_complete():
114
114
  logger.info("setup model in %s", self.femag.workdir)
@@ -192,7 +192,8 @@ class ParameterStudy(object):
192
192
  objective_vars)
193
193
 
194
194
  if immutable_model:
195
- modelfiles = self.setup_model(builder, model, recsin=fea.recsin)
195
+ modelfiles = self.setup_model(builder, model, recsin=fea.recsin,
196
+ feloss=simulation.get('feloss', ''))
196
197
  logger.info("Files %s", modelfiles+extra_files)
197
198
  logger.info("model %s", model.props())
198
199
  for k in ('name', 'poles', 'outer_diam', 'airgap', 'bore_diam',
@@ -262,6 +263,10 @@ class ParameterStudy(object):
262
263
  p, int(np.ceil(len(par_range)/popsize)),
263
264
  np.shape(f))
264
265
  job.cleanup()
266
+ try:
267
+ feloss = fea.calc_fe_loss
268
+ except AttributeError:
269
+ feloss = ''
265
270
  for k, x in enumerate(population):
266
271
  task = job.add_task(self.result_func)
267
272
  for fn in extra_files:
@@ -284,7 +289,8 @@ class ParameterStudy(object):
284
289
  for mc in self.femag.copy_magnetizing_curves(
285
290
  model,
286
291
  dir=task.directory,
287
- recsin=fea.recsin):
292
+ recsin=fea.recsin,
293
+ feloss=feloss):
288
294
  task.add_file(mc)
289
295
  set_magnet_properties(model, fea, self.femag.magnets)
290
296
  task.add_file(
@@ -325,7 +331,7 @@ class ParameterStudy(object):
325
331
  shutil.copy(ff, repdir)
326
332
  calcid += 1
327
333
  if isinstance(r, dict) and 'error' in r:
328
- logger.warn("job %d failed: %s", k, r['error'])
334
+ logger.warning("job %d failed: %s", k, r['error'])
329
335
  if objective_vars:
330
336
  f.append([float('nan')]*len(objective_vars))
331
337
  else:
femagtools/plot/nc.py CHANGED
@@ -14,7 +14,7 @@ DEFAULT_CMAP='viridis'
14
14
  """default colormap (see https://matplotlib.org/stable/users/explain/colors/colormaps.html)"""
15
15
 
16
16
 
17
- def spel(isa, superelements=[], with_axis=False, ax=0):
17
+ def spel(isa, superelements=[], with_axis=False, with_wiredir=False, ax=0):
18
18
  """plot super elements of I7/ISA7 model
19
19
  Args:
20
20
  isa: Isa7 object
@@ -35,7 +35,7 @@ def spel(isa, superelements=[], with_axis=False, ax=0):
35
35
  color=isa.color[se.color], lw=0))
36
36
  try:
37
37
  # draw wire direction
38
- if se.subregion:
38
+ if se.subregion and with_wiredir:
39
39
  if se.subregion.curdir != 0:
40
40
  wkey = se.subregion.winding.key
41
41
  if se.subregion.curdir < 0:
femagtools/semi_fea.py ADDED
@@ -0,0 +1,108 @@
1
+ import numpy as np
2
+ from .utils import fft
3
+ import logging
4
+
5
+ logger = logging.getLogger('femagtools.semi_fea')
6
+
7
+ def shift_array(v, idx):
8
+ '''shift array by index'''
9
+ return v[idx::] + v[0:idx]
10
+
11
+ def fft_filter(result_fft, perc=0.01):
12
+ '''filter FFT result with amplitude'''
13
+ result = {"order": [], "y":[]}
14
+ base_amp = result_fft['a']
15
+ for i, j in enumerate(result_fft['nue']):
16
+ if j >= perc*base_amp:
17
+ result['order'].append(i)
18
+ result['y'].append(j)
19
+ return result
20
+
21
+ def fast_skew_cogg(result, skew_setup):
22
+ '''Calculate cogging torque/Back-EMF with step skewing based on unskewed result
23
+ Arguments:
24
+ result: BCH objects
25
+ skew_setup(dict): {"skew_angle": 10, "nu_skew_steps": 2}
26
+ '''
27
+ skew_angle = skew_setup['skew_angle']
28
+ num_skew_steps =skew_setup['num_skew_steps']
29
+ skew_angle_intern = 0.0
30
+ skew_angle_array = []
31
+ T_slice = []
32
+ bemf = {"1": [], "2": [], "3": []}
33
+ bemf_slice = {"1": [], "2": [], "3": []}
34
+ bemf_skew = {"1": [], "2": [], "3": []}
35
+ bemf_skew_fft = {"1": [], "2": [], "3": []}
36
+
37
+ keyset = ('1', '2', '3')
38
+
39
+ # check if skew steps equals 2
40
+ if num_skew_steps == 2:
41
+ skew_angle_intern = skew_angle
42
+ skew_angle_array = [-skew_angle_intern/2, skew_angle_intern/2]
43
+ else:
44
+ skew_angle_intern = skew_angle/num_skew_steps*(num_skew_steps-1)/2
45
+ skew_angle_array = np.linspace(-skew_angle_intern, skew_angle_intern,
46
+ num_skew_steps, endpoint=True).tolist()
47
+
48
+ angle = result.torque[-1]['angle']
49
+ T = result.torque[-1]['torque'][0:-1]
50
+ # get back-emf from BCH
51
+ for i in keyset:
52
+ bemf[i] = result.flux[i][-1]['voltage_dpsi'][0:-1]
53
+
54
+ angl_resl = angle[1]
55
+ tmp = np.unique(np.abs(skew_angle_array))
56
+ skew_angl_resl = 0.0
57
+ if np.amin(tmp) == 0.0:
58
+ skew_angl_resl = tmp[1]
59
+ else:
60
+ skew_angl_resl = tmp[0]
61
+
62
+ divider = skew_angl_resl/angl_resl
63
+ if divider - np.floor(divider) > 1e-15:
64
+ # TODO: Interpolation if angle resolution doesn't match
65
+ logger.warning("Wrong Mesh Size in the airgap mesh")
66
+ else:
67
+ logger.info(f"number of element shifted {divider}")
68
+
69
+ for i in skew_angle_array:
70
+ idx = int(i/angl_resl)
71
+ if i != 0:
72
+ T_slice.append(shift_array(T, idx))
73
+ for j in keyset:
74
+ bemf_slice[j].append(shift_array(bemf[j], idx))
75
+ else:
76
+ # do nothing
77
+ T_slice.append(T)
78
+ for j in keyset:
79
+ bemf_slice[j].append(bemf[j])
80
+
81
+ # average torque
82
+ T_sum = 0
83
+ for i in T_slice:
84
+ T_sum += np.array(i)
85
+ T_skew = (T_sum/num_skew_steps).tolist()
86
+ T_skew += [T_skew[0]]
87
+ T_fft = fft_filter(fft(angle, T_skew, pmod=2))
88
+
89
+ # average back-emf
90
+ for j in keyset:
91
+ flx_skew = 0
92
+ for k in bemf_slice[j]:
93
+ flx_skew+=np.array(k)
94
+ bemf_skew[j] = (flx_skew/num_skew_steps).tolist()
95
+ bemf_skew[j] += [bemf_skew[j][0]]
96
+ bemf_skew_fft[j] = fft_filter(fft(angle, bemf_skew[j], pmod=2))
97
+
98
+ for i in range(len(T_slice)):
99
+ T_slice[i] = (np.array(T_slice[i])/num_skew_steps).tolist()
100
+ T_slice[i]+=[T_slice[i][0]]
101
+
102
+ return {"angle": angle,
103
+ "cogging_torque": T_skew,
104
+ "cogging_torque_fft": T_fft,
105
+ "BEMF": bemf_skew,
106
+ "BEMF_fft": bemf_skew_fft,
107
+ "cogging_torque_slice": T_slice}
108
+
@@ -88,9 +88,6 @@ m.pole_width = ${model['pole_width']*1e3}
88
88
  % if hasattr(model, 'lfe'):
89
89
  m.arm_length = ${model.get(['lfe'])*1e3}
90
90
  % endif
91
- % if hasattr(model, 'lfe'):
92
- m.arm_length = ${model.get(['lfe'])*1e3}
93
- % endif
94
91
  % if hasattr(model, 'winding'):
95
92
  % if 'num_par_wdgs' in model.winding:
96
93
  m.num_par_wdgs = ${model.winding['num_par_wdgs']}