protected override void analyze() { FEMM femm = FEMM.DefaultFEMM; // open original femm file femm.open(Path_OriginalFEMFile); // modify current, freq femm.mi_probdef(Freq, FEMM.UnitsType.millimeters, FEMM.ProblemType.planar, 1e-8, Motor.GeneralParams.MotorLength, 7, FEMM.ACSolverType.Succ_Approx); string ia = string.Format("{0}", Current); string ib = string.Format("{0}+I*{1}", Current * Math.Cos(-2 * Math.PI / 3), Current * Math.Sin(-2 * Math.PI / 3)); string ic = string.Format("{0}+I*{1}", Current * Math.Cos(2 * Math.PI / 3), Current * Math.Sin(2 * Math.PI / 3)); femm.mi_modifycircuitCurrent("A", ia); femm.mi_modifycircuitCurrent("B", ib); femm.mi_modifycircuitCurrent("C", ic); // save as new file and analyze femm.mi_saveas(WorkingFEMFile); femm.mi_analyze(true); femm.mi_close(); femm.open(WorkingANSFile); measureData(femm); femm.mo_close(); }
/// <summary> /// analyze will be sealed here, the child class can only do the measure action /// </summary> protected sealed override void analyze() { FEMM femm = null; if (FEMMToUse != null && FEMMToUse.Count > 0) { femm = FEMMToUse[0]; } else { femm = FEMM.DefaultFEMM; } // open original femm file femm.open(Path_OriginalFEMFile); // no modification (if other like transient will need to rotate rotor) // save as new file and analyze if (!DoAnalysisOnOriginalFEMFile) { femm.mi_saveas(WorkingFEMFile); } femm.mi_analyze(true); femm.mi_close(); if (!File.Exists(WorkingANSFile)) { log.Error(WorkingANSFile + " not exists, there may be something wrong with Femm file " + WorkingFEMFile); return; } femm.open(WorkingANSFile); try { measureInOpenedFem(femm); } catch (Exception ex) { log.Error(ex.Message); } femm.mo_close(); }
protected override void measureInOpenedFem(FEMM femm) { // Begin to measure FEMM.LineIntegralResult lir = new FEMM.LineIntegralResult(); VPMMotor Motor = this.Motor as VPMMotor; VPMRotor Rotor = Motor.Rotor; Stator3Phase Stator = Motor.Stator; GeneralParameters GeneralParams = Motor.GeneralParams; AirgapNormal Airgap = Motor.Airgap; PMStaticResults Results = this.Results as PMStaticResults; double xS = Rotor.Rrotor * Math.Cos(Rotor.alpha * 0.9999); double yS = Rotor.Rrotor * Math.Sin(Rotor.alpha * 0.9999); // get phiD femm.mo_addcontour(xS, yS); //femm.mo_selectpoint(Rotor.xR, Rotor.yR); femm.mo_addcontour(xS, -yS); femm.mo_bendcontour(-360 / (2 * Rotor.p), 1); lir = femm.mo_lineintegral_full(); Results.phiD = Math.Abs(lir.totalBn); // get phiM femm.mo_clearcontour(); femm.mo_selectpoint(Rotor.xD, Rotor.yD); femm.mo_selectpoint(Rotor.xG, Rotor.yG); FEMM.LineIntegralResult rr = femm.mo_lineintegral(FEMM.LineIntegralType.Bn); Results.phiM = Math.Abs(rr.totalBn * 2); // get phib femm.mo_clearcontour(); femm.mo_selectpoint(Rotor.xH, Rotor.yH); femm.mo_selectpoint(Rotor.xH, -Rotor.yH); rr = femm.mo_lineintegral(FEMM.LineIntegralType.Bn); Results.phib = Math.Abs(rr.totalBn); femm.mo_clearcontour(); femm.mo_selectpoint(Rotor.xE, Rotor.yE); femm.mo_selectpoint(Rotor.xA, Rotor.yA); rr = femm.mo_lineintegral(FEMM.LineIntegralType.Bn); Results.phib += Math.Abs(rr.totalBn * 2); // get phisigmaFe femm.mo_clearcontour(); femm.mo_addcontour(Rotor.xA, Rotor.yA); femm.mo_addcontour(xS, yS); rr = femm.mo_lineintegral(FEMM.LineIntegralType.Bn); Results.phiFe = Math.Abs(rr.totalBn * 2); // get phisigmaS double xZ = (Stator.Rinstator + 5) * Math.Cos(2 * Math.PI / (4 * Rotor.p) - 2 * Math.PI / 180); double yZ = (Stator.Rinstator + 5) * Math.Sin(2 * Math.PI / (4 * Rotor.p) - 2 * Math.PI / 180); femm.mo_clearcontour(); femm.mo_selectpoint(Rotor.xS, Rotor.yS); femm.mo_selectpoint(xZ, yZ); rr = femm.mo_lineintegral(FEMM.LineIntegralType.Bn); Results.phisigmaS = Math.Abs(rr.totalBn * 2); // get FM femm.mo_clearcontour(); femm.mo_addcontour((Rotor.xI + Rotor.xF) / 2, (Rotor.yI + Rotor.yF) / 2); femm.mo_addcontour((Rotor.xD + Rotor.xG) / 2, (Rotor.yD + Rotor.yG) / 2); rr = femm.mo_lineintegral(FEMM.LineIntegralType.Ht); Results.FM = Math.Abs(rr.totalHt); // get B_airgap int n = 128; Results.Bairgap = new PointD[n * 2]; double RR = (Rotor.Rrotor + Stator.Rinstator) / 2; for (int i = 0; i < n; i++) { double a = -(i - n / 2.0) / n * 2 * Rotor.alpha; double px = RR * Math.Cos(a); double py = RR * Math.Sin(a); FEMM.PointValues pv = femm.mo_getpointvalues(px, py); Results.Bairgap[i].X = 2 * Rotor.alpha * RR * i / n; Results.Bairgap[i].Y = pv.B1 * Math.Cos(a) + pv.B2 * Math.Sin(a); if (double.IsNaN(Results.Bairgap[i].Y)) { Results.Bairgap[i].Y = 0; } if (Results.Bdelta_max < Math.Abs(Results.Bairgap[i].Y)) { Results.Bdelta_max = Math.Abs(Results.Bairgap[i].Y); } } // make a mirror (odd function) double dd = 2 * Rotor.alpha * RR; for (int i = 0; i < n; i++) { Results.Bairgap[i + n].X = Results.Bairgap[i].X + dd; Results.Bairgap[i + n].Y = -Results.Bairgap[i].Y; } double wd = Rotor.gammaMedeg / 180 * (Rotor.Rrotor + Airgap.delta / 2) * 2 * Math.PI / (2 * Rotor.p); Results.Bdelta = Results.phiD / (GeneralParams.MotorLength * wd * 1e-6); // psiM Dictionary <String, FEMM.CircuitProperties> cps = Stator.getCircuitsPropertiesInAns(femm); if (cps.ContainsKey("A") && cps.ContainsKey("B") && cps.ContainsKey("C")) { Fdq fdq = ParkTransform.abc_dq(cps["A"].fluxlinkage, cps["B"].fluxlinkage, cps["C"].fluxlinkage, 0); Results.psiM = fdq.Magnitude; } else { Results.psiM = double.NaN; } femm.mo_close(); }
private void AnalyzeOne(int step, FEMM femm = null) { if (femm == null) { femm = FEMM.DefaultFEMM; } // open femm file femm.open(Motor.Path_FEMMFile); ///// Rotate rotor to an angle // clear the way (line-boundary conditional in airgap) Motor.Airgap.MakeWayInAirgapBeforeRotationInFEMM(femm); // normalize, make rotorAngle inside (-2alpha,2alpha) double xRotorAngle = RotorAngle; if (!Motor.GeneralParams.FullBuildFEMModel) { double alphax2 = 2 * Motor.Rotor.alphaDegree; if (xRotorAngle > alphax2 || xRotorAngle < -alphax2) { xRotorAngle = xRotorAngle - Math.Round(xRotorAngle / (2 * alphax2)) * 2 * alphax2; } } // rotate rotor Motor.Rotor.RotateRotorInFEMM(xRotorAngle, femm); // modify airgap (in case partial build) Motor.Airgap.ModifyAirgapAfterRotationInFEMM(xRotorAngle, femm); // modify stator currents double current = MaxCurrent / StepCount * (step - StepCount);//step can be -StepCount to +StepCount (total 2*StepCount+1), current=-Max->+Max double IA = current * Math.Cos(Beta * Math.PI / 180); double IB = current * Math.Cos(Beta * Math.PI / 180 - 2 * Math.PI / 3); double IC = current * Math.Cos(Beta * Math.PI / 180 + 2 * Math.PI / 3); Dictionary <String, double> currents = new Dictionary <string, double>() { { "A", IA }, { "B", IB }, { "C", IC } }; Motor.Stator.SetStatorCurrentsInFEMM(currents, femm); // save as new file fem (temp only) String stepfileFEM = OutDir + "\\" + String.Format("{0:D4}.FEM", step); femm.mi_saveas(stepfileFEM); // analyze femm.mi_analyze(true); // close femm.mi_close(); // delete temp file //File.Delete(stepfileFEM); // open ans file to measure ? String stepfileANS = Path.GetDirectoryName(stepfileFEM) + "\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".ans"; femm.open(stepfileANS); Dictionary <String, FEMM.CircuitProperties> cps = Motor.Stator.getCircuitsPropertiesInAns(femm); MMAnalysisOneStepResult result = new MMAnalysisOneStepResult(); result.Iabc = new Fabc { a = cps["A"].current, b = cps["B"].current, c = cps["C"].current }; result.FluxLinkage_abc = new Fabc { a = cps["A"].fluxlinkage, b = cps["B"].fluxlinkage, c = cps["C"].fluxlinkage }; result.Vabc = new Fabc { a = cps["A"].volts, b = cps["B"].volts, c = cps["C"].volts }; // no currents, then save the fluxlinkageM, which is importance to calculate Ld if (current == 0) { Results.FluxLinkageM = result.FluxLinkage_dq.d; } result.FluxLinkage_M = Results.FluxLinkageM; Results[step] = result; femm.mo_close(); log.Info("Step " + step + " done."); }
/// <summary> /// Analyze 1 step, using femm window /// </summary> /// <param name="data"></param> /// <param name="step"></param> /// <param name="femm"></param> private void AnalyzeOne(int step, FEMM femm = null) { if (femm == null) { femm = FEMM.DefaultFEMM; } double t = Results.Times[step]; double rotorAngle = RotorSpeedDegreeSecond * t + StartAngle; // run script to update IA,IB,IC NLua.Lua lua_state = LuaHelper.GetLuaState(); String[] currentFormulas = { IA, IB, IC }; String[] circuitNames = { "A", "B", "C" }; Dictionary <String, double> currents = new Dictionary <string, double>(); try { lua_state["step"] = step; lua_state["time"] = t; lua_state["omega"] = RotorSpeedRadSecond; for (int i = 0; i < currentFormulas.Length; i++) { double Ix = (double)lua_state.DoString("return " + currentFormulas[i])[0]; currents[circuitNames[i]] = Ix; } } catch (Exception ex) { log.Error("Lua error :" + ex.Message); //TODO: maybe halt analysis since there maybe big problem } // open femm file femm.open(Motor.Path_FEMMFile); // clear the way (line-boundary conditional in airgap) Motor.Airgap.MakeWayInAirgapBeforeRotationInFEMM(femm); // normalize, make rotorAngle inside (-2alpha,2alpha) double xRotorAngle = rotorAngle; if (!Motor.GeneralParams.FullBuildFEMModel) { double alphax2 = 2 * Motor.Rotor.alphaDegree; if (xRotorAngle > alphax2 || xRotorAngle < -alphax2) { xRotorAngle = xRotorAngle - Math.Round(xRotorAngle / (2 * alphax2)) * 2 * alphax2; } } // rotate rotor Motor.Rotor.RotateRotorInFEMM(xRotorAngle, femm); // modify airgap (in case partial build) Motor.Airgap.ModifyAirgapAfterRotationInFEMM(xRotorAngle, femm); // modify stator currents using args passed from OnBeginAnalyzeOne Motor.Stator.SetStatorCurrentsInFEMM(currents, femm); // save as new file fem (temp only) String stepfileFEM = OutDir + "\\" + String.Format("{0:D4}.FEM", step); femm.mi_saveas(stepfileFEM); // analyze femm.mi_analyze(true); // close femm.mi_close(); // delete temp file //File.Delete(stepfileFEM); // open ans file to measure ? String stepfileANS = Path.GetDirectoryName(stepfileFEM) + "\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".ans"; femm.open(stepfileANS); //String bmpFile = config.OutDir + "\\bmp\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".bmp"; //femm.mo_savebitmap(bmpFile); // get circuits properties from stator TransientStepResult result = new TransientStepResult(); Dictionary <String, FEMM.CircuitProperties> cps = Motor.Stator.getCircuitsPropertiesInAns(femm); double torque = Motor.Rotor.getTorqueInAns(femm); result.Torque = torque; result.RotorAngle = rotorAngle; result.CircuitProperties = cps; Results[step] = result; OnFinishAnalyzeOneStep(this, result); log.Info(String.Format("Time {0:F5}: {1}", t, torque)); femm.mo_close(); }
private void AnalyzeOne(int step, FEMM femm = null) { if (femm == null) { femm = FEMM.DefaultFEMM; } // open femm file femm.open(Path_OriginalFEMFile); ///// Rotate rotor to an angle // clear the way (line-boundary conditional in airgap) Motor.Airgap.RemoveBoundary(femm); // normalize, make rotorAngle inside (-2alpha,2alpha) to build FEMM model double femmRotorAngle = Motor.GetNormalizedRotorAngle(RotorAngle); // rotate rotor Motor.Rotor.RotateRotorInFEMM(femmRotorAngle, femm); // modify airgap (in case partial build) Motor.Airgap.AddBoundaryAtAngle(femmRotorAngle, femm); // modify stator currents // testing iq=const, id change to see how inductance d,q change //double id = 80.0 / StepCount * (step - StepCount); //double iq = 40.0; //double current = Math.Sqrt(id * id + iq * iq); //double Beta = Math.Abs(id) > 1e-8 ? Math.Atan(iq / id) * 180 / Math.PI : 90.0; //if (Beta < 0) // Beta += 180; // Test change beta, not current //double beta = (this.Beta - 100) / StepCount * step + 100; //double current = MaxCurrent; var stator = Motor.Stator as Stator3Phase; // Change current double current; if (Only2ndQuarter) { int c = NotIncludeCurrentZero ? 1 : 0; current = MaxCurrent * (step + c) / StepCount; } else { int c = NotIncludeCurrentZero ? (step < StepCount ? 0 : 1) : 0; current = MaxCurrent / StepCount * ((step + c) - StepCount); } // correction beta with current rotor angle and original angle (0) double beta = this.Beta + (RotorAngle - stator.VectorMMFAngle) * Motor.Rotor.p;//in degree double IA = current * Math.Cos(beta * Math.PI / 180); double IB = current * Math.Cos(beta * Math.PI / 180 - 2 * Math.PI / 3); double IC = current * Math.Cos(beta * Math.PI / 180 + 2 * Math.PI / 3); Dictionary <String, double> currents = new Dictionary <string, double>() { { "A", IA }, { "B", IB }, { "C", IC } }; Motor.Stator.SetStatorCurrentsInFEMM(currents, femm); // save as new file fem (temp only) String stepfileFEM = Path_ToAnalysisVariant + "\\" + String.Format("{0:D4}.FEM", step); femm.mi_saveas(stepfileFEM); // analyze femm.mi_analyze(true); // close femm.mi_close(); // delete temp file //File.Delete(stepfileFEM); // open ans file to measure ? String stepfileANS = Path.GetDirectoryName(stepfileFEM) + "\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".ans"; femm.open(stepfileANS); Dictionary <String, FEMM.CircuitProperties> cps = Motor.Stator.getCircuitsPropertiesInAns(femm); double t = Motor.Rotor.getTorqueInAns(femm); PMMMAnalysisOneStepResult result = new PMMMAnalysisOneStepResult(); result.Iabc = new Fabc { a = cps["A"].current, b = cps["B"].current, c = cps["C"].current }; result.FluxLinkage_abc = new Fabc { a = cps["A"].fluxlinkage, b = cps["B"].fluxlinkage, c = cps["C"].fluxlinkage }; result.Vabc = new Fabc { a = cps["A"].volts, b = cps["B"].volts, c = cps["C"].volts }; // no currents, then save the fluxlinkageM, which is importance to calculate Ld var Results = this.Results as PM_MMAnalysisResults; result.torque = t; result.FluxLinkage_M = Results.FluxLinkageM; result.theta_e = (RotorAngle - stator.VectorMMFAngle) * Motor.Rotor.p * Math.PI / 180;//in radian Results[step] = result; femm.mo_close(); }
/// <summary> /// Analyze 1 step, using femm window /// </summary> /// <param name="data"></param> /// <param name="step"></param> /// <param name="femm"></param> private void AnalyzeOne(int step, FEMM femm = null) { if (femm == null) { femm = FEMM.DefaultFEMM; } if (SkewAngle <= 0 || NSkewSegment <= 0) { // prepare arguments for this step TransientStepArgs args = PrepareStepArgs(step); // before analyzing OnStartAnalyzeOneStep(this, args); // open femm file femm.open(Path_OriginalFEMFile); // modify rotor: rotate, or other things that we can think of DoModifyRotor(args, femm); // modify stator: change current, or something else? DoModifyStator(args, femm); // save as new file fem (temp only) String stepfileFEM = Path_ToAnalysisVariant + "\\" + String.Format("{0:D4}.FEM", step); femm.mi_saveas(stepfileFEM); // analyze femm.mi_analyze(true); // close femm.mi_close(); // open ans file to measure ? String stepfileANS = Path.GetDirectoryName(stepfileFEM) + "\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".ans"; femm.open(stepfileANS); // do measure torque or anything else DoMeasureData(args, femm); // put in the list var Results = this.Results as Transient3PhaseMotorResults; Results[step] = args; //log.Info(String.Format("Time {0:F5}: {1}", t, torque)); // save as bitmap if (ExportGif) { femm.mo_clearblock(); femm.mo_zoom(0, -Motor.Stator.RYoke, Motor.Stator.RYoke, Motor.Stator.RYoke); femm.mo_showdensityplot(true, false, 0, 2.1, FEMM.DensityPlotType.bmag); femm.mo_savebitmap(Path.GetDirectoryName(stepfileFEM) + "\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".bmp"); } // close femm.mo_close(); // after analyzing OnFinishAnalyzeOneStep(this, args); } else // has skewangle { // prepare arguments for this step TransientStepArgs total_args = PrepareStepArgs(step); // before analyzing OnStartAnalyzeOneStep(this, total_args); for (int k = 0; k < 2 * NSkewSegment + 1; k++) { TransientStepArgs args = PrepareStepArgs(step); double sk = (k - NSkewSegment) * SkewAngle / (2 * NSkewSegment); args.skewAngleAdded = sk; args.RotorAngle += sk; // open femm file femm.open(Path_OriginalFEMFile); // modify rotor: rotate, or other things that we can think of DoModifyRotor(args, femm); // modify stator: change current, or something else? DoModifyStator(args, femm); // save as new file fem (temp only) String stepfileFEM = Path_ToAnalysisVariant + "\\" + String.Format("{0:D4}_{1}.FEM", step, k); femm.mi_saveas(stepfileFEM); // analyze femm.mi_analyze(true); // close femm.mi_close(); // open ans file to measure ? String stepfileANS = Path.GetDirectoryName(stepfileFEM) + "\\" + Path.GetFileNameWithoutExtension(stepfileFEM) + ".ans"; femm.open(stepfileANS); // do measure torque or anything else DoMeasureData(args, femm); // initialize data for total if (k == 0) { total_args = JSON.DeepCopy(args); total_args.Torque = 0; foreach (string key in total_args.CircuitProperties.Keys) { total_args.CircuitProperties[key].fluxlinkage = 0; } } femm.mo_close(); total_args.Torque += args.Torque / (2 * NSkewSegment + 1); foreach (string key in total_args.CircuitProperties.Keys) { total_args.CircuitProperties[key].fluxlinkage += args.CircuitProperties[key].fluxlinkage / (2 * NSkewSegment + 1); } } // put in the list var Results = this.Results as Transient3PhaseMotorResults; Results[step] = total_args; //log.Info(String.Format("Time {0:F5}: {1}", t, torque)); // after analyzing OnFinishAnalyzeOneStep(this, total_args); } }