| Quintiq file version 2.0 | 
| #parent: #root | 
| Method InitConstraintsForStockingPointInPeriods ( | 
|   CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program, | 
|   const RunContextForCapacityPlanning runcontext, | 
|   const RunContextMeta rcm, | 
|   const LibOpt_Scope scope, | 
|   const constcontent ProductInStockingPointInPeriodPlanningLeafs leafpispips | 
| ) const | 
| { | 
|   Description: 'Initialize hard constraints for stocking points' | 
|   TextBody: | 
|   [* | 
|     starttime := OS::PrecisionCounter();  | 
|     constname := typeof( MPSPInPeriodInventoryLevelConstraint ); | 
|      | 
|     scalefactor_spinvqty_const := this.ScaleConstraintTerm( typeof( MPSPInvQtyVariable ), constname ); | 
|     scalefactor_invqty_const := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), constname ); | 
|     scalefactor_stockingpointcapacityoverloaded_const := this.ScaleConstraintTerm( typeof( MPStockingPointCapacityOverloadedVariable ), constname ); | 
|      | 
|     scalefactor_rhs_const := this.ScaleConstraintRHS( constname, 1.0 ); | 
|      | 
|     spipstoconsider := selectset(  scope.GetStockingPointInPeriodInOptimizerRunConst(),  | 
|                                    Elements,  | 
|                                    spip, | 
|                                    not spip.IsPeriodFrozen() | 
|                                    and not spip.StockingPoint_MP().IsPlannedInfinite() ); | 
|      | 
|      | 
|     // Inventory of stocking point in period = sum of inventory of pispip + dead inventory | 
|      | 
|     SelectorMeta::CheckNoGap( scope ); // debugmode check only | 
|      | 
|     // first create constraints | 
|     traverse( spipstoconsider, Elements, spip )  | 
|     { | 
|       // define overload - using same scaling factors as SPInPeriodInventoryLevelConstraint | 
|       constrlimit := program.SPInPeriodOverloadConstraints().New( spip );  | 
|       constrlimit.Sense( '<=' );  | 
|       constrlimit.RHSValue( spip.MaxCapacity() * scalefactor_rhs_const );  | 
|       constrlimit.NewTerm( 1.0 * scalefactor_spinvqty_const, | 
|                      program.SPInvQtyVariables().Get( spip ) ); | 
|        | 
|       constrlimit.NewTerm( -1.0 * scalefactor_stockingpointcapacityoverloaded_const, program.StockingPointCapacityOverloadedVariables().Get( spip ) ); | 
|      | 
|       if ( runcontext.IsMetaIteration() )  | 
|       { | 
|           // define overload for meta against slightly reduced capacity. Used to supress tiny overloads due to mismatches | 
|            | 
|         restrictedcap := spip.MaxCapacity() * ( 1 - rcm.OptionRestrictedSPCapacityEpsilon() );  | 
|         constrlimitmeta := program.SPInPeriodOverloadMetaConstraints().New( spip );  | 
|         constrlimitmeta.Sense( '<=' );  | 
|         constrlimitmeta.RHSValue( restrictedcap * scalefactor_rhs_const );  | 
|         constrlimitmeta.NewTerm( 1.0 * scalefactor_spinvqty_const, | 
|                        program.SPInvQtyVariables().Get( spip ) ); | 
|          | 
|         constrlimitmeta.NewTerm( -1.0 * scalefactor_stockingpointcapacityoverloaded_const, program.StockingPointCapacityOverloadedMetaVariables().Get( spip ) ); | 
|       } | 
|        | 
|       // const UoM: SP | 
|       // define inventory level by summing pispips that contribute | 
|       const := program.SPInPeriodInventoryLevelConstraints().New( spip ); | 
|       const.Sense( '=' ); | 
|       // RHS UoM: SP | 
|       const.RHSValue( spip.InventoryLevelEnd() * scalefactor_rhs_const ); // we initialize like this, so we can reduce it each time we add a pispip. This way we can avoid having to traverse all pispips (only those in scope) | 
|       // Term UoM: SP | 
|       const.NewTerm( 1.0 * scalefactor_spinvqty_const, | 
|                      program.SPInvQtyVariables().Get( spip ) ); | 
|     } | 
|      | 
|     // add constraint terms by traversing scope on  | 
|     traverse( leafpispips, Elements, pispip )  | 
|     { | 
|       // figure out which spip 'pispip' contributes to: if pispip.SPIP is in scope it is direct, otherwise of 'pispip' is the last pispip in scope for the pisp  | 
|       pisp := pispip.ProductInStockingPoint_MP();  | 
|       pisp_is_relevant := not pisp.IsNegativeInventoryAllowed() | 
|                   and not pisp.IsExcluded() | 
|                   and not pisp.StockingPoint_MP().IsPlannedInfinite(); | 
|       spip := pispip.AsStockingPointInPeriod();               | 
|       if ( pisp_is_relevant and not spip.IsPeriodFrozen() )  | 
|       { | 
|         const := program.SPInPeriodInventoryLevelConstraints().Get( spip ); | 
|         uomconversionfactor := pisp.UOMConversionForSPIPConstraint();    | 
|         // Term: uomconversion * InvQty  | 
|           // UoM:   [PISP to SP] * [PISP] | 
|         const.NewTerm( -uomconversionfactor * scalefactor_invqty_const, program.InvQtyVariables().Get( pispip ) ); | 
|                         | 
|         // now reduce RHS for this term because we know it is in scope | 
|         newrhs := this.GetConstraintRHS( const, scalefactor_rhs_const ) - uomconversionfactor * pispip.InventoryLevelEnd();  | 
|         const.RHSValue( newrhs * scalefactor_rhs_const );  | 
|                                                                                                    | 
|         if ( pisp.LatestPISPIPInScope() = pispip ) // special case, we also need to include the contibution to later spip | 
|         { | 
|           current := spip.NextSPIPPlanning();  | 
|           currentpispip := pispip.NextPlanningPISPIP();  | 
|            | 
|           while ( not isnull( current ) )  | 
|           { | 
|             if ( not current.IsPeriodFrozen() and scope.Contains( current.SPIPInOptimizerRun() ) )  | 
|             { | 
|               const_current := program.SPInPeriodInventoryLevelConstraints().Get( current );  | 
|               const_current.NewTerm( -uomconversionfactor * scalefactor_invqty_const, program.InvQtyVariables().Get( pispip ) ); | 
|                | 
|               // Take *out* currentpispip.InventoryEnd from RHS contribution, but add in the delta = currentpispip.InventoryLevelEnd - pispip.InventoryLevelEnd. So net put in pispip.InventoryLevelEnd.   | 
|               newrhs := this.GetConstraintRHS( const_current, scalefactor_rhs_const ) - uomconversionfactor * pispip.InventoryLevelEnd() | 
|               const_current.RHSValue( newrhs * scalefactor_rhs_const ); | 
|             } | 
|              | 
|             current := current.NextSPIPPlanning();  | 
|             currentpispip := currentpispip.NextPlanningPISPIP();  | 
|           }  | 
|         }                                                                                                                                                                                                                                                                                                  | 
|       }  | 
|     } | 
|      | 
|        | 
|     // final constraint modifications + deal with out of scope SPIP | 
|     traverse( spipstoconsider, Elements, spip )  | 
|     {  | 
|       const := program.SPInPeriodInventoryLevelConstraints().Get( spip );                                | 
|       changeunscaledRHS := this.FilterCPLEXNoise( const, scalefactor_rhs_const ); // can consider this to be the change in the inventory level out of scope. Using it to compute existing violation for meta | 
|        | 
|       varattrworkaround := program.NewVariable(  'UnscaledRHSModificationSPInPeriodInventoryLevelConstraints', spip );  | 
|       varattrworkaround.Enabled( false );  | 
|       this.StoreValueInVariable( varattrworkaround, changeunscaledRHS );  | 
|                                                                                                                | 
|       nextspip := spip.GetNextPlanningSPIP();                   | 
|       if ( not runcontext.IsSmartPlan()  | 
|            and not isnull( nextspip )  | 
|            and not scope.Contains( nextspip.SPIPInOptimizerRun() ) )  | 
|       { | 
|         spipblockafter_outofscope := nextspip.GetMaximalOutOfScope( scope );  | 
|         mininventoryspaceleftafter := min( spipblockafter_outofscope,  | 
|                                            Elements,  | 
|                                            spipfuture,  | 
|                                            spipfuture.Start() > spip.Start(),  | 
|                                            maxvalue ( 0.0, spipfuture.MaxCapacity() - spipfuture.InventoryLevelEnd() ) );   | 
|         // we should limit the increase in the last period by maxinventorydiffafter. We do this by changing the variable upper bound  | 
|      | 
|         restrictedcapacity := maxvalue( 0.0, spip.InventoryLevelEnd() + mininventoryspaceleftafter );  | 
|         // We set the limit always not to make worse any violation in the plan after what is in scope.   | 
|         // using same (mass) scaling factor as inventy define constraint | 
|         var := program.SPInvQtyVariables().Get( spip ); | 
|         restrictedmaxcap := program.SPRestrictedCapacityForOutOfScopeConstraints().New( spip ); // introduce this constraint because merging of constraints ignores the var bound (parallel cplex) | 
|         restrictedmaxcap.Sense( '<=' );  | 
|         restrictedmaxcap.RHSValue( scalefactor_rhs_const * restrictedcapacity ); | 
|         restrictedmaxcap.NewTerm( 1.0 * scalefactor_spinvqty_const, var );   | 
|         restrictedmaxcap.NewTerm( -1.0 * scalefactor_spinvqty_const, program.SPInvOutOfScopeSlackVariables().Get( spip ) );   | 
|       } | 
|     } | 
|     endtime := OS::PrecisionCounter();  | 
|     durationmethod := (endtime - starttime ) / OS::PrecisionCounterFrequency();  | 
|     debuginfo(  'TIME FOR SP constraint = ', durationmethod , 'sec' ); | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |