陈清红
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
Quintiq file version 2.0
#parent: #root
Method InitConstraintsForOperationDependentDemandInputGroup_Add (
  CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
  const RunContextForCapacityPlanning runcontext,
  const LibOpt_Scope scope,
  const OperationInputGroup group,
  const Period_MP period,
  const PeriodTaskOperation pto,
  Real scalefactor_periodtask_constddqty,
  Real scalefactor_periodtask_constupper,
  Real scalefactor_operationinputgroupover_constupper,
  Real scalefactor_periodtask_constlower,
  Real scalefactor_operationinputgroupunder_constlower,
  Real scalefactor_partialoperationdemandqty_constddqty,
  Real scalefactor_partialoperationdemandqty_constupper,
  Real scalefactor_partialoperationdemandqty_constlower,
  Real scalefactor_partialoperationdemandqty_constrelative,
  Real scalefactor_operationdemandqty_constddqty,
  Real scalefactor_operationdemandqty_constupper,
  Real scalefactor_operationdemandqty_constlower,
  Boolean usingproportionalleadtimelogic
) const
{
  Description: 'Initialize constraints for dependent demands which is part of an input group'
  TextBody:
  [*
    operation := group.Operation();
    
    // Input group fulfilled quantity = sum of all dependent demand fulfilled quantity of the group
    // constddqty constraint UoM: Unit
    constddqty := program.OperationInputGroupDependentDemandQtyConstraints().New( group, period );
    constddqty.Sense( '=' );
    // using default RHS 0.0 for comstddqty
    
    // Term: -group.Factor  *  PTQty variable
    // UoM:      [-]        *      [Unit]
    constddqty.NewTerm( -group.Factor() * scalefactor_periodtask_constddqty,
                        program.PTQtyVariables().Get( operation, period ) );
    
    // Step 1: Upper and lower bound of DD, must be according to min quantity and max quantity of operation input
    // Step 2: Relative duration
    // Only select the input where the product is included.
    traverse( group, OperationInput, oi, oi.HasRegularProductforOptimizer() or oi.GetIsProductInOptimizerRun( runcontext.IsPostProcessing() ) )
    {
      maxfactor := oi.MaxQuantityFactor();
      minfactor := oi.MinQuantityFactor();
    
      // If the dependent demand is part of a user period task, we use the factor that is currently used
      traverse( oi, DependentDemand, dd,
                dd.PeriodTaskOperation().HasUserQuantity()
                and dd.PeriodTask_MP().UnitPeriod().Period_MP() = period )
      {
        usedfactor := guard( dd.Quantity() / dd.DependentDemandInputGroup().GetGroupQuantity(), 0.0 );
        maxfactor := usedfactor;
        minfactor := usedfactor;
      }
    
      // constupper constraint UoM: Unit
      constupper := program.OperationInputGroupUpperBoundConstraints().New( oi, period );
      constupper.Sense( '<=' );
      // using default RHS 0.0 for constupper
    
      // Term: -maxfactor   *  PTQty variable
      // UoM:    [-]        *     [Unit]
      constupper.NewTerm( -maxfactor * scalefactor_periodtask_constupper, program.PTQtyVariables().Get( operation, period ) );
    
      // Term UoM: Unit
      constupper.NewTerm( -1.0 * scalefactor_operationinputgroupover_constupper, program.OperationInputGroupOverVariables().Get( oi, period ) );
    
      // constlower constraint UoM: Unit
      constlower := program.OperationInputGroupLowerBoundConstraints().New( oi, period );
      constlower.Sense( '>=' );
      // using default RHS 0.0 for constlower
    
      // Term: -minfactor   *  PTQty variable
      // UoM:    [-]        *     [Unit]
      constlower.NewTerm( -minfactor * scalefactor_periodtask_constlower, program.PTQtyVariables().Get( operation, period ) );
    
      // Term UoM: Unit
      constlower.NewTerm( scalefactor_operationinputgroupunder_constlower, program.OperationInputGroupUnderVariables().Get( oi, period ) );
    
      fac := 1.0;
      sourceuom := oi.PISPUnitOfMeasurement();
      targetuom := operation.Unit().UnitOfMeasure_MP();
      uomconversion := guard( sourceuom.GetConversionFactor( targetuom, null( Product_MP ) ), 1.0 );
    
      if( operation.HasLeadTime() )
      {
        pispipperiods := construct( Period_MPs, constcontent ); 
        if ( this.GetPeriodsFromPeriodTaskOperation() ) // avoid call to below method which just (re)finds pto and then returns dd periods
        {
          traverse( pto, DependentDemand.ProductInStockingPointInPeriodPlanningLeaf.Period_MP, ddperiod ) 
          {
            pispipperiods.Add( ddperiod ); 
          }
          pispipperiods := pispipperiods.Unique(); 
        }
        else
        {
          CapacityPlanningSuboptimizer::GetOperationDependentDemandPeriods( period, operation, &pispipperiods, this.GetPeriodsFromPeriodTaskOperation() );
        }
        ddstart := Process_MP::GetDependentDemandEarliestStart( period, operation.LeadTime(), operation.Unit().MacroPlan().GlobalParameters_MP(), operation );
        ddend := Process_MP::GetOperationEnd( period, ddstart );
    
        traverse( pispipperiods, Elements, pispipperiod )
        {
    
    
          // Term:        conversion factor            *       PartialOperationDemandQty variable
          // UoM: [Conversion from Input PISP to Unit]   *                  [Input PISP]
          varpodqty := program.PartialOperationDemandQtyVariables().Get( oi, pispipperiod, period );
          constddqty.NewTerm( uomconversion * scalefactor_partialoperationdemandqty_constddqty, varpodqty );
          constupper.NewTerm( uomconversion * scalefactor_partialoperationdemandqty_constupper, varpodqty );
          constlower.NewTerm( uomconversion * scalefactor_partialoperationdemandqty_constlower, varpodqty );
    
          // Only enable if the DD spans across period, meaning, 1 period task has 2 dependent demands for the same operation input
          // Calculate the quantity that span at current period and previous period
          // For example, dependent demand span across Jan and Feb.
          // The relative duration of Jan = 0.6, Feb = 0.4
          // Then, they must be adhered to: ( fulfilled demand on Jan / 0.6 )  =  ( fulfilled demand on Feb / 0.4 )
          // The constraint is defined for the pispipperiod and the next period
          // If the pispipperiod ends after the dependent demand ends, the next period is not relevant and we do not need the constraint
    
          if( pispipperiod.End() < ddend and usingproportionalleadtimelogic )
          {
            // constrelative constraint UoM: Input PISP
            constrelative := program.RelativeDurationConstraints().New( oi, operation, period, pispipperiod );
            constrelative.Sense( '=' );
            // using default RHS 0.0 for constrelative
    
            start := Process_MP::GetDependentDemandStart( ddstart, pispipperiod );
            end := Process_MP::GetDependentDemandEnd( ddend, pispipperiod );
            relativeduration := Process_MP::GetRelativeDuration( start, end, period );
    
            // Term: factor/relative duration   *       PartialOperationDemandQty variable
            // UoM:          [-]                *                  [Input PISP]
            constrelative.NewTerm( ( fac / relativeduration ) * scalefactor_partialoperationdemandqty_constrelative,
                                   program.PartialOperationDemandQtyVariables().Get( oi, pispipperiod, period ) );
            nextpispipperiod := pispipperiod.NextPlanningPeriod();
            startnext := Process_MP::GetDependentDemandStart( ddstart, nextpispipperiod );
            endnext := Process_MP::GetDependentDemandEnd( ddend, nextpispipperiod );
            relativedurationnext := Process_MP::GetRelativeDuration( startnext, endnext, period )
    
            // Term: -factor/relative duration   *    PartialOperationDemandQty variable
            // UoM:         [-]                 *               [Input PISP]        
            constrelative.NewTerm( ( -fac / relativedurationnext ) * scalefactor_partialoperationdemandqty_constrelative,
                                   program.PartialOperationDemandQtyVariables().Get( oi, nextpispipperiod, period ) );
          }
        }
      }
      else // operation does not have lead time logic
      {
        // Term:        conversion factor            *   OperationDemandQty variable
        // UoM: [Conversion from Input PISP to Unit]   *           [Input PISP]
        varoperationdemandqty := program.OperationDemandQtyVariables().Get( oi, period );
        constddqty.NewTerm( uomconversion * scalefactor_operationdemandqty_constddqty, varoperationdemandqty );
        constupper.NewTerm( uomconversion * scalefactor_operationdemandqty_constupper, varoperationdemandqty );
        constlower.NewTerm( uomconversion * scalefactor_operationdemandqty_constlower, varoperationdemandqty );
      }
    } // end traverse OperationInput oi
  *]
  InterfaceProperties { Accessibility: 'Module' }
}