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
Quintiq file version 2.0
#parent: #root
Method AdditionalConstraintMaxInventory (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const LibOpt_Scope scope,
  const RunContextForCapacityPlanning runcontext,
  const RunContextMeta runcontextmeta
) const
{
  TextBody:
  [*
    if ( runcontext.IsMetaIteration() and runcontextmeta.OptionUseAdditonalMaxInventoryConstraint() ) 
    {
      constname := typeof( MPMetaLimitMaxInventoryPastHorizonConstraint ); 
      scalefactor_invqty_const := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), constname );
      scalefactor_slack_const := this.ScaleConstraintTerm( typeof( MPMetaLimitMaxInventoryPastHorizonSlackVariable ), constname );
      scalefactor_rhs_const := this.ScaleConstraintRHS( constname, 1.0 );
    
      traverse( scope.GetProductInStockingPointInOptimizerRunConst(), Elements.LatestPISPIPInScope, pispip )            
      {
        smallestgap := pispip.GetSmallestMaxGap(); 
        
        // we only allow the inventory level to increase by smallest gap at the pispip so we do not introduce additional constraints. 
        currentlevelincludesmaxinventorylevelgoal := runcontext.WeightLevelNonFinancial().MaximumInventoryLevel() <= this.FocusLevel()
                                                     and runcontext.WeightLevelNonFinancial().MaximumInventoryLevelWeight() > 0.0; 
        if ( smallestgap.IsFinite() and currentlevelincludesmaxinventorylevelgoal ) 
        {
          
          constr := program.MetaLimitMaxInventoryPastHorizonConstraints().New( pispip );
          slackvar := program.MetaLimitMaxInventoryPastHorizonSlackVariables().New( pispip ); 
          constr.Sense( '<=' );
          constr.NewTerm( -1.0 * scalefactor_slack_const, slackvar ); 
          
          
          pispipinvend := pispip.InventoryLevelEnd();
          rhs := maxvalue( 0.0, pispipinvend + smallestgap ); 
          // We only update the RHS if the quantity is larger than the feasibility tolerance to avoid numerical instability
          rhs := ifexpr( abs( rhs * scalefactor_rhs_const ) >= this.SmallestFeasibilityTolerance(), rhs, 0.0 ) ;
          constr.RHSValue( rhs * scalefactor_rhs_const );
          
          // Term UoM: PISP
          if ( pispip.IsLeafPlanning() ) 
          {
            constr.NewTerm( 1.0 * scalefactor_invqty_const, program.InvQtyVariables().Get( pispip )  );
          }
          else
          {
            this.AddTermsToInventorySpecificationHighLevelConstraint( program, constr, pispip, scope, scalefactor_invqty_const, scalefactor_rhs_const ); 
          }
          
          lowerlimitconstr := program.FindConstraint( CapacityPlanningSuboptimizer::ConstraintNameLowerBoundLastPeriodInventory(), pispip ); // need to be consistent with this lower bound due to pre solve numerical issues
          if ( not isnull( lowerlimitconstr ) ) 
          {
            lowerlimit :=  -lowerlimitconstr.RHSValue(); 
            upperlimit := constr.RHSValue(); 
            if ( lowerlimit >= upperlimit ) 
            {
              lowerlimitconstr.RHSValue( 1000.0 ); // work around epsilon logic
              lowerlimitconstr.RHSValue( -upperlimit ); 
            }
          }
        }
      }
    }
  *]
  InterfaceProperties { Accessibility: 'Module' }
}