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.
- femagtools/__init__.py +1 -1
- femagtools/amela.py +2 -2
- femagtools/dxfsl/area.py +130 -26
- femagtools/dxfsl/conv.py +2 -14
- femagtools/dxfsl/converter.py +69 -12
- femagtools/dxfsl/fslrenderer.py +15 -13
- femagtools/dxfsl/geom.py +153 -82
- femagtools/dxfsl/journal.py +2 -2
- femagtools/dxfsl/machine.py +19 -15
- femagtools/dxfsl/shape.py +3 -0
- femagtools/ecloss.py +386 -2
- femagtools/femag.py +82 -9
- femagtools/fsl.py +52 -56
- femagtools/machine/pm.py +1 -1
- femagtools/machine/sm.py +16 -8
- femagtools/mcv.py +128 -124
- femagtools/me.py +13 -13
- femagtools/model.py +8 -2
- femagtools/plot/fieldlines.py +1 -1
- femagtools/plot/mcv.py +18 -0
- femagtools/plot/wdg.py +2 -2
- femagtools/svgfsl/converter.py +1 -1
- femagtools/templates/afm_rotor.mako +4 -0
- femagtools/templates/gen_hairpin_winding.mako +36 -45
- femagtools/templates/magnetIron.mako +1 -1
- femagtools/templates/magnetIron2.mako +1 -1
- femagtools/templates/magnetIron3.mako +1 -1
- femagtools/templates/magnetIron4.mako +1 -1
- femagtools/templates/magnetIron5.mako +1 -1
- femagtools/templates/magnetIronV.mako +1 -1
- femagtools/templates/magnetSector.mako +1 -1
- femagtools/templates/mesh-airgap.mako +12 -6
- femagtools/templates/prepare_thermal.mako +199 -61
- femagtools/windings.py +25 -20
- {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/METADATA +20 -20
- {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/RECORD +42 -43
- {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/WHEEL +1 -1
- tests/test_mcv.py +106 -1
- tests/test_windings.py +5 -0
- tests/test_mcvwriter.py +0 -96
- {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/LICENSE +0 -0
- {femagtools-1.7.9.dist-info → femagtools-1.8.1.dist-info}/entry_points.txt +0 -0
- {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
|
-
#
|
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 =
|
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
|
-
|
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)
|