Quintiq file version 2.0
|
#parent: #root
|
Method CapacityPlanningAlgorithmHandleFeasibleCampaignSequencing (
|
CapacityPlanningSuboptimizer_CapacityPlanningAlgorithm program,
|
const RunContextForCapacityPlanning runcontext,
|
LibOpt_Task task
|
)
|
{
|
Description: 'The method called in "Handle feasible" tab to handle the campaign sequencing part of the optimizer'
|
TextBody:
|
[*
|
//Only call campaign sequencing code if the campaign sequencing optimizer is enabled and we have campaigns in the strategy
|
if( runcontext.UseCampaignSequenceOptimizer() and runcontext.UseCampaign() )
|
{
|
campaignsforwritetransitions := construct( Campaign_MPs );
|
this.LoggedTotalCampaignUnitPeriodUtilizationSlackVar( this.GetOptimalValue( program.TotalUnitPeriodCampaignUtilizationSlackVariables().Get() ) );
|
scope := task.Scope();
|
durZero := Duration::Zero();
|
|
//Cleanup all the campaigns we have so far
|
traverse( scope.GetUnitInOptimizerRun(), Elements, unit, unit.HasCampaignType() )
|
{
|
periodsinscope := selectsortedset( scope.GetPeriodInOptimizerRun(),
|
Elements,
|
p,
|
p.GetIsInCampaignTypeHorizonUnit( unit )
|
and not Unit::GetIsPeriodFrozen( unit, p ),
|
p.Start() );
|
if( periodsinscope.Size() > 0 )
|
{
|
campaigns := selectset( unit, Campaign_MP, c,c.Start() >= periodsinscope.Element( 0 ).Start() );
|
Campaign_MP::Delete( campaigns );
|
traverse( unit, OptCampaignUnit.LastCampaignBeforeOptHorizon, last ) // we cannot delete the last campaign before opt horizon, but we should reset to the preprocess duration (for meta iterations)
|
{
|
last.Duration( last.CampaignOptimizerPreProcessDuration() );
|
}
|
}
|
}
|
|
currentcampaignfordurationsetting := null( Campaign_MP );
|
//Get all the relevant OptCampaignUnitSubPeriods
|
ocusps := selectsortedset( scope.GetUnitPeriodInOptimizerRun(),
|
Elements.OptCampaignUnitSubPeriod,
|
ocusp,
|
true,
|
ocusp.UnitPeriod().UnitID(),
|
ocusp.StartTime(),
|
ocusp.EndTime() ) ;
|
currentoptunit := null( OptCampaignUnit );
|
|
traverse( ocusps, Elements, ocusp, not ocusp.IsFixed() )
|
{
|
if( not currentoptunit = ocusp.OptCampaignUnit() )
|
{
|
currentoptunit := ocusp.OptCampaignUnit();
|
currentcampaignfordurationsetting := currentoptunit.LastCampaignBeforeOptHorizon();
|
initialdurationfornexttransition := Duration::Zero();
|
if ( not isnull( currentoptunit.LastTransitionBeforeOptHorizon() ) )
|
{
|
assert( isnull( currentcampaignfordurationsetting ), 'not expecting last campaign to be set in case transition was set' );
|
currentcampaignfordurationsetting := currentoptunit.LastTransitionBeforeOptHorizon().FromCampaign();
|
initialdurationfornexttransition := ocusp.StartTime() - currentoptunit.LastTransitionBeforeOptHorizon().Start();
|
|
// init quantities on campaign period task in scope to 0. Add to this below as we go along growing campaigns.
|
traverse( currentcampaignfordurationsetting, CampaignPeriod_MP, cp, cp.Start() >= ocusp.StartTime() )
|
{
|
traverse( cp, PeriodTaskInCampaign, ptc )
|
{
|
ptc.Quantity( 0.0 );
|
}
|
}
|
}
|
|
if ( not isnull( currentcampaignfordurationsetting ) )
|
{
|
currentcampaignfordurationsetting.HasNextTransitionDurationByOpt( false );
|
currentcampaignfordurationsetting.NextTransitionDurationByOpt( initialdurationfornexttransition );
|
}
|
}
|
//Go over all the selected combis
|
|
selectedcombiset := construct( OptCampaignCombis );
|
traverse( ocusp, OptCampaignCombiActive, combi )
|
{
|
combi.IsSelectedByOptimizer( program.IsCampaignCombiSelectedVariables().Get( combi ).OptimalValue() > 0.5 );
|
if ( combi.IsSelectedByOptimizer() )
|
{
|
selectedcombiset.Add( combi );
|
}
|
}
|
assert( selectedcombiset.Size() <= 1, 'Error: expecting at most 1 combi selected per campaign unit sub period' );
|
|
traverse( selectedcombiset, Elements, combi )
|
{
|
// example for multi combi:
|
// subperiodseqnr name obtain duration B as
|
//
|
// 0 B start+end
|
// 1 B start+end
|
// 2 B start+end
|
// 3 B start
|
// 3 B->C end
|
//
|
// combi:
|
// 0 B start+end
|
// 1 B start
|
// 1 B->C mid
|
// 1 C mid
|
// 1 C->B mid
|
// 1 B mid
|
// 1 B->D end
|
//
|
// rule: check within subperiod voor
|
// if first within sub period -> start
|
// if last within sub period -> end (special case: both first and last, then start+end)
|
// else not first and not last -> mid
|
//
|
combi.CreatePeriodTaskInCombiElements( program, this );
|
starttime := ocusp.StartTime();
|
element := combi.FirstCombiElement();
|
elspassed := 0;
|
stopfordurationzerocampaignlastperiod := false;
|
while( elspassed < combi.NumberOfElements() and not stopfordurationzerocampaignlastperiod )
|
{
|
elduration := element.GetDurationInCampaignUnitSubPeriod( program, this );
|
|
if ( element.istype( OptCampaignCombiElementTransition ) )
|
{
|
if ( not isnull( currentcampaignfordurationsetting ) )
|
{
|
// we store the decided duration on the last campaign, because transitions are declarative in the model
|
currentcampaignfordurationsetting.HasNextTransitionDurationByOpt( true );
|
durationsofar := currentcampaignfordurationsetting.NextTransitionDurationByOpt();
|
currentcampaignfordurationsetting.NextTransitionDurationByOpt( durationsofar + elduration );
|
currentcampaignfordurationsetting.OptNextCampaignTypeName( element.astype( OptCampaignCombiElementTransition ).TransitionType_MP().ToCampaignTypeName() );
|
currentcampaignfordurationsetting.WriteOptCampaignPeriodTaskQuantities( task, element.astype( OptCampaignCombiElementTransition ) ); // store on campaign so we can write below after propagation
|
campaignsforwritetransitions.Add( currentcampaignfordurationsetting );
|
}
|
else
|
{
|
task.Log( 'optimizer decides transition but last campaign is null, unit id='
|
+ element.OptCampaignCombi().OptCampaignUnitSubPeriod().UnitPeriod().UnitID()
|
+ 'elt = ' + element.Name()
|
+ 'combi = ' + combi.Name() );
|
}
|
if ( element.NextIsCampaign() )
|
{
|
currentcampaignfordurationsetting := null( Campaign_MP ); // finished with transition, ensure for next campaign we will store durations in the freshly created campaign
|
}
|
}
|
else
|
{
|
// So it is a campaign
|
// only need to plan campaigns
|
if( element.IsFirstElementInUnitSubPeriod()
|
and not isnull( currentcampaignfordurationsetting )
|
and element.OptCampaignElementType().astype( OptCampaignCampaignType ).CampaignType_MP() = currentcampaignfordurationsetting.CampaignType_MP() )
|
{
|
currentcampaignfordurationsetting.Duration( currentcampaignfordurationsetting.Duration() + elduration );
|
currentcampaignfordurationsetting.WriteOptCampaignPeriodTaskQuantities( task, element.astype( OptCampaignCombiElementCampaign ) );
|
}
|
//we don't want to plan in case of 0 duration at the end of the horizon
|
else if( not isnull( element.OptCampaignUnitSubPeriod().NextSubPeriod() ) or elduration > durZero )
|
{
|
campaigntype := element.astype( OptCampaignCombiElementCampaign ).CampaignType_MP();
|
unit := ocusp.UnitPeriod().Unit();
|
campaign := Campaign_MP::Create( unit,
|
starttime,
|
elduration,
|
-1,
|
campaigntype.DefaultMinQuantity(),
|
campaigntype.DefaultMaxQuantity(),
|
campaigntype.DefaultMinDuration(),
|
campaigntype.DefaultMaxDuration(),
|
campaigntype.Name(),
|
OS::GenerateGUIDAsString(),
|
'Created by optimizer', //JPS1 NEEDS TO CHANGE
|
campaigntype.HasInputMaxQuantity(),
|
campaigntype.HasInputMaxDuration(),
|
false );
|
|
campaign.SynchronizePeriodTaskInCampaigns();
|
currentcampaignfordurationsetting := campaign
|
Transaction::Transaction().Propagate( relation( Campaign_MP, CampaignPeriod_MP) );
|
Transaction::Transaction().Propagate( relation( CampaignPeriod_MP, PeriodTaskInCampaign) );
|
|
currentcampaignfordurationsetting.WriteOptCampaignPeriodTaskQuantities( task, element.astype( OptCampaignCombiElementCampaign ) );
|
}
|
else
|
{
|
// we did not create a new campaign, or add duration to an existing one.
|
// we know: elduration 0 and nextsubperiod = null. Stop processing combi by setting flag to exit loop
|
stopfordurationzerocampaignlastperiod := true;
|
}
|
}
|
|
//storing the value for debugging
|
element.OptimizerDuration( elduration )
|
//While loop bookkeeping
|
starttime := starttime + elduration;
|
|
element := element.NextCombiElement();
|
elspassed := elspassed + 1;
|
} // end while combi elements
|
} //end traverse combiset
|
} // end traverse opt campaign unit sub period
|
|
// debug writing attributes
|
if ( this.Optimization().astype( Optimization ).DebugMode() )
|
{
|
traverse( scope.GetUnitPeriodInOptimizerRun(), Elements.OptCampaignUnitSubPeriod, ocusp )
|
{
|
traverse( ocusp, OptCampaignCombiElementActive, ocelt, ocelt.OptCampaignCombi().DebugIsFixedPlanned() )
|
{
|
ocet := ocelt.OptCampaignElementType();
|
subperiod := ocelt.OptCampaignUnitSubPeriod();
|
midvar := program.DurationOfCampaignElementTypeMidVariables().Get( ocet, subperiod );
|
endvar := program.DurationOfCampaignElementTypeEndVariables().Get( ocet, subperiod );
|
startvar := program.DurationOfCampaignElementTypeStartVariables().Get( ocet, subperiod );
|
ocelt.DebugDurationOfCampaignElementTypeEndVariables( endvar.OptimalValue() ); // note that these create duplicates among all combis
|
ocelt.DebugDurationOfCampaignElementTypeMidVariables( midvar.OptimalValue() ); // that share the element type for the particular sub period
|
ocelt.DebugDurationOfCampaignElementTypeStartVariables( startvar.OptimalValue() ); // if we would not limit to selected combis only
|
}
|
|
ocusp.DebugOverloadedEnd( 0.0 );
|
ocusp.DebugOverloadedMid( 0.0 );
|
ocusp.DebugOverloadedStart( 0.0 );
|
ocusp.DebugUnderloadedEnd( 0.0 );
|
ocusp.DebugUnderloadedMid( 0.0 );
|
ocusp.DebugUnderloadedStart( 0.0 );
|
|
traverse( ocusp, OptCampaignCombiElement.OptCampaignElementType, ocet )
|
{
|
varperiodoverloadstart := program.CampaignElementTypePeriodOverloadedStartVariables().Get( ocet, ocusp );
|
varperiodoverloadmid := program.CampaignElementTypePeriodOverloadedMidVariables().Get( ocet, ocusp );
|
varperiodoverloadend := program.CampaignElementTypePeriodOverloadedEndVariables().Get( ocet, ocusp );
|
varperiodunderloadedstart := program.CampaignElementTypePeriodUnderloadedStartVariables().Get( ocet, ocusp );
|
varperiodunderloadedmid := program.CampaignElementTypePeriodUnderloadedMidVariables().Get( ocet, ocusp );
|
varperiodunderloadedend := program.CampaignElementTypePeriodUnderloadedEndVariables().Get( ocet, ocusp );
|
|
ocusp.DebugOverloadedEnd( ocusp.DebugOverloadedEnd() + varperiodoverloadend.OptimalValue() );
|
ocusp.DebugOverloadedMid( ocusp.DebugOverloadedMid( ) + varperiodoverloadmid.OptimalValue() );
|
ocusp.DebugOverloadedStart( ocusp.DebugOverloadedStart() + varperiodoverloadstart.OptimalValue() );
|
ocusp.DebugUnderloadedEnd( ocusp.DebugUnderloadedEnd() + varperiodunderloadedend.OptimalValue() );
|
ocusp.DebugUnderloadedMid( ocusp.DebugUnderloadedMid() + varperiodunderloadedmid.OptimalValue() );
|
ocusp.DebugUnderloadedStart( ocusp.DebugUnderloadedStart() + varperiodunderloadedstart.OptimalValue() );
|
}
|
|
ocusp.CalcDebugUnderloadedTotal();
|
ocusp.CalcDebugOverloadedTotal();
|
}
|
}
|
|
Transaction::Transaction().Propagate( relation( TransitionType_MP, Transition_MP ) );
|
Transaction::Transaction().Propagate( relation( Campaign_MP, PlanningCampaignPeriod ) );
|
Transaction::Transaction().Propagate( relation( Transition_MP, TransitionPeriod_MP ) );
|
Transaction::Transaction().Propagate( relation( TransitionPeriod_MP, PeriodTaskInTransition ) );
|
|
// now the transition and transition period objects are there
|
|
campaignsforwritetransitions := campaignsforwritetransitions.Unique();
|
traverse( campaignsforwritetransitions, Elements, campaign )
|
{
|
transition := campaign.ToTransition();
|
nonzeroqtyset := selectset( campaign, OptCampaignPeriodTaskInCombiElement, pt, true, pt.Quantity() > 0.0 );
|
combielements := selectset( nonzeroqtyset, Elements.OptCampaignCombiElement.astype( OptCampaignCombiElementTransition ), elt, true, true );
|
combielements := combielements.Unique();
|
if ( isnull( transition ) and combielements.Size() > 0 )
|
{
|
task.Log( 'CAMPAIGN no next transition to write nonzero qty to' );
|
}
|
if ( not isnull( transition ) )
|
{
|
// reset all period task in transition object associated already to the transition. Other ones created on the fly below as we will need them
|
traverse( transition, TransitionPeriod_MP.PeriodTaskInTransition, pt, not pt.TransitionPeriod_MP().UnitPeriod().IsPeriodFrozen() )
|
{
|
pt.Quantity( 0.0 );
|
}
|
|
traverse( combielements, Elements, elt )
|
{
|
transition.WriteOptCampaignPeriodTaskQuantities( task, elt );
|
}
|
}
|
}
|
}
|
*]
|
InterfaceProperties { Accessibility: 'Module' }
|
}
|