Quintiq file version 2.0
|
#parent: #root
|
Method InitConstraintsForInventoryMixBalancing (
|
CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
|
const RunContextForCapacityPlanning runcontext,
|
const LibOpt_Scope scope
|
) const
|
{
|
Description: 'Initialize the constraints that are related to balancing the inventory mix'
|
TextBody:
|
[*
|
if( runcontext.UseInventoryMixBalancing() )
|
{
|
overalldiffconstrname := typeof( MPOverallDifferenceInDemandDaysConstraint );
|
overallmaxconstrname := typeof( MPOverallMaxInvInDemandDaysConstraint );
|
overallminconstrname := typeof( MPOverallMinInvInDemandDaysConstraint );
|
diffindaysconstrname := typeof( MPDifferenceInDemandDaysConstraint );
|
mininvindaysconstrname := typeof( MPMinInvInDemandDaysConstraint );
|
maxinvindaysconstrname := typeof( MPMaxInvInDemandDaysConstraint );
|
|
scalefactor_overalldiff_overalldiffconstr := this.ScaleConstraintTerm( typeof( MPOverallDiffInvInDemandDaysVariable ), overalldiffconstrname );
|
scalefactor_overallmax_overalldiffconstr := this.ScaleConstraintTerm( typeof( MPOverallMaxInvInDemandDaysVariable ), overalldiffconstrname );
|
scalefactor_overallmin_overalldiffconstr := this.ScaleConstraintTerm( typeof( MPOverallMinInvInDemandDaysVariable ), overalldiffconstrname );
|
scalefactor_overallmax_overallmaxconstrname := this.ScaleConstraintTerm( typeof( MPOverallMaxInvInDemandDaysVariable ), overallmaxconstrname );
|
scalefactor_overallmin_overallminconstrname := this.ScaleConstraintTerm( typeof( MPOverallMinInvInDemandDaysVariable ), overallminconstrname );
|
scalefactor_diffindays_diffindaysconstr := this.ScaleConstraintTerm( typeof( MPDifferenceInDemandDaysVariable ), diffindaysconstrname );
|
scalefactor_maxinvindays_diffindaysconstr := this.ScaleConstraintTerm( typeof( MPMaxInvInDemandDaysVariable ), diffindaysconstrname );
|
scalefactor_mininvindays_diffindaysconstr := this.ScaleConstraintTerm( typeof( MPMinInvInDemandDaysVariable ), diffindaysconstrname );
|
scalefactor_maxinvindays_overallmaxconstr := this.ScaleConstraintTerm( typeof( MPMaxInvInDemandDaysVariable ), overallmaxconstrname );
|
scalefactor_mininvindays_overallminconstr := this.ScaleConstraintTerm( typeof( MPMinInvInDemandDaysVariable ), overallminconstrname );
|
scalefactor_maxinvindays_maxinvindaysconstr := this.ScaleConstraintTerm( typeof( MPMaxInvInDemandDaysVariable ), maxinvindaysconstrname );
|
scalefactor_mininvindays_mininvindaysconstr := this.ScaleConstraintTerm( typeof( MPMinInvInDemandDaysVariable ), mininvindaysconstrname );
|
scalefactor_invqty_maxinvindaysconstr := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), maxinvindaysconstrname );
|
scalefactor_invqty_mininvindaysconstr := this.ScaleConstraintTerm( typeof( MPInvQtyVariable ), mininvindaysconstrname );
|
|
scalefactor_rhs_mininvindaysconstr := this.ScaleConstraintRHS( mininvindaysconstrname, 1.0 );
|
scalefactor_rhs_maxinvindaysconstr := this.ScaleConstraintRHS( maxinvindaysconstrname, 1.0 );
|
|
productcategories := Product_MP::GetProductCategoriesForOptimization( this.MacroPlan(), scope );
|
|
allperiods := construct( Period_MPs, constcontent );
|
traverse( productcategories, Elements, productcategory )
|
{
|
// Retrieve all pispips that are part of this product category and that are part of the optimizer run
|
pispips := selectset( productcategory.GetLeavesConst(), Elements.ProductInStockingPoint_MP.ProductInStockingPointInPeriodPlanning, pispip,
|
scope.Contains( pispip.PISPIPInOptimizerRun() ) );
|
|
// Select all periods related to these pispips
|
periods := selectset( pispips, Elements.Period_MP, period, true );
|
|
// The inventory mix will be balanced per period
|
traverse( periods, Elements, period )
|
{
|
allperiods.Add( period );
|
|
// The overall max is equal to the maximum of all max inv in demand days
|
// OverallMaxInvInDemandDays(period) ≥ MaxInvInDemandDays( category, period ) ∀category,period
|
// overallmaxconstr UoM: Days
|
overallmaxconstr := program.OverallMaxInvInDemandDaysConstraints().New( productcategory, period );
|
overallmaxconstr.Sense( '>=' );
|
overallmaxconstr.RHSValue( 0.0 );
|
overallmaxconstr.NewTerm( scalefactor_overallmax_overallmaxconstrname, program.OverallMaxInvInDemandDaysVariables().Get( period ) );
|
|
// The overall min is equal to the minimum of all min inv in demand days
|
// OverallMinInvInDemandDays ≤ MinInvInDemandDays( category, period ) ∀category,period
|
// overallminconstr UoM: Days
|
overallminconstr := program.OverallMinInvInDemandDaysConstraints().New( productcategory, period );
|
overallminconstr.Sense( '<=' );
|
overallminconstr.RHSValue( 0.0 );
|
overallminconstr.NewTerm( scalefactor_overallmin_overallminconstrname, program.OverallMinInvInDemandDaysVariables().Get( period ) );
|
|
// The difference in days is equal to the max inventory in days minus the min inventory in days.
|
// DifferenceInDays(category,period) = MaxInvInDemandDays(category,period) - MinInvInDemandDays(category,period) ∀category,period
|
// diffindays constraint UoM: Days
|
diffindaysconstr := program.DifferenceInDemandDaysConstraints().New( productcategory, period );
|
diffindaysconstr.Sense( '=' );
|
diffindaysconstr.RHSValue( 0.0 );
|
// term UoM: Days
|
diffindaysconstr.NewTerm( -1.0 * scalefactor_diffindays_diffindaysconstr, program.DifferenceInDemandDaysVariables().Get( productcategory, period ) );
|
diffindaysconstr.NewTerm( scalefactor_maxinvindays_diffindaysconstr, program.MaxInvInDemandDaysVariables().Get( productcategory, period ) );
|
diffindaysconstr.NewTerm( -1.0 * scalefactor_mininvindays_diffindaysconstr, program.MinInvInDemandDaysVariables().Get( productcategory, period ) );
|
|
// Add terms to the overall constraints
|
// Term UoM: Days
|
overallmaxconstr.NewTerm( -1.0 * scalefactor_maxinvindays_overallmaxconstr, program.MaxInvInDemandDaysVariables().Get( productcategory, period ) );
|
overallminconstr.NewTerm( -1.0 * scalefactor_mininvindays_overallminconstr, program.MinInvInDemandDaysVariables().Get( productcategory, period ) );
|
|
traverse( productcategory.GetLeafPISPIPsConst( period.Start(), period.End() ), Elements.astype( ProductInStockingPointInPeriodPlanning ), pispip,
|
pispip.ProductInStockingPoint_MP().IsIncludedInBalancing()
|
and pispip.ProductInStockingPoint_MP().Product_MP().IsIncludedInBalancing() )
|
{
|
// The inventory of each product in each period is sufficient to cover a certain number of demand days. This is denoted the inventory in days.
|
// The minimum inventory in demand days is equal to the minimum of all products within a product category in each period
|
// MinInvDemandDays(product category) ≤ ( InvQty(product, period) - TargetInv(product, period) )/ DemandPerDay(product, period) ∀product category, product ∈ product category, period
|
// mininvindays constraint UoM: Days
|
mininvindaysconstr := program.MinInvInDemandDaysConstraints().New( productcategory, pispip );
|
mininvindaysconstr.Sense( '<=' );
|
mininvindaysconstr.RHSValue( 0.0 );
|
// Term UoM: Days
|
mininvindaysconstr.NewTerm( scalefactor_mininvindays_mininvindaysconstr, program.MinInvInDemandDaysVariables().Get( productcategory, period ) );
|
|
// The inventory of each product in each period is sufficient to cover a certain number of demand days. This is denoted the inventory in days.
|
// The maximum inventory in demand days is equal to the maximum of the inventory in days of all products within a product category in each period
|
// MaxInvDemandDays(product category) ≥ ( InvQty(product, period) - TargetInv(product, period) )/ DemandPerDay(product, period) ∀product category, product ∈ product category, period
|
// maxinvindays constraint UoM: Days
|
|
maxinvindaysconstr := program.MaxInvInDemandDaysConstraints().New( productcategory, pispip );
|
maxinvindaysconstr.Sense( '>=' );
|
maxinvindaysconstr.RHSValue( 0.0 );
|
// Term UoM: Days
|
maxinvindaysconstr.NewTerm( scalefactor_maxinvindays_maxinvindaysconstr, program.MaxInvInDemandDaysVariables().Get( productcategory, period ) );
|
|
demandperday := pispip.GetAverageSalesDemandQtyPerDay();
|
|
// If the average demand per day is 0 (meaning there is no demand)
|
// then we should not balance this pispip
|
if( demandperday <> 0 )
|
{
|
// If this pispip is part of the optimizer run, we need to a constraint term
|
if( scope.Contains( pispip.PISPIPInOptimizerRun() ) )
|
{
|
// The terms we are adding here are (InvEnd - InvTarget)/Demand per day
|
// Therefore, we first add InvEnd/Demand per day as a variable term
|
// and then update the RHS with - InvTarget/Demand per day
|
|
// Term: - ( 1 / demandperday ) * InvQty variable
|
// UoM: 1 / [ PISP UoM * Day ] * [PISP UoM]
|
mininvindaysconstr.NewTerm( - ( 1 / demandperday ) * scalefactor_invqty_mininvindaysconstr, program.InvQtyVariables().Get( pispip ) );
|
maxinvindaysconstr.NewTerm( - ( 1 / demandperday ) * scalefactor_invqty_maxinvindaysconstr, program.InvQtyVariables().Get( pispip ) );
|
|
// Update RHS with - InvTarget/Demand per day
|
targetindays := 0.0;
|
if( pispip.GetHasTargetInventory() )
|
{
|
if( pispip.GetHasTargetInDays() )
|
{
|
targetindays := pispip.TargetInDays();
|
}
|
else
|
{
|
targetindays := pispip.TargetInQuantity() / demandperday;
|
}
|
newrhsmin := this.GetConstraintRHS( mininvindaysconstr, scalefactor_rhs_mininvindaysconstr ) - targetindays;
|
mininvindaysconstr.RHSValue( newrhsmin * scalefactor_rhs_mininvindaysconstr );
|
newrhsmax := this.GetConstraintRHS( maxinvindaysconstr, scalefactor_rhs_maxinvindaysconstr ) - targetindays;
|
maxinvindaysconstr.RHSValue( newrhsmax * scalefactor_rhs_maxinvindaysconstr );
|
}
|
}
|
// otherwise, we need to update the RHS since the invqty is fixed
|
else
|
{
|
targetinqty := 0.0;
|
if( pispip.GetHasTargetInDays() )
|
{
|
targetinqty := pispip.TargetInDays() * demandperday;
|
}
|
else
|
{
|
targetinqty := pispip.TargetInQuantity();
|
}
|
|
newrhsmin := this.GetConstraintRHS( mininvindaysconstr, scalefactor_rhs_mininvindaysconstr ) + ( ( pispip.InventoryLevelEnd() - targetinqty ) / demandperday );
|
mininvindaysconstr.RHSValue( newrhsmin * scalefactor_rhs_mininvindaysconstr );
|
newrhsmax := this.GetConstraintRHS( maxinvindaysconstr, scalefactor_rhs_maxinvindaysconstr ) + ( ( pispip.InventoryLevelEnd() - targetinqty ) / demandperday );
|
maxinvindaysconstr.RHSValue( newrhsmax * scalefactor_rhs_maxinvindaysconstr );
|
}
|
}
|
}
|
} // end traverse period
|
} // end traverse product category
|
|
allperiods := allperiods.Unique();
|
|
traverse( allperiods, Elements, period )
|
{
|
// The overall difference is equal to the overall max minus the overall min
|
// OverallDifferenceInDays(period) = OverallMaxInvInDemandDays(period) - OverallMinInvInDemandDays(period) ∀period
|
// overalldiffconstr UoM: Days
|
overalldiffconstr := program.OverallDifferenceInDemandDaysConstraints().New( period );
|
overalldiffconstr.Sense( '=' );
|
overalldiffconstr.RHSValue( 0.0 );
|
overalldiffconstr.NewTerm( -1.0 * scalefactor_overalldiff_overalldiffconstr, program.OverallDiffInvInDemandDaysVariables().Get( period ) );
|
overalldiffconstr.NewTerm( scalefactor_overallmax_overalldiffconstr, program.OverallMaxInvInDemandDaysVariables().Get( period ) );
|
overalldiffconstr.NewTerm( -1.0 * scalefactor_overallmin_overalldiffconstr, program.OverallMinInvInDemandDaysVariables().Get( period ) );
|
}
|
}
|
*]
|
InterfaceProperties { Accessibility: 'Module' }
|
}
|