private void analyzeAnIndividual(Individual ind, out string infotext, out string log, out AbstractMotor motor)
        {
            double[] gens = ind.Genes;

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Took " + (sw.ElapsedMilliseconds / 1000) + " secs. Dict size = " + dict_fitness.Count);

            infotext = "";
            AbstractMMAnalyser mma = null;

            try
            {
                lua_constructMotorfromGens(gens, currentConfig.luascript_constructMotor, out motor, out mma);
            }
            catch (Exception ex)
            {
                sb.AppendLine("Motor constructing script failed: " + ex.Message);
                motor = null;
            }

            // finish script
            if (motor != null && currentConfig.luascript_finish != null && currentConfig.luascript_finish != "")
            {
                try
                {
                    //lua["fitness"] = fitness;
                    var a = motor.GetAnalyticalAnalyser();
                    a.RunAnalysis();

                    // finite elements analysis if available
                    object fem_results = null;
                    if (lua["useFEM"] != null && a.isDataValid() && mma != null)
                    {
                        // static analysis
                        String outdir = OutputFolderFEM + "\\" + motor.GetMD5String();
                        Directory.CreateDirectory(outdir);
                        String femFile = outdir + "\\static.FEM";
                        if (!motor.isFEMModelReady(femFile))
                        {
                            motor.BuildFEMModel(femFile);
                        }

                        AbstractStaticAnalyser staticAnalyser = motor.GetStaticAnalyser();
                        staticAnalyser.Path_OriginalFEMFile        = femFile;
                        staticAnalyser.OutputPath                  = outdir;
                        staticAnalyser.DoAnalysisOnOriginalFEMFile = true;
                        staticAnalyser.RunAnalysis();

                        // mma analysis
                        mma.Path_OriginalFEMFile = femFile;
                        mma.CustomOutputDir      = outdir;
                        mma.RunAnalysis();
                        fem_results = mma.Results;

                        FEMM.CloseFemm();
                    }


                    object[] o = null;
                    lock (lock_dostring)
                    {
                        lua["motor"]      = motor;
                        lua["gens"]       = gens;
                        lua["results"]    = a.Results;
                        lua["femresults"] = fem_results;

                        o = lua.DoString(currentConfig.luascript_finish, "FinishScript");
                    }

                    if (o != null && o.Length > 0 && o[0] is string)
                    {
                        infotext = o[0].ToString();
                        sb.AppendLine("FinishScript:\r\n" + (o[0] as string));
                    }
                }
                catch (Exception ex)
                {
                    sb.AppendLine("Finish script failed: " + ex.Message);
                }

                //
                sb.AppendLine("Motor details:");
                sb.AppendLine(JSON.Beautify(JSON.ToJSON(motor)));
            }

            log = sb.ToString();
        }
        /// <summary>
        /// Calculate fitness from gens, return value is nullable
        /// </summary>
        /// <param name="gens"></param>
        /// <returns></returns>
        private double[] fitnessFunction(double[] gens)
        {
            double d = 0;

            for (int i = 0; i < gens.Length; i++)
            {
                d += gens[i] * Math.Pow(10, i);
            }

            if (dict_fitness.ContainsKey(d))
            {
                return(dict_fitness[d]);
            }

            // construct motor from gens, using Lua script
            AbstractMotor      motor = null;
            AbstractMMAnalyser mma   = null;

            lua_constructMotorfromGens(gens, currentConfig.luascript_constructMotor, out motor, out mma);

            // motor calculate points coordinates
            motor.CalcPointsCoordinates();

            // if params invalid, return fitness as null
            if (!motor.IsParamsValid())
            {
                foreach (ParamValidationInfo pvi in motor.ListParamsValidation)
                {
                    if (pvi.msgType == ParamValidationInfo.MessageType.Error)
                    {
                        Console.WriteLine("Error: \t+ " + pvi.message);
                    }
                }

                return(dict_fitness[d] = null);
            }

            // analytical analysis
            var a = motor.GetAnalyticalAnalyser();

            a.RunAnalysis();
            if (!a.isDataValid())
            {
                return(dict_fitness[d] = null);
            }

            // finite elements analysis if available
            object fem_results = null;

            if (lua["useFEM"] != null && mma != null)
            {
                // static analysis
                String outdir = OutputFolderFEM + "\\" + motor.GetMD5String();
                Directory.CreateDirectory(outdir);
                String femFile = outdir + "\\static.FEM";
                if (!motor.isFEMModelReady(femFile))
                {
                    motor.BuildFEMModel(femFile, FEMMToUse[0]);
                }

                AbstractStaticAnalyser staticAnalyser = motor.GetStaticAnalyser();
                staticAnalyser.Path_OriginalFEMFile        = femFile;
                staticAnalyser.OutputPath                  = outdir;
                staticAnalyser.DoAnalysisOnOriginalFEMFile = true;
                staticAnalyser.FEMMToUse = FEMMToUse;
                staticAnalyser.RunAnalysis();

                // for some reason, analysis was not succesful?
                if (!File.Exists(staticAnalyser.WorkingANSFile))
                {
                    return(dict_fitness[d] = null);
                }

                // mma analysis
                mma.Path_OriginalFEMFile = femFile;
                mma.CustomOutputDir      = outdir;
                mma.FEMMToUse            = FEMMToUse;
                mma.RunAnalysis();
                fem_results = mma.Results;

                // to take pictures of progress
                //FEMMToUse[0].open(femFile);

                //Invoke((Action)delegate ()
                //{
                //    individualCount++;
                //    Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                //    Graphics gr = Graphics.FromImage(bmp);
                //    gr.CopyFromScreen(0, 0, 0, 0, bmp.Size);
                //    bmp.Save("D:\\captures\\" + string.Format("{0:D8}", individualCount) + ".png", System.Drawing.Imaging.ImageFormat.Png);
                //});

                //FEMMToUse[0].mi_close();
            }

            var f = lua_fitnessFunction(a.Results, fem_results, currentConfig.luascript_fitness_function);

            dict_fitness[d] = f;

            return(f);
        }