Decision Optimization

Decision Optimization

Delivers prescriptive analytics capabilities and decision intelligence to improve decision-making.

 View Only
  • 1.  Grades and Tonnes

    Posted Wed November 05, 2008 08:26 PM

    Originally posted by: SystemAdmin


    [vamos said:]

    Has anyone modeled a problem in OPL that could be described as a blending problem with periods, changing blend source material quality, blend quality constraints and the option of stockpiling material of different qualities for future use?

    My specific problem is that  we mine material and feed it to a processing plant, any excess material is stockpiled over a number of periods. Material is broken into categories but the material is never consistent, so any stockpiled material is a blend whose grades/tonnes need to be recalculated every period to take into consideration any new material from the mine added and any material that went to the processing plant from the stockpile. How to optimise process plant feed with this constraint is where I have problems. How do you get OPL to create essentially new feed materials on the fly for each period? If anyone thinks they can help me please let me know and if you need more information I can provide a not working model file and data file.

    Thanks in advance,

    Ben
    #DecisionOptimization
    #OPLusingCPLEXOptimizer


  • 2.  Re: Grades and Tonnes

    Posted Thu November 13, 2008 02:48 PM

    Originally posted by: SystemAdmin


    [Didier Vidal said:]

    Vamos,

    I'm not sure I understand the problem... Is it that the equation to compute the new feed material at each time unit is non linear ?


    Didier.
    #DecisionOptimization
    #OPLusingCPLEXOptimizer


  • 3.  Re: Grades and Tonnes

    Posted Wed November 26, 2008 08:12 PM

    Originally posted by: SystemAdmin


    [vamos said:]

    //This is what i have done below, without having stockpiles it works fine. The periods are linear I just need to put any material that is not
    //sent to the mill to be stockpiled and the grade changed to balance the stockpile inventory. And of course the stockpile can be used
    //to feed the processing plant (mill) as well. Mine material is not homogenous hence the stockpile properties are dynamic based on what goes in and 
    //out.
    //There are three general stockpile grade calculation methods, 1.Blend (as is this case), 2. First in First Out and 3. Last in first out.

    //If you omit the constraint ctCalcGrade the optimisation fails  with CPLEX Error  5002: Q in objective is not positive semi-definite.


    {int} Periods = ...;
    int NPeriods = ...;
    int SL_NPeriods = NPeriods - 1;
    range NPeriod = 1..NPeriods;
    {int} SPPeriods = ...;
    range ZPeriods = 0..NPeriods;


    //Array types for mine and stockpile mill feed sources
    //and plants that require filling
    {string} FeedMaterialNames = ...;
    {string} Campaigna = ...;


    //Data arrays for properties of feed material and processing plants
    tuple RockProperties {
      string ID;
      float Tonnes;
      float Cu;
      //float Cus;
      //float Acido;
      //float CO3;
      //float Pot_Finos;
      //float Mezcla;
      int Period;
      string CampaignType;
      //float RehandleCosts;
    }

    {RockProperties} FeedData = ...;

    tuple MillProperties {
      string CampaignType;
      float Tonnes;
      //float maxCO3;
      //float maxAcido;
      //float maxMezcla;
      //float Solub;
      //float maxCu;
      int Period;
    }

    {MillProperties} MillData = ...;


    tuple PeriodMaterialName {
      string ID;
      int Period;
    }
    tuple PeriodMatName { 
      PeriodMaterialName PN;
      string c;
    }
    {PeriodMatName} PeriodMatNames = { < <I,Period>, Campaign > | <I,tonnes,Cu,Period,Campaign> in FeedData };
    {PeriodMaterialName} PeriodMaterialNames = { PN | <PN,c> in PeriodMatNames};

    float Grade[PeriodMatNames] = [ <<m.ID, m.Period>,m.CampaignType> : m.Cu | m in FeedData ];
    float Tonnes[PeriodMatNames] = [ <<m.ID, m.Period>,m.CampaignType> : m.Tonnes | m in FeedData ];

    tuple Campaign{
    string c;
    int p;
    }
    {Campaign} Campaigns = { <c,p> | <c,t,p> in MillData};

    float CampaignTonnes[Campaigns] = [ <c.CampaignType, c.Period> : c.Tonnes | c in MillData];

    {PeriodMaterialName} CampaignPeriods[c in Campaigna] = { n | <n,c> in PeriodMatNames};

    //Declare descision variable for holding of solution.
    //tonnes that are optimal from each of the possible material sources
    //and to which plants they should go to
    dvar float+ MineFeed[PeriodMatNames];
    dvar float+ SPFeed[PeriodMatNames];
    dvar float+ SPGrade[PeriodMatNames];
    dvar float+ SPBalance[PeriodMatNames];



    maximize
      sum(<<i,n>,c> in PeriodMatNames, p in 1..NPeriods)
      (MineFeed[<<i,p>,c>] * Grade[<<i,p>,c>] + SPFeed[<<i,p>,c>]*SPGrade[<<i,p-1>,c>]);
     
    //Constraints
      subject to {
     
        //Make sure do not feed more material than required for campaign
        forall (c in Campaigna, p in Periods)
        ctCampaignTonnes:
        sum(<i,p> in CampaignPeriods[c]) (MineFeed[<<i,p>,c>] + SPFeed[<<i,p>,c>]) <= CampaignTonnes[<c,p>];

        //Do not feed more material than available for each period.
        forall (m in PeriodMatNames)
        ctFeedTonnes:
        MineFeed[m] <= Tonnes[m];<br />   
        forall (<<i,n>,c> in PeriodMatNames, p in 1..NPeriods)
        ctSPFeedTonnes:
        SPFeed[<<i,p>,c>] <= SPBalance[<<i,p-1>,c>];
       
        forall (m in PeriodMatNames)
        ctMaxSPFeed:
        SPFeed[m] <= 400000;<br />  
      //Period 0 is intialisation of stockpile material
        forall (<<i,0>,c> in PeriodMatNames)
        ctMineFeedP0:
        MineFeed[<<i,0>,c>] == 0;
     
        forall (<<i,0>,c> in PeriodMatNames)
        ctSPFeedP0:
        sum(<i,0> in CampaignPeriods[c])
        SPFeed[<<i,0>,c>] == 0;
       
        forall (<<i,0>,c> in PeriodMatNames)
        ctSPGradeP0:
        sum(<i,0> in CampaignPeriods[c])
        SPGrade[<<i,0>,c>] == Grade[<<i,0>,c>];
     
        forall (<<i,0>,c> in PeriodMatNames)
        ctSPBalanceP0:
        SPBalance[<<i,0>,c>] == Tonnes[<<i,0>,c>]; 
       

        forall (<<i,n>,c> in PeriodMatNames, p in 1..NPeriods)
        ctCalcSPBalance:
        (Tonnes[<<i,p>,c>]-MineFeed[<<i,p>,c>] - SPFeed[<<i,p>,c>] + SPBalance[<<i,p-1>,c>]) == SPBalance[<<i,p>,c>];
       
        forall (<<i,n>,c> in PeriodMatNames, p in 1..NPeriods)
        ctCalcGrade:
    SPGrade[<<i,p>,c>]*SPBalance[<<i,p>,c>] - (SPBalance[<<i,p>,c>]
    - Tonnes[<<i,p>,c>] + MineFeed[<<i,p>,c>])*SPGrade[<<i,p-1>,c>] -
    (Tonnes[<<i,p>,c>]-MineFeed[<<i,p>,c>])*Grade[<<i,p>,c>]== 0 ;

    }

    tuple result {
      int Period;
      string Campaign;
      string MineFeedMaterial;
      float MineFeedTonnes;
      //float StockpileFeedTonnes;
    }

      //send results to database
      {result} Result =
        { <p,c,i,MineFeed&#91;<<i,p>,c>]> | <<i,p>,c> in PeriodMatNames };
      execute DISPLAY_RESULT{
        writeln("Result = ",Result);
      }


    ///////Test Data file/////////////
    Periods = {0,1,2,3};
    SPPeriods = {0,1,2};
    NPeriods = 3;

    FeedMaterialNames = {HGOX, HGSUL, LGOX, LGSUL};
    Campaigna = {Oxido, Sulfuro};

    FeedData = {<HGOX, 100000, 1.6, 0, Oxido>,<HGOX, 300000, 1.8, 1, Oxido>,<HGOX, 250000, 1.4, 2, Oxido>,<HGOX, 250000, 1.7, 3, Oxido>,
    <HGSUL, 150000, 2.2, 0, Sulfuro>,<HGSUL, 300000, 2.0, 1, Sulfuro>,<HGSUL, 325000, 1.9, 2, Sulfuro>,<HGSUL, 325000, 1.9, 3, Sulfuro>,
    <LGOX, 1350000, 0.5, 0, Oxido>,<LGOX, 350000, 0.65, 1, Oxido>,<LGOX, 100000, 0.43, 2, Oxido>,<LGOX, 120000, 0.43, 3, Oxido>,
    <LGSUL, 327000, 0.5, 0, Sulfuro>,<LGSUL, 427000, 0.47, 1, Sulfuro>,<LGSUL, 234000, 0.56, 2, Sulfuro>,<LGSUL, 234000, 0.56, 3, Sulfuro>};

    MillData = {<Oxido,0,0>,<Sulfuro,0,0>,<Oxido,425000,1>,<Sulfuro,425000,1>,<Oxido,425000,2>,<Sulfuro,425000,2>,<Oxido,425000,3>,<Sulfuro,425000,3>};

    //Result to DBUpdate(db,INSERT INTO OptimisedMillFeed(Period,PlanID,Plant,FeedSourceID,Tonnes) VALUES(?,?,?,?,?));
    #DecisionOptimization
    #OPLusingCPLEXOptimizer


  • 4.  Re: Grades and Tonnes

    Posted Thu November 27, 2008 05:50 PM

    Originally posted by: SystemAdmin


    [Didier Vidal said:]

    Vamos,

    I don't sure I have a solution to your problem. But at least, I can explain what happens:

    In CPLEX, if you multiply decision variables:
      - Your problem is no longer a linear problem. If it is a QP or QCP problem, CPLEX can handle it.
      - This can only be done in a constraint of the form <= or >=. But never in an equality constraint. This is why the constraint ctCalcGrade is refused.
      - If you multiply decision variables, the multiplication must be in the form T'QT, where T is your vector of variable, and Q is a positive semi-definite matrix. The way I see this is that your terms must be squares of linear combinations of your variables multiplied by positive coefficients. Your objective doesn't have this form.


    You should try to find a way to model your problem without multiplication of decision variables.
    #DecisionOptimization
    #OPLusingCPLEXOptimizer


  • 5.  Re: Grades and Tonnes

    Posted Tue December 02, 2008 12:31 AM

    Originally posted by: SystemAdmin


    [vamos said:]

    Thanks for the explanation Didier. I will have to try and model the problem differently.
    #DecisionOptimization
    #OPLusingCPLEXOptimizer