admin
2025-01-22 7e31442f0e9b07764e9c6a9680d3d4aeba5fe1de
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
Quintiq file version 2.0
#parent: #root
Method InitConstraintsForStockingPointInPeriods (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const RunContextForCapacityPlanning runcontext,
  const RunContextMeta rcm,
  const LibOpt_Scope scope,
  const constcontent ProductInStockingPointInPeriodPlanningLeafs leafpispips
) const
{
  Description: 'Initialize hard constraints for stocking points'
  TextBody:
  [*
    starttime := OS::PrecisionCounter(); 
    constname := typeof( MPSPInPeriodInventoryLevelConstraint );
    
    scalefactor_spinvqty_const := this.ScaleConstraintTerm( typeof( MPSPInvQtyVariable ), constname );
    scalefactor_invqty_const := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), constname );
    scalefactor_stockingpointcapacityoverloaded_const := this.ScaleConstraintTerm( typeof( MPStockingPointCapacityOverloadedVariable ), constname );
    
    scalefactor_rhs_const := this.ScaleConstraintRHS( constname, 1.0 );
    
    spipstoconsider := selectset(  scope.GetStockingPointInPeriodInOptimizerRunConst(), 
                                   Elements, 
                                   spip,
                                   not spip.IsPeriodFrozen()
                                   and not spip.StockingPoint_MP().IsPlannedInfinite() );
    
    
    // Inventory of stocking point in period = sum of inventory of pispip + dead inventory
    
    SelectorMeta::CheckNoGap( scope ); // debugmode check only
    
    // first create constraints
    traverse( spipstoconsider, Elements, spip ) 
    {
      // define overload - using same scaling factors as SPInPeriodInventoryLevelConstraint
      constrlimit := program.SPInPeriodOverloadConstraints().New( spip ); 
      constrlimit.Sense( '<=' ); 
      constrlimit.RHSValue( spip.MaxCapacity() * scalefactor_rhs_const ); 
      constrlimit.NewTerm( 1.0 * scalefactor_spinvqty_const,
                     program.SPInvQtyVariables().Get( spip ) );
      
      constrlimit.NewTerm( -1.0 * scalefactor_stockingpointcapacityoverloaded_const, program.StockingPointCapacityOverloadedVariables().Get( spip ) );
    
      if ( runcontext.IsMetaIteration() ) 
      {
          // define overload for meta against slightly reduced capacity. Used to supress tiny overloads due to mismatches
          
        restrictedcap := spip.MaxCapacity() * ( 1 - rcm.OptionRestrictedSPCapacityEpsilon() ); 
        constrlimitmeta := program.SPInPeriodOverloadMetaConstraints().New( spip ); 
        constrlimitmeta.Sense( '<=' ); 
        constrlimitmeta.RHSValue( restrictedcap * scalefactor_rhs_const ); 
        constrlimitmeta.NewTerm( 1.0 * scalefactor_spinvqty_const,
                       program.SPInvQtyVariables().Get( spip ) );
        
        constrlimitmeta.NewTerm( -1.0 * scalefactor_stockingpointcapacityoverloaded_const, program.StockingPointCapacityOverloadedMetaVariables().Get( spip ) );
      }
      
      // const UoM: SP
      // define inventory level by summing pispips that contribute
      const := program.SPInPeriodInventoryLevelConstraints().New( spip );
      const.Sense( '=' );
      // RHS UoM: SP
      const.RHSValue( spip.InventoryLevelEnd() * scalefactor_rhs_const ); // we initialize like this, so we can reduce it each time we add a pispip. This way we can avoid having to traverse all pispips (only those in scope)
      // Term UoM: SP
      const.NewTerm( 1.0 * scalefactor_spinvqty_const,
                     program.SPInvQtyVariables().Get( spip ) );
    }
    
    // add constraint terms by traversing scope on 
    traverse( leafpispips, Elements, pispip ) 
    {
      // figure out which spip 'pispip' contributes to: if pispip.SPIP is in scope it is direct, otherwise of 'pispip' is the last pispip in scope for the pisp 
      pisp := pispip.ProductInStockingPoint_MP(); 
      pisp_is_relevant := not pisp.IsNegativeInventoryAllowed()
                  and not pisp.IsExcluded()
                  and not pisp.StockingPoint_MP().IsPlannedInfinite();
      spip := pispip.AsStockingPointInPeriod();              
      if ( pisp_is_relevant and not spip.IsPeriodFrozen() ) 
      {
        const := program.SPInPeriodInventoryLevelConstraints().Get( spip );
        uomconversionfactor := pisp.UOMConversionForSPIPConstraint();   
        // Term: uomconversion * InvQty 
          // UoM:   [PISP to SP] * [PISP]
        const.NewTerm( -uomconversionfactor * scalefactor_invqty_const, program.InvQtyVariables().Get( pispip ) );
                       
        // now reduce RHS for this term because we know it is in scope
        newrhs := this.GetConstraintRHS( const, scalefactor_rhs_const ) - uomconversionfactor * pispip.InventoryLevelEnd(); 
        const.RHSValue( newrhs * scalefactor_rhs_const ); 
                                                                                                  
        if ( pisp.LatestPISPIPInScope() = pispip ) // special case, we also need to include the contibution to later spip
        {
          current := spip.NextSPIPPlanning(); 
          currentpispip := pispip.NextPlanningPISPIP(); 
          
          while ( not isnull( current ) ) 
          {
            if ( not current.IsPeriodFrozen() and scope.Contains( current.SPIPInOptimizerRun() ) ) 
            {
              const_current := program.SPInPeriodInventoryLevelConstraints().Get( current ); 
              const_current.NewTerm( -uomconversionfactor * scalefactor_invqty_const, program.InvQtyVariables().Get( pispip ) );
              
              // Take *out* currentpispip.InventoryEnd from RHS contribution, but add in the delta = currentpispip.InventoryLevelEnd - pispip.InventoryLevelEnd. So net put in pispip.InventoryLevelEnd.  
              newrhs := this.GetConstraintRHS( const_current, scalefactor_rhs_const ) - uomconversionfactor * pispip.InventoryLevelEnd()
              const_current.RHSValue( newrhs * scalefactor_rhs_const );
            }
            
            current := current.NextSPIPPlanning(); 
            currentpispip := currentpispip.NextPlanningPISPIP(); 
          } 
        }                                                                                                                                                                                                                                                                                                 
      } 
    }
    
      
    // final constraint modifications + deal with out of scope SPIP
    traverse( spipstoconsider, Elements, spip ) 
    { 
      const := program.SPInPeriodInventoryLevelConstraints().Get( spip );                               
      changeunscaledRHS := this.FilterCPLEXNoise( const, scalefactor_rhs_const ); // can consider this to be the change in the inventory level out of scope. Using it to compute existing violation for meta
      
      varattrworkaround := program.NewVariable(  'UnscaledRHSModificationSPInPeriodInventoryLevelConstraints', spip ); 
      varattrworkaround.Enabled( false ); 
      this.StoreValueInVariable( varattrworkaround, changeunscaledRHS ); 
                                                                                                              
      nextspip := spip.GetNextPlanningSPIP();                  
      if ( not runcontext.IsSmartPlan() 
           and not isnull( nextspip ) 
           and not scope.Contains( nextspip.SPIPInOptimizerRun() ) ) 
      {
        spipblockafter_outofscope := nextspip.GetMaximalOutOfScope( scope ); 
        mininventoryspaceleftafter := min( spipblockafter_outofscope, 
                                           Elements, 
                                           spipfuture, 
                                           spipfuture.Start() > spip.Start(), 
                                           maxvalue ( 0.0, spipfuture.MaxCapacity() - spipfuture.InventoryLevelEnd() ) );  
        // we should limit the increase in the last period by maxinventorydiffafter. We do this by changing the variable upper bound 
    
        restrictedcapacity := maxvalue( 0.0, spip.InventoryLevelEnd() + mininventoryspaceleftafter ); 
        // We set the limit always not to make worse any violation in the plan after what is in scope.  
        // using same (mass) scaling factor as inventy define constraint
        var := program.SPInvQtyVariables().Get( spip );
        restrictedmaxcap := program.SPRestrictedCapacityForOutOfScopeConstraints().New( spip ); // introduce this constraint because merging of constraints ignores the var bound (parallel cplex)
        restrictedmaxcap.Sense( '<=' ); 
        restrictedmaxcap.RHSValue( scalefactor_rhs_const * restrictedcapacity );
        restrictedmaxcap.NewTerm( 1.0 * scalefactor_spinvqty_const, var );  
        restrictedmaxcap.NewTerm( -1.0 * scalefactor_spinvqty_const, program.SPInvOutOfScopeSlackVariables().Get( spip ) );  
      }
    }
    endtime := OS::PrecisionCounter(); 
    durationmethod := (endtime - starttime ) / OS::PrecisionCounterFrequency(); 
    debuginfo(  'TIME FOR SP constraint = ', durationmethod , 'sec' );
  *]
  InterfaceProperties { Accessibility: 'Module' }
}