femagtools 1.7.9__py3-none-any.whl → 1.8.1__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 (43) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/amela.py +2 -2
  3. femagtools/dxfsl/area.py +130 -26
  4. femagtools/dxfsl/conv.py +2 -14
  5. femagtools/dxfsl/converter.py +69 -12
  6. femagtools/dxfsl/fslrenderer.py +15 -13
  7. femagtools/dxfsl/geom.py +153 -82
  8. femagtools/dxfsl/journal.py +2 -2
  9. femagtools/dxfsl/machine.py +19 -15
  10. femagtools/dxfsl/shape.py +3 -0
  11. femagtools/ecloss.py +386 -2
  12. femagtools/femag.py +82 -9
  13. femagtools/fsl.py +52 -56
  14. femagtools/machine/pm.py +1 -1
  15. femagtools/machine/sm.py +16 -8
  16. femagtools/mcv.py +128 -124
  17. femagtools/me.py +13 -13
  18. femagtools/model.py +8 -2
  19. femagtools/plot/fieldlines.py +1 -1
  20. femagtools/plot/mcv.py +18 -0
  21. femagtools/plot/wdg.py +2 -2
  22. femagtools/svgfsl/converter.py +1 -1
  23. femagtools/templates/afm_rotor.mako +4 -0
  24. femagtools/templates/gen_hairpin_winding.mako +36 -45
  25. femagtools/templates/magnetIron.mako +1 -1
  26. femagtools/templates/magnetIron2.mako +1 -1
  27. femagtools/templates/magnetIron3.mako +1 -1
  28. femagtools/templates/magnetIron4.mako +1 -1
  29. femagtools/templates/magnetIron5.mako +1 -1
  30. femagtools/templates/magnetIronV.mako +1 -1
  31. femagtools/templates/magnetSector.mako +1 -1
  32. femagtools/templates/mesh-airgap.mako +12 -6
  33. femagtools/templates/prepare_thermal.mako +199 -61
  34. femagtools/windings.py +25 -20
  35. {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/METADATA +20 -20
  36. {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/RECORD +42 -43
  37. {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/WHEEL +1 -1
  38. tests/test_mcv.py +106 -1
  39. tests/test_windings.py +5 -0
  40. tests/test_mcvwriter.py +0 -96
  41. {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/LICENSE +0 -0
  42. {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/entry_points.txt +0 -0
  43. {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/top_level.txt +0 -0
femagtools/ecloss.py CHANGED
@@ -1,7 +1,7 @@
1
1
  '''Calculate Magnet Losses with IALH Method
2
2
 
3
3
  '''
4
- __author__ = 'Max Hullmann, Dapu Zhang'
4
+ __author__ = 'Max Hullmann, Dapu Zhang, Ivan Solc'
5
5
 
6
6
  import logging
7
7
  import warnings
@@ -95,6 +95,59 @@ def binterp(x, y, xq, yq, b):
95
95
  inp = f(np.array([[i, j] for i, j in zip(xq, yq)]))
96
96
  return inp.reshape(len(np.unique(yq)), len(np.unique(xq)), -1)
97
97
 
98
+ def binterp_ialh2(x, y, xq, yq, b):
99
+ '''interpolate flux density with Rbf interpolator'''
100
+ f = RBFInterpolator(np.array([[i, j] for i, j in zip(x, y)]), b, kernel='thin_plate_spline')
101
+ inp = f(np.array([[i, j] for i, j in zip(xq, yq)]))
102
+ return inp.reshape(len(np.unique(xq)), -1)
103
+
104
+ def Segmentation(wm, hm, lm, elxy, nsegx, nsegy, nsegz):
105
+ ''' Creates new magnet elements' center points' x,y coordinates based on the number of segments and the original data resolution
106
+
107
+ Inputs: total magnet dimensions (width - wm, height - hm, axial length - lm)
108
+ elxy - dictionary with global and local reference frame x,y coordinates of elements centerpoints
109
+ number of magnet segments : nsegx - number of segments in x (wm), nsegy - segments in y = 1 (hm), nsegz - segments in axial direction (lm)
110
+ Returns: number of elements in x direction per magnet segment - nx_new
111
+ number of elements in y direction per magnet segment - ny new
112
+ number of elements in z direction per magnet segment - nz new
113
+ x,y coordinates of each element's centerpoint in local reference frame, for entire magnet - xx, yy
114
+ '''
115
+ # Default nx,ny,nz without considering the segmentation
116
+ be = np.sqrt(wm*hm/elxy['excp'].shape[0]) #square elements
117
+ nx_new = int(np.around(wm/be))
118
+ ny_new = int(np.around(hm/be))
119
+ nz_new = int(np.around(lm/be))
120
+
121
+ if (nsegx > 1) or (nsegy > 1):
122
+ # limits the number of elements per segment to 10 to avoid unnecessarily detailed resolution
123
+ nx_new = int(max(10,int(np.around(nx_new*np.sqrt(1/nsegx)))))
124
+ ny_new = int(max(10,int(np.around(ny_new*np.sqrt(1/nsegy)))))
125
+ nz_new = int(max(10,int(np.around(nz_new*np.sqrt(1/nsegz)))))
126
+
127
+ wms = wm/nsegx
128
+ hms = hm/nsegy
129
+ x0 = 0 # offset for excpl
130
+ y0 = 0 # offset for eycpl
131
+
132
+ segxcpl = np.linspace(wms/2/nx_new, wms - wms/nx_new/2, nx_new) + x0 # x center points of wanted elements distribution, local ref frame, 1st segment
133
+ segycpl = np.linspace(hms/2/ny_new, hms - hms/ny_new/2, ny_new) + y0 # y center points of wanted elements distribution, local ref frame, 1st segment
134
+
135
+ x,y = np.meshgrid(segycpl, segxcpl)
136
+ xx = np.zeros((x.shape[0]*nsegx, x.shape[1]*nsegy)) # new centerpoint coordinates for entire magnet
137
+ yy = np.zeros_like(xx)
138
+ a=np.zeros((x.shape[0]*nsegx))
139
+ b=np.zeros((y.shape[1]*nsegy))
140
+
141
+ for ii in range(nsegx):
142
+ a[ii*nx_new: (ii+1)*nx_new] = segxcpl + wms*ii # generation of x coordinates of elements for each magnet segment
143
+ for jj in range(ny_new*nsegy):
144
+ xx[:,jj] = np.atleast_2d(a)
145
+ for jj in range(nsegy):
146
+ b[jj*ny_new: (jj+1)*ny_new] = segycpl + hms*jj # generation of y coordinates of elements for each magnet segment
147
+ for ii in range(nx_new*nsegx):
148
+ yy[ii,:] = np.atleast_2d(b)
149
+
150
+ return nx_new, ny_new, nz_new, xx, yy
98
151
 
99
152
  class MagnLoss(Amela):
100
153
  '''Calculate Magnet Losses with IALH Methode
@@ -135,7 +188,7 @@ class MagnLoss(Amela):
135
188
 
136
189
  self.segz = kwargs.get('segz', [0])
137
190
  self.is_meter = False
138
- # determine the number of segments in z direction
191
+ # Determine the number of segments in z direction
139
192
  for i in range(len(self.segz)):
140
193
  if self.segz[i] > 0:
141
194
  self.lm = self.ls/self.segz[i]
@@ -386,3 +439,334 @@ class MagnLoss(Amela):
386
439
  all_load_cases.append(ialh_loss)
387
440
 
388
441
  return all_load_cases
442
+
443
+
444
+
445
+ def ialh2(self, sx_ampl, sy_ampl, sx_phase, sy_phase, freq, wm, hm, lm, delta_eff):
446
+ ''' Calculates eddy current losses for each point (sx, sy, f) in one magnet segment, using ialh2 method
447
+
448
+ Inputs: sx_ampl - dBx/dt amplitudes for each element, for each frequency, for that magnet segment
449
+ sy_ampl - dBy/dt amplitudes for each element, for each frequency, for that magnet segment
450
+ sx_phase - x phase of each element for each frequency, for that magnet segment
451
+ sy_phase - y phase of each element for each frequency, for that magnet segment
452
+ freq - every considered frequency in Hertz
453
+ nx, ny - number of elements in each magnet segment
454
+ wm,hm,lm - width (x), height (y), axial length (z) of each magnet segment
455
+ delta_eff - effective airgap between magnet and rotor iron
456
+ Returns: Ploss_x - losses [W] generated from x component of magnetic flux, for a given magnet segment
457
+ Ploss_y - losses [W] generated from y component of magnetic flux, for a given magnet segment
458
+ '''
459
+ sigma_x = self.sigma
460
+ sigma_y = self.sigma
461
+ sigma_z = self.sigma
462
+ delta_xy_x = delta_eff
463
+ delta_xy_y = delta_eff
464
+ delta_yz_y = delta_eff
465
+ delta_xz_x = delta_eff
466
+ epsilon_z_y = 1. + self.mur*delta_xy_y / hm
467
+ epsilon_z_x = 1. + self.mur*delta_xy_x / hm
468
+ epsilon_x_y = 1. + self.mur*delta_yz_y / hm
469
+ epsilon_y_x = 1. + self.mur*delta_xz_x / hm
470
+ rx = sigma_z/sigma_x
471
+ ry = sigma_z/sigma_y
472
+ p0 = 0.5*hm*lm*wm**3*sigma_z
473
+
474
+ (nx, ny, nf) = sx_ampl.shape
475
+
476
+ Ploss_x = np.zeros((nx,ny,nf))
477
+ Ploss_y = np.zeros_like(Ploss_x)
478
+
479
+ for f in range (nf): #loop over each frequency
480
+ epsilon = wm*np.sqrt(MUR0*self.mur*sigma_z/epsilon_z_y*np.pi*freq[f])
481
+
482
+ for n in range (int(nx/2)):
483
+ for m in range (int(ny/2)):
484
+ symmetry = 4 if (m > 0) & (n > 0) else 2 # symmetry factor 4 due to double symmetry utilization.
485
+
486
+ for k in range (0,1000,2): # loop is symmetrical on one side
487
+ complex_xnmkf = sx_ampl[n,m,f]*2/(np.pi*(k+1))*epsilon_z_x/epsilon_z_y*(m*np.pi*wm/hm)/(2j *epsilon**2 + (n*np.pi)**2 + (m*np.pi*wm/hm)**2 + ry*((k+1)*np.pi*wm/lm)**2)
488
+ complex_ynmkf = sy_ampl[n,m,f]*2/(np.pi*(k+1)) *n*np.pi/(2j *epsilon**2 + (n*np.pi)**2 + (m*np.pi*wm/hm)**2 + rx*((k+1)*np.pi*wm/lm)**2)
489
+ real_x_nmkf = np.real(complex_xnmkf)
490
+ imag_x_nmkf = np.imag(complex_xnmkf)
491
+ real_y_nmkf = np.real(complex_ynmkf)
492
+ imag_y_nmkf = np.imag(complex_ynmkf)
493
+
494
+ # Loss component x
495
+ Plossxnmkf = 2.*symmetry*p0*((sx_ampl[n,m,f]*2/(np.pi*(k+1))*epsilon_y_x/epsilon_z_y)**2*ry
496
+ *((k+1)*np.pi*wm/lm)**2/(4*epsilon**4 + ((n*np.pi)**2 + (m*np.pi*wm/hm)**2 + ry*((k+1)*np.pi*wm/lm)**2)**2) + real_x_nmkf**2 + imag_x_nmkf**2
497
+ - 2*np.cos(sy_phase[n,m,f] - sx_phase[n,m,f])*(real_y_nmkf*real_x_nmkf + imag_y_nmkf*imag_x_nmkf)
498
+ - 2*np.sin(sy_phase[n,m,f] - sx_phase[n,m,f])*(real_y_nmkf*imag_x_nmkf - imag_y_nmkf*real_x_nmkf))
499
+ # Loss component y
500
+ Plossynmkf = 2.*symmetry*p0*((sy_ampl[n,m,f]*2/(np.pi*(k+1))*epsilon_x_y/epsilon_z_y)**2*rx
501
+ *((k+1)*np.pi*wm/lm)**2/(4*epsilon**4 + ((n*np.pi)**2 + (m*np.pi*wm/hm)**2 + rx*((k+1)*np.pi*wm/lm)**2)**2) + real_y_nmkf**2 + imag_y_nmkf**2)
502
+
503
+ if (Ploss_x[n,m,f] + Ploss_y[n,m,f]) == 0: # preventing division by zero in termination criteria evaluation
504
+ Ploss_x[n,m,f] += Plossxnmkf
505
+ Ploss_y[n,m,f] += Plossynmkf
506
+ continue
507
+ # termination criteria for k loop -> amplitude proportional to 1/k^2
508
+ if (k > 1) & ((Plossxnmkf + Plossynmkf)/(Ploss_x[n,m,f] + Ploss_y[n,m,f]) < 1e-4):
509
+ Ploss_x[n,m,f] += Plossxnmkf
510
+ Ploss_y[n,m,f] += Plossynmkf
511
+ break
512
+
513
+ Ploss_x[n,m,f] += Plossxnmkf
514
+ Ploss_y[n,m,f] += Plossynmkf
515
+
516
+ return np.sum(Ploss_x), np.sum(Ploss_y)
517
+
518
+ def diffDQ(self, bx_pm_3D, by_pm_3D, T):
519
+ ''' Calculates the time derivative of Bx, By
520
+
521
+ Inputs: bx_pm_3D - bx (AC component) for each element for one magnet segment, for each simulation step
522
+ by_pm_3D - by (AC component) for each element for one magnet segment, for each simulation step
523
+ T - total simulation time for periodic simulation (see periodicity_id fcn for more)
524
+ Returns: sx_pm - dBx/dt for each element for one magnet segment, for each simulation step
525
+ sy_pm - dBy/dt for each element for one magnet segment, for each simulation step
526
+ '''
527
+ (nx, ny, nt) = bx_pm_3D.shape
528
+ sx_pm_3D = np.zeros((nx, ny, nt))
529
+ sy_pm_3D = np.zeros_like(sx_pm_3D)
530
+
531
+ ti = np.linspace(0, T, nt)
532
+ timestep = ti[1] - ti[0]
533
+
534
+ sx_pm_3D = -np.diff(bx_pm_3D, n=1, axis=-1, append=np.reshape(bx_pm_3D[:,:,1], (nx, ny, 1)))/timestep
535
+ sy_pm_3D = -np.diff(by_pm_3D, n=1, axis=-1, append=np.reshape(by_pm_3D[:,:,1], (nx, ny, 1)))/timestep
536
+
537
+ return sx_pm_3D, sy_pm_3D
538
+
539
+ def diffFreqD(self, bx_pm_3D, by_pm_3D, T):
540
+ ''' Calculates the time derivative of Bx, By, advanced method
541
+
542
+ Inputs: bx_pm_3D - bx (AC component) for each element for one magnet segment, for each simulation step
543
+ by_pm_3D - by (AC component) for each element for one magnet segment, for each simulation step
544
+ T - total simulation time for periodic simulation (see periodicity_id fcn for more)
545
+ Returns: sx_pm - dBx/dt for each element for one magnet segment, for each simulation step
546
+ sy_pm - dBy/dt for each element for one magnet segment, for each simulation step
547
+ '''
548
+
549
+ (nx, ny, nt) = bx_pm_3D.shape
550
+ sx_pm_3D = np.zeros((nx, ny, nt))
551
+ sy_pm_3D = np.zeros((nx, ny, nt))
552
+ ti = np.linspace(0, T, nt)
553
+ timestep = ti[1] - ti[0]
554
+
555
+ freq = np.fft.rfftfreq(nt-1, timestep)
556
+ nf = freq.shape[0]
557
+ amplbx = np.zeros((freq.shape))
558
+ amplby = np.zeros((freq.shape))
559
+ complbx = np.zeros((nx, ny, nf)).astype(complex)
560
+ complby = np.zeros((nx, ny, nf)).astype(complex)
561
+
562
+ for ii in range(nx):
563
+ for jj in range (ny):
564
+ complbx[ii,jj,:] = np.fft.rfftn(bx_pm_3D[ii,jj,0:nt-1])
565
+ complby[ii,jj,:] = np.fft.rfftn(by_pm_3D[ii,jj,0:nt-1])
566
+ amplbx = amplbx + abs(complbx[ii,jj,0:nf])/nf
567
+ amplby = amplby + abs(complby[ii,jj,0:nf])/nf
568
+
569
+ amplbx = amplbx/(nx*ny)
570
+ amplby = amplby/(nx*ny)
571
+ amplb = np.sqrt(amplbx**2 + amplby**2)
572
+ fmax2 = 0.5*freq[-1]
573
+
574
+ if sum(amplb) == 0:
575
+ warnings.warn('Bx and By data equals to zero, check simulation parameters for this loadcase')
576
+ filt = 0
577
+
578
+ if sum(amplb) > 0:
579
+ pec = (np.multiply(amplb,freq))**2
580
+ pecmax = np.max(pec)
581
+ pec = pec/pecmax
582
+
583
+ fecmax = fmax2
584
+ imax = int(np.floor(nf/2))
585
+ filt0 = np.ones((nf))
586
+ for ii in range(imax,nf):
587
+ filt0[ii] = fecmax/freq[ii]
588
+
589
+ # determine the last significant frequency
590
+ pecf = pec*filt0
591
+ ilim = 0
592
+ feclim = 0
593
+ Ath = 0.05
594
+ for ii in range(1,nf):
595
+ jj = nf - 1 - ii
596
+ if pecf[jj] - pecf[jj + 1] > Ath:
597
+ ilim = jj
598
+ feclim = freq[jj]
599
+ break
600
+
601
+ filt = np.ones((nf))
602
+ for ii in range(ilim,nf):
603
+ filt[ii] = (feclim/freq[ii])
604
+ for ii in range(nx): # Derivation in frequency domain
605
+ for jj in range(ny):
606
+ complbx[ii,jj,:] = -complbx[ii,jj,:]*freq*filt*np.pi*2j
607
+ complby[ii,jj,:] = -complby[ii,jj,:]*freq*filt*np.pi*2j
608
+
609
+ for ii in range (nx): # Inverse Fourier-Transformation
610
+ for jj in range (ny):
611
+ sx = np.fft.irfftn(complbx[ii,jj,:], [nt - 1])
612
+ sy = np.fft.irfftn(complby[ii,jj,:], [nt - 1])
613
+ sx = np.append(sx, sx[0])
614
+ sy = np.append(sy, sy[0])
615
+ sx_pm_3D[ii,jj,:] = sx
616
+ sy_pm_3D[ii,jj,:] = sy
617
+
618
+ return sx_pm_3D, sy_pm_3D
619
+
620
+ def Process_B_data(self, nx, ny, nsegx, nsegy, nt, elxy, bxy, excpl_new, eycpl_new):
621
+ ''' Processes flux density data: interpolates Bx, By to new resolution defined in Segmentation fcn
622
+ calculates the dB/dt for x,y axes
623
+ calculates the FFT of those derivations
624
+
625
+ Inputs: nx,ny - number of elements for each magnet segment (inherited from Segmentation fcn)
626
+ nsegx,nsegy - number of magnet segments in x,y axis
627
+ nt - number of time steps (inherited from result of periodicity function), corresponds to 1 period
628
+ elxy - dictionary with original excpl,eycpl
629
+ bxy - dictionary with original flux denisities bxl, byl - bx, by in local reference frame
630
+ excpl_new, eycpl_new - x,y coordinates for new elements (inherited from Segmentation fcn)
631
+ Returns: sx_abs - dBx/dt amplitudes for each element, for each frequency, for entire magnet
632
+ sy_abs - dBy/dt amplitudes for each element, for each frequency, for entire magnet
633
+ sx_phase - x phase of each element for each frequency, for entire magnet
634
+ sy_phase - y phase of each element for each frequency, for entire magnet
635
+ freq_range - every considered frequency in Hertz
636
+ '''
637
+ nx_tot = int(nx*nsegx)
638
+ ny_tot = int(ny*nsegy)
639
+
640
+ Bxl_ac = np.zeros((np.asarray(bxy['bxl']).shape))
641
+ Byl_ac = np.zeros_like(Bxl_ac)
642
+
643
+ # Remove the DC component of the original bxl, byl
644
+ for ii in range(Bxl_ac.shape[0]):
645
+ Bxl_ac[ii,:] = bxy['bxl'][ii,:] - np.mean(bxy['bxl'][ii,:])
646
+ for ii in range(Byl_ac.shape[0]):
647
+ Byl_ac[ii,:] = bxy['byl'][ii,:] - np.mean(bxy['byl'][ii,:])
648
+
649
+ xx_ = excpl_new.ravel()
650
+ yy_ = eycpl_new.ravel()
651
+ bx_3d_ac = np.zeros((nx_tot,ny_tot,nt))
652
+ by_3d_ac = np.zeros_like(bx_3d_ac)
653
+
654
+ # Interpolation to the new resolution -> [nx*nsegx, ny*nsegy, nt]
655
+ by_3d_ac = binterp_ialh2(elxy['excpl'], elxy['eycpl'], xx_, yy_, Byl_ac[:, 0:nt])
656
+ if self.is_x:
657
+ bx_3d_ac = binterp_ialh2(elxy['excpl'], elxy['eycpl'], xx_, yy_, Bxl_ac[:, 0:nt])
658
+ bx_3d_ac = bx_3d_ac.reshape(nx_tot,ny_tot,nt)
659
+ by_3d_ac = by_3d_ac.reshape(nx_tot,ny_tot,nt)
660
+
661
+ xx_ = xx_.reshape(nx_tot, ny_tot)
662
+ yy_ = yy_.reshape(nx_tot, ny_tot)
663
+
664
+ if nt % 2:
665
+ nf = int((nt - 1)/2 + 1)
666
+ else:
667
+ nf = int(nt / 2)
668
+
669
+ sx_abs = np.zeros((2*nx_tot, 2*ny_tot, nf))
670
+ sy_abs = np.zeros_like(sx_abs)
671
+ sx_phase = np.zeros_like(sx_abs)
672
+ sy_phase = np.zeros_like(sx_phase)
673
+ for ii in range (nsegx):
674
+ for jj in range (nsegy):
675
+
676
+ diffgrad = 3 # choose the derivation metod. diffFreqD method recommended
677
+ if diffgrad == 1:
678
+ sx_pm, sy_pm = self.diffDQ(bx_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
679
+ by_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
680
+ self.tgrid)
681
+ else:
682
+ sx_pm, sy_pm = self.diffFreqD(bx_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
683
+ by_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
684
+ self.tgrid)
685
+
686
+ Gx_seg = np.zeros((2*nx, 2*ny, nt-1)) # omit the last step of the time vector -> duplicated with the first step
687
+ Gx_seg[0:nx, 0:ny,:] = +sx_pm[:,:, 0:-1]
688
+ Gx_seg[0:nx,ny:,:] = -sx_pm[:,::-1, 0:-1] # this section flips and "doubles" the data for each segment, so FFT can include every point properly
689
+ Gx_seg[nx:,:,:] = np.flipud(Gx_seg[0:nx,:,:])
690
+
691
+ Gy_seg = np.zeros((2*nx, 2*ny, nt-1)) # omit the last step of time vector -> duplicated with the first step
692
+ Gy_seg[0:nx, 0:ny,:] = +sy_pm[:,:, 0:-1]
693
+ Gy_seg[nx:,0:ny,:] = -sy_pm[::-1,:, 0:-1] # this section flips and "doubles" the data for each segment, so FFT can include every point properly
694
+ Gy_seg[:,ny:,:] = np.fliplr(Gy_seg[:,0:ny,:])
695
+
696
+ sx_FFT_seg = np.fft.rfftn(Gx_seg)
697
+ sy_FFT_seg = np.fft.rfftn(Gy_seg)
698
+ sx_abs_seg = 2*abs(sx_FFT_seg /(2*nx*2*ny*(nt - 1))) # dBx/dt amplitudes for each segment element, for each frequency for that magnet segment
699
+ sy_abs_seg = 2*abs(sy_FFT_seg /(2*nx*2*ny*(nt - 1))) # dBy/dt amplitudes for each segment element, for each frequency for that magnet segment
700
+ sx_phase_seg = np.angle(sx_FFT_seg)
701
+ sy_phase_seg = np.angle(sy_FFT_seg)
702
+ freq_range = np.fft.rfftfreq(nt-1, self.tgrid/(nt-1)) # every considered frequency in Hertz
703
+
704
+ sx_abs[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:] = sx_abs_seg # dBx/dt amplitudes for each element, for each frequency, for entire magnet
705
+ sy_abs[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:] = sy_abs_seg # dBy/dt amplitudes for each element, for each frequency, for entire magnet
706
+ sx_phase[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:] = sx_phase_seg # x phase of each element for each frequency, for entire magnet
707
+ sy_phase[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:] = sy_phase_seg # y phase of each element for each frequency, for entire magnet
708
+
709
+ return sx_abs, sy_abs, sx_phase, sy_phase, freq_range
710
+
711
+ def loss_ialh2(self, sx_abs, sy_abs, sx_phase, sy_phase, freq_range, nx, ny, wm, hm, lm, nsegx, nsegy, nsegz, delta_eff):
712
+ ''' Loops over each magnet segment and calculates the losses for the entire magnet
713
+
714
+ Inputs: sx_abs, sy_abs - dBx/dt, dBy/dt amplitudes for each element, for each frequency, in entire magnet
715
+ sx_phase, sy_phase - corresponding phases of each freq for each element in entire magnet
716
+ freq_range - every considered frequency in Hz
717
+ nx, ny - number of elements in each magnet segment in x and y direction
718
+ wm, hm, lm - total magnet width (x), height (y), axial length (z)
719
+ nsegx, nsegy, nsegz - number of magnet segments in x,y,z direction
720
+ delta_eff - needed for ialh2 losses calculation, effective airgap between magnet and rotor iron
721
+ Returns: total eddy current losses for entire magnet (all segments), for 1 magnet of the machine
722
+ '''
723
+ pec = np.zeros((nsegx, nsegy, nsegz))
724
+
725
+ for ii in range(nsegx):
726
+ for jj in range(nsegy): # nsegy is always = 1
727
+ for kk in range(nsegz):
728
+ Plossx, Plossy = self.ialh2(sx_abs[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:], sy_abs[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:],
729
+ sx_phase[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:], sy_phase[2*ii*nx:2*(ii+1)*nx, 2*jj*ny:2*(jj+1)*ny,:],
730
+ freq_range, wm/nsegx, hm/nsegy, lm/nsegz, delta_eff)
731
+ pec[ii,jj,kk] = (Plossx + Plossy)
732
+
733
+ return np.sum(pec) # total eddy current losses for the entire magnet (all segments)
734
+
735
+
736
+ def calc_losses_ialh2(self, nsegx=1, nsegy=1, nsegz=1):
737
+ ''' Calculates magnet losses for every load case
738
+
739
+ Inputs: number of magnet segments in x,y,z direction
740
+ Returns: all_load_cases: list of losses for all load cases
741
+ '''
742
+
743
+ nsegx = max(1,nsegx) # 1 = no segmentation
744
+ nsegz = max(1,nsegz) # 1 = no segmentation
745
+ nsegy = 1 # y segmentation not supported, nsegy is always = 1
746
+
747
+ delta_eff = 0
748
+
749
+ all_load_cases = []
750
+ for k in self.pm: # loop for each load case
751
+ ialh_loss = 0
752
+ loss_detail = []
753
+ for i in k: # loop for each superelement in a case
754
+ logger.info(f'magnet width and height: {i["wm"]:.2f}mm {i["hm"]:.2f}mm')
755
+ logger.info(f'number of magnet segments: x: {nsegx:.0f} y: {nsegy:.0f} z: {nsegz:.0f}')
756
+ (nt, bx_fft, by_fft) = self.periodicity_id(i['bl']) # finds the time periodic part of the simulation
757
+ (nx, ny, nz, excpl_new, eycpl_new) = Segmentation(i['wm'], i['hm'], i['lm'], i['elcp'], nsegx, nsegy, nsegz)
758
+ # conversion from mm to m for wm,hm,lm
759
+ wm = i['wm']/1000
760
+ hm = i['hm']/1000
761
+ lm = i['lm']/1000
762
+ self.consider_bx(wm, hm, bx_fft, by_fft)
763
+ (sx_abs, sy_abs, sx_phase, sy_phase, freq_range) = self.Process_B_data(nx, ny, nsegx, nsegy, nt, i['elcp'], i['bl'], excpl_new, eycpl_new)
764
+ loss = self.loss_ialh2(sx_abs, sy_abs, sx_phase, sy_phase, freq_range, nx, ny, wm, hm, lm, nsegx, nsegy, nsegz, delta_eff) * self.numpoles
765
+ ialh_loss += loss
766
+ logger.info(f'Loadcase {i["loadcase"]}, Superelement {i["spel_key"]}, Total losses = {loss:.3f} W')
767
+ loss_detail.append([i['spel_key'], loss/self.numpoles])
768
+ self.th_loss.append(loss_detail)
769
+ all_load_cases.append(ialh_loss)
770
+
771
+ return all_load_cases
772
+
femagtools/femag.py CHANGED
@@ -359,6 +359,72 @@ class BaseFemag(object):
359
359
 
360
360
  return dict()
361
361
 
362
+ def read_hsn(self, modelname=None):
363
+ import numpy as np
364
+ "read heat network result"
365
+ _map = {
366
+ "StZa": "plfe1",
367
+ "outs": "-",
368
+ "StJo": "plfe1",
369
+ "Slot": "-",
370
+ "Shaf": "-",
371
+ "Iron": "plfe2",
372
+ "PMag": "plmag",
373
+ "PMag_1": "plmag",
374
+ "PMag_2": "plmag",
375
+ "PMag_3": "plmag",
376
+ "PMag_4": "plmag",
377
+ "W1 ": "plcu1",
378
+ "W2 ": "plcu1",
379
+ "W3 ": "plcu1"
380
+ }
381
+ if not modelname:
382
+ modelname = self._get_modelname_from_log()
383
+ hsn_list = sorted(glob.glob(os.path.join(
384
+ self.workdir, modelname+'.hsn')))
385
+ with open(hsn_list[-1], 'r') as f:
386
+ hsn_data = json.load(f)
387
+
388
+ # area calculation
389
+ nc_file = self.read_nc(modelname)
390
+ slot_area = 0.0
391
+ wdg_area = []
392
+ for i in nc_file.windings:
393
+ for j in i.subregions:
394
+ wdg_area.append(j.area())
395
+
396
+ slot_area = np.sum(wdg_area).item()/3
397
+ magnet_area = 0.0
398
+ num_sreg_mag = 0
399
+ area = dict()
400
+ for i in nc_file.subregions:
401
+ if i.name in ("StZa", "StJo", "Iron"):
402
+ area[i.name] = i.area().item()
403
+ elif i.name == "PMag":
404
+ magnet_area += i.area().item()
405
+ num_sreg_mag += 1
406
+ else:
407
+ pass
408
+
409
+ area['PMag'] = magnet_area/num_sreg_mag
410
+ for i in ('W1', 'W2', 'W3'):
411
+ area[i] = slot_area
412
+
413
+ pmag_index = []
414
+ if "Nodes" in hsn_data:
415
+ for k ,i in enumerate(hsn_data['Nodes']):
416
+ i.update({"mass": i['weight'], "losses": _map[i['Name']]})
417
+ if "PMag" in i['Name']:
418
+ pmag_index.append(k)
419
+ if i['Name'].strip() in area.keys():
420
+ i.update({"area": area[i['Name'].strip()]})
421
+ if pmag_index:
422
+ for i in range(len(pmag_index)):
423
+ hsn_data["Nodes"][pmag_index[i]]['Name'] = f"PMag_{i+1}"
424
+ with open(hsn_list[-1], 'w') as f:
425
+ json.dump(hsn_data, f)
426
+ return nc_file
427
+
362
428
  def _get_modelname_from_log(self):
363
429
  """
364
430
  Read the modelname from the Femag Log file
@@ -402,7 +468,13 @@ class BaseFemag(object):
402
468
  return {'t': ttemp[0], 'temperature': ttemp[1]}
403
469
 
404
470
  if simulation['calculationMode'] == 'hsn':
405
- model = self.read_nc()
471
+ model = None
472
+ try:
473
+ model = self.read_hsn()
474
+ except:
475
+ pass
476
+ if model is None:
477
+ model = self.read_nc()
406
478
  return model.get_minmax_temp()
407
479
 
408
480
  if not bch:
@@ -456,7 +528,8 @@ class BaseFemag(object):
456
528
  ops = [k for k in range(len(bch.torque))]
457
529
  m = femagtools.ecloss.MagnLoss(self.workdir, self.modelname, ibeta=ops)
458
530
  try:
459
- magn_losses = m.calc_losses()
531
+ # change from ialh to ialh2: since v1.8.1
532
+ magn_losses = m.calc_losses_ialh2()
460
533
  except:
461
534
  magn_losses = [0 for i in range(len(ops))]
462
535
 
@@ -472,11 +545,11 @@ class BaseFemag(object):
472
545
  bch.magnet_loss_th = m.th_loss
473
546
  except:
474
547
  pass
475
- try:
476
- if hasattr(self, 'dy2'):
548
+ try:
549
+ if hasattr(self, 'dy2'):
477
550
  setattr(bch, 'dy2', self.dy2)
478
- except:
479
- pass
551
+ except:
552
+ pass
480
553
  return bch
481
554
 
482
555
 
@@ -608,10 +681,10 @@ class Femag(BaseFemag):
608
681
  stateofproblem = 'mag_static'
609
682
 
610
683
  self.run(fslfile, options, fsl_args, stateofproblem=stateofproblem)
611
-
612
- try:
684
+
685
+ try:
613
686
  setattr(self, "dy2", machine['stator']['dy2'])
614
- except:
687
+ except:
615
688
  pass
616
689
  if simulation:
617
690
  return self.readResult(simulation)