コード例 #1
0
        static void CreateSaveRestart(string cpFileName)
        {
            GAMSWorkspace ws;

            if (Environment.GetCommandLineArgs().Length > 1)
            {
                ws = new GAMSWorkspace(workingDirectory: Path.GetDirectoryName(cpFileName), systemDirectory: Environment.GetCommandLineArgs()[1]);
            }
            else
            {
                ws = new GAMSWorkspace(workingDirectory: Path.GetDirectoryName(cpFileName));
            }
            GAMSJob     j1  = ws.AddJobFromString(GetBaseModelText());
            GAMSOptions opt = ws.AddOptions();

            opt.Action = GAMSOptions.EAction.CompileOnly;


            GAMSCheckpoint cp = ws.AddCheckpoint(Path.GetFileName(cpFileName));

            j1.Run(opt, cp);

            opt.Dispose();
        }
コード例 #2
0
ファイル: Benders2Stage.cs プロジェクト: nherazo1/Dutties
        static void Main(string[] args)
        {
            GAMSWorkspace ws;

            if (Environment.GetCommandLineArgs().Length > 1)
            {
                ws = new GAMSWorkspace(systemDirectory: Environment.GetCommandLineArgs()[1]);
            }
            else
            {
                ws = new GAMSWorkspace();
            }
            GAMSJob data = ws.AddJobFromString(GetDataText());

            GAMSOptions optData = ws.AddOptions();

            optData.Defines.Add("useBig", "1");
            optData.Defines.Add("nrScen", "100");

            data.Run(optData);

            optData.Dispose();
            GAMSParameter scenarioData = data.OutDB.GetParameter("ScenarioData");

            GAMSOptions opt = ws.AddOptions();

            opt.Defines.Add("datain", data.OutDB.Name);
            int maxiter = 40;

            opt.Defines.Add("maxiter", maxiter.ToString());
            opt.AllModelTypes = "cplexd";

            GAMSCheckpoint cpMaster = ws.AddCheckpoint();
            GAMSCheckpoint cpSub    = ws.AddCheckpoint();

            ws.AddJobFromString(GetMasterText()).Run(opt, cpMaster, data.OutDB);

            GAMSModelInstance masteri  = cpMaster.AddModelInstance();
            GAMSParameter     cutconst = masteri.SyncDB.AddParameter("cutconst", 1, "Benders optimality cut constant");
            GAMSParameter     cutcoeff = masteri.SyncDB.AddParameter("cutcoeff", 2, "Benders optimality coefficients");
            GAMSVariable      theta    = masteri.SyncDB.AddVariable("theta", 0, VarType.Free, "Future profit function variable");
            GAMSParameter     thetaFix = masteri.SyncDB.AddParameter("thetaFix", 0, "");

            masteri.Instantiate("masterproblem max zmaster using lp", opt, new GAMSModifier(cutconst), new GAMSModifier(cutcoeff), new GAMSModifier(theta, UpdateAction.Fixed, thetaFix));

            ws.AddJobFromString(GetSubText()).Run(opt, cpSub, data.OutDB);

            GAMSModelInstance subi     = cpSub.AddModelInstance();
            GAMSParameter     received = subi.SyncDB.AddParameter("received", 1, "units received from master");
            GAMSParameter     demand   = subi.SyncDB.AddParameter("demand", 1, "stochastic demand");

            subi.Instantiate("subproblem max zsub using lp", opt, new GAMSModifier(received), new GAMSModifier(demand));

            opt.Dispose();

            double lowerbound = double.NegativeInfinity, upperbound = double.PositiveInfinity, objmaster = double.PositiveInfinity;
            int    iter = 1;

            do
            {
                Console.WriteLine("Iteration: " + iter);
                // Solve master
                if (1 == iter) // fix theta for first iteration
                {
                    thetaFix.AddRecord().Value = 0;
                }
                else
                {
                    thetaFix.Clear();
                }

                masteri.Solve(GAMSModelInstance.SymbolUpdateType.BaseCase);
                Console.WriteLine(" Master " + masteri.ModelStatus + " : obj=" + masteri.SyncDB.GetVariable("zmaster").FirstRecord().Level);
                if (1 < iter)
                {
                    upperbound = masteri.SyncDB.GetVariable("zmaster").FirstRecord().Level;
                }
                objmaster = masteri.SyncDB.GetVariable("zmaster").FirstRecord().Level - theta.FirstRecord().Level;

                // Set received from master
                received.Clear();
                foreach (GAMSVariableRecord r in masteri.SyncDB.GetVariable("received"))
                {
                    received.AddRecord(r.Keys).Value = r.Level;
                    cutcoeff.AddRecord(iter.ToString(), r.Keys[0]);
                }

                cutconst.AddRecord(iter.ToString());
                double objsub = 0.0;
                foreach (GAMSSetRecord s in data.OutDB.GetSet("s"))
                {
                    demand.Clear();
                    foreach (GAMSSetRecord j in data.OutDB.GetSet("j"))
                    {
                        demand.AddRecord(j.Keys).Value = scenarioData.FindRecord(s.Keys[0], j.Keys[0]).Value;
                    }

                    subi.Solve(GAMSModelInstance.SymbolUpdateType.BaseCase);
                    Console.WriteLine(" Sub " + subi.ModelStatus + " : obj=" + subi.SyncDB.GetVariable("zsub").FirstRecord().Level);


                    double probability = scenarioData.FindRecord(s.Keys[0], "prob").Value;
                    objsub += probability * subi.SyncDB.GetVariable("zsub").FirstRecord().Level;
                    foreach (GAMSSetRecord j in data.OutDB.GetSet("j"))
                    {
                        cutconst.FindRecord(iter.ToString()).Value            += probability * subi.SyncDB.GetEquation("market").FindRecord(j.Keys).Marginal *demand.FindRecord(j.Keys).Value;
                        cutcoeff.FindRecord(iter.ToString(), j.Keys[0]).Value += probability * subi.SyncDB.GetEquation("selling").FindRecord(j.Keys).Marginal;
                    }
                }
                lowerbound = Math.Max(lowerbound, objmaster + objsub);
                iter++;
                if (iter == maxiter + 1)
                {
                    throw new Exception("Benders out of iterations");
                }

                Console.WriteLine(" lowerbound: " + lowerbound + " upperbound: " + upperbound + " objmaster: " + objmaster);
            } while ((upperbound - lowerbound) >= 0.001 * (1 + Math.Abs(upperbound)));

            masteri.Dispose();
            subi.Dispose();
        }
コード例 #3
0
ファイル: Benders2StageMT.cs プロジェクト: nherazo1/Dutties
        static void Main(string[] args)
        {
            GAMSWorkspace ws;

            if (Environment.GetCommandLineArgs().Length > 1)
            {
                ws = new GAMSWorkspace(systemDirectory: Environment.GetCommandLineArgs()[1]);
            }
            else
            {
                ws = new GAMSWorkspace();
            }
            GAMSJob data = ws.AddJobFromString(GetDataText());

            GAMSOptions optData = ws.AddOptions();

            optData.Defines.Add("useBig", "1");
            optData.Defines.Add("nrScen", "100");

            data.Run(optData);

            optData.Dispose();
            GAMSParameter scenarioData = data.OutDB.GetParameter("ScenarioData");

            GAMSOptions opt = ws.AddOptions();

            opt.Defines.Add("datain", data.OutDB.Name);
            int maxiter = 40;

            opt.Defines.Add("maxiter", maxiter.ToString());
            opt.AllModelTypes = "cplexd";

            GAMSCheckpoint cpMaster = ws.AddCheckpoint();
            GAMSCheckpoint cpSub    = ws.AddCheckpoint();

            ws.AddJobFromString(GetMasterText()).Run(opt, cpMaster, data.OutDB);

            GAMSModelInstance masteri  = cpMaster.AddModelInstance();
            GAMSParameter     cutconst = masteri.SyncDB.AddParameter("cutconst", 1, "Benders optimality cut constant");
            GAMSParameter     cutcoeff = masteri.SyncDB.AddParameter("cutcoeff", 2, "Benders optimality coefficients");
            GAMSVariable      theta    = masteri.SyncDB.AddVariable("theta", 0, VarType.Free, "Future profit function variable");
            GAMSParameter     thetaFix = masteri.SyncDB.AddParameter("thetaFix", 0, "");

            masteri.Instantiate("masterproblem max zmaster using lp", opt, new GAMSModifier(cutconst), new GAMSModifier(cutcoeff), new GAMSModifier(theta, UpdateAction.Fixed, thetaFix));

            ws.AddJobFromString(GetSubText()).Run(opt, cpSub, data.OutDB);

            int numThreads = 2;

            GAMSModelInstance[] subi = new GAMSModelInstance[numThreads];
            Queue <Tuple <string, double, Dictionary <string, double> > > demQueue = new Queue <Tuple <string, double, Dictionary <string, double> > >();

            for (int i = 0; i < numThreads; i++)
            {
                subi[i] = cpSub.AddModelInstance();
                GAMSParameter received = subi[i].SyncDB.AddParameter("received", 1, "units received from first stage solution");
                GAMSParameter demand   = subi[i].SyncDB.AddParameter("demand", 1, "stochastic demand");

                subi[i].Instantiate("subproblem max zsub using lp", opt, new GAMSModifier(received), new GAMSModifier(demand));
            }
            opt.Dispose();

            double lowerbound = double.NegativeInfinity, upperbound = double.PositiveInfinity, objmaster = double.PositiveInfinity;
            int    iter = 1;

            do
            {
                Console.WriteLine("Iteration: " + iter);
                // Solve master
                if (1 == iter) // fix theta for first iteration
                {
                    thetaFix.AddRecord().Value = 0;
                }
                else
                {
                    thetaFix.Clear();
                }

                masteri.Solve(GAMSModelInstance.SymbolUpdateType.BaseCase);
                Console.WriteLine(" Master " + masteri.ModelStatus + " : obj=" + masteri.SyncDB.GetVariable("zmaster").FirstRecord().Level);
                if (1 < iter)
                {
                    upperbound = masteri.SyncDB.GetVariable("zmaster").FirstRecord().Level;
                }
                objmaster = masteri.SyncDB.GetVariable("zmaster").FirstRecord().Level - theta.FirstRecord().Level;

                foreach (GAMSSetRecord s in data.OutDB.GetSet("s"))
                {
                    Dictionary <string, double> demDict = new Dictionary <string, double>();
                    foreach (GAMSSetRecord j in data.OutDB.GetSet("j"))
                    {
                        demDict[j.Keys[0]] = scenarioData.FindRecord(s.Keys[0], j.Keys[0]).Value;
                    }
                    demQueue.Enqueue(new Tuple <string, double, Dictionary <string, double> >(s.Keys[0], scenarioData.FindRecord(s.Keys[0], "prob").Value, demDict));
                }

                for (int i = 0; i < numThreads; i++)
                {
                    subi[i].SyncDB.GetParameter("received").Clear();
                }
                foreach (GAMSVariableRecord r in masteri.SyncDB.GetVariable("received"))
                {
                    cutcoeff.AddRecord(iter.ToString(), r.Keys[0]);
                    for (int i = 0; i < numThreads; i++)
                    {
                        subi[i].SyncDB.GetParameter("received").AddRecord(r.Keys).Value = r.Level;
                    }
                }

                cutconst.AddRecord(iter.ToString());
                double objsubsum = 0.0;

                // solve multiple model instances in parallel
                Object   queueMutex = new Object();
                Object   ioMutex    = new Object();
                double[] objsub     = new double[numThreads];
                Dictionary <string, double>[] coef = new Dictionary <string, double> [numThreads];
                double[] cons = new double[numThreads];

                for (int i = 0; i < numThreads; i++)
                {
                    coef[i] = new Dictionary <string, double>();
                    foreach (GAMSSetRecord j in data.OutDB.GetSet("j"))
                    {
                        coef[i].Add(j.Keys[0], 0.0);
                    }
                }

                Parallel.For(0, numThreads, delegate(int i) { ScenSolve(subi[i], ref cons[i], ref coef[i], demQueue, ref objsub[i], queueMutex, ioMutex); });

                for (int i = 0; i < numThreads; i++)
                {
                    objsubsum += objsub[i];
                    cutconst.FindRecord(iter.ToString()).Value += cons[i];
                    foreach (GAMSSetRecord j in data.OutDB.GetSet("j"))
                    {
                        cutcoeff.FindRecord(iter.ToString(), j.Keys[0]).Value += coef[i][j.Keys[0]];
                    }
                }
                lowerbound = Math.Max(lowerbound, objmaster + objsubsum);

                iter++;
                if (iter == maxiter + 1)
                {
                    throw new Exception("Benders out of iterations");
                }

                Console.WriteLine(" lowerbound: " + lowerbound + " upperbound: " + upperbound + " objmaster: " + objmaster);
            } while ((upperbound - lowerbound) >= 0.001 * (1 + Math.Abs(upperbound)));

            masteri.Dispose();
            foreach (GAMSModelInstance inst in subi)
            {
                inst.Dispose();
            }
        }
