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