| 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' } | 
| } |