陈清红
2025-04-14 880f3c0257eeb8c37761d484258fdd102a369a19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
Quintiq file version 2.0
#parent: #root
Method InitConstraintsForBalanceSplitShelfLife (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const constcontent ProductInStockingPointInPeriods smartplanpispips,
  constcontent ProductInStockingPoint_MPs intermediatepisps,
  const ProductInStockingPointInPeriodPlanning pispip,
  DateTime firstpispipforbalancestart,
  DateTime lastpispipstart,
  const RunContextForCapacityPlanning runcontext,
  const LibOpt_Scope scope,
  Real scalefactor_demandslack_const,
  Real scalefactor_tripnewsupply_const,
  Real scalefactor_invqty_const,
  Real scalefactor_salesdemandqty_const,
  Real scalefactor_dependentdemandinpispip_const,
  Real scalefactor_delayedsalesdemandqty_const,
  Real scalefactor_periodtaskqty_const,
  Real scalefactor_expired_const,
  Real scalefactor_rhs_constr
) const
{
  Description:
  [*
    To balance the demands and supply in pispip for a pispip that has IsOptShelfLife or IsOptMaturation
    Dependent demands must be fulfilled in full quantity. Sales demands can be fulfilled halfly.
  *]
  TextBody:
  [*
    traverse( pispip, ProductInStockingPoint_MP.IncomingShelfLifeDay, islday ) 
    {
      constr := program.BalanceShelfLifeConstraints().New( pispip, islday );
      constr.Sense( '=' );
    
      // RHS UoM: PISP
      rhs := this.GetInventoryRHSForShelfLifeSplitBalanceConstraint( pispip, islday, firstpispipforbalancestart, runcontext, scope );  
      rhs_scaled := rhs * scalefactor_rhs_constr
    
      constr.RHSValue( rhs_scaled );
    
      // Inventory end for pispip
      // Term UoM: PISP
      constr.NewTerm( -1.0 * scalefactor_invqty_const, program.InvQtyShelfLifeVariables().Get( pispip, islday ) );
    
      // Penalty for decreasing demand quantity in a pispip for balancing the constraint
      // (A positive DemandSlack represents a supply that is "magically" created to even out the balance constraint)
      // Term UoM: PISP
      // Remove the slack in case we only plan 1 step upstream and it is an intermediary product.      
      if( this.GetIsBalanceSlackAllowed( pispip, smartplanpispips, intermediatepisps, runcontext ) )
      {  
        constr.NewTerm( 1.0 * scalefactor_demandslack_const,
                       program.DemandSlackShelfLifeVariables().Get( pispip, islday ) );                  
      }
      
      // New supplies from operations added to shelflife 0 
      if ( islday.ShelfLifeDays() = 0 ) 
      {
        ispostprocessing := runcontext.IsPostProcessing(); 
        traverse( pispip, 
                  ProductInStockingPoint_MP.OperationOutputAvailableForOptimization, 
                  output,
                  output.HasRegularProductforOptimizer() 
                  or output.GetIsProductInOptimizerRun( ispostprocessing ) )
        {             
          // Term UoM: Output PISP
          this.AddConstraintForOperationNewSupplies( output, pispip.Period_MP(), null( Period_MP ),
                                                     program, 1.0, constr, scalefactor_periodtaskqty_const, scope );
        }
      }
      
      
      // New supplies from trips, for those productintrip that are part of the optimizer run and add islday.ShelfLifeDays to the shelf life
      traverse( pispip, astype( ProductInStockingPointInPeriodPlanningLeaf ).NewSupply.ProductInTrip, productintrip,
                scope.Contains(  productintrip.ProductInTripInOptimizerRun() ) 
                and productintrip.Trip().GetShelfLifeAgeToAdd() = islday.ShelfLifeDays() ) 
      {
        // Term UoM: Output PISP
        constr.NewTerm( 1.0 * scalefactor_tripnewsupply_const, program.TripNewSupplyVariables().Get( productintrip ) );
      }
    
      // Inventory carried forward, if the previous pispip exists and is part of the optimizer run
      previouspispip := pispip.PreviousPlanningPISPIP();
      if( not isnull( previouspispip ) and previouspispip.Start() >= firstpispipforbalancestart ) 
      {
           // Term UoM: PISP
          constr.NewTerm( 1.0 * scalefactor_invqty_const,   program.InvQtyShelfLifeVariables().Get( previouspispip, islday ) );
      }
    
      // Sales demands added to balance constraint pispip salesdemands
      traverse( pispip.GetLeafSalesDemandInPeriod(), Elements, lsdip, not lsdip.IsPostponed() or lsdip.IsManuallyPostponed() ) // analogous to non-shelflife balance 
      {
        // Leaf sales demands added to balance constraint
        constr.NewTerm( -1.0 * scalefactor_salesdemandqty_const,
                       program.SalesDemandShelfLifeQtyVariables().Get( lsdip, islday ) );
      }
      traverse( pispip.GetDisaggregatedSalesDemandInPeriod(), Elements, dsdip, not dsdip.IsPostponed() ) // analogous to non-shelflife balance 
      {
        // Disaggregated sales demands added to balance constraint
        constr.NewTerm( -1.0 * scalefactor_salesdemandqty_const,
                       program.DisaggregatedSalesDemandShelfLifeQtyVariables().Get( dsdip, islday ) );
      }
    
      // Dependent demands, with slack to prevent infeasibility
      // Term UoM: PISP
      traverse( pispip, ProductInStockingPoint_MP.OutgoingShelfLifeDay, oslday ) 
      {
        constr.NewTerm( -1.0 * scalefactor_dependentdemandinpispip_const,
                       program.DependentDemandInPISPIPShelfLifeVariables().Get( pispip, islday, oslday ) );
      }  
      
      //subtract the number of products that have expired in this pispip
      if ( pispip.ProductInStockingPoint_MP().IsOptShelfLife() ) 
      {
        constr.NewTerm( -1.0 * scalefactor_expired_const, program.ExpiredForAgeVariables().Get( pispip, islday ) );
      }
    
      // Postponed demands which are postponed to this period
      previouspispip := pispip.PreviousPlanningPISPIP();
      
      maxnumberofpostponement := pispip.ProductInStockingPoint_MP().OptimizerMaxPostponementPeriod(); // set in init instance for performance 
      for( i := 1;
           i <= maxnumberofpostponement // within the maximum number of postponement periods
           and not isnull( previouspispip ); // the previous pispip exists
           i++ )
      {
        traverse( previouspispip.astype( ProductInStockingPointInPeriodPlanningLeaf ), PlanningBaseSalesDemandInPeriodForOptimizationPostponable, sd )
        {                                             
          var := sd.GetDelayedSalesDemandShelfLifeQtyVariable( program, pispip.Period_MP(), islday ); 
          if( not isnull( var ) )
          {
            // Term UoM: PISP
            constr.NewTerm( -1.0 * scalefactor_delayedsalesdemandqty_const, var );
          }
        }
        previouspispip := previouspispip.PreviousPlanningPISPIP();
      }
    }
  *]
  InterfaceProperties { Accessibility: 'Module' }
}