haorenhui
2023-10-30 6d6cc10d9e8e242661da7fd655dec155a09d676c
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
Quintiq file version 2.0
#parent: #root
Method InitConstraintsForBalanceNoShelfLife (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const constcontent ProductInStockingPointInPeriods smartplanpispips,
  constcontent ProductInStockingPoint_MPs intermediatepisps,
  const ProductInStockingPointInPeriodPlanningLeaf pispip,
  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
    Dependent demands must be fulfilled in full quantity. Sales demands can be fulfilled halfly.
  *]
  TextBody:
  [*
    //   SUM( factor * relativeduration * PTQty ( operationoutput.operation, ptperiod )
    // + SUM( TripNewSupply ( newsupply.productintrip ) 
    // + InvQty ( Previous ) 
    // - SUM( SalesDemandQty ( leaf sales demand ) )
    // - SUM( DelayedSalesDemandQty ( delayed leaf sales demand ) )
    // - SUM( DisaggregatedSalesDemandQty ( disaggregated sales demand ) )
    // - SUM( DelayedDisaggregatedSalesDemandQty( delayed disaggregated sales demand ) )
    // - DependentDemandInPISPIP
    // - TargetInvQty
    // - UnallocQty 
    // - Expiry (pispip) 
    // = - InventorySupplyQuantity âˆ€ pispip where operationoutput, newsupply, sales demands   âˆˆ pispip
    
    
    
                                 
    // unallocated supply = new supply + inventory supply + inventory carried forward - demands (sales demands ( Leaf sales demands and Disaggregated sales demands), dependent demand, inventory demand )
    // constr constraint UoM: PISP
    
    
    
    constr := program.BalanceConstraints().New( pispip );
    constr.Sense( '=' );
    
    // RHS UoM: PISP
    rhs := this.GetInventoryRHSForBalanceConstraint( pispip, 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.InvQtyVariables().Get( pispip ) );
    
    // 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.DemandSlackVariables().Get( pispip ) );                  
    }
    
    // New supplies from operations
    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
    traverse( pispip, NewSupplyTrip.ProductInTrip, productintrip )
    {
      // Term UoM: Output PISP
      pitnsvar := program.TripNewSupplyVariables().Find( productintrip );
      if ( not isnull( pitnsvar ) ) // if not isnull means in scope and vice-versa
      { 
        constr.NewTerm( scalefactor_tripnewsupply_const, pitnsvar );
      }
    }
    
    // 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() >= pispip.ProductInStockingPoint_MP().EarliestPISPIPInScope().Start() ) 
    {
         // Term UoM: PISP
        constr.NewTerm( scalefactor_invqty_const,
                       program.InvQtyVariables().Get( previouspispip ) );
    }
    
    // Sales demands added to balance constraint pispip salesdemands
    traverse( pispip.GetLeafSalesDemandInPeriod(), Elements, lsdip, not lsdip.IsPostponed() or lsdip.IsManuallyPostponed() ) // note GetLeafSalesDemandInPeriod can include postponed sd, but non manual postponed 
    {                                                                                                      // sd is never added to scope (only the original) (*)
      // Leaf sales demands added to balance constraint
      constr.NewTerm( -1.0 * scalefactor_salesdemandqty_const,
                     program.SalesDemandQtyVariables().Get( lsdip ) );
    }
    
    traverse( pispip.GetDisaggregatedSalesDemandInPeriod(), 
              Elements, 
              dsdip, 
              not dsdip.IsPostponed() ) // similar remark as (*) above. Note disaggregates sales demand cannot be manually postponed
    {
      // Disaggregated sales demands added to balance constraint
      constr.NewTerm( -1.0 * scalefactor_salesdemandqty_const,
                     program.DisaggregatedSalesDemandQtyVariables().Get( dsdip ) );
    }
    
    // Dependent demands, with slack to prevent infeasibility
    // Term UoM: PISP
    constr.NewTerm( -1.0 * scalefactor_dependentdemandinpispip_const,
                   program.DependentDemandInPISPIPVariables().Get( pispip ) );
    
    
    if ( pispip.ProductInStockingPoint_MP().IsOptShelfLife() ) // note we always include the unsplit balance constraint defined in the current method 
    {                                                          // so pispip can still have shelf life defined
      constr.NewTerm( -1.0 * scalefactor_expired_const, program.ExpiredVariables().Get( pispip ) );
    }
    
    // PISPs with allowed negative inventory
    // Define positive inventory to allow inventory cost calculation
    if( pispip.ProductInStockingPoint_MP().IsNegativeInventoryAllowed() )
    {
      posinvconst := program.PositiveInventoryConstraints().New( pispip );
      posinvconst.Sense( '>=' );
      posinvconst.RHSValue( 0.0 );
      
      posinvconst.NewTerm( scalefactor_invqty_const, program.PosInvQtyVariables().Get( pispip ) );  
      posinvconst.NewTerm( -1.0 * scalefactor_invqty_const,program.InvQtyVariables().Get( pispip ) );
    }
    
    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 := null( MPVariable );
        
        var := sd.GetDelayedSalesDemandQtyVariable( program, pispip.Period_MP() );
        
        if( not isnull( var ) )
        {
          // Term UoM: PISP
          constr.NewTerm( -1.0 * scalefactor_delayedsalesdemandqty_const, var );
        }
      }
      previouspispip := previouspispip.PreviousPlanningPISPIP();
    }
  *]
  InterfaceProperties { Accessibility: 'Module' }
}