コード例 #4
0
        static void Main(string[] args)
        {
            GAMSWorkspace ws;

            if (Environment.GetCommandLineArgs().Length > 1)
            {
                ws = new GAMSWorkspace(systemDirectory: Environment.GetCommandLineArgs()[1]);
            }
            else
            {
                ws = new GAMSWorkspace();
            }
            // instantiate GAMSOptions and define parameters
            GAMSOptions  opt          = ws.AddOptions();
            GAMSDatabase cutstockData = ws.AddDatabase("csdata");

            opt.AllModelTypes = "Cplex";
            opt.OptCR         = 0.0; // Solve to optimality
            int maxpattern = 35;

            opt.Defines.Add("pmax", maxpattern.ToString());
            opt.Defines.Add("solveMasterAs", "RMIP");

            // define input data
            Dictionary <string, double> d = new Dictionary <string, double>()
            {
                { "i1", 97 }, { "i2", 610 }, { "i3", 395 }, { "i4", 211 }
            };
            Dictionary <string, double> w = new Dictionary <string, double>()
            {
                { "i1", 47 }, { "i2", 36 }, { "i3", 31 }, { "i4", 14 }
            };
            int r = 100; // raw width

            // cutstockData.AddSet("i", 1, "widths").AddRecords(d.Keys);
            // cutstockData.AddParameter("d", 1, "demand").AddRecords(d);
            // cutstockData.AddParameter("w", 1, "width").AddRecords(w);
            // cutstockData.AddParameter("r", 0, "raw width").AddRecord().Value = r;

            GAMSSet       widths   = cutstockData.AddSet("i", 1, "widths");
            GAMSParameter rawWidth = cutstockData.AddParameter("r", 0, "raw width");
            GAMSParameter demand   = cutstockData.AddParameter("d", 1, "demand");
            GAMSParameter width    = cutstockData.AddParameter("w", 1, "width");

            rawWidth.AddRecord().Value = r;
            foreach (string i in d.Keys)
            {
                widths.AddRecord(i);
            }
            foreach (KeyValuePair <string, double> t in d)
            {
                demand.AddRecord(t.Key).Value = t.Value;
            }
            foreach (KeyValuePair <string, double> t in w)
            {
                width.AddRecord(t.Key).Value = t.Value;
            }

            // create initial checkpoint
            GAMSCheckpoint masterCP      = ws.AddCheckpoint();
            GAMSJob        masterInitJob = ws.AddJobFromString(GetMasterModel());

            masterInitJob.Run(opt, masterCP, cutstockData);

            GAMSJob masterJob = ws.AddJobFromString("execute_load 'csdata', aip, pp; solve master min z using %solveMasterAs%;", masterCP);

            GAMSSet       pattern     = cutstockData.AddSet("pp", 1, "pattern index");
            GAMSParameter patternData = cutstockData.AddParameter("aip", 2, "pattern data");

            // Initial pattern: pattern i hold width i
            int patternCount = 0;

            foreach (KeyValuePair <string, double> t in w)
            {
                patternData.AddRecord(t.Key, pattern.AddRecord((++patternCount).ToString()).Keys[0]).Value = (int)(r / t.Value);
            }

            // create model instance for sub job
            GAMSCheckpoint subCP = ws.AddCheckpoint();

            ws.AddJobFromString(GetSubModel()).Run(opt, subCP, cutstockData);
            GAMSModelInstance subMI = subCP.AddModelInstance();

            // define modifier demdual
            GAMSParameter demandDual = subMI.SyncDB.AddParameter("demdual", 1, "dual of demand from master");

            subMI.Instantiate("pricing min z using mip", opt, new GAMSModifier(demandDual));

            // find new pattern
            bool patternAdded = true;

            do
            {
                masterJob.Run(opt, masterCP, cutstockData);
                // Copy duals into gmssubMI.SyncDB DB
                demandDual.Clear();
                foreach (GAMSEquationRecord dem in masterJob.OutDB.GetEquation("demand"))
                {
                    demandDual.AddRecord(dem.Keys[0]).Value = dem.Marginal;
                }

                subMI.Solve();
                if (subMI.SyncDB.GetVariable("z").FindRecord().Level < -0.00001)
                {
                    if (patternCount == maxpattern)
                    {
                        Console.Out.WriteLine("Out of pattern. Increase maxpattern (currently {0}).", maxpattern);
                        patternAdded = false;
                    }
                    else
                    {
                        Console.WriteLine("New patter! Value: " + subMI.SyncDB.GetVariable("z").FindRecord().Level);
                        GAMSSetRecord s = pattern.AddRecord((++patternCount).ToString());
                        foreach (GAMSVariableRecord y in subMI.SyncDB.GetVariable("y"))
                        {
                            if (y.Level > 0.5)
                            {
                                patternData.AddRecord(y.Keys[0], s.Keys[0]).Value = Math.Round(y.Level);
                            }
                        }
                    }
                }
                else
                {
                    patternAdded = false;
                }
            } while (patternAdded);

            // solve final MIP
            opt.Defines["solveMasterAs"] = "MIP";
            masterJob.Run(opt, cutstockData);
            Console.WriteLine("Optimal Solution: {0}", masterJob.OutDB.GetVariable("z").FindRecord().Level);
            foreach (GAMSVariableRecord xp in masterJob.OutDB.GetVariable("xp"))
            {
                if (xp.Level > 0.5)
                {
                    Console.Out.Write("  pattern {0} {1} times: ", xp.Keys[0], xp.Level);
                    GAMSParameterRecord aip = masterJob.OutDB.GetParameter("aip").FirstRecord(" ", xp.Keys[0]);
                    do
                    {
                        Console.Out.Write(" {0}: {1}", aip.Keys[0], aip.Value);
                    } while (aip.MoveNext());
                    Console.Out.WriteLine();
                }
            }
            // clean up of unmanaged ressources
            cutstockData.Dispose();
            subMI.Dispose();
            opt.Dispose();
        }
