| Quintiq file version 2.0 | 
| #parent: #root | 
| Method InitVariablesForPISPIP ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const constcontent ProductInStockingPointInPeriods pispipssmartplan, | 
|   const LibOpt_Scope scope, | 
|   const constcontent ProductInStockingPointInPeriodPlannings pispipsinrun, | 
|   constcontent ProductInStockingPointInPeriodPlanningLeafs leafpispipsinrun, | 
|   const constcontent ProductInStockingPoint_MPs pispsinrun | 
| ) const | 
| { | 
|   Description: 'Initialize all variables related to pispip, including fulfillment of sales demand, target inventory level, etc' | 
|   TextBody: | 
|   [* | 
|     runcontext := this.GetRunContextConst();  | 
|      | 
|     // Shelf life variable   | 
|     traverse( pispsinrun, Elements, pisp, pisp.IsOptShelfLife() or pisp.IsOptMaturation() )  | 
|     { | 
|       if ( pisp.IsOptShelfLife() )  | 
|       { | 
|         program.PISPTotalExpiryVariables().New( pisp ); | 
|       } | 
|       initpispips := pisp.PISPInOptimizerRun().GetPISPIPForShelfLifeOptimizer( scope );  | 
|       traverse( initpispips, Elements, pispip ) | 
|       { | 
|         traverse( pispip, ProductInStockingPoint_MP.IncomingShelfLifeDay, islday )  | 
|         { | 
|           program.CumulativeProductionVariables().New( pispip, islday ); | 
|         } | 
|        | 
|         if ( pisp.IsOptShelfLife() )  | 
|         { | 
|           program.ExpiredVariables().New( pispip ); | 
|           program.InvQtyBlockedVariables().New( pispip );  | 
|         } | 
|         traverse( pispip, ProductInStockingPoint_MP.IncomingShelfLifeDay, islday )  | 
|         {     | 
|           if ( pisp.IsOptMaturation() or pisp.IsOptShelfLife() )  | 
|           { | 
|             traverse( pisp, OutgoingShelfLifeDay, oslday )  | 
|             { | 
|               program.DependentDemandInPISPIPShelfLifeVariables().New( pispip, islday, oslday ); | 
|             } | 
|           } | 
|           if ( pisp.IsOptShelfLife() )  | 
|           {                             | 
|             program.CumulativeDemandVariables().New( pispip, islday ); | 
|             program.CumulativeWasteVariables().New( pispip,islday ); | 
|             program.WasteVariables().New( pispip, islday ); | 
|             program.ExpiredForAgeVariables().New( pispip, islday );  | 
|             // >> split variables for balance  | 
|           | 
|             program.InvQtyShelfLifeVariables().New( pispip, islday ); | 
|             program.DemandSlackShelfLifeVariables().New( pispip, islday );   | 
|           } | 
|         } | 
|        | 
|         // Maturation variables | 
|         if( pispip.ProductInStockingPoint_MP().IsOptMaturation() ) | 
|         { | 
|           traverse( pispip, ProductInStockingPoint_MP.OutgoingShelfLifeDay, oslday )  | 
|           { | 
|             program.CumulativeDemandMaturationVariables().New( pispip, oslday );  | 
|             program.MaturationSlackVariables().New( pispip, oslday );  | 
|           } | 
|         } | 
|       } | 
|     } | 
|      | 
|     traverse( leafpispipsinrun, Elements, pispip )  | 
|     { | 
|       // DependentDemandInPISPIP variable UoM: PISP | 
|       program.DependentDemandInPISPIPVariables().New( pispip );   // Total dependent demand quantity in pispip | 
|       // DemandSlack variable UoM: PISP | 
|       program.DemandSlackVariables().New( pispip );     // Total decreased quantity of dependent demand to make the balance constraint feasible | 
|            | 
|       if( pispip.MustInitializeUserSupplyConstraint( runcontext, scope ) ) | 
|       { | 
|         // UserTotalSupply slack variables UoM: PISP | 
|         program.UserTotalSupplyUnderVariables().New( pispip ); | 
|         program.UserTotalSupplyOverVariables().New( pispip ); | 
|       } | 
|        | 
|       // Inventory quantity | 
|       // InvQty variable UoM: PISP | 
|       varinvqty := program.InvQtyVariables().New( pispip ); | 
|      | 
|       // If this pispip allows negative inventory then invqty and unallocqty are allowed to be negative | 
|       if( pispip.ProductInStockingPoint_MP().IsNegativeInventoryAllowed() ) | 
|       { | 
|         lowerbound := -Real::MaxReal(); | 
|         this.FreezeVariableLowerBound( varinvqty, lowerbound ); | 
|          | 
|         // Positive inventory quantity | 
|         program.PosInvQtyVariables().New( pispip ); | 
|       } | 
|     } | 
|      | 
|     // declare PosInvQtyPastLast in case we need inventory holding costs | 
|     if ( runcontext.IsMetaIteration() and this.GetInitializeFinancialConstraints( runcontext ) )  | 
|     { | 
|       traverse( pispsinrun, Elements, pisp )  | 
|       { | 
|         last := pisp.LatestPISPIPInScope();  | 
|         current := guard( last.Next().astype(  ProductInStockingPointInPeriodPlanningLeaf ), null(  ProductInStockingPointInPeriodPlanningLeaf ) );  | 
|          | 
|         while ( not isnull( current ) )  | 
|         { | 
|           program.PosInvQtyPastLastVariables().New( current );  | 
|           current := current.NextPlanningPISPIP().astype(  ProductInStockingPointInPeriodPlanningLeaf );  | 
|         } | 
|       } | 
|     } | 
|      | 
|     //Only plan demand for selected PISPIP(s) during smart planning if requested by user. | 
|     if( runcontext.IsSmartPlan() | 
|         and this.IsOnlyPlanDemandForSmartPlanPISPIPs() ) | 
|     { | 
|       castedpispipsmartplan := construct( ProductInStockingPointInPeriodPlanningLeafs, constcontent ) ; | 
|       traverse( pispipssmartplan, Elements.astype( ProductInStockingPointInPeriodPlanningLeaf ), p )  | 
|       { | 
|         castedpispipsmartplan.Add( p );  | 
|       } | 
|       pispipnotselected := leafpispipsinrun.Difference( castedpispipsmartplan );  | 
|      | 
|       //First bound all input pispips sales demand variables. | 
|       traverse( pispipnotselected, Elements, pispip )  | 
|       { | 
|         traverse( pispip, PlanningBaseSalesDemandInPeriodForOptimization, sd ) | 
|         {       | 
|           var := null( MPVariable ); | 
|           if( sd.istype( LeafSalesDemandInPeriod ) ) | 
|           { | 
|             var := program.SalesDemandQtyVariables().Get( sd.astype( LeafSalesDemandInPeriod ) ); //pispipsmartplan elements should be a subset of the direct optimizer PISPIP relation. | 
|           } | 
|           else if( sd.istype( DisaggregatedSalesDemandInPeriod ) ) | 
|           { | 
|             var := program.DisaggregatedSalesDemandQtyVariables().Get( sd.astype( DisaggregatedSalesDemandInPeriod ) ); //pispipsmartplan elements should be a subset of the direct optimizer PISPIP relation. | 
|           } | 
|           if( not isnull( var ) ) | 
|           { | 
|             lowerbound := sd.FulfilledQuantity(); | 
|             upperbound := sd.FulfilledQuantity();     | 
|             this.FreezeVariableLowerUpperBound( var, lowerbound, upperbound ); | 
|           } | 
|           // Freeze delayed sales demand variables | 
|           sd.FreezeVariablesForPostponedSalesDemands( this, program, scope );  | 
|         } | 
|       } | 
|     }     | 
|      | 
|     // Create slack variables for the inventory specification constraints | 
|     // These specifications can also be on high level products, so we cannot use the regular traverse over the PISPIPsInOptimizerRun | 
|     leafpispips := null( ProductInStockingPointInPeriodPlannings, constcontent, owning ); | 
|     pispips := this.GetPISPIPsForInventorySpecifications( scope, false, &leafpispips ); | 
|      | 
|     traverse( leafpispips, Elements, pispip, pispip.GetHasTargetInventory() ) // in case of meta optimizer call these do not include pispips prior to the scope (all in scope) | 
|     { | 
|       // TargetInvQty variable UoM: PISP | 
|       vartarget := program.TargetInvQtyVariables().New( pispip ); | 
|       if( pispip.ProductInStockingPoint_MP().IsNegativeInventoryAllowed() ) // note carriedfwd inventory constraints states TargetInvQty < = InvQty at a pispip | 
|       { | 
|         vartarget.LowerBound( Real::MinReal() );  | 
|       } | 
|     } | 
|      | 
|     optscopestart := runcontext.FirstPeriod_MP().Start();  | 
|     traverse( pispips, Elements, pispip ) | 
|     { | 
|       hastargetinventory := pispip.GetHasTargetInventory(); | 
|       hasminlevel := pispip.GetHasMinLevel();  | 
|       hasmaxlevel := pispip.GetHasMaxLevel();  | 
|       hasspec := hasmaxlevel or hasminlevel or hastargetinventory;  | 
|       if ( hasspec and pispip.Start() < optscopestart )  | 
|       { | 
|         program.InvQtySpecPriorToHorizonSlackVariables().New( pispip ); // slack variable ( e.g. feedback makes we cannot limit violation prior to the horizon by a hard constraint)  | 
|       } | 
|       if( hastargetinventory ) | 
|       { | 
|         // TargetInvQtyUnder variable UoM: PISP | 
|         program.InvQtyUnderTargetVariables().New( pispip );    // Penalty of not reaching the target | 
|       } | 
|      | 
|       if( hasminlevel ) | 
|       { | 
|         // MinInvQtyUnder variable UoM: PISP | 
|         program.MinInvQtyUnderVariables().New( pispip );       // Penalty for not reaching the minimum inventory level | 
|       } | 
|        | 
|       if( hasmaxlevel ) | 
|       { | 
|         // MaxInvQtyOver variable UoM: PISP | 
|         program.MaxInvQtyOverVariables().New( pispip );        // Penalty for not reaching the maximum inventory level | 
|       } | 
|     } | 
|      | 
|     pispips := this.GetPISPIPsForDemandFulfillment( scope, pispipsinrun ); | 
|     traverse( pispips, Elements, pispip ) | 
|     { | 
|       // DemandFulfillmentInPISPIP variable UoM: PISP | 
|       program.DemandFulfillmentInPISPIPVariables().New( pispip );  // Total demand fulfillment quantity    | 
|     } | 
|      | 
|     // Define the servicelevel variables for each servicelevel that that has at least one sdip on a pispip in this optimizer run | 
|     traverse( this.MacroPlan(), AllServiceLevelBase, sl, sl.GetIsInOptimizerRun( scope ) ) | 
|     { | 
|       // If this is a fulfillment goal related service level then add the related variables | 
|       if( sl.IsUsedForPlanningFulfillmentSystem() ) | 
|       { | 
|         program.FulfillmentTargetVariables().New( sl ); | 
|       } | 
|       else if( sl.IsUsedForSafetyStockCalculation() ) | 
|       { | 
|         program.ServiceLevelQtyVariables().New( sl ); | 
|       } | 
|     } | 
|      | 
|     if( runcontext.UseInventoryMixBalancing() ) | 
|     { | 
|       allperiods := construct( Period_MPs, constcontent );  | 
|       productcategories := Product_MP::GetProductCategoriesForOptimization( this.MacroPlan(), scope ); | 
|            | 
|       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() ) )  | 
|                               // we can avoid scope.Contains(  pispip.PISPIPInOptimizerRun() ) ) because we are in the same transaction | 
|         // Select all periods related to these pispips | 
|         periods := selectset( pispips, Elements.Period_MP, period, true ); | 
|        | 
|         traverse( periods, Elements, period ) | 
|         {      | 
|           allperiods.Add( period );  | 
|           // Create the variables per product category and period which are used to balance the inventory mix within a category in a period | 
|           varmax := program.MaxInvInDemandDaysVariables().New( productcategory, period ); | 
|           varmin := program.MinInvInDemandDaysVariables().New( productcategory, period ); | 
|           varmax.LowerBound( Real::MinReal() ); | 
|           varmin.LowerBound( Real::MinReal() ); | 
|           program.DifferenceInDemandDaysVariables().New( productcategory, period ); | 
|         } | 
|       } | 
|       allperiods := allperiods.Unique();    | 
|      | 
|       traverse( allperiods, Elements, period )    | 
|       { | 
|          // Create the min and max inventory in days variables which are used to balance the inventory quantities per product category | 
|          // Create the overall variables that are used in the balancing of the categories | 
|          overallmaxvar := program.OverallMaxInvInDemandDaysVariables().New( period ); | 
|          overallmaxvar.LowerBound( Real::MinReal() ); | 
|          overallminvar := program.OverallMinInvInDemandDaysVariables().New( period ); | 
|          overallminvar.LowerBound( Real::MinReal() ); | 
|          program.OverallDiffInvInDemandDaysVariables().New( period ); | 
|       } | 
|     } | 
|      | 
|     // for debugging to fix a pispip fulfilled sales demand | 
|     /*  | 
|     if( runcontext.IsMetaIteration() ) | 
|     { | 
|       //First bound all input pispips sales demand variables. | 
|       traverse( leafpispipsinrun, Elements, pispip)  | 
|       { | 
|         detected :=  pispip.ProductInStockingPoint_MP().Product_MP().Name() = 'FG-2-24-110-4614-12-0'  | 
|                      and pispip.StockingPointInPeriod().StockingPoint_MP().Name() = '624790'  | 
|                      and pispip.Start().Date() = Date::Construct( 2020, 7, 1 );  | 
|      | 
|         if ( detected )  | 
|         { | 
|           traverse( pispip, PlanningBaseSalesDemandInPeriodForOptimization, sd ) | 
|           {       | 
|             var := null( MPVariable ); | 
|             if( sd.istype( LeafSalesDemandInPeriod ) ) | 
|             { | 
|               var := program.SalesDemandQtyVariables().Get( sd.astype( LeafSalesDemandInPeriod ) ); //pispipsmartplan elements should be a subset of the direct optimizer PISPIP relation. | 
|             } | 
|             else if( sd.istype( DisaggregatedSalesDemandInPeriod ) ) | 
|             { | 
|               var := program.DisaggregatedSalesDemandQtyVariables().Get( sd.astype( DisaggregatedSalesDemandInPeriod ) ); //pispipsmartplan elements should be a subset of the direct optimizer PISPIP relation. | 
|             } | 
|             if( not isnull( var ) ) | 
|             { | 
|               lowerbound := ifexpr( detected, sd.Quantity(), sd.FulfilledQuantity() ); | 
|               upperbound := sd.Quantity();     | 
|               this.FreezeVariableLowerUpperBound( var, lowerbound, upperbound ); | 
|               if( detected )  | 
|               { | 
|                 debuginfo(  'FIXING sales demand - DEBUGGING', pispip.ProductInStockingPoint_MP().Name(), pispip.Start(), ' to ', sd.Quantity() );  | 
|               } | 
|             } | 
|           } | 
|         } | 
|       } | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |