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(); }
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> /// Make a femm model using parameters from rotor creation process /// </summary> /// <param name="outfile">Output femm model</param> /// <param name="original">The original femm model to insert into</param> public void MakeAFEMMModelFile(String outfile, String original = "") { if (!isPointsCoordCalculated) { CalcPointsCoordinates(); } // create new or open an exist if (original == "") { FEMM.newdocument(FEMM.DocumentType.Magnetic); } else { FEMM.open(original); } // double Rrotor = RotorParams.Rrotor; double alpha = 2 * Math.PI / (4 * RotorParams.p); double alphaM = RotorParams.alphaM; int p = RotorParams.p; ///// Build 1 poles // point far right of rotors double xI = Rrotor; double yI = 0; // segments (magnet) FEMM.mi_addSegmentEx(xA, yA, xB, yB, Group_Lines); //AB FEMM.mi_addSegmentEx(xA, yA, xF, yF, Group_Lines); //AF FEMM.mi_addSegmentEx(xA, yA, xD, yD, Group_Lines); //AD FEMM.mi_addSegmentEx(xB, yB, xC, yC, Group_Lines); //BC FEMM.mi_addSegmentEx(xC, yC, xD, yD, Group_Lines); //CD // mirrored segments (magnet) FEMM.mi_addSegmentEx(xA, -yA, xB, -yB, Group_Lines); //AB FEMM.mi_addSegmentEx(xA, -yA, xF, -yF, Group_Lines); //AF FEMM.mi_addSegmentEx(xA, -yA, xD, -yD, Group_Lines); //AD FEMM.mi_addSegmentEx(xB, -yB, xC, -yC, Group_Lines); //BC FEMM.mi_addSegmentEx(xC, -yC, xD, -yD, Group_Lines); //CD // barrier segments FEMM.mi_addSegmentEx(xC, yC, xC, -yC, Group_Lines); //AB FEMM.mi_addSegmentEx(xD, yD, xD, -yD, Group_Lines); //AF // arcsegments double a = (Math.Atan(yF / xF) - Math.Atan(yB / xB)) * 180 / Math.PI; FEMM.mi_addArcEx(xB, yB, xF, yF, a, 1, Group_Lines); FEMM.mi_addArcEx(xF, -yF, xB, -yB, a, 1, Group_Lines); // arcsegments rotor FEMM.mi_addArcEx(xI, yI, xG, yG, alpha * 180 / Math.PI, 1, Group_Lines); FEMM.mi_addArcEx(xG, -yG, xI, yI, alpha * 180 / Math.PI, 1, Group_Lines); // blocks // air block label double air1X = (xA + xB + xF) / 3; double air1Y = (yA + yB + yF) / 3; FEMM.mi_addBlockLabelEx(air1X, air1Y, AirBlockName, Group_Label); FEMM.mi_addBlockLabelEx(air1X, -air1Y, AirBlockName, Group_Label); FEMM.mi_addBlockLabelEx((xC + xD) / 2, 0, AirBlockName, Group_Label); // magnet block label double magBlockLabelX = (xA + xC) / 2; double magBlockLabelY = (yA + yC) / 2; FEMM.mi_addBlockLabelEx(magBlockLabelX, magBlockLabelY, MagnetBlockName, Group_Label, alphaM * 180 / Math.PI - 90 + 180);//+180 but set back later FEMM.mi_addBlockLabelEx(magBlockLabelX, -magBlockLabelY, MagnetBlockName, Group_Label, 90 - alphaM * 180 / Math.PI + 180); ///// Build 2 poles FEMM.mi_clearselected(); FEMM.mi_selectgroup(Group_Lines); FEMM.mi_selectgroup(Group_Label); FEMM.mi_copyrotate(0, 0, 360 / (2 * p), 1, FEMM.EditMode.group); // rotate the magnet direction FEMM.mi_clearselected(); FEMM.mi_selectlabel(magBlockLabelX, magBlockLabelY); FEMM.mi_setblockprop(MagnetBlockName, true, 0, "", alphaM * 180 / Math.PI - 90, Group_Label, 0); FEMM.mi_clearselected(); FEMM.mi_selectlabel(magBlockLabelX, -magBlockLabelY); FEMM.mi_setblockprop(MagnetBlockName, true, 0, "", 90 - alphaM * 180 / Math.PI, Group_Label, 0); ///// Build p-1 pair of poles remaining FEMM.mi_clearselected(); FEMM.mi_selectgroup(Group_Lines); FEMM.mi_selectgroup(Group_Label); FEMM.mi_copyrotate(0, 0, 360 / p, p - 1, FEMM.EditMode.group); FEMM.mi_addBlockLabelEx(0, 0, SteelBlockName, Group_Rotor_Steel); // clear selected, refresh, and go to natural zoom FEMM.mi_clearselected(); FEMM.mi_zoomnatural(); // save as if (Path.GetDirectoryName(outfile) == "") { outfile = Path.GetDirectoryName(original) + "\\" + outfile; } FEMM.mi_saveas(outfile); FEMM.mi_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); } }