lazhen
2025-01-09 8afe90b633046db39042aada36b88193062f8cff
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
Quintiq file version 2.0
#parent: #root
Method NextS (
  Number periodNumber,
  ProductInStockingPoint_MP pisp
) as owning ProbabilityDistribution
{
  TextBody:
  [*
    // calculates the starting stock distrbution of given period, at given stockingpoint
    // if leadtime to stockingpoint is 2 periods
    // calculation starts at period - 2
    // start by assuming that at period - 2 there is target left
    // the loop through periods up to period-1, and at every period
    // add expected demand and change in target, substract demand distribution and
    // take maximum with 0, to not allow negative inventory
    
    dsum := MEIO_Engine::GetZeroDistribution();
    // if period - stockinpoint.leadtime is after start of planning horizon, we have target set a target that we can use
    // based on the simulations, mp hardly ever reached the correct target, and interal service level tries to describe this
    
    if(  periodNumber > pisp.MEIO_Leadtime() ) 
    {
      startminleadpispip := pisp.GetPISPIPFromPeriodNumber( periodNumber - pisp.MEIO_Leadtime() );
      dsum := MEIO_Engine::GetDiracDistribution( MEIO_Engine::GetInternalSeviceLevel() * startminleadpispip.MEIO_Target() ); 
    }
    //# in other cases we start with the target of the first period that we have set earlier (or manually in mp)
    else
    {
      pispip := pisp.FirstPlanningPISPIP(); 
      dsum := MEIO_Engine::GetDiracDistribution( pispip.TargetInQuantity() );
    }
    // we only really calculate anything if leaditme is >0, if leadimte =0 we dont need safetystock at all
    if ( pisp.MEIO_Leadtime() > 0)
    {
      // we collect all the children of the stockingpoint (we will aggregate the demand from these)    
      listofnodes := MEIO_Engine::GetTreePISP( pisp ); //Traverse(stockingpoint, c('level'))
      //#start period is leadtime ago or first period, 
      startperiod := maxvalue( periodNumber - pisp.MEIO_Leadtime(), 1 ); //(meio_R_internal_period-pisp.MEIO_Leadtime(), 1)
      //endperiod is previous period or start period
      endperiod := maxvalue(startperiod, periodNumber-1)
      
      //if startperiod is endperiod we would only add the resupply of that and substract the demand
      //in other cases we loop through all startperiod -> endperiod
     
      // we will add the expected demand and substract the demand distribution
      traverse( listofnodes, Elements, nodepisp )
      {
        standarddev := MEIO_Engine::GetDemandStandardDeviation( nodepisp, startperiod );  
        dsum := ProbabilityDistribution::Sum( dsum,  NormalDistribution::Construct( 0.0, standarddev ), this.MEIO_Parameters().SampleSizeForDistributions() ); 
      } 
      //# if there are other periods we do the same as for startperiod
      for ( p:= startperiod + 1; p<= endperiod;  p++ )
      {
        supply := 0.0; 
        
        // if period is before leadtime of the stockingpoint, we cannot control the supply anymore and thus we use the values from mp
        if ( p <= pisp.MEIO_Leadtime() )  
        {
          supply := maxvalue( pisp.GetPISPIPFromPeriodNumber( p ).NewSupplyQuantity(), 
                              MEIO_Engine::GetDemandExpectedValue(pisp, p ) ) //); 
        }
        //# in other cases we use as a supply the change in targets + expected demand
        else
        {
          supply := maxvalue( MEIO_Engine::GetDemandExpectedValue( pisp, p ) 
                              + pisp.GetPISPIPFromPeriodNumber( p ).MEIO_Target()
                              - pisp.GetPISPIPFromPeriodNumber( p - 1 ).MEIO_Target(), 
                              0 ); 
        }
        // here we add the supply and substract the demand distribution
        
        minusdemanddistr := MEIO_Engine::GetDemandDistributionTimesMinusOne( pisp, p ); // workaround because ProbabilityDistribution::Subtract gives verify errors
        dsum := ProbabilityDistribution::Sum( dsum, minusdemanddistr, this.MEIO_Parameters().SampleSizeForDistributions() ); 
        dsum := ProbabilityDistribution::Sum( dsum, DiscreteDistribution::Construct( supply ), this.MEIO_Parameters().SampleSizeForDistributions() ); 
        // for the child nodes we do not add change in targets as supply, only the expected demands
        if(p > pisp.MEIO_Leadtime() ) 
        {
          if(listofnodes.Size() > 1 ) 
          {
            traverse( listofnodes, Elements, node, not node = pisp ) 
            {
              nodeminusdemanddistr := MEIO_Engine::GetDemandDistributionTimesMinusOne( node, p )
              dsum := ProbabilityDistribution::Sum( dsum, nodeminusdemanddistr, this.MEIO_Parameters().SampleSizeForDistributions() ); 
              dsum := ProbabilityDistribution::Sum( dsum, DiscreteDistribution::Construct( MEIO_Engine::GetDemandExpectedValue( node, p ) ), this.MEIO_Parameters().SampleSizeForDistributions() );  
            } // traverse
          } // if 
        } // if 
      } // for loop
    } // if 
    
    // we don't allow negative inventory
    dsum := ProbabilityDistribution::Max( dsum, MEIO_Engine::GetZeroDistribution(), this.MEIO_Parameters().SampleSizeForDistributions() ); 
    
    return &dsum;
  *]
}