| Quintiq file version 2.0 | 
| #parent: #root | 
| Method AdditionalConstraintMaxInventory ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const LibOpt_Scope scope, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   const RunContextMeta runcontextmeta | 
| ) const | 
| { | 
|   TextBody: | 
|   [* | 
|     if ( runcontext.IsMetaIteration() and runcontextmeta.OptionUseAdditonalMaxInventoryConstraint() )  | 
|     { | 
|       constname := typeof( MPMetaLimitMaxInventoryPastHorizonConstraint );  | 
|       scalefactor_invqty_const := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), constname ); | 
|       scalefactor_slack_const := this.ScaleConstraintTerm( typeof( MPMetaLimitMaxInventoryPastHorizonSlackVariable ), constname ); | 
|       scalefactor_rhs_const := this.ScaleConstraintRHS( constname, 1.0 ); | 
|      | 
|       traverse( scope.GetProductInStockingPointInOptimizerRunConst(), Elements.LatestPISPIPInScope, pispip )             | 
|       { | 
|         smallestgap := pispip.GetSmallestMaxGap();  | 
|          | 
|         // we only allow the inventory level to increase by smallest gap at the pispip so we do not introduce additional constraints.  | 
|         currentlevelincludesmaxinventorylevelgoal := runcontext.WeightLevelNonFinancial().MaximumInventoryLevel() <= this.FocusLevel() | 
|                                                      and runcontext.WeightLevelNonFinancial().MaximumInventoryLevelWeight() > 0.0;  | 
|         if ( smallestgap.IsFinite() and currentlevelincludesmaxinventorylevelgoal )  | 
|         { | 
|            | 
|           constr := program.MetaLimitMaxInventoryPastHorizonConstraints().New( pispip ); | 
|           slackvar := program.MetaLimitMaxInventoryPastHorizonSlackVariables().New( pispip );  | 
|           constr.Sense( '<=' ); | 
|           constr.NewTerm( -1.0 * scalefactor_slack_const, slackvar );  | 
|            | 
|            | 
|           pispipinvend := pispip.InventoryLevelEnd(); | 
|           rhs := maxvalue( 0.0, pispipinvend + smallestgap );  | 
|           // We only update the RHS if the quantity is larger than the feasibility tolerance to avoid numerical instability | 
|           rhs := ifexpr( abs( rhs * scalefactor_rhs_const ) >= this.SmallestFeasibilityTolerance(), rhs, 0.0 ) ; | 
|           constr.RHSValue( rhs * scalefactor_rhs_const ); | 
|            | 
|           // Term UoM: PISP | 
|           if ( pispip.IsLeafPlanning() )  | 
|           { | 
|             constr.NewTerm( 1.0 * scalefactor_invqty_const, program.InvQtyVariables().Get( pispip )  ); | 
|           } | 
|           else | 
|           { | 
|             this.AddTermsToInventorySpecificationHighLevelConstraint( program, constr, pispip, scope, scalefactor_invqty_const, scalefactor_rhs_const );  | 
|           } | 
|            | 
|           lowerlimitconstr := program.FindConstraint( CapacityPlanningSuboptimizer::ConstraintNameLowerBoundLastPeriodInventory(), pispip ); // need to be consistent with this lower bound due to pre solve numerical issues | 
|           if ( not isnull( lowerlimitconstr ) )  | 
|           { | 
|             lowerlimit :=  -lowerlimitconstr.RHSValue();  | 
|             upperlimit := constr.RHSValue();  | 
|             if ( lowerlimit >= upperlimit )  | 
|             { | 
|               lowerlimitconstr.RHSValue( 1000.0 ); // work around epsilon logic | 
|               lowerlimitconstr.RHSValue( -upperlimit );  | 
|             } | 
|           } | 
|         } | 
|       } | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |