femagtools 1.7.8__py3-none-any.whl → 1.8.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 (54) hide show
  1. femagtools/__init__.py +1 -1
  2. femagtools/amela.py +2 -2
  3. femagtools/dxfsl/area.py +142 -9
  4. femagtools/dxfsl/conv.py +2 -9
  5. femagtools/dxfsl/converter.py +33 -9
  6. femagtools/dxfsl/fslrenderer.py +13 -12
  7. femagtools/dxfsl/geom.py +39 -6
  8. femagtools/dxfsl/journal.py +2 -2
  9. femagtools/dxfsl/machine.py +14 -13
  10. femagtools/dxfsl/shape.py +3 -0
  11. femagtools/dxfsl/svgparser.py +31 -4
  12. femagtools/ecloss.py +381 -2
  13. femagtools/femag.py +55 -0
  14. femagtools/fsl.py +74 -47
  15. femagtools/isa7.py +41 -0
  16. femagtools/job.py +2 -2
  17. femagtools/machine/afpm.py +5 -1
  18. femagtools/machine/pm.py +1 -1
  19. femagtools/machine/sm.py +14 -0
  20. femagtools/machine/utils.py +4 -3
  21. femagtools/mcv.py +128 -124
  22. femagtools/me.py +13 -13
  23. femagtools/model.py +14 -1
  24. femagtools/moo/population.py +9 -7
  25. femagtools/nc.py +12 -0
  26. femagtools/plot/__init__.py +1 -1
  27. femagtools/plot/fieldlines.py +1 -1
  28. femagtools/plot/mcv.py +18 -0
  29. femagtools/plot/nc.py +22 -5
  30. femagtools/plot/wdg.py +40 -7
  31. femagtools/svgfsl/converter.py +6 -0
  32. femagtools/templates/gen_hairpin_winding.mako +36 -45
  33. femagtools/templates/gen_winding.mako +7 -0
  34. femagtools/templates/magnetIron.mako +30 -46
  35. femagtools/templates/magnetIron2.mako +39 -0
  36. femagtools/templates/magnetIron3.mako +39 -0
  37. femagtools/templates/magnetIron4.mako +39 -0
  38. femagtools/templates/magnetIron5.mako +39 -0
  39. femagtools/templates/magnetIronV.mako +34 -54
  40. femagtools/templates/magnetSector.mako +32 -47
  41. femagtools/templates/mesh-airgap.mako +12 -6
  42. femagtools/templates/prepare_thermal.mako +354 -0
  43. femagtools/templates/statorRotor3.mako +3 -22
  44. femagtools/windings.py +92 -59
  45. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/METADATA +20 -18
  46. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/RECORD +53 -53
  47. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/WHEEL +1 -1
  48. tests/test_fsl.py +1 -1
  49. tests/test_mcv.py +106 -1
  50. tests/test_windings.py +18 -2
  51. tests/test_mcvwriter.py +0 -96
  52. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/LICENSE +0 -0
  53. {femagtools-1.7.8.dist-info → femagtools-1.8.0.dist-info}/entry_points.txt +0 -0
  54. {femagtools-1.7.8.dist-info → femagtools-1.8.0.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)
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,329 @@ 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
+ pec = (np.multiply(amplb,freq))**2
576
+ pecmax = np.max(pec)
577
+ pec = pec/pecmax
578
+
579
+ fecmax = fmax2
580
+ imax = int(np.floor(nf/2))
581
+ filt0 = np.ones((nf))
582
+ for ii in range(imax,nf):
583
+ filt0[ii] = fecmax/freq[ii]
584
+
585
+ # determine the last significant frequency
586
+ pecf = pec*filt0
587
+ ilim = 0
588
+ feclim = 0
589
+ Ath = 0.05
590
+ for ii in range(1,nf):
591
+ jj = nf - 1 - ii
592
+ if pecf[jj] - pecf[jj + 1] > Ath:
593
+ ilim = jj
594
+ feclim = freq[jj]
595
+ break
596
+
597
+ filt = np.ones((nf))
598
+ for ii in range(ilim,nf):
599
+ filt[ii] = (feclim/freq[ii])
600
+ for ii in range(nx): # Derivation in frequency domain
601
+ for jj in range(ny):
602
+ complbx[ii,jj,:] = -complbx[ii,jj,:]*freq*filt*np.pi*2j
603
+ complby[ii,jj,:] = -complby[ii,jj,:]*freq*filt*np.pi*2j
604
+
605
+ for ii in range (nx): # Inverse Fourier-Transformation
606
+ for jj in range (ny):
607
+ sx_pm_3D[ii,jj,0:-1] = np.fft.irfftn(complbx[ii,jj,:])
608
+ sy_pm_3D[ii,jj,0:-1] = np.fft.irfftn(complby[ii,jj,:])
609
+ sx_pm_3D[ii,jj,nt-1] = sx_pm_3D[ii,jj,0]
610
+ sy_pm_3D[ii,jj,nt-1] = sy_pm_3D[ii,jj,0]
611
+
612
+ return sx_pm_3D, sy_pm_3D
613
+
614
+ def Process_B_data(self, nx, ny, nsegx, nsegy, nt, elxy, bxy, excpl_new, eycpl_new):
615
+ ''' Processes flux density data: interpolates Bx, By to new resolution defined in Segmentation fcn
616
+ calculates the dB/dt for x,y axes
617
+ calculates the FFT of those derivations
618
+
619
+ Inputs: nx,ny - number of elements for each magnet segment (inherited from Segmentation fcn)
620
+ nsegx,nsegy - number of magnet segments in x,y axis
621
+ nt - number of time steps (inherited from result of periodicity function), corresponds to 1 period
622
+ elxy - dictionary with original excpl,eycpl
623
+ bxy - dictionary with original flux denisities bxl, byl - bx, by in local reference frame
624
+ excpl_new, eycpl_new - x,y coordinates for new elements (inherited from Segmentation fcn)
625
+ Returns: sx_abs - dBx/dt amplitudes for each element, for each frequency, for entire magnet
626
+ sy_abs - dBy/dt amplitudes for each element, for each frequency, for entire magnet
627
+ sx_phase - x phase of each element for each frequency, for entire magnet
628
+ sy_phase - y phase of each element for each frequency, for entire magnet
629
+ freq_range - every considered frequency in Hertz
630
+ '''
631
+ nx_tot = int(nx*nsegx)
632
+ ny_tot = int(ny*nsegy)
633
+
634
+ Bxl_ac = np.zeros((np.asarray(bxy['bxl']).shape))
635
+ Byl_ac = np.zeros_like(Bxl_ac)
636
+
637
+ # Remove the DC component of the original bxl, byl
638
+ for ii in range(Bxl_ac.shape[0]):
639
+ Bxl_ac[ii,:] = bxy['bxl'][ii,:] - np.mean(bxy['bxl'][ii,:])
640
+ for ii in range(Byl_ac.shape[0]):
641
+ Byl_ac[ii,:] = bxy['byl'][ii,:] - np.mean(bxy['byl'][ii,:])
642
+
643
+ xx_ = excpl_new.ravel()
644
+ yy_ = eycpl_new.ravel()
645
+ bx_3d_ac = np.zeros((nx_tot,ny_tot,nt))
646
+ by_3d_ac = np.zeros_like(bx_3d_ac)
647
+
648
+ # Interpolation to the new resolution -> [nx*nsegx, ny*nsegy, nt]
649
+ by_3d_ac = binterp_ialh2(elxy['excpl'], elxy['eycpl'], xx_, yy_, Byl_ac[:, 0:nt])
650
+ if self.is_x:
651
+ bx_3d_ac = binterp_ialh2(elxy['excpl'], elxy['eycpl'], xx_, yy_, Bxl_ac[:, 0:nt])
652
+ bx_3d_ac = bx_3d_ac.reshape(nx_tot,ny_tot,nt)
653
+ by_3d_ac = by_3d_ac.reshape(nx_tot,ny_tot,nt)
654
+
655
+ xx_ = xx_.reshape(nx_tot, ny_tot)
656
+ yy_ = yy_.reshape(nx_tot, ny_tot)
657
+
658
+ if nt % 2:
659
+ nf = int((nt - 1)/2 + 1)
660
+ else:
661
+ nf = int(nt / 2)
662
+
663
+ sx_abs = np.zeros((2*nx_tot, 2*ny_tot, nf))
664
+ sy_abs = np.zeros_like(sx_abs)
665
+ sx_phase = np.zeros_like(sx_abs)
666
+ sy_phase = np.zeros_like(sx_phase)
667
+ for ii in range (nsegx):
668
+ for jj in range (nsegy):
669
+
670
+ diffgrad = 3 # choose the derivation metod. diffFreqD method recommended
671
+ if diffgrad == 1:
672
+ sx_pm, sy_pm = self.diffDQ(bx_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
673
+ by_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
674
+ self.tgrid)
675
+ else:
676
+ sx_pm, sy_pm = self.diffFreqD(bx_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
677
+ by_3d_ac[ii*nx:(ii+1)*nx, jj*ny:(jj+1)*ny,:],
678
+ self.tgrid)
679
+
680
+ Gx_seg = np.zeros((2*nx, 2*ny, nt-1)) # omit the last step of the time vector -> duplicated with the first step
681
+ Gx_seg[0:nx, 0:ny,:] = +sx_pm[:,:, 0:-1]
682
+ 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
683
+ Gx_seg[nx:,:,:] = np.flipud(Gx_seg[0:nx,:,:])
684
+
685
+ Gy_seg = np.zeros((2*nx, 2*ny, nt-1)) # omit the last step of time vector -> duplicated with the first step
686
+ Gy_seg[0:nx, 0:ny,:] = +sy_pm[:,:, 0:-1]
687
+ 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
688
+ Gy_seg[:,ny:,:] = np.fliplr(Gy_seg[:,0:ny,:])
689
+
690
+ sx_FFT_seg = np.fft.rfftn(Gx_seg)
691
+ sy_FFT_seg = np.fft.rfftn(Gy_seg)
692
+ 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
693
+ 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
694
+ sx_phase_seg = np.angle(sx_FFT_seg)
695
+ sy_phase_seg = np.angle(sy_FFT_seg)
696
+ freq_range = np.fft.rfftfreq(nt-1, self.tgrid/(nt-1)) # every considered frequency in Hertz
697
+
698
+ 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
699
+ 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
700
+ 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
701
+ 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
702
+
703
+ return sx_abs, sy_abs, sx_phase, sy_phase, freq_range
704
+
705
+ def loss_ialh2(self, sx_abs, sy_abs, sx_phase, sy_phase, freq_range, nx, ny, wm, hm, lm, nsegx, nsegy, nsegz, delta_eff):
706
+ ''' Loops over each magnet segment and calculates the losses for the entire magnet
707
+
708
+ Inputs: sx_abs, sy_abs - dBx/dt, dBy/dt amplitudes for each element, for each frequency, in entire magnet
709
+ sx_phase, sy_phase - corresponding phases of each freq for each element in entire magnet
710
+ freq_range - every considered frequency in Hz
711
+ nx, ny - number of elements in each magnet segment in x and y direction
712
+ wm, hm, lm - total magnet width (x), height (y), axial length (z)
713
+ nsegx, nsegy, nsegz - number of magnet segments in x,y,z direction
714
+ delta_eff - needed for ialh2 losses calculation, effective airgap between magnet and rotor iron
715
+ Returns: total eddy current losses for entire magnet (all segments), for 1 magnet of the machine
716
+ '''
717
+ pec = np.zeros((nsegx, nsegy, nsegz))
718
+
719
+ for ii in range(nsegx):
720
+ for jj in range(nsegy): # nsegy is always = 1
721
+ for kk in range(nsegz):
722
+ 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,:],
723
+ 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,:],
724
+ freq_range, wm/nsegx, hm/nsegy, lm/nsegz, delta_eff)
725
+ pec[ii,jj,kk] = (Plossx + Plossy)
726
+
727
+ return np.sum(pec) # total eddy current losses for the entire magnet (all segments)
728
+
729
+
730
+ def calc_losses_ialh2(self, nsegx=1, nsegy=1, nsegz=1):
731
+ ''' Calculates magnet losses for every load case
732
+
733
+ Inputs: number of magnet segments in x,y,z direction
734
+ Returns: all_load_cases: list of losses for all load cases
735
+ '''
736
+
737
+ nsegx = max(1,nsegx) # 1 = no segmentation
738
+ nsegz = max(1,nsegz) # 1 = no segmentation
739
+ if nsegy != 1:
740
+ nsegy = 1 # y segmentation not supported, nsegy is always = 1
741
+
742
+ delta_eff = 0
743
+
744
+ all_load_cases = []
745
+ for k in self.pm: # loop for each load case
746
+ ialh_loss = 0
747
+ loss_detail = []
748
+ for i in k: # loop for each superelement in a case
749
+ logger.info(f'magnet width and height: {i["wm"]:.2f}mm {i["hm"]:.2f}mm')
750
+ logger.info(f'number of magnet segments: x: {nsegx:.0f} y: {nsegy:.0f} z: {nsegz:.0f}')
751
+ (nt, bx_fft, by_fft) = self.periodicity_id(i['bl']) # finds the time periodic part of the simulation
752
+ (nx, ny, nz, excpl_new, eycpl_new) = Segmentation(i['wm'], i['hm'], i['lm'], i['elcp'], nsegx, nsegy, nsegz)
753
+ # conversion from mm to m for wm,hm,lm
754
+ wm = i['wm']/1000
755
+ hm = i['hm']/1000
756
+ lm = i['lm']/1000
757
+ self.consider_bx(wm, hm, bx_fft, by_fft)
758
+ (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)
759
+ 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
760
+ ialh_loss += loss
761
+ #print(f'Loadcase {i['loadcase']}, Superelement {i['spel_key']}, Total losses = {loss:.3f} W')
762
+ loss_detail.append([i['spel_key'], loss/self.numpoles])
763
+ self.th_loss.append(loss_detail)
764
+ all_load_cases.append(ialh_loss)
765
+
766
+ return all_load_cases
767
+
femagtools/femag.py CHANGED
@@ -359,6 +359,43 @@ class BaseFemag(object):
359
359
 
360
360
  return dict()
361
361
 
362
+ def read_hsn(self, modelname=None):
363
+ "read heat network result"
364
+ _map = {
365
+ "StZa": "plfe1",
366
+ "outs": "-",
367
+ "StJo": "plfe1",
368
+ "Slot": "-",
369
+ "Shaf": "-",
370
+ "Iron": "plfe2",
371
+ "PMag": "plmag",
372
+ "PMag_1": "plmag",
373
+ "PMag_2": "plmag",
374
+ "PMag_3": "plmag",
375
+ "PMag_4": "plmag",
376
+ "W1 ": "plcu1",
377
+ "W2 ": "plcu1",
378
+ "W3 ": "plcu1"
379
+ }
380
+ if not modelname:
381
+ modelname = self._get_modelname_from_log()
382
+ hsn_list = sorted(glob.glob(os.path.join(
383
+ self.workdir, modelname+'.hsn')))
384
+ with open(hsn_list[-1], 'r') as f:
385
+ hsn_data = json.load(f)
386
+ pmag_index = []
387
+ if "Nodes" in hsn_data:
388
+ for k ,i in enumerate(hsn_data['Nodes']):
389
+ i.update({"mass": i['weight'], "losses": _map[i['Name']]})
390
+ if "PMag" in i['Name']:
391
+ pmag_index.append(k)
392
+ if pmag_index:
393
+ for i in range(len(pmag_index)):
394
+ hsn_data["Nodes"][pmag_index[i]]['Name'] = f"PMag_{i+1}"
395
+ with open(hsn_list[-1], 'w') as f:
396
+ json.dump(hsn_data, f)
397
+ return hsn_data
398
+
362
399
  def _get_modelname_from_log(self):
