#include "stdafx.h" #include "problem.h" #include "fullmatrix.h" #define ElementsPerSkinDepth 10 ///////////////////////////////////////////////////////////////////////////// // CNode construction CNode::CNode() { x=0.; y=0.; IsSelected=FALSE; BoundaryMarker=-1; } CComplex CNode::CC() { return (x+I*y); } double CNode::GetDistance(double xo, double yo) { return sqrt((x-xo)*(x-xo) + (y-yo)*(y-yo)); } void CNode::ToggleSelect() { if (IsSelected==TRUE) IsSelected=FALSE; else IsSelected=TRUE; } ///////////////////////////////////////////////////////////////////////////// // CMeshNode construction CMeshNode::CMeshNode() { x=0.; y=0.; A.re=0; A.im=0; msk=0; } CComplex CMeshNode::CC() { return (x+I*y); } double CMeshNode::GetDistance(double xo, double yo) { return sqrt((x-xo)*(x-xo) + (y-yo)*(y-yo)); } ///////////////////////////////////////////////////////////////////////////// // CSegment construction CSegment::CSegment() { n0=0; n1=0; IsSelected=FALSE; BoundaryMarker=-1; InGroup=0; } void CSegment::ToggleSelect() { if (IsSelected==TRUE) IsSelected=FALSE; else IsSelected=TRUE; } ///////////////////////////////////////////////////////////////////////////// // CArcSegment construction CArcSegment::CArcSegment() { n0=0; n1=0; IsSelected=FALSE; MaxSideLength=-1; ArcLength=90.; BoundaryMarker=-1; InGroup=0; } void CArcSegment::ToggleSelect() { if (IsSelected==TRUE) IsSelected=FALSE; else IsSelected=TRUE; } ///////////////////////////////////////////////////////////////////////////// // CNode construction CBlockLabel::CBlockLabel() { x=0.; y=0.; MaxArea=0.; IsSelected=FALSE; InGroup=0; InCircuit=0; BlockType=-1; IsExternal=FALSE; IsDefault=FALSE; Case=0; dVolts=0.; J=0.; FillFactor=1; o=0; mu=0; LocalEnergy=0; } void CBlockLabel::ToggleSelect() { if (IsSelected==TRUE) IsSelected=FALSE; else IsSelected=TRUE; } double CBlockLabel::GetDistance(double xo, double yo) { return sqrt((x-xo)*(x-xo) + (y-yo)*(y-yo)); } CMaterialProp::CMaterialProp() { BlockName="New Material"; mu_x=1.; mu_y=1.; // permeabilities, relative BHpoints=0; Bdata=NULL; Hdata=NULL; slope=NULL; H_c=0.; // magnetization, A/m Nrg=0.; Jr=0.; Ji=0.; // applied current density, MA/m^2 Cduct=0.; // conductivity of the material, MS/m Lam_d=0.; // lamination thickness, mm Theta_hn=0.; // hysteresis angle, degrees Theta_hx=0.; // hysteresis angle, degrees Theta_hy=0.; // hysteresis angle, degrees NStrands=0; // number of strands per wire WireD=0; LamFill=1.; // lamination fill factor; LamType=0; // type of lamination; MuMax=0.; // maximum permeability (used for incremental permeability problems Frequency=0; // problem frequency in Hz (needed for incremental permeability problems } CMaterialProp::~CMaterialProp() { if (Bdata!=NULL) free(Bdata); if (Hdata!=NULL) free(Hdata); if (slope!=NULL) free(slope); } 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; CComplexFullMatrix 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;i= 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; } 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); } double CMaterialProp::GetH(double x) { return Re(GetH(CComplex(x))); } CComplex CMaterialProp::GetH(CComplex x) { double b,z,z2,l; CComplex p,h; int i; b=abs(x); if((BHpoints==0) || (b==0)) return 0; p=x/b; if(b>Bdata[BHpoints-1]) return p*(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 p*h; } return 0; } double CMaterialProp::GetB(double hc) { if (BHpoints==0) return muo*mu_x*hc; double b,bo; b=0; do{ bo = b; b = bo + (hc-GetH(bo))/Re(GetdHdB(bo)); }while (fabs(b-bo)>1.e-8); return b; } // GetEnergy for the magnetostatic case double CMaterialProp::GetEnergy(double x) { double b,z,z2,l,nrg; double b0,b1,h0,h1,dh0,dh1; int i; b=fabs(x); nrg=0.; if(BHpoints==0) return 0; for(i=0;i=b0) && (b<=b1)){ l=b1-b0; z=(b-b0)/l; z2=z*z; nrg += (dh0*l*l*(6. + z*(-8. + 3.*z))*z2)/12. + (h0*l*z*(2. + (-2. + z)*z2))/2. - (h1*l*(-2. + z)*z2*z)/2. + (dh1*l*l*(-4. + 3.*z)*z2*z)/12; return nrg; } else{ // point isn't in this section, but add in the // energy required to pass through it. b0=Bdata[i]; h0=Re(Hdata[i]); b1=Bdata[i+1]; h1=Re(Hdata[i+1]); dh0=Re(slope[i]); dh1=Re(slope[i+1]); nrg += ((b0 - b1)*((b0 - b1)*(dh0 - dh1) - 6.*(h0 + h1)))/12.; } } // if we've gotten to this point, the point is off the scale, // so we have to extrapolate the rest of the way... h0=Re(Hdata[BHpoints-1]); dh0=Re(slope[BHpoints-1]); b0=Bdata[BHpoints-1]; nrg += ((b - b0)*(b*dh0 - b0*dh0 + 2*h0))/2.; return nrg; } double CMaterialProp::GetCoEnergy(double b) { return (fabs(b)*GetH(b) - GetEnergy(b)); } double CMaterialProp::DoEnergy(double b1, double b2) { // calls the raw routine to get point energy, // but deals with the load of special cases that // arise because I insisted on trying to deal with // laminations on a continuum basis. double nrg,biron,bair; // easiest case: the material is linear! if (BHpoints==0) { double h1,h2; if(LamType==0){ // laminated in-plane h1=b1/((1.+LamFill*(mu_x-1.))*muo); h2=b2/((1.+LamFill*(mu_y-1.))*muo); } if(LamType==1){ // laminated parallel to x; h1=b1/((1.+LamFill*(mu_x-1.))*muo); h2=b1*(LamFill/(mu_y*muo) + (1. - LamFill)/muo); } if(LamType==2){ // laminated parallel to x; h2=b1/((1.+LamFill*(mu_y-1.))*muo); h1=b1*(LamFill/(mu_x*muo) + (1. - LamFill)/muo); } if(LamType>2){ h1=b1/muo; h2=b2/muo; } return ((h1*b1+h2*b2)/2.); } // Rats! The material is nonlinear. Now, we have to do // a bit of work to get the energy. if(LamType==0) nrg = GetEnergy(sqrt(b1*b1+b2*b2)); if(LamType==1){ biron=sqrt((b1/LamFill)*(b1/LamFill) + b2*b2); bair=b2; nrg = LamFill*GetEnergy(biron)+(1-LamFill)*bair*bair/(2.*muo); } if(LamType==2){ biron=sqrt((b2/LamFill)*(b2/LamFill) + b1*b1); bair=b1; nrg = LamFill*GetEnergy(biron)+(1-LamFill)*bair*bair/(2.*muo); } return nrg; } double CMaterialProp::DoCoEnergy(double b1, double b2) { double nrg,biron,bair; // easiest case: the material is linear! // in this case, energy and coenergy are the same! if (BHpoints==0) return DoEnergy(b1,b2); if(LamType==0) nrg = GetCoEnergy(sqrt(b1*b1+b2*b2)); if(LamType==1){ biron=sqrt((b1/LamFill)*(b1/LamFill) + b2*b2); bair=b2; nrg = LamFill*GetCoEnergy(biron)+(1-LamFill)*bair*bair/(2.*muo); } if(LamType==2){ biron=sqrt((b2/LamFill)*(b2/LamFill) + b1*b1); bair=b1; nrg = LamFill*GetCoEnergy(biron)+(1-LamFill)*bair*bair/(2.*muo); } return nrg; } double CMaterialProp::DoEnergy(CComplex b1, CComplex b2) { // This one is meant for the frequency!=0 case. // Fortunately, there's not so much effort in this case. CComplex mu1,mu2,h1,h2; GetMu(b1,b2,mu1,mu2); h1=b1/(mu1*muo); h2=b2/(mu2*muo); return (Re(h1*conj(b1)+h2*conj(b2))/4.); } double CMaterialProp::DoCoEnergy(CComplex b1, CComplex b2) { return DoEnergy(b1,b2); } void CMaterialProp::GetMu(CComplex b1, CComplex b2, CComplex &mu1, CComplex &mu2) { // gets the permeability, given a flux density // version for frequency!=0 CComplex biron; // easiest case: the material is linear! if (BHpoints==0) { mu1=mu_fdx; mu2=mu_fdy; return; } // Rats! The material is nonlinear. if(MuMax>0) // incremental solution case... { // Arrgh!!! need incremental permeability about flux density from // DC solution, not flux density of AC solution here. // we're assuming that, in this case, b1 and b2 _do_ contain the DC operating point IncrementalPermeability(sqrt(Re(b1*conj(b1)+b2*conj(b2))),mu1,mu2); // MsgBox("mu=%g %g; b=%g",Re(mu1),Im(mu1),sqrt(Re(b1*conj(b1)+b2*conj(b2)))); return; } CComplex muiron; if(LamType==0){ biron=sqrt(b1*conj(b1)+b2*conj(b2)); if(abs(biron)<1.e-08) mu1=1./slope[0]; //catch degenerate case else mu1=biron/GetH(biron); mu2=mu1; } if(LamType==1){ biron=sqrt((b1/LamFill)*(b1/LamFill) + b2*b2); if(abs(biron)<1.e-08) muiron=1./slope[0]; else muiron=biron/GetH(biron); mu1=muiron*LamFill; mu2=1./(LamFill/muiron + (1. - LamFill)/muo); } if(LamType==2){ biron=sqrt((b2/LamFill)*(b2/LamFill) + b1*b1); if(abs(biron)<1.e-08) muiron=1./slope[0]; else muiron=biron/GetH(biron); mu2=muiron*LamFill; mu1=1./(LamFill/muiron + (1. - LamFill)/muo); } // convert to relative permeability mu1/=muo; mu2/=muo; return; } void CMaterialProp::GetMu(double b1, double b2, double &mu1, double &mu2) { // gets the permeability, given a flux density // double biron; mu1=mu2=muo; // default // catch the incremental permeability case if (MuMax>0) // for DC problems, MuMax is sleazily used as a flag to tell that it's incremental { IncrementalPermeability(sqrt(b1*b1 + b2*b2), mu1, mu2); return; } // easiest case: the material is linear! if (BHpoints==0) { if(LamType==0){ // laminated in-plane mu1=((1.+LamFill*(mu_x-1.))*muo); mu2=((1.+LamFill*(mu_y-1.))*muo); } if(LamType==1){ // laminated parallel to x; mu1=((1.+LamFill*(mu_x-1.))*muo); mu2=1./(LamFill/(mu_y*muo) + (1. - LamFill)/muo); } if(LamType==2){ // laminated parallel to x; mu2=((1.+LamFill*(mu_y-1.))*muo); mu1=1./(LamFill/(mu_x*muo) + (1. - LamFill)/muo); } } // Rats! The material is nonlinear. else{ double muiron; if(LamType==0){ biron=sqrt(b1*b1+b2*b2); if(biron<1.e-08) mu1=1./Re(slope[0]); //catch degenerate case else mu1=biron/GetH(biron); mu2=mu1; } if(LamType==1){ biron=sqrt((b1/LamFill)*(b1/LamFill) + b2*b2); if(biron<1.e-08) muiron=1./Re(slope[0]); else muiron=biron/GetH(biron); mu1=muiron*LamFill; mu2=1./(LamFill/muiron + (1. - LamFill)/muo); } if(LamType==2){ biron=sqrt((b2/LamFill)*(b2/LamFill) + b1*b1); if(biron<1.e-08) muiron=1./Re(slope[0]); else muiron=biron/GetH(biron); mu2=muiron*LamFill; mu1=1./(LamFill/muiron + (1. - LamFill)/muo); } } // convert to relative permeability mu1/=muo; mu2/=muo; return; } // get incremental permeability of a nonlinear material for use in // incremental permeability formulation about DC offset void CMaterialProp::IncrementalPermeability(double B, CComplex &mu1, CComplex &mu2) { // B == flux density in Tesla // w == frequency in rad/s double muinc,murel; CComplex k; double mu,w; w=Frequency*2.*PI; // get incremental permeability of the DC material // (i.e. incremental permeability at the offset) muinc=1./(muo*Re(GetdHdB(B))); if (B==0) murel = (1./(muo*Re(slope[0]))); else murel=B/(muo*GetH(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; } } // get incremental permeability of a nonlinear material for use in // incremental permeability formulation about DC offset 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))); if (B == 0) murel = (1. / (muo*Re(slope[0]))); else murel = B / (muo*GetH(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; } CBoundaryProp::CBoundaryProp() { BdryName="New Boundary"; BdryFormat=0; // type of boundary condition we are applying // 0 = constant value of A // 1 = Small skin depth eddy current BC // 2 = Mixed BC A0=0.; A1=0.; A2=0.; phi=0.; // set value of A for BdryFormat=0; Mu=0.; Sig=0.; // material properties necessary to apply // eddy current BC c0=0.; c1=0.; // coefficients for mixed BC } CPointProp::CPointProp() { PointName="New Point Property"; Jr=0.; Ji=0.; // applied point current, A Ar=0.; Ai=0.; // prescribed nodal value; } CCircuit::CCircuit() { CircName="New Circuit"; CircType=0; Amps=0.; }