haorenhui
2023-10-30 6d6cc10d9e8e242661da7fd655dec155a09d676c
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
Quintiq file version 2.0
#parent: #root
Method Search (
  ProductInStockingPointInPeriodPlanningLeaf pispip
) as Boolean
{
  Description: 'Search procedure to determine safety stock level of a given pispip'
  TextBody:
  [*
    period := pispip.MEIO_PeriodNumber();  
    node := pispip.ProductInStockingPoint_MP(); 
    pisp_sl := pispip.GetServiceLevelPercentage() / 100
    capped_margin := minvalue( this.MEIO_Parameters().InitialGuessMarigin(), pisp_sl );
    guess := pisp_sl - capped_margin;
    target := 0.0; 
    
    // Make an initial guess of target level based on the inverse of the cumulative distribution of the demand at the given PISPIP
    if( pispip.SalesDemandQuantity() = 0 )
    {
      if ( this.MEIO_Parameters().OptionMakeInitialGuessForNoSalesDemand() ) 
      {
        leafset := selectset( MEIO_Engine::GetTreePISP( node ), Elements, n, true, n.MEIO_IsLeaf() );   
        avgdemand := average( leafset, Elements, leaf, true, leaf.GetPISPIPFromPeriodNumber( period ).MEIO_DemandExpectedValue() ); 
        avgstd := average( leafset, Elements, leaf, true, leaf.GetPISPIPFromPeriodNumber( period ).MEIO_DemandStandardDeviation() );  
        avgdistr := MEIO_Engine::GetRandomDistribution( avgdemand, avgstd ); 
        target := maxvalue( avgdistr.Quantile( guess ) - 
                              avgdemand, 
                              0 ); 
      }
    }
    else
    {
      target := maxvalue( MEIO_Engine::GetDemandDistribution( node, period ).Quantile( guess ) - 
                            MEIO_Engine::GetDemandExpectedValue( node, period ), 
                            0 ); 
    }
    
    service_level := 0.0;
    iter := 1;
    continue := pispip.ProductInStockingPoint_MP().Echelon()>1
                and (pispip.DependentDemandAndSalesDemandQuantity() > 0 or not pispip.ProductInStockingPoint_MP().IsMostDownstreamPisp() );
    std := 0.0; 
    reachservicelevel := false; 
    if( continue )
    {
      std := this.AggregateStd( pispip.ProductInStockingPoint_MP(), pispip.MEIO_PeriodNumber() ); //get the standard deviation of the sales demand
    }
    smallesttarget_formaxservicelevel := target; // keep track of this and fall back on it if we do not manage to reach the required service level
    maxservicelevelsofar := 0.0; 
    
    while( continue ) // While stopping condition is not reached, continue to predict service level for increasing targets 
    {
      this.CallForServiceLevel( node, period, target, service_level, iter );
    
      // Create snapshot
      snapshot := null( MEIO_Snapshot ); 
      if( this.MEIO_Parameters().CreateSnapshots() )
      {
        snapshot := MEIO_Snapshot::Create( this, 
                                           iter,
                                           std, 
                                           target, 
                                           service_level, 
                                           pispip.ProductInStockingPoint_MP().Name(), 
                                           pispip.MEIO_PeriodNumber(), 
                                           MEIO_Snapshot::SEARCH(), 
                                           pisp_sl, 
                                           this.SupplyChain(), 
                                           -1.0, // max service level so far - supplied below
                                           -1.0 );  // min target for max service level -supplied below 
                                        
        }
      
      reachservicelevel := service_level >= pisp_sl; 
      continue := not reachservicelevel
                  and iter < this.MEIO_Parameters().NrOfIterations();
                  
      if ( MEIO_Engine::RoundToImprovementTolerance( service_level ) > maxservicelevelsofar ) 
      {
        smallesttarget_formaxservicelevel := target; 
        maxservicelevelsofar := MEIO_Engine::RoundToImprovementTolerance( service_level ); 
      }
      if ( this.MEIO_Parameters().CreateSnapshots() ) 
      {
        snapshot.MaxRoundedServiceLevelSoFar( maxservicelevelsofar ); 
        snapshot.MinTargetForMaxRoundedServiceLevel( smallesttarget_formaxservicelevel ); 
      }               
      iter := iter + 1;
      
      // Increase target for next step
      target := target + this.GetTargetIncrement( service_level, std, pispip );
    } // while loop  
    
    if ( not reachservicelevel ) // did not manage to reach the service level, pick smallest target reaching max level 
    {
      pispip.MEIO_Target( smallesttarget_formaxservicelevel ); 
      pispip.MEIO_Stockin( maxservicelevelsofar ); 
    
      if( this.MEIO_Parameters().CreateSnapshots() )
      {
        MEIO_Snapshot::Create( this, 
                               iter,
                               std, 
                               smallesttarget_formaxservicelevel, 
                               maxservicelevelsofar, 
                               pispip.ProductInStockingPoint_MP().Name(), 
                               pispip.MEIO_PeriodNumber(), 
                               MEIO_Snapshot::SEARCH() + MEIO_Engine::ServiceLevelNotReached(), 
                               pisp_sl, 
                               this.SupplyChain(), 
                               maxservicelevelsofar, 
                               smallesttarget_formaxservicelevel );  
      }
    }
    
    return true;
  *]
}