363
400
  """
364
401
  Read the modelname from the Femag Log file
@@ -401,6 +438,14 @@ class BaseFemag(object):
401
438
  ttemp = list(zip(*temp))
402
439
  return {'t': ttemp[0], 'temperature': ttemp[1]}
403
440
 
441
+ if simulation['calculationMode'] == 'hsn':
442
+ try:
443
+ hsn_result = self.read_hsn()
444
+ except:
445
+ pass
446
+ model = self.read_nc()
447
+ return model.get_minmax_temp()
448
+
404
449
  if not bch:
405
450
  bch = self.read_bch(self.modelname)
406
451
  if simulation['calculationMode'] == 'pm_sym_fast' or \
@@ -468,6 +513,11 @@ class BaseFemag(object):
468
513
  bch.magnet_loss_th = m.th_loss
469
514
  except:
470
515
  pass
516
+ try:
517
+ if hasattr(self, 'dy2'):
518
+ setattr(bch, 'dy2', self.dy2)
519
+ except:
520
+ pass
471
521
  return bch
472
522
 
473
523
 
@@ -599,6 +649,11 @@ class Femag(BaseFemag):
599
649
  stateofproblem = 'mag_static'
600
650
 
601
651
  self.run(fslfile, options, fsl_args, stateofproblem=stateofproblem)
652
+
653
+ try:
654
+ setattr(self, "dy2", machine['stator']['dy2'])
655
+ except:
656
+ pass
602
657
  if simulation:
603
658
  return self.readResult(simulation)
604
659