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