| Quintiq file version 2.0 | 
| #parent: #root | 
| Method SelectOptimizerInputForSmartPlan ( | 
|   ProductInStockingPointInPeriodPlannings pispipsmartplan, | 
|   LaneLegs lanelegsforoptimization, | 
|   Period_MPs periods, | 
|   Boolean isusingselectedunits, | 
|   Units units, | 
|   Boolean skipgapfillfinalrelations, | 
|   Process_MP process, | 
|   LibOpt_Scope scope, | 
|   RunContextForCapacityPlanning runcontext, | 
|   Boolean resetvisited | 
| ) | 
| { | 
|   Description: 'Selects the pispips that should be used in the Smart plan' | 
|   TextBody: | 
|   [* | 
|      | 
|     startingpispips := construct( ProductInStockingPointInPeriods ); | 
|     this.RemoveAllSidePISPIPs(); // collect all side products here and add those later, so we do explore onward during the recursive method.  | 
|     this.ResetRelationsStoredVisited( resetvisited );  | 
|     this.PISPIPNewlyVisited( relflush ); // to keep track of what has been visited in this method call | 
|      | 
|     // Select the starting pispips based on the user input | 
|     // The starting pispips are the selected pispips plus their previous n-1 pispips | 
|     isupstream := this.GetIsUpstream();  | 
|      | 
|     traverse( pispipsmartplan, Elements, pispip ) | 
|     { | 
|       for( n := 1; | 
|            n <= this.NumberOfSmartPlanPeriods()  | 
|            and not isnull( pispip ); | 
|            n++ ) | 
|       { | 
|         if ( not runcontext.IsMetaIteration() or pispip.Period_MP().IsInOptimizerPuzzle() )  | 
|         { | 
|           startingpispips.Add( pispip ); | 
|         } | 
|         if( isupstream ) | 
|         { | 
|           pispip := pispip.PreviousPlanningPISPIP(); | 
|         } | 
|         else | 
|         { | 
|           pispip := pispip.NextPlanningPISPIP(); | 
|         } | 
|       } | 
|     } | 
|      | 
|     // The optimization is performed on planning / leaf pispips, so we should make sure we have selected those | 
|     // Assuming that all starting pispips are planning pispips | 
|     traverse( startingpispips, Elements, pispip, | 
|               not pispip.IsLeafPlanning() ) | 
|     { | 
|       startingpispips.Remove( pispip ); | 
|       traverse( pispip.GetLeafPISPIPs(), Elements, leafpispip ) | 
|       { | 
|         startingpispips.Add( leafpispip ); | 
|       } | 
|     } | 
|      | 
|     startingpispips := startingpispips.Unique(); | 
|      | 
|     if ( not runcontext.IsMetaIteration() )  | 
|     { | 
|       totalnumberofpispips := counter( this,  | 
|                                        MacroPlan.LeafProductInStockingPoint.ProductInStockingPointInPeriodPlanning,  | 
|                                        pispip,  | 
|                                        not pispip.IsPeriodFrozen() );   | 
|        | 
|        | 
|       // If the user has selected more than X% of the PISPIPs, it is faster to create all trips upfront  | 
|       // Otherwise, the trips will be created as they are needed in the algorithm.SetOptimizerInputSmartPlan method | 
|       if( totalnumberofpispips > 0 | 
|           and ( ( startingpispips.Elements( relsize ) / totalnumberofpispips ) > this.SmartPlanPISPIPThreshold() ) ) | 
|       { | 
|         updatedtrips := construct( Trips ); | 
|         // Create all possible combi of trips as optimizer input | 
|         this.CreateTrips( lanelegsforoptimization, periods, updatedtrips ); | 
|          | 
|         // Without the Transaction.Propagate() the trips will not have the required relations for the SetOptimizerInput methods | 
|         Transaction::Transaction().Propagate( relation( Trip, PeriodTaskLaneLeg ) ); | 
|         Transaction::Transaction().Propagate( relation( ProductInTrip, DependentDemand ) ); | 
|         Transaction::Transaction().Propagate( relation( ProductInTrip, NewSupply ) ); | 
|       } | 
|     } | 
|      | 
|     // Get the routings related to the starting PISPIPs that are viable for the optimizer run. | 
|     if( runcontext.IsOnlyPlanOneStepUpstream() ) | 
|     { | 
|       this.SetPISPIPRoutings( startingpispips, guard( process.astype( Operation ), null( Operation ) ), scope, runcontext ); | 
|       this.SetPISPIPLaneLegs( startingpispips, guard( process.astype( LaneLeg ), null( LaneLeg ) ), scope, runcontext ); | 
|     } | 
|      | 
|     // Select all pispips, operations, trips, etc based on the starting pispips | 
|     traverse( startingpispips,  | 
|               Elements.astype( ProductInStockingPointInPeriodPlanningLeaf ),  | 
|               pispip, | 
|               pispip.IsPlanning()       | 
|               and pispip.IsLeafPlanning() | 
|               and not pispip.IsPeriodFrozen() | 
|               and not pispip.Period_MP().IsHistorical() ) | 
|     { | 
|       this.SetOptimizerInputSmartPlan( pispip,  | 
|                                        isusingselectedunits,  | 
|                                        units,  | 
|                                        0,                        // depth | 
|                                        scope,  | 
|                                        runcontext );   | 
|     } | 
|      | 
|     this.AddAllSidePISPIPsToScope( scope, runcontext ); // now make sure to add the side pispips | 
|      | 
|     if ( not skipgapfillfinalrelations )  | 
|     { | 
|       // We need to make sure we optimize over a continuous horizon | 
|       // Therefore we need to add the missing pispips | 
|       // Adding these pispips and their upstream pispips could introduce new gaps in the horizon | 
|       // So we have to iterate until we have no more gaps | 
|       // The 100 is used as an upperbound to prevent an infinite loop (in practice we should terminate after a couple iterations) | 
|        | 
|       this.SmartPlanGapFill( isusingselectedunits, units, scope, runcontext );  | 
|        | 
|       this.SmartPlanSetFinalRunRelations( isupstream, scope, runcontext ); | 
|        | 
|       this.SmartPlanGapFill( isusingselectedunits, units, scope, runcontext ); // need to gapfill one more time because previous method can create gaps again | 
|     } | 
|   *] | 
|   InterfaceProperties { Accessibility: 'Module' } | 
| } |