yanweiyuan3
2023-08-09 588bc7829387dfc761cc25f06f77d4c81818bd10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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' }
}