| Quintiq file version 2.0 | 
| #parent: #root | 
| Method InitConstraintsForDemandFulfillmentInPISPIP ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   const LibOpt_Scope scope, | 
|   const constcontent ProductInStockingPointInPeriodPlannings pispipsinrun, | 
|   Number threadnr | 
| ) const | 
| { | 
|   Description: 'Calculate the total demand fulfillment for a pispip' | 
|   TextBody: | 
|   [* | 
|     //   SUM( OperationDemandQty ( operationinput, period ) ) | 
|     // + SUM( TripDemandQty(dependentdemand.productintrip) ) | 
|     // + SUM( SalesDemandQty ( leaf sales demand ) ) | 
|     // + SUM( DelayedSalesDemandQty(delayed leaf sales demand ) ) | 
|     // + SUM( DisaggregatedSalesDemandQty ( disaggregated sales demand ) ) | 
|     // + SUM( DelayedDisaggregatedSalesDemandQty( delayed disaggregated sales demand ) ) | 
|     // – DemandFulfillmentInPISPIP | 
|     // = 0   ∀ pispip where operationinput, period, sales demands, dependentdemand, ∈ pispip | 
|      | 
|     constname := typeof( MPDemandFulfillmentInPISPIPConstraint ); | 
|      | 
|     scalefactor_demandfulfillmentinpispip_const := this.ScaleConstraintTerm( typeof( MPDemandFulfillmentInPISPIPVariable ), constname ); | 
|     scalefactor_operationdemandqty_const := this.ScaleConstraintTerm( typeof( MPOperationDemandQtyVariable ), constname ); | 
|     scalefactor_tripdemandqty_const := this.ScaleConstraintTerm( typeof( MPTripDemandQtyVariable ), constname ); | 
|     scalefactor_salesdemandqty_const := this.ScaleConstraintTerm( typeof( MPSalesDemandQtyVariable ), constname ); | 
|     scalefactor_delayedsalesdemandqty_const := this.ScaleConstraintTerm( typeof( MPDelayedSalesDemandQtyVariable ), constname ); | 
|      | 
|     scalefactor_rhs_const := this.ScaleConstraintRHS( constname, 1.0 ); | 
|      | 
|     // The demand fulfillment constraint should be specified for all pispips whose inventory specification in days is considered | 
|     pispips := this.GetPISPIPsForDemandFulfillment( scope, pispipsinrun ); | 
|      | 
|     // Calculate the total demand fulfillment for used in inventory specification in days. | 
|     traverse( pispips, Elements, pispip, CapacityPlanningSuboptimizer::GetThreadNr( this.ThreadAParameter(), this.ThreadBParameter(), pispip.PreThreadNr() ) = threadnr  ) | 
|     { | 
|       // const constraint UoM: PISP | 
|       const := program.DemandFulfillmentInPISPIPConstraints().New( pispip ); | 
|       const.Sense( '=' ); | 
|       const.RHSValue( 0.0 * scalefactor_rhs_const ); | 
|      | 
|       // Term UoM: PISP | 
|       const.NewTerm( -1.0 * scalefactor_demandfulfillmentinpispip_const, | 
|                      program.DemandFulfillmentInPISPIPVariables().Get( pispip ) ); | 
|      | 
|       traverse( pispip.GetLeavesOfProductDimensionConst(),  | 
|                 Elements.astype( ProductInStockingPointInPeriodPlanningLeaf ),  | 
|                 activepispip, | 
|                 pispip.IsLeafPlanning() or not activepispip.ProductInStockingPoint_MP().IsNegativeInventoryAllowed()  ) | 
|       { | 
|         // The leaf pispip only have variables if they are part of this optimizer run | 
|         if( activepispip = pispip // we know pispip is in scope so we can skip the check (for leaves this will be the only activepispip) | 
|             or scope.Contains( activepispip.PISPIPInOptimizerRun() ) )  | 
|         { | 
|           // Dependent demands for operations | 
|           traverse( activepispip, ProductInStockingPoint_MP.OperationInputAvailableForOptimization, input ) | 
|           { | 
|             var := program.OperationDemandQtyVariables().Find( input, pispip.Period_MP() ); | 
|             // The OperationDemandQty variable will be null if this combination of operation and period are not part of this optimizer run | 
|             // In that case, this variable is not relevant and should not be added to this constraint | 
|             // Also if the product is not part of the optimizer run, we should not add this term | 
|             if( not isnull( var ) | 
|                 and ( input.HasRegularProductforOptimizer()  | 
|                       or input.GetIsProductInOptimizerRun( runcontext.IsPostProcessing() ) ) ) | 
|             { | 
|               // Term UoM: PISP | 
|               const.NewTerm( 1.0 * scalefactor_operationdemandqty_const, | 
|                              var ); | 
|             } | 
|             // If the variable is null (and the decision is thus out of scope of the optimizer ), then we need to update the RHS to reflect this frozen quantity | 
|             else | 
|             { | 
|               ptoperation := PeriodTaskOperation::FindPeriodTaskOperationTypeIndex( pispip.Period_MP().Start(), input.Operation().ID() ); | 
|               dependentdemand := select( ptoperation, DependentDemand, dd, dd.ProcessInput() = input ); | 
|               if( not isnull( dependentdemand ) ) | 
|               { | 
|                 newrhs := this.GetConstraintRHS( const, scalefactor_rhs_const ) - dependentdemand.FulfilledQuantity(); | 
|                 const.RHSValue( newrhs * scalefactor_rhs_const ); | 
|               } | 
|             } | 
|           } | 
|        | 
|           // Dependent demands for trips | 
|           traverse( activepispip, DependentDemandTrip.ProductInTrip, productintrip ) | 
|           { | 
|             if( scope.Contains( productintrip.ProductInTripInOptimizerRun() ) )  | 
|             { | 
|               // Term UoM: PISP | 
|               const.NewTerm( 1.0 * scalefactor_tripdemandqty_const, program.TripDemandQtyVariables().Get( productintrip ) ); | 
|             } | 
|             else if( productintrip.Quantity() > 0.0 ) | 
|             { | 
|               fulfilledqty := guard(  productintrip.DependentDemand().FulfilledQuantity(), 0.0 );  | 
|               newrhs := this.GetConstraintRHS( const, scalefactor_rhs_const ) - fulfilledqty; | 
|               const.RHSValue( newrhs * scalefactor_rhs_const ); | 
|             } | 
|           } | 
|      | 
|           // Sales demands | 
|           traverse( activepispip, PlanningBaseSalesDemandInPeriodForOptimization, sd ) | 
|           { | 
|             if( sd.istype( LeafSalesDemandInPeriod ) ) | 
|             { | 
|               // Term UoM: PISP | 
|               const.NewTerm( 1.0 * scalefactor_salesdemandqty_const, program.SalesDemandQtyVariables().Get( sd.astype( LeafSalesDemandInPeriod ) ) ); | 
|             } | 
|             else if( sd.istype( DisaggregatedSalesDemandInPeriod ) ) | 
|             { | 
|               const.NewTerm( 1.0 * scalefactor_salesdemandqty_const, program.DisaggregatedSalesDemandQtyVariables().Get( sd.astype( DisaggregatedSalesDemandInPeriod ) ) ); | 
|             }                     | 
|           } | 
|            | 
|           // Calculating the postponed sales demand fulfillment | 
|           // E.g., if the current period is period 11, max postponment period = 3, this retrieves postponed sales demands to period 11 from period 10, 9, 8. | 
|           maxpostponedperiod := pispip.ProductInStockingPoint_MP().OptimizerMaxPostponementPeriod(); // set in in init instance per pisp for performance  | 
|           previouspispip := activepispip.PreviousPlanningPISPIP(); | 
|      | 
|           for( i := 1; | 
|                i <= maxpostponedperiod                     // within the maximum number of postponement periods | 
|                and not isnull( previouspispip );            // the next pispip exists | 
|                i++ ) | 
|           { | 
|             traverse( previouspispip.astype( ProductInStockingPointInPeriodPlanningLeaf ), PlanningBaseSalesDemandInPeriodForOptimizationPostponable, sd ) | 
|             { | 
|               var := sd.GetDelayedSalesDemandQtyVariable( program, activepispip.Period_MP() ); | 
|      | 
|               if( not isnull( var ) ) | 
|               { | 
|                 // Term UoM: PISP | 
|                 const.NewTerm( 1.0 * scalefactor_delayedsalesdemandqty_const, var ); | 
|               } | 
|             } | 
|             previouspispip := previouspispip.PreviousPlanningPISPIP(); | 
|           } | 
|         } | 
|         // If the leaf pispip is not in the optimizer run we still need to update the RHS | 
|         else | 
|         { | 
|           // The RHS should be updated for the dependent demands that are related to the pispip that is out of scope of this optimizer run | 
|           traverse( activepispip, DependentDemandOperation, dependentdemand ) | 
|           { | 
|             newrhs := this.GetConstraintRHS( const, scalefactor_rhs_const ) - dependentdemand.FulfilledQuantity(); | 
|             const.RHSValue( newrhs * scalefactor_rhs_const ); | 
|           } | 
|            | 
|           traverse( activepispip, DependentDemandTrip.ProductInTrip, pit, not scope.Contains( pit.ProductInTripInOptimizerRun() ) ) | 
|           { | 
|             newrhs := this.GetConstraintRHS( const, scalefactor_rhs_const ) - pit.Quantity() | 
|             const.RHSValue( newrhs * scalefactor_rhs_const ); | 
|           } | 
|         } | 
|       } | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |