陈清红
2025-04-14 880f3c0257eeb8c37761d484258fdd102a369a19
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
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
    
    gp := this.MacroPlan().GlobalParameters_MP();
    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 );
    
    //term UoM: Unit
    
    if( this.MacroPlan().HasTool() )
    {
      //calculate changeovercapacity
      changeovercapacity := gp.ChangeoverTime().HoursAsReal();
      
      //Add changeover capacity consumption
      minconst.NewTerm( changeovercapacity * subopt.ScaleConstraintTerm( typeof( MPUnitPeriodNrOfToolChangeVariable ), typeofexpression( minconst ) ),
                        program.UnitPeriodNrOfToolChangeVariables().Get( this ) );
      
      //Add changeover capacity consumption
      maxconst.NewTerm( changeovercapacity * subopt.ScaleConstraintTerm( typeof( MPUnitPeriodNrOfToolChangeVariable ), typeofexpression( maxconst ) ),
                        program.UnitPeriodNrOfToolChangeVariables().Get( this ) );
    }
    
    // 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
        {
          targetuom := ptll.UnitPeriod().Unit().UnitOfMeasure_MP();
          uomconversion := productintrip.GetUOMConversionFactor( targetuom );
          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(), 0.0 );
      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 );
    }
    
    //this.CutOffRHS( subopt, maxconst, scalefactor_rhs_maxconst ); 
    //this.CutOffRHS( subopt, minconst, scalefactor_rhs_minconst );
  *]
}