コード例 #5
0
        public void ProcessTask(TaskManager.TaskDetails oTask, out List <string> lsLogs, out string sOutput, out string sStatus)
        {
            // cast input values
            CutStockInput oInput = new CutStockInput();

            oInput = (CutStockInput)oDF.XmlStringToObject(oTask.InputValues, oInput);

            CutStockOutput oOutput = new CutStockOutput();

            // call processing algorithm

            GAMSWorkspace ws = new GAMSWorkspace();

            lsLog.Add("working directory : " + ws.WorkingDirectory);
            // instantiate GAMSOptions and define parameters
            GAMSOptions  opt          = ws.AddOptions();
            GAMSDatabase cutstockData = ws.AddDatabase("csdata");

            opt.AllModelTypes = "Cplex";
            opt.OptCR         = 0.0; // Solve to optimality
            opt.Defines.Add("pmax", oInput.MaxPattern.ToString());
            opt.Defines.Add("solveMasterAs", "RMIP");

            GAMSSet       widths   = cutstockData.AddSet("i", 1, "widths");
            GAMSParameter rawWidth = cutstockData.AddParameter("r", 0, "raw width");
            GAMSParameter demand   = cutstockData.AddParameter("d", 1, "demand");
            GAMSParameter width    = cutstockData.AddParameter("w", 1, "width");

            rawWidth.AddRecord().Value = oInput.RawWidth;

            foreach (CutItem oI in oInput.Items)
            {
                widths.AddRecord(oI.Name);
                demand.AddRecord(oI.Name).Value = oI.Demand;
                width.AddRecord(oI.Name).Value  = oI.Width;
            }

            // create initial checkpoint
            GAMSCheckpoint masterCP      = ws.AddCheckpoint();
            GAMSJob        masterInitJob = ws.AddJobFromString(GetMasterModel());

            masterInitJob.Run(opt, masterCP, cutstockData);

            GAMSJob masterJob = ws.AddJobFromString("execute_load 'csdata', aip, pp; solve master min z using %solveMasterAs%;", masterCP);

            GAMSSet       pattern     = cutstockData.AddSet("pp", 1, "pattern index");
            GAMSParameter patternData = cutstockData.AddParameter("aip", 2, "pattern data");

            // Initial pattern: pattern i hold width i
            int patternCount = 0;

            foreach (GAMSParameterRecord rec in width)
            {
                patternData.AddRecord(rec.Keys[0], pattern.AddRecord((++patternCount).ToString()).Keys[0]).Value = (int)(oInput.RawWidth / rec.Value);
            }

            // create model instance for sub job
            GAMSCheckpoint subCP = ws.AddCheckpoint();

            ws.AddJobFromString(GetSubModel()).Run(opt, subCP, cutstockData);
            GAMSModelInstance subMI = subCP.AddModelInstance();

            // define modifier demdual
            GAMSParameter demandDual = subMI.SyncDB.AddParameter("demdual", 1, "dual of demand from master");

            subMI.Instantiate("pricing min z using mip", opt, new GAMSModifier(demandDual));

            // find new pattern
            bool patternAdded = true;

            do
            {
                masterJob.Run(opt, masterCP, cutstockData);
                // Copy duals into gmssubMI.SyncDB DB
                demandDual.Clear();
                foreach (GAMSEquationRecord dem in masterJob.OutDB.GetEquation("demand"))
                {
                    demandDual.AddRecord(dem.Keys[0]).Value = dem.Marginal;
                }

                subMI.Solve();
                if (subMI.SyncDB.GetVariable("z").FindRecord().Level < -0.00001)
                {
                    if (patternCount == oInput.MaxPattern)
                    {
                        lsLog.Add("Out of pattern. Increase maxpattern (currently " + oInput.MaxPattern + ")." + Environment.NewLine);
                        patternAdded = false;
                    }
                    else
                    {
                        lsLog.Add("New pattern! Value: " + subMI.SyncDB.GetVariable("z").FindRecord().Level + Environment.NewLine);
                        GAMSSetRecord s = pattern.AddRecord((++patternCount).ToString());
                        foreach (GAMSVariableRecord y in subMI.SyncDB.GetVariable("y"))
                        {
                            if (y.Level > 0.5)
                            {
                                patternData.AddRecord(y.Keys[0], s.Keys[0]).Value = Math.Round(y.Level);
                            }
                        }
                    }
                }
                else
                {
                    patternAdded = false;
                }
            } while (patternAdded);

            // solve final MIP
            opt.Defines["solveMasterAs"] = "MIP";
            masterJob.Run(opt, cutstockData);
            var dlevel = masterJob.OutDB.GetVariable("z").FindRecord().Level;

            oOutput.OptimalSolution = (int)dlevel;
            lsLog.Add("Optimal Solution: " + dlevel + Environment.NewLine);
            List <Pattern> lP = new List <Pattern>();

            foreach (GAMSVariableRecord xp in masterJob.OutDB.GetVariable("xp"))
            {
                string sPatternLog = "";
                if (xp.Level > 0.5)
                {
                    sPatternLog = string.Format("  pattern {0,2} {1,4} times: ", xp.Keys[0], Math.Round(xp.Level));
                    GAMSParameterRecord aip = masterJob.OutDB.GetParameter("aip").FirstRecord(" ", xp.Keys[0]);

                    Pattern oP = new Pattern()
                    {
                        Name = "pattern " + xp.Keys[0].ToString(), Count = (int)Math.Round(xp.Level)
                    };
                    List <PatternPiece> lPP = new List <PatternPiece>();
                    int    x     = 0;
                    double scale = 100 / masterJob.OutDB.GetParameter("r").FirstRecord().Value;
                    do
                    {
                        sPatternLog += " " + aip.Keys[0] + ": " + aip.Value.ToString();
                        // draw cuts
                        for (int i = 0; i < aip.Value; i++)
                        {
                            PatternPiece oPP = new PatternPiece()
                            {
                                Name  = aip.Keys[0],
                                Width = (int)(width.FindRecord(aip.Keys[0]).Value *scale),
                                Left  = x,
                                Color = oInput.Items.Where(xi => xi.Name == aip.Keys[0]).FirstOrDefault().Color
                            };
                            lPP.Add(oPP);
                            // x += oPP.Width - 1;
                            x += oPP.Width;
                        }
                    } while (aip.MoveNext());

                    if (x < 100)
                    {
                        PatternPiece oPExcess = new PatternPiece()
                        {
                            Name  = "E",
                            Width = (int)(rawWidth.FirstRecord().Value *scale - x),
                            Left  = x,
                            Color = "CCCCCC"
                        };
                        lPP.Add(oPExcess);
                    }
                    lsLog.Add(sPatternLog);
                    oP.Pieces = lPP.ToArray();
                    lP.Add(oP);
                }
            }

            oOutput.Patterns = lP.ToArray();
            // clean up of unmanaged resources
            cutstockData.Dispose();
            subMI.Dispose();
            opt.Dispose();


            sOutput = oDF.ObjectToXmlString(oOutput);
            sStatus = "completed";
            lsLogs  = lsLog;
        }