陈清红
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
Quintiq file version 2.0
#parent: #root
Method InitConstraintsForShiftPatternFirstPeriod (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const LibOpt_Scope scope,
  const UnitPeriodTime up,
  const ShiftPattern sp,
  Real scalefactor_spisused_const1,
  Real scalefactor_spisfirst_const1,
  Real scalefactor_spisused_const2,
  Real scalefactor_spisfirst_const2,
  Real scalefactor_spisused_const3,
  Real scalefactor_spisfirst_const3,
  Real scalefactor_spisfirst_const4,
  Real scalefactor_spisused_const4,
  Real scalefactor_rhs_const2,
  Real scalefactor_rhs_const3,
  Real scalefactor_rhs_const4
) const
{
  Description: 'Checks if a shift pattern is the first one in a sequence of assignments.'
  TextBody:
  [*
    previousup := up.PreviousPlanningUnitPeriod();
    
    // If this period does not have a previous unit period
    if( isnull( previousup ) )
    {
      // Constraint: any shift pattern on a period that doesn't have a previous period is considered the "first one" in a sequence of assignments.
      // ShiftPatternIsFirst[sp][up0] = ShiftPatternIsUsed[sp][up0]
      firstperiodconstr1 := program.FirstPeriodStartOfPlanningConstraints().New( sp, up );
      firstperiodconstr1.Sense( "=" );
      firstperiodconstr1.RHSValue( 0.0 );
      
      firstperiodconstr1.NewTerm( 1.0 * scalefactor_spisfirst_const1, program.ShiftPatternIsFirstVariables().Get( sp, up ) );
      firstperiodconstr1.NewTerm( -1.0 * scalefactor_spisused_const1, program.ShiftPatternIsUsedVariables().Get( sp, up ) );
    }
    else // This period has a previous unit period
    {
      // Constraint: when a shift pattern starts being used on unit, it is considered as the "first one" in a sequence of assignments.
      // ShiftPatternIsFirst[sp][up] >= ShiftPatternIsUsed[sp][up] - ShiftPatternIsUsed[sp][up-1]
      firstperiodconstr2 := program.FirstPeriodShiftOnUnitConstraints().New( sp, up );
      firstperiodconstr2.Sense( ">=" );
      firstperiodconstr2.RHSValue( 0.0 );
      
      firstperiodconstr2.NewTerm( 1.0 * scalefactor_spisfirst_const2, program.ShiftPatternIsFirstVariables().Get( sp, up ) );
      
      // If this unit period is in scope, add a term with the corresponding variable. Otherwise, update the RHS.
      if( up.IsInScopeForShiftOptimization( scope ) )
      {
        firstperiodconstr2.NewTerm( -1.0 * scalefactor_spisused_const2, program.ShiftPatternIsUsedVariables().Get( sp, up ) );
      }
      else
      {
        newrhs := ifexpr( up.ShiftPattern() = sp, 1, 0 );
        firstperiodconstr2.RHSValue( newrhs * scalefactor_rhs_const2 );
      }
      
      // If the previous unit period is in scope, add a term with the corresponding variable. Otherwise, update the RHS.
      if( previousup.IsInScopeForShiftOptimization( scope ) )
      {
        firstperiodconstr2.NewTerm( 1.0 * scalefactor_spisused_const2, program.ShiftPatternIsUsedVariables().Get( sp, previousup ) );
      }
      else
      {
        newterm := ifexpr( previousup.astype( UnitPeriodTime ).ShiftPattern() = sp, 1, 0 );
        newrhs := this.GetConstraintRHS( firstperiodconstr2, scalefactor_rhs_const2 ) - newterm;
        firstperiodconstr2.RHSValue( newrhs * scalefactor_rhs_const2 );
      }
      
      // Constraint: a shift pattern cannot be the "first one" in a sequence of assignments if it is not used.
      // ShiftPatternIsFirst[sp][up] <= ShiftPatternIsUsed[sp][up]
      firstperiodconstr3 := program.FirstPeriodShiftNotUsedConstraints().New( sp, up );
      firstperiodconstr3.Sense( "<=" );
      
      firstperiodconstr3.NewTerm( 1.0 * scalefactor_spisfirst_const3, program.ShiftPatternIsFirstVariables().Get( sp, up ) );
      
      // If this unit period is in scope, add a term with the corresponding variable. Otherwise, update the RHS.
      // Note: up can be out of scope when this method is called 1 unit period after the scope, 
      // in order to set the "SPIsFirst" variable for the minimum duration constraint.
      if( up.IsInScopeForShiftOptimization( scope ) )
      {
        firstperiodconstr3.NewTerm( -1.0 * scalefactor_spisused_const3, program.ShiftPatternIsUsedVariables().Get( sp, up ) );
        firstperiodconstr3.RHSValue( 0.0 );
      }
      else
      {
        newrhs := ifexpr( up.ShiftPattern() = sp, 1, 0 );
        firstperiodconstr3.RHSValue( newrhs * scalefactor_rhs_const3 );
      }
      
      // Constraint: a shift pattern cannot be the "first one" in a sequence of assignments if it is used during the previous period.
      // ShiftPatternIsFirst[sp][up] <= 1 - ShiftPatternIsUsed[sp][up-1]
      firstperiodconstr4 := program.FirstPeriodShiftAlreadyUsedConstraints().New( sp, up );
      firstperiodconstr4.Sense( "<=" );
      
      firstperiodconstr4.NewTerm( 1.0 * scalefactor_spisfirst_const4, program.ShiftPatternIsFirstVariables().Get( sp, up ) );
      
      if( previousup.IsInScopeForShiftOptimization( scope ) )
      {
        firstperiodconstr4.NewTerm( 1.0 * scalefactor_spisused_const4, program.ShiftPatternIsUsedVariables().Get( sp, previousup ) );
        firstperiodconstr4.RHSValue( 1.0 * scalefactor_rhs_const4 );
      }
      else
      {
        rhs := ifexpr( previousup.astype( UnitPeriodTime ).ShiftPattern() = sp, 0, 1 );
        firstperiodconstr4.RHSValue( rhs * scalefactor_rhs_const4 );
      }
    }
  *]
  InterfaceProperties { Accessibility: 'Module' }
}