#include #include #include #include #include "fkn.h" #include "fknDlg.h" #include "complex.h" #include "mesh.h" #include "spars.h" #include "fullmatrix.h" #include "FemmeDocCore.h" #define ElementsPerSkinDepth 10 CMaterialProp::~CMaterialProp() { if (Bdata!=NULL) free(Bdata); if (Hdata!=NULL) free(Hdata); if (slope!=NULL) free(slope); } CMaterialProp::CMaterialProp() { slope=NULL; Bdata=NULL; Hdata=NULL; } void CMaterialProp::GetSlopes() { GetSlopes(0); } void CMaterialProp::GetSlopes(double omega) { if (BHpoints==0) return; // catch trivial case; if (slope!=NULL) return; // already have computed the slopes; int i,k; BOOL CurveOK=FALSE; BOOL ProcessedLams=FALSE; CFullMatrix L; double l1,l2; CComplex *hn; double *bn; CComplex mu; L.Create(BHpoints); bn =(double *) calloc(BHpoints,sizeof(double)); hn =(CComplex *)calloc(BHpoints,sizeof(CComplex)); slope=(CComplex *)calloc(BHpoints,sizeof(CComplex)); // strip off some info that we can use during the first // nonlinear iteration; mu_x = Bdata[1]/(muo*abs(Hdata[1])); mu_y = mu_x; Theta_hx=Theta_hn; Theta_hy=Theta_hn; // first, we need to doctor the curve if the problem is // being evaluated at a nonzero frequency. if(omega>0) { // Make an effective B-H curve for harmonic problems. // this one convolves B(H) where H is sinusoidal with // a sine at the same frequency to get the effective // amplitude of B double munow,mumax=0; for(i=1;imumax) mumax=munow; } // apply complex permeability to approximate the // effects of hysteresis. We will follow a kludge // suggested by O'Kelly where hysteresis angle // is proportional to permeability. This implies // that loss goes with B^2 for(i=1;i0) { u0=sqrt(u0); X0=-(c1 + u0)/(2.*c2); X1=(-c1 + u0)/(2.*c2); } //now, see if we've struck gold! if (((X0>=0.)&&(X0<=L))||((X1>=0.)&&(X1<=L))) CurveOK=FALSE; } if(CurveOK!=TRUE) //remedial action { // Smooth out input points // to get rid of rapid transitions; // Uses a 3-point moving average for(i=1;i0) && (Lam_d!=0) && (Cduct!=0)) { // Calculate a new apparent b and h for each point on the // curve to account for the laminations. for(i=1;iBdata[BHpoints-1]) return (Hdata[BHpoints-1] + slope[BHpoints-1]*(b-Bdata[BHpoints-1])); for(i=0;i=Bdata[i]) && (b<=Bdata[i+1])){ l=(Bdata[i+1]-Bdata[i]); z=(b-Bdata[i])/l; z2=z*z; h=(1.-3.*z2+2.*z2*z)*Hdata[i] + z*(1.-2.*z+z2)*l*slope[i] + z2*(3.-2.*z)*Hdata[i+1] + z2*(z-1.)*l*slope[i+1]; return h; } return CComplex(0); } CComplex CMaterialProp::GetdHdB(double B) { double b,z,l; CComplex h; int i; b=fabs(B); if(BHpoints==0) return CComplex(b/(mu_x*muo)); if(b>Bdata[BHpoints-1]) return slope[BHpoints-1]; for(i=0;i=Bdata[i]) && (b<=Bdata[i+1])){ l=(Bdata[i+1]-Bdata[i]); z=(b-Bdata[i])/l; h=6.*z*(z-1.)*Hdata[i]/l + (1.-4.*z+3.*z*z)*slope[i] + 6.*z*(1.-z)*Hdata[i+1]/l + z*(3.*z-2.)*slope[i+1]; return h; } return CComplex(0); } CComplex CMaterialProp::Get_v(double B) { if (B==0) return slope[0]; return (GetH(B)/B); } CComplex CMaterialProp::Get_dvB2(double B) { if (B==0) return 0; return 0.5*(GetdHdB(B)/(B*B) - GetH(B)/(B*B*B)); } void CMaterialProp::GetBHProps(double B, double &v, double &dv) { // version to use in the magnetostatic case in // which we know that v and dv ought to be real-valued. CComplex vc,dvc; GetBHProps(B,vc,dvc); v =Re(vc); dv=Re(dvc); } void CMaterialProp::GetBHProps(double B, CComplex &v, CComplex &dv) { double b,z,z2,l; CComplex h,dh; int i; b=fabs(B); if(BHpoints==0){ v=mu_x; dv=0; return; } if(b==0){ v=slope[0]; dv=0; return; } if(b>Bdata[BHpoints-1]){ h=(Hdata[BHpoints-1] + slope[BHpoints-1]*(b-Bdata[BHpoints-1])); dh=slope[BHpoints-1]; v=h/b; dv=0.5*(dh/(b*b) - h/(b*b*b)); return; } for(i=0;i=Bdata[i]) && (b<=Bdata[i+1])){ l=(Bdata[i+1]-Bdata[i]); z=(b-Bdata[i])/l; z2=z*z; h=(1.-3.*z2+2.*z2*z)*Hdata[i] + z*(1.-2.*z+z2)*l*slope[i] + z2*(3.-2.*z)*Hdata[i+1] + z2*(z-1.)*l*slope[i+1]; dh=6.*z*(z-1.)*Hdata[i]/l + (1.-4.*z+3.*z*z)*slope[i] + 6.*z*(1.-z)*Hdata[i+1]/l + z*(3.*z-2.)*slope[i+1]; v=h/b; dv=0.5*(dh/(b*b) - h/(b*b*b)); return; } } CComplex CMaterialProp::LaminatedBH(double w, int i) { int k,n,iter=0; CComplex *m0,*m1,*b,*x; double L,o,d,ds,B,lastres,res; double Relax=1; CComplex mu,vo,vi,c,H; CComplex Md,Mo; BOOL Converged=FALSE; res=0; // Base the required element spacing on the skin depth // at the surface of the material mu=Bdata[i]/Hdata[i]; o=Cduct*1.e6; d=(Lam_d*0.001)/2.; ds=sqrt(2/(w*o*abs(mu))); n= ElementsPerSkinDepth * ((int) ceil(d/ds)); L=d/((double) n); x =(CComplex *)calloc(n+1,sizeof(CComplex)); b =(CComplex *)calloc(n+1,sizeof(CComplex)); m0=(CComplex *)calloc(n+1,sizeof(CComplex)); m1=(CComplex *)calloc(n+1,sizeof(CComplex)); do{ // make sure that the old stuff is wiped out; for(k=0;k<=n;k++) { m0[k]=0; m1[k]=0; b[k] =0; } // build matrix for(k=0;k= 0; k--) b[k] = (b[k] - m1[k]*b[k + 1])/m0[k]; iter++; lastres=res; res=abs(b[n]-x[n])/d; if (res<1.e-8) Converged=TRUE; // Do the same relaxation scheme as is implemented // in the solver to make sure that this effective // lamination permeability calculation converges if(iter>5) { if ((res>lastres) && (Relax>0.1)) Relax/=2.; else Relax+= 0.1 * (1. - Relax); } for(k=0;k<=n;k++) x[k]=Relax*b[k]+(1.0-Relax)*x[k]; }while(Converged!=TRUE); mu = x[n]/(Hdata[i]*d); free(x ); free(b ); free(m0); free(m1); return mu; } // get incremental permeability of a nonlinear material for use in // incremental permeability formulation about DC offset void CMaterialProp::IncrementalPermeability(double B, double w, CComplex &mu1, CComplex &mu2) { // B == flux density in Tesla // w == frequency in rad/s double muinc,murel; CComplex k; double mu; // get incremental permeability of the DC material // (i.e. incremental permeability at the offset) muinc=1./(muo*Re(GetdHdB(B))); murel=1./(muo*Re(Get_v(B))); // if material is not laminated, just apply hysteresis lag... if ((Lam_d==0) || (LamFill==0)) { mu1=muinc*exp(-I*Theta_hn*DEG*muinc/MuMax); mu2=murel*exp(-I*Theta_hn*DEG*murel/MuMax); return; } // crap. Need to make an equivalent permeability that rolls in the effects of laminated // eddy currents, using the incremental permeability as the basis for creating the impedance. // this can get annoying because we need to back out the iron portion of the permeability // in the lamfill<1 case... if (Cduct!=0) { CComplex deg45; deg45=1+I; CComplex K,halflag; double ds; // incremental permeability direction mu = (muinc - (1.-LamFill))/LamFill; halflag=exp(-I*Theta_hn*DEG*mu/(2.*MuMax)); ds=sqrt(2./(0.4*PI*w*Cduct*mu)); K=halflag*deg45*Lam_d*0.001/(2.*ds); mu1=(LamFill*mu*tanh(K)/K + (1.- LamFill)); // normal permeability direction mu = (murel - (1.-LamFill))/LamFill; halflag=exp(-I*Theta_hn*DEG*mu/(2.*MuMax)); ds=sqrt(2./(0.4*PI*w*Cduct*mu)); K=halflag*deg45*Lam_d*0.001/(2.*ds); mu2=(LamFill*mu*tanh(K)/K + (1.- LamFill)); return; } else{ // incremental permeability direction mu = (muinc - (1.-LamFill))/LamFill; mu1=(mu*exp(-I*Theta_hn*DEG*mu/MuMax)*LamFill + (1.-LamFill)); // normal permeability direction mu = (murel - (1.-LamFill))/LamFill; mu2=(mu*exp(-I*Theta_hn*DEG*mu/MuMax)*LamFill + (1.-LamFill)); return; } } void CMaterialProp::IncrementalPermeability(double B, double &mu1, double &mu2) { // B == flux density in Tesla double muinc, murel; // get incremental permeability of the DC material // (i.e. incremental permeability at the offset) muinc = 1. / (muo*Re(GetdHdB(B))); murel = 1. / (muo*Re(Get_v(B))); // if material is not laminated, just return if ((Lam_d == 0) || (LamFill == 0)){ mu1 = muinc; mu2 = murel; return; } // incremental permeability direction mu1 = (muinc*LamFill + (1. - LamFill)); // normal permeability direction mu2 = (murel*LamFill + (1. - LamFill)); return; }