hongji.li
2023-11-07 1a1ba3ad5ed9e4380185aa1ccad20204a0e5f115
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
Quintiq file version 2.0
#parent: #root
MethodOverride InitConstraintsForCapacityUsage (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  MPConstraint minconst,
  MPConstraint maxconst,
  const RunContextForCapacityPlanning runcontext,
  const LibOpt_Scope scope,
  const CapacityPlanningSuboptimizer subopt
) const
{
  TextBody:
  [*
    // MvE: Apart from the UoM and the closing of units, this method seems like an exact copy of the default InitConstraintsForCapacityUsage on UnitPeriod. Shall I delete this one?
    // minconst and maxconst UoM: Time
    
    bound := 0.0;                                   // To prevent infeasible when the unit is closed
    
    scalefactor_periodtaskqty_maxconst := subopt.ScaleConstraintTerm( typeof( MPPTQtyVariable ), typeofexpression( maxconst ) );
    scalefactor_periodtaskqty_minconst := subopt.ScaleConstraintTerm( typeof( MPPTQtyVariable ), typeofexpression( minconst ) );
    scalefactor_tripnewsupply_maxconst := subopt.ScaleConstraintTerm( typeof( MPTripNewSupplyVariable ), typeofexpression( maxconst ) );
    scalefactor_tripnewsupply_minconst := subopt.ScaleConstraintTerm( typeof( MPTripNewSupplyVariable ), typeofexpression( minconst ) );
    
    scalefactor_rhs_minconst := subopt.ScaleConstraintRHS( typeofexpression( minconst ), 1.0 );
    scalefactor_rhs_maxconst := subopt.ScaleConstraintRHS( typeofexpression( maxconst ), 1.0 );
    
    // Capacity usage = Sum of PTQty * Process.QuantityToProcessFactor
    // For time based, we need extra logic to convert to duration needed
    operations := subopt.GetOperationsForUnitPeriod( scope, this );
    traverse( operations, Elements, operation )
    {
      coeff := operation.GetCapacityUsagePerQuantity( this );
      var := program.PTQtyVariables().Get( operation, this.Period_MP() );
    
      // Term: coeff * PTQty variable
      // UoM:   [Time / Unit]       [Unit]
      maxconst.NewTerm( coeff * scalefactor_periodtaskqty_maxconst, var );
      minconst.NewTerm( coeff * scalefactor_periodtaskqty_minconst, var );
    
      bound := bound + coeff * var.LowerBound();
    }
    
    if ( this.Unit().LaneLegForOptimization( relsize ) > 0 ) 
    {
      traverse( this, PeriodTask_MP.astype( PeriodTaskLaneLeg ), ptll,
                scope.Contains(  ptll.Trip().TripInOptimizerRun() ) )     // Only if the trip is part of this optimizer run
      {
        traverse( ptll, Trip.ProductInTrip, productintrip,
                  scope.Contains(  productintrip.ProductInTripInOptimizerRun() ) ) // Only if the productintrip is part of this optimizer run
        {
          uomconversion := productintrip.UnitConversionFactor();
          factor := ptll.DurationInTrip().DaysAsReal() * ptll.Process_MP().GetCapacityUsagePerQuantity( this );
          var := program.TripNewSupplyVariables().Get( productintrip );
      
          // Term:    uomconversion   *      factor          * TripNewSupply variable
          // UoM: [Output PISP to Unit] * [days * Time / Unit] *      [Output PISP]         //For transport the capacity is defined per day so the trip duration in days accounts for that
          maxconst.NewTerm( uomconversion * factor * scalefactor_tripnewsupply_maxconst, var );
          minconst.NewTerm( uomconversion * factor * scalefactor_tripnewsupply_minconst, var );
      
          bound := bound + factor * var.LowerBound();
        }
      }
    }
    
    // Put capacity constraints on a higher level unit.
    traverse( this, ChildOfUnitDimension, childunitperiod )
    {
      operations := subopt.GetOperationsForUnitPeriod( scope, childunitperiod );
    
      traverse( operations, Elements, operation )
      {
        factor := operation.GetCapacityUsagePerQuantity( this );
        var := program.PTQtyVariables().Get( operation, this.Period_MP() );
    
        // Term:    factor     *  PTQty variable
        // UoM: [Time / Unit]  *     [Unit]
        maxconst.NewTerm( factor * scalefactor_periodtaskqty_maxconst, var );
    
        bound := bound + factor * var.LowerBound();
      }
    }
    
    varoverload := program.UnitCapacityOverloadedTimeVariables().Get( this );
    
    // Term: UnitCapacityOverloaded variable
    // UoM:         [Time]
    maxconst.NewTerm( -1.0 * subopt.ScaleConstraintTerm( typeof( MPUnitCapacityOverloadedTimeVariable ), typeofexpression( maxconst ) ), varoverload );
    
    if( this.NrOfOpen() <= 0 )    // If the unit is closed, it should not be allowed to plan anything, and the overloaded capacity is served as the slack to prevent infeasible
    {
      subopt.FreezeVariableUpperBound( varoverload, bound );
    }
    
    // Update the RHS based on the trips outside the optimizer horizon
    if ( this.Unit().LaneLegForOptimization( relsize ) > 0 ) 
    {
      traverse( this, PeriodTask_MP.astype( PeriodTaskLaneLeg ), ptll )
      {
        traverse( ptll, Trip.ProductInTrip, pit,
                not scope.Contains(  pit.ProductInTripInOptimizerRun() )
                and (pit.HasRegularProductForOptimizer() or pit.Product_MP().GetIsInOptimizerRun( runcontext.IsPostProcessing() ) ) )
        {
          // The quantity of this product in trip that is outside the horizon times the duration of this trip that occurs during this unit period
          fixedquantity := pit.QuantityInProcessUOM() * ( ptll.DurationInTrip() / Duration::Days( 1 ) );
          newrhsminconst := subopt.GetConstraintRHS( minconst, scalefactor_rhs_minconst ) - fixedquantity;
          minconst.RHSValue( newrhsminconst * scalefactor_rhs_minconst );
        
          newrhsmaxconst := subopt.GetConstraintRHS( maxconst, scalefactor_rhs_maxconst ) - fixedquantity;
          maxconst.RHSValue( newrhsmaxconst * scalefactor_rhs_minconst );
        }
      }
    }
    
    // Update the RHS based on the operations outside the optimizer horizon
    traverse( this, PeriodTaskOperation, periodtask, isnull( periodtask.UnitPeriodWhenInScope() ) )
    {
      fixedquantity := guard( periodtask.QuantityToProcess() / periodtask.RequiredCapacity(), periodtask.QuantityToProcess() ); // requiredcapacity for trip is zero all the time
      newrhsminconst := subopt.GetConstraintRHS( minconst, scalefactor_rhs_minconst ) - fixedquantity;
      minconst.RHSValue( newrhsminconst * scalefactor_rhs_minconst );
    
      newrhsmaxconst := subopt.GetConstraintRHS( maxconst, scalefactor_rhs_maxconst ) - fixedquantity;
      maxconst.RHSValue( newrhsmaxconst * scalefactor_rhs_maxconst );
    }
  *]
}