Quintiq file version 2.0 
 | 
#parent: #root 
 | 
Method InitConstraintsForInventoryMixBalancing ( 
 | 
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, 
 | 
  const RunContextForCapacityPlanning runcontext, 
 | 
  const LibOpt_Scope scope 
 | 
) const 
 | 
{ 
 | 
  Description: 'Initialize the constraints that are related to balancing the inventory mix' 
 | 
  TextBody: 
 | 
  [* 
 | 
    if( runcontext.UseInventoryMixBalancing() ) 
 | 
    {        
 | 
      overalldiffconstrname := typeof( MPOverallDifferenceInDemandDaysConstraint ); 
 | 
      overallmaxconstrname := typeof( MPOverallMaxInvInDemandDaysConstraint ); 
 | 
      overallminconstrname := typeof( MPOverallMinInvInDemandDaysConstraint ); 
 | 
      diffindaysconstrname := typeof( MPDifferenceInDemandDaysConstraint ); 
 | 
      mininvindaysconstrname := typeof( MPMinInvInDemandDaysConstraint ); 
 | 
      maxinvindaysconstrname := typeof( MPMaxInvInDemandDaysConstraint ); 
 | 
       
 | 
      scalefactor_overalldiff_overalldiffconstr := this.ScaleConstraintTerm( typeof( MPOverallDiffInvInDemandDaysVariable ), overalldiffconstrname ); 
 | 
      scalefactor_overallmax_overalldiffconstr := this.ScaleConstraintTerm( typeof( MPOverallMaxInvInDemandDaysVariable ), overalldiffconstrname ); 
 | 
      scalefactor_overallmin_overalldiffconstr := this.ScaleConstraintTerm( typeof( MPOverallMinInvInDemandDaysVariable ), overalldiffconstrname ); 
 | 
      scalefactor_overallmax_overallmaxconstrname := this.ScaleConstraintTerm( typeof( MPOverallMaxInvInDemandDaysVariable ), overallmaxconstrname ); 
 | 
      scalefactor_overallmin_overallminconstrname := this.ScaleConstraintTerm( typeof( MPOverallMinInvInDemandDaysVariable ), overallminconstrname ); 
 | 
      scalefactor_diffindays_diffindaysconstr := this.ScaleConstraintTerm( typeof( MPDifferenceInDemandDaysVariable ), diffindaysconstrname ); 
 | 
      scalefactor_maxinvindays_diffindaysconstr := this.ScaleConstraintTerm( typeof( MPMaxInvInDemandDaysVariable ), diffindaysconstrname ); 
 | 
      scalefactor_mininvindays_diffindaysconstr := this.ScaleConstraintTerm( typeof( MPMinInvInDemandDaysVariable ), diffindaysconstrname ); 
 | 
      scalefactor_maxinvindays_overallmaxconstr := this.ScaleConstraintTerm( typeof( MPMaxInvInDemandDaysVariable ), overallmaxconstrname ); 
 | 
      scalefactor_mininvindays_overallminconstr := this.ScaleConstraintTerm( typeof( MPMinInvInDemandDaysVariable ), overallminconstrname ); 
 | 
      scalefactor_maxinvindays_maxinvindaysconstr := this.ScaleConstraintTerm( typeof( MPMaxInvInDemandDaysVariable ), maxinvindaysconstrname ); 
 | 
      scalefactor_mininvindays_mininvindaysconstr := this.ScaleConstraintTerm( typeof( MPMinInvInDemandDaysVariable ), mininvindaysconstrname ); 
 | 
      scalefactor_invqty_maxinvindaysconstr := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), maxinvindaysconstrname ); 
 | 
      scalefactor_invqty_mininvindaysconstr := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), mininvindaysconstrname ); 
 | 
       
 | 
      scalefactor_rhs_mininvindaysconstr := this.ScaleConstraintRHS( mininvindaysconstrname, 1.0 ); 
 | 
      scalefactor_rhs_maxinvindaysconstr := this.ScaleConstraintRHS( maxinvindaysconstrname, 1.0 ); 
 | 
       
 | 
      productcategories := Product_MP::GetProductCategoriesForOptimization( this.MacroPlan(), scope ); 
 | 
       
 | 
      allperiods := construct( Period_MPs, constcontent );  
 | 
      traverse( productcategories, Elements, productcategory ) 
 | 
      { 
 | 
        // Retrieve all pispips that are part of this product category and that are part of the optimizer run 
 | 
        pispips := selectset( productcategory.GetLeavesConst(), Elements.ProductInStockingPoint_MP.ProductInStockingPointInPeriodPlanning, pispip, 
 | 
                              scope.Contains( pispip.PISPIPInOptimizerRun() ) ); 
 | 
                               
 | 
        // Select all periods related to these pispips 
 | 
        periods := selectset( pispips, Elements.Period_MP, period, true ); 
 | 
         
 | 
        // The inventory mix will be balanced per period  
 | 
        traverse( periods, Elements, period ) 
 | 
        {                            
 | 
          allperiods.Add( period );        
 | 
     
 | 
            // The overall max is equal to the maximum of all max inv in demand days 
 | 
            // OverallMaxInvInDemandDays(period) ≥ MaxInvInDemandDays( category, period ) ∀category,period 
 | 
            // overallmaxconstr UoM: Days 
 | 
            overallmaxconstr := program.OverallMaxInvInDemandDaysConstraints().New( productcategory, period ); 
 | 
            overallmaxconstr.Sense( '>=' ); 
 | 
            overallmaxconstr.RHSValue( 0.0 ); 
 | 
            overallmaxconstr.NewTerm( scalefactor_overallmax_overallmaxconstrname, program.OverallMaxInvInDemandDaysVariables().Get( period ) ); 
 | 
           
 | 
            // The overall min is equal to the minimum of all min inv in demand days 
 | 
            // OverallMinInvInDemandDays ≤ MinInvInDemandDays( category, period ) ∀category,period 
 | 
            // overallminconstr UoM: Days 
 | 
            overallminconstr := program.OverallMinInvInDemandDaysConstraints().New( productcategory, period ); 
 | 
            overallminconstr.Sense( '<=' ); 
 | 
            overallminconstr.RHSValue( 0.0 ); 
 | 
            overallminconstr.NewTerm( scalefactor_overallmin_overallminconstrname, program.OverallMinInvInDemandDaysVariables().Get( period ) );         
 | 
           
 | 
          // The difference in days is equal to the max inventory in days minus the min inventory in days. 
 | 
          // DifferenceInDays(category,period) = MaxInvInDemandDays(category,period) - MinInvInDemandDays(category,period)  ∀category,period 
 | 
          // diffindays constraint UoM: Days 
 | 
          diffindaysconstr := program.DifferenceInDemandDaysConstraints().New( productcategory, period ); 
 | 
          diffindaysconstr.Sense( '=' ); 
 | 
          diffindaysconstr.RHSValue( 0.0 ); 
 | 
          // term UoM: Days 
 | 
          diffindaysconstr.NewTerm( -1.0 * scalefactor_diffindays_diffindaysconstr, program.DifferenceInDemandDaysVariables().Get( productcategory, period ) ); 
 | 
          diffindaysconstr.NewTerm( scalefactor_maxinvindays_diffindaysconstr, program.MaxInvInDemandDaysVariables().Get( productcategory, period ) ); 
 | 
          diffindaysconstr.NewTerm( -1.0 * scalefactor_mininvindays_diffindaysconstr, program.MinInvInDemandDaysVariables().Get( productcategory, period ) );     
 | 
             
 | 
          // Add terms to the overall constraints 
 | 
          // Term UoM: Days 
 | 
          overallmaxconstr.NewTerm( -1.0 * scalefactor_maxinvindays_overallmaxconstr, program.MaxInvInDemandDaysVariables().Get( productcategory, period ) ); 
 | 
          overallminconstr.NewTerm( -1.0 * scalefactor_mininvindays_overallminconstr, program.MinInvInDemandDaysVariables().Get( productcategory, period ) ); 
 | 
           
 | 
          traverse( productcategory.GetLeafPISPIPsConst( period.Start(), period.End() ), Elements.astype( ProductInStockingPointInPeriodPlanning ), pispip,  
 | 
                    pispip.ProductInStockingPoint_MP().IsIncludedInBalancing()  
 | 
                    and pispip.ProductInStockingPoint_MP().Product_MP().IsIncludedInBalancing() )                                
 | 
          {        
 | 
            // The inventory of each product in each period is sufficient to cover a certain number of demand days. This is denoted the inventory in days. 
 | 
            // The minimum inventory in demand days is equal to the minimum of all products within a product category in each period 
 | 
            // MinInvDemandDays(product category) ≤ ( InvQty(product, period) - TargetInv(product, period) )/ DemandPerDay(product, period)  ∀product category, product ∈ product category, period 
 | 
            // mininvindays constraint UoM: Days 
 | 
            mininvindaysconstr := program.MinInvInDemandDaysConstraints().New( productcategory, pispip ); 
 | 
            mininvindaysconstr.Sense( '<=' ); 
 | 
            mininvindaysconstr.RHSValue( 0.0 ); 
 | 
            // Term UoM: Days 
 | 
            mininvindaysconstr.NewTerm( scalefactor_mininvindays_mininvindaysconstr, program.MinInvInDemandDaysVariables().Get( productcategory, period ) ); 
 | 
       
 | 
            // The inventory of each product in each period is sufficient to cover a certain number of demand days. This is denoted the inventory in days. 
 | 
            // The maximum inventory in demand days is equal to the maximum of the inventory in days of all products within a product category in each period 
 | 
            // MaxInvDemandDays(product category) ≥ ( InvQty(product, period) - TargetInv(product, period) )/ DemandPerDay(product, period)  ∀product category, product ∈ product category, period 
 | 
            // maxinvindays constraint UoM: Days 
 | 
             
 | 
            maxinvindaysconstr := program.MaxInvInDemandDaysConstraints().New( productcategory, pispip ); 
 | 
            maxinvindaysconstr.Sense( '>=' ); 
 | 
            maxinvindaysconstr.RHSValue( 0.0 ); 
 | 
            // Term UoM: Days 
 | 
            maxinvindaysconstr.NewTerm( scalefactor_maxinvindays_maxinvindaysconstr, program.MaxInvInDemandDaysVariables().Get( productcategory, period ) ); 
 | 
                                       
 | 
            demandperday := pispip.GetAverageSalesDemandQtyPerDay(); 
 | 
             
 | 
            // If the average demand per day is 0 (meaning there is no demand) 
 | 
            // then we should not balance this pispip 
 | 
            if( demandperday <> 0 ) 
 | 
            { 
 | 
              // If this pispip is part of the optimizer run, we need to a constraint term 
 | 
              if( scope.Contains( pispip.PISPIPInOptimizerRun() ) ) 
 | 
              {   
 | 
                // The terms we are adding here are (InvEnd - InvTarget)/Demand per day 
 | 
                // Therefore, we first add InvEnd/Demand per day as a variable term 
 | 
                // and then update the RHS with - InvTarget/Demand per day 
 | 
                 
 | 
                // Term:  - ( 1 / demandperday )  * InvQty variable 
 | 
                // UoM:   1 / [ PISP UoM  * Day ] *   [PISP UoM] 
 | 
                mininvindaysconstr.NewTerm( - ( 1 / demandperday ) * scalefactor_invqty_mininvindaysconstr, program.InvQtyVariables().Get( pispip ) ); 
 | 
                maxinvindaysconstr.NewTerm( - ( 1 / demandperday ) * scalefactor_invqty_maxinvindaysconstr, program.InvQtyVariables().Get( pispip ) ); 
 | 
       
 | 
                // Update RHS with - InvTarget/Demand per day 
 | 
                targetindays := 0.0; 
 | 
                if( pispip.GetHasTargetInventory() ) 
 | 
                { 
 | 
                  if( pispip.GetHasTargetInDays() ) 
 | 
                  { 
 | 
                    targetindays := pispip.TargetInDays(); 
 | 
                  } 
 | 
                  else 
 | 
                  { 
 | 
                    targetindays := pispip.TargetInQuantity() / demandperday; 
 | 
                  } 
 | 
                  newrhsmin := this.GetConstraintRHS( mininvindaysconstr, scalefactor_rhs_mininvindaysconstr ) - targetindays; 
 | 
                  mininvindaysconstr.RHSValue( newrhsmin * scalefactor_rhs_mininvindaysconstr ); 
 | 
                  newrhsmax := this.GetConstraintRHS( maxinvindaysconstr, scalefactor_rhs_maxinvindaysconstr ) - targetindays; 
 | 
                  maxinvindaysconstr.RHSValue( newrhsmax * scalefactor_rhs_maxinvindaysconstr ); 
 | 
                }                                
 | 
              } 
 | 
              // otherwise, we need to update the RHS since the invqty is fixed 
 | 
              else 
 | 
              { 
 | 
                targetinqty := 0.0; 
 | 
                if( pispip.GetHasTargetInDays() ) 
 | 
                { 
 | 
                  targetinqty := pispip.TargetInDays() * demandperday; 
 | 
                } 
 | 
                else 
 | 
                { 
 | 
                  targetinqty := pispip.TargetInQuantity(); 
 | 
                } 
 | 
                 
 | 
                newrhsmin := this.GetConstraintRHS( mininvindaysconstr, scalefactor_rhs_mininvindaysconstr ) + ( ( pispip.InventoryLevelEnd() - targetinqty ) / demandperday ); 
 | 
                mininvindaysconstr.RHSValue( newrhsmin * scalefactor_rhs_mininvindaysconstr ); 
 | 
                newrhsmax := this.GetConstraintRHS( maxinvindaysconstr, scalefactor_rhs_maxinvindaysconstr ) + ( ( pispip.InventoryLevelEnd() - targetinqty ) / demandperday ); 
 | 
                maxinvindaysconstr.RHSValue( newrhsmax * scalefactor_rhs_maxinvindaysconstr ); 
 | 
              } 
 | 
            } 
 | 
          } 
 | 
        } // end traverse period 
 | 
      } // end traverse product category 
 | 
       
 | 
      allperiods := allperiods.Unique();  
 | 
       
 | 
      traverse( allperiods, Elements, period )  
 | 
      {       
 | 
        // The overall difference is equal to the overall max minus the overall min 
 | 
        // OverallDifferenceInDays(period) = OverallMaxInvInDemandDays(period) - OverallMinInvInDemandDays(period) ∀period 
 | 
        // overalldiffconstr UoM: Days 
 | 
        overalldiffconstr := program.OverallDifferenceInDemandDaysConstraints().New( period ); 
 | 
        overalldiffconstr.Sense( '=' ); 
 | 
        overalldiffconstr.RHSValue( 0.0 ); 
 | 
        overalldiffconstr.NewTerm( -1.0 * scalefactor_overalldiff_overalldiffconstr, program.OverallDiffInvInDemandDaysVariables().Get( period ) ); 
 | 
        overalldiffconstr.NewTerm( scalefactor_overallmax_overalldiffconstr, program.OverallMaxInvInDemandDaysVariables().Get( period ) ); 
 | 
        overalldiffconstr.NewTerm( -1.0 * scalefactor_overallmin_overalldiffconstr, program.OverallMinInvInDemandDaysVariables().Get( period ) ); 
 | 
      } 
 | 
    } 
 | 
  *] 
 | 
  InterfaceProperties { Accessibility: 'Module' } 
 | 
} 
 |