| Quintiq file version 2.0 | 
| #parent: #root | 
| Method InitConstraintsGoalsForDriverInventoryHolding ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   const LibOpt_Scope scope, | 
|   const constcontent ProductInStockingPoint_MPs pispsinrun | 
| ) const | 
| { | 
|   Description: 'Init constraints goals for PISPIPs' | 
|   TextBody: | 
|   [* | 
|     // Inventory holding accounts | 
|      | 
|     icconstname := typeof( MPDriverInventoryHoldingConstraint ); | 
|     driverinventoryholding_varname := typeof( MPDriverInventoryHoldingVariable ); | 
|     invqty_varname := typeof( MPInvQtyVariable ); | 
|     partialoperationdemandqty_varname := typeof( MPPartialOperationDemandQtyVariable ); | 
|     tripdemandqty_varname := typeof( MPTripDemandQtyVariable ); | 
|      | 
|     scalefactor_driver_icconst := this.ScaleConstraintTerm( driverinventoryholding_varname, icconstname ); | 
|     scalefactor_invqty_icconst := this.ScaleConstraintTerm( invqty_varname, icconstname ); | 
|     scalefactor_partialoperationdemandqty_icconst := this.ScaleConstraintTerm( partialoperationdemandqty_varname, icconstname ); | 
|     scalefactor_tripdemandqty_icconst := this.ScaleConstraintTerm( tripdemandqty_varname, icconstname ); | 
|      | 
|     scalefactor_rhs_icconst := this.ScaleConstraintRHS( icconstname, 1.0 ); | 
|      | 
|     ispostprocessing := runcontext.IsPostProcessing();  | 
|      | 
|     driver := select( this, MacroPlan.AccountCostDriver, driver, driver.Name() = Translations::MP_AccountAssignmentCostDriverInventoryHolding() ); // unique | 
|     traverse( scope.GetAccountsInOptimizerRunConst(), Elements, account, account.HasInventoryHoldingAssignment() ) // condition holds implies driver not null | 
|     { | 
|       // Inventory cost of each account = inventory quantity * cost + pre-processing quantity * day * cost per day + post-processing quantity * day * cost per day | 
|       // icconst UoM: Monetary | 
|       icconst := program.DriverInventoryHoldingConstraints().New( account, driver ); | 
|       icconst.Sense( '=' );  | 
|       icconst.RHSValue( 0.0 * scalefactor_rhs_icconst ); | 
|       // Term UoM: Monetary | 
|       icconst.NewTerm( 1.0 * scalefactor_driver_icconst, program.DriverInventoryHoldingVariables().Get( account, driver ) ); | 
|      | 
|       traverse( pispsinrun, Elements, pisp )  | 
|       { | 
|         traverse( pisp, PISPAccountForInventoryHoldingOptimizer, aa, aa.AccountCostDriver() = driver and aa.Account_MP() = account )  | 
|         // Only consider those pispips that are part of this optimizer run | 
|         // Only include those pispips where the product is included. | 
|         {  | 
|           if ( runcontext.IsMetaIteration() )  | 
|           { | 
|             // correction for pispip past scope | 
|             last := pisp.LatestPISPIPInScope();  | 
|             current := guard( last.Next().astype(  ProductInStockingPointInPeriodPlanningLeaf ), null(  ProductInStockingPointInPeriodPlanningLeaf ) );  | 
|              | 
|             while ( not isnull( current ) )  | 
|             { | 
|               var := program.PosInvQtyPastLastVariables().Get(  current );  | 
|               icconst.NewTerm( -current.GetBaseInventoryHoldingCostPerQuantity( aa ) * scalefactor_invqty_icconst,  | 
|                                  var );               // Inventory cost  | 
|               current := current.NextPlanningPISPIP().astype( ProductInStockingPointInPeriodPlanningLeaf );                             | 
|             }   | 
|           } | 
|               | 
|           traverse( aa.GetLeafPISPIPsInScope(),  | 
|                     Elements,  | 
|                     pispip )  | 
|           { | 
|        | 
|             if( pispip.ProductInStockingPoint_MP().IsNegativeInventoryAllowed() ) | 
|             { | 
|               // Negative inventory allowed case: only consider positive inventory | 
|               // Term: -holdingcostperqty * (Pos)InvQty variable | 
|               // UoM:    [Monetary/PISP]    *   [PISP] | 
|               icconst.NewTerm( -pispip.GetBaseInventoryHoldingCostPerQuantity( aa ) * scalefactor_invqty_icconst,  | 
|                                program.PosInvQtyVariables().Get( pispip ) );               // Inventory cost  | 
|             } | 
|             else | 
|             { | 
|               // Regular case: no negative inventory | 
|               icconst.NewTerm( -pispip.GetBaseInventoryHoldingCostPerQuantity( aa ) * scalefactor_invqty_icconst,  | 
|                                 program.InvQtyVariables().Get( pispip ) );               // Inventory cost    | 
|             } | 
|          | 
|             pispipperiod := pispip.Period_MP(); | 
|        | 
|             // Dependent demands WIP cost for operations that are part of this optimizer run | 
|             if ( this.GetPeriodsFromPeriodTaskOperation() )  | 
|             {  | 
|               traverse( pispip, DependentDemandOperation, dd ) // better path for performance | 
|               { | 
|                 pto := dd.PeriodTaskOperation();  | 
|                 operation := pto.Operation();  | 
|                 if ( operation.HasLeadTime() )  | 
|                 { | 
|                   input := dd.ProcessInput().astype( OperationInput );  | 
|                   if ( ( input.HasRegularProductforOptimizer() or input.GetIsProductInOptimizerRun( ispostprocessing ) ) | 
|                        and not isnull( input.PISPwhenAvailableForOptimization() )  | 
|                        and scope.Contains( pto.PeriodTaskOperationInOptimizerRun() ) )   | 
|                   { | 
|                     cost := pispip.GetBaseWIPCostPerQuantityPerDay( aa ) * operation.LeadTime().DaysAsReal(); | 
|                     traverse( pto, NewSupply.ProductInStockingPointInPeriodPlanningLeaf.Period_MP, nsperiod, not nsperiod = pispipperiod )  | 
|                     { | 
|                       partialoperationdemandqtyvar := program.PartialOperationDemandQtyVariables().Find( input, pispipperiod, nsperiod ); | 
|                       // If the variable does not exist, then this combination of operation and period is not considered by the optimizer | 
|                       // Therefore, this term should then also not exist | 
|                       if( not isnull( partialoperationdemandqtyvar ) ) | 
|                       { | 
|                         // Term:    -cost      * PartialOperationDemandQty variable | 
|                         // UoM:  [Monetary/PISP] *           [PISP] | 
|                         icconst.NewTerm( -cost * scalefactor_partialoperationdemandqty_icconst, partialoperationdemandqtyvar ); | 
|                       } | 
|                     } | 
|                   } | 
|                 } | 
|               } | 
|             } | 
|             else | 
|             { | 
|               traverse( pispip,  | 
|                         ProductInStockingPoint_MP.OperationInputAvailableForOptimization,  | 
|                         input, | 
|                         guard( scope.Contains( input.Operation().OperationInOptimizerRun() ), false ) | 
|                         and input.HasRegularProductforOptimizer() or input.GetIsProductInOptimizerRun( ispostprocessing ) ) | 
|               { | 
|                 operation := input.Operation(); | 
|          | 
|                 if( operation.HasLeadTime() ) | 
|                 { | 
|                   cost := pispip.GetBaseWIPCostPerQuantityPerDay( aa ) | 
|                           * operation.LeadTime().DaysAsReal(); | 
|                            | 
|                   ptperiods := CapacityPlanningSuboptimizer::GetOperationPeriodTaskPeriodsForPreprocessing( pispipperiod, operation); | 
|                                | 
|                   traverse( ptperiods, Elements, ptperiod, | 
|                             ptperiod <> pispipperiod )              // WIP cost incurred for dependent demand that happens earlier than the period task. | 
|                   { | 
|                     partialoperationdemandqtyvar := program.PartialOperationDemandQtyVariables().Find( input, pispipperiod, ptperiod ); | 
|                     // If the variable does not exist, then this combination of operation and period is not considered by the optimizer | 
|                     // Therefore, this term should then also not exist | 
|                     if( not isnull( partialoperationdemandqtyvar ) ) | 
|                     { | 
|                       // Term:    -cost      * PartialOperationDemandQty variable | 
|                       // UoM:  [Monetary/PISP] *           [PISP] | 
|                       icconst.NewTerm( -cost * scalefactor_partialoperationdemandqty_icconst, partialoperationdemandqtyvar ); | 
|                     } | 
|                   } | 
|                 } | 
|               } | 
|             } | 
|        | 
|             // Inventory cost for trips that are part of this optimizer run | 
|             pits := selectset(  pispip, DependentDemandTrip.ProductInTrip, productintrip,  | 
|                       scope.Contains( productintrip.ProductInTripInOptimizerRun() ) ) ;  | 
|             basecostperquantityperday := ifexpr(  pits.Size() > 0, pispip.GetBaseWIPCostPerQuantityPerDay( aa ),  0.0 );  | 
|                                     | 
|             traverse( pits, Elements, productintrip ) | 
|             { | 
|               cost := basecostperquantityperday * productintrip.Trip().LeadTime().DaysAsReal(); | 
|               // Term:   -cost      * TripDemandQty variable | 
|               // UoM: [Monetary/PISP] *         [PISP] | 
|               icconst.NewTerm( -cost * scalefactor_tripdemandqty_icconst, program.TripDemandQtyVariables().Get( productintrip ) ); | 
|             } | 
|           } // end traverse leaf pispips | 
|         } // end traverse account assignment (PISPAccount) | 
|       } // end traverse pisp in scope | 
|     } // end traverse accounts | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |