Kevin Kok Khah Whey
2023-11-07 5ae534ab606e6f2ba5ea60914224d665b0447d5a
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
Quintiq file version 2.0
#parent: #root
Method InitConstraintsSequentialAtEnd (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const RunContextForCapacityPlanning runcontext,
  const LibOpt_Scope scope,
  const constcontent ProductInStockingPointInPeriodPlanningLeafs leafplanningpispips
) const
{
  Description: 'method to be executed after parallel cplex constraint init has taken places ( used for initialzing items that need data from multiple threads)'
  TextBody:
  [*
    
    // Define the goals for each level
    this.InitConstraintsForGoalsForLevelGoal( program,runcontext, scope ); // lkeep last because for meta we obtain values from existing plan before this
    
    if ( runcontext.IsMetaIteration() 
         and this.FocusLevel() > 0 
         and this.GetRunContextMeta().OptionFixDemandSlackVar() 
         and this.DoLevelCollapse( runcontext ) )
    {
      traverse( leafplanningpispips, Elements, pispip ) 
      {
        boundforvar := pispip.DependentDemandUnfulfilledQuantity()
        this.FreezeSlackVariableMeta( program, pispip, boundforvar, program.DemandSlackVariables().Get( pispip ), program.TotalSlackConstraints().Get() );
      } 
      
      if( runcontext.GetConsiderTotalUserSupply( scope ) )
      {
        pispipsforconstraint := runcontext.GetPISPISPForUserSupplyConstraint( scope, leafplanningpispips ); 
        
        traverse( pispipsforconstraint, Elements.astype( ProductInStockingPointInPeriodPlanningLeaf ), pispip ) 
        {
          // constr constraint UoM: PISP
          constr := program.TotalSlackUserSupplyConstraints().Get(); 
          varover := program.UserTotalSupplyOverVariables().Get( pispip ); 
          varunder := program.UserTotalSupplyUnderVariables().Get( pispip ); 
          oversupply := maxvalue( 0.0, pispip.SupplyQuantity()  - pispip.TotalSupplyUser() ); 
          undersupply := maxvalue( 0.0, pispip.TotalSupplyUser() - pispip.SupplyQuantity() ); 
          this.FreezeSlackVariableMeta( program, pispip, oversupply, varover, constr );
          this.FreezeSlackVariableMeta( program, pispip, undersupply, varunder, constr );
        }
      }
    }
    
    if ( runcontext.UseCampaignSequenceOptimizer() and runcontext.RunWithDebugCampaignCombis() ) 
    {
      // for running with manual fixed combis (debugging) it easy not to have selected a combi for a unit period, so we add a slack variable to safeguard against it
      level_zero := select(  this, MacroPlan.StrategyMacroPlan.StrategyLevelMacroPlan, slm, true, slm.Level() = 0 ); 
      traverse( scope.GetUnitPeriodInOptimizerRunConst(), Elements.OptCampaignUnitSubPeriod, ocusp )
      {
        constr := program.CalcDurationOfOptCampaignUnitSubPeriodConstraints().Get( ocusp );
        debuginfo( 'Warning - running with fixed campaign combis. Adding slack variable ', constr.Name() );  
        slackvar := program.SlackDebugCampaignCombisVariables().New( ocusp ); 
        c := program.GoalConstraints().Get( level_zero );  
        c.NewTerm( -1000.0, slackvar ); 
        constr.NewTerm( 1.0, slackvar );   
      }
    }
    
    // Define PosInvPastLast variable in case inventory holding needs to be computed
    if ( runcontext.IsMetaIteration() and this.GetInitializeFinancialConstraints( runcontext ) ) 
    {
        constname :=  typeof( MPDefinePosInvPastLastConstraint );
    
        scalefactor_posinvpastlast_const := this.ScaleConstraintTerm( typeof( MPPosInvQtyPastLastVariable ), constname );
        scalefactor_invqty_const := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), constname );
        scalefactor_rhs_constr := this.ScaleConstraintRHS( constname, 1.0 );
    
      traverse( scope.GetProductInStockingPointInOptimizerRunConst(), Elements, pisp ) 
      {
    
    
    
        last := pisp.LatestPISPIPInScope(); 
        current := guard( last.Next().astype(  ProductInStockingPointInPeriodPlanningLeaf ), null(  ProductInStockingPointInPeriodPlanningLeaf ) ); 
        
        while ( not isnull( current ) ) 
        {
          var := program.PosInvQtyPastLastVariables().Get( current ); 
          
          // PosInvPastLast_current >= current.InventoryLevelEnd + (InvQty_last - last.InventoryLevelEnd ) 
          //
          // example: if we decrease the inventory of last by 5 this says PosInvPastLast_current >= current.InventoryLevelEnd - 5. 
          //          meaning if current.InventoryLevelEnd is <= 5, we can avoid inventory holding cost
          // similarly if we increase by 5, posInvPastLast_current >= current.InventoryLevelEnd + 5 and we collect inventory holding cost in case previous inventory was bigger than 5
          
          c := program.DefinePosInvPastLastConstraints().New( current ); 
          c.Sense( '>=' ); 
          c.RHSValue( ( current.InventoryLevelEnd() - last.InventoryLevelEnd() ) * scalefactor_rhs_constr );  
          c.NewTerm( 1.0 * scalefactor_posinvpastlast_const, var );
          c.NewTerm( -1.0 * scalefactor_invqty_const, program.InvQtyVariables().Get( last ) );  
          
          this.FilterCPLEXNoise( c, 1.0 ); 
          
          current := current.NextPlanningPISPIP().astype(  ProductInStockingPointInPeriodPlanningLeaf ); 
        }
      }
    }
    
    traverse( runcontext, UserPeriodTask, pt, pt.HasUserQuantity() and pt.HasDependentDemandUserQuantity() ) // deal with potential infeasible manual fixed planning, where both 
    {                                                                                                        // periodtask dependent demand and new supply quantity are set
      slackconstr := program.TotalSlackConstraints().Get(); 
      period := pt.UnitPeriod().Period_MP(); 
      
      constddqtyname := typeof( MPOperationDependentDemandQtyConstraint );
      constddpartialname := typeof( MPPartialOperationDependentDemandQtyConstraint );
      constslack := typeof( MPTotalSlackConstraint ); 
    
      scalefactor_slack_constpartial := this.ScaleConstraintTerm( typeof( MPPartialOperationDependentDemandQtySlackNegVariable ), constddpartialname );
      scalefactor_partialslack_slackconst := this.ScaleConstraintTerm( typeof( MPPartialOperationDependentDemandQtySlackNegVariable ), constslack );
      scalefactor_slack_slackconst := this.ScaleConstraintTerm( typeof( MPOperationDependentDemandQtySlackNegVariable ), constslack );
      scalefactor_slack_constdd := this.ScaleConstraintTerm( typeof( MPOperationDependentDemandQtySlackNegVariable ), constddqtyname );
    
    
      traverse( pt, DependentDemand, dd, dd.HasUserQuantity() )
      {
        pispipperiod := dd.ProductInStockingPointInPeriodPlanningLeaf().Period_MP(); 
        input := dd.ProcessInput().astype( OperationInput ); 
        if ( pt.Operation().HasLeadTime() ) 
        {
          c := program.PartialOperationDependentDemandQtyConstraints().Find( input, pispipperiod, period );
          if ( not isnull( c ) ) 
          {
            slacknegvar := program.PartialOperationDependentDemandQtySlackNegVariables().New( input, pispipperiod, period ); 
            slackposvar := program.PartialOperationDependentDemandQtySlackPosVariables().New( input, pispipperiod, period ); 
            c.NewTerm( 1.0 * scalefactor_slack_constpartial, slackposvar ); 
            c.NewTerm( -1.0 * scalefactor_slack_constpartial, slacknegvar ); 
            
            slackconstr.NewTerm( -1.0 * scalefactor_partialslack_slackconst, slacknegvar ); 
            slackconstr.NewTerm( -1.0 * scalefactor_partialslack_slackconst, slackposvar ); 
          }
        } 
        else
        {
          c := program.OperationDependentDemandQtyConstraints().Find( input, period );
          if ( not isnull( c ) ) 
          {
            slackposvar := program.OperationDependentDemandQtySlackPosVariables().New( input, period ); 
            slacknegvar := program.OperationDependentDemandQtySlackNegVariables().New( input, period ); 
            c.NewTerm( 1.0 * scalefactor_slack_constdd, slackposvar ); 
            c.NewTerm( -1.0 * scalefactor_slack_constdd, slacknegvar ); 
            
            slackconstr.NewTerm( -1.0 * scalefactor_slack_slackconst, slacknegvar ); 
            slackconstr.NewTerm( -1.0 * scalefactor_slack_slackconst, slackposvar ); 
          }
        } 
      }    
    }
    
    program.GenerateNames(); // need to otherwise .sav files are anonymized
  *]
  InterfaceProperties { Accessibility: 'Module' }
}