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