| 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' } | 
| } |