Skip to content

Commit 3dd0e54

Browse files
committed
Improve statistic handling
- Add monitors to all basic model elements - Add name to monitors - Adapt more samples to make use of monitoring - Bug fixes
1 parent 3bf93c0 commit 3dd0e54

17 files changed

+318
-87
lines changed

Samples/BankRenege.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,20 @@ public void Simulate(int rseed = 41) {
6464
// Setup and start the simulation
6565
var start = new DateTime(2014, 2, 1);
6666
// Create an environment and start the setup process
67-
var env = new Simulation(start, 41);
67+
var env = new Simulation(start, rseed, defaultStep: TimeSpan.FromMinutes(1));
6868
env.Log("== Bank renege ==");
69-
var counter = new Resource(env, capacity: 1);
69+
var counter = new Resource(env, capacity: 1) {
70+
BreakOffTime = new SampleMonitor(name: "BreakOffTime", collect: true),
71+
Utilization = new TimeSeriesMonitor(env, name: "Utilization"),
72+
WaitingTime = new SampleMonitor(name: "Waiting time", collect: true),
73+
QueueLength = new TimeSeriesMonitor(env, name: "Queue Length", collect: true),
74+
};
7075
env.Process(Source(env, counter));
7176
env.Run();
77+
env.Log(counter.BreakOffTime.Summarize());
78+
env.Log(counter.Utilization.Summarize());
79+
env.Log(counter.WaitingTime.Summarize());
80+
env.Log(counter.QueueLength.Summarize());
7281
}
7382
}
7483
}

Samples/GasStationRefueling.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,13 @@ private IEnumerable<Event> Car(string name, Simulation env, Resource gasStation,
7070
if (litersRequired > fuelPump.Level) {
7171
var level = fuelPump.Level;
7272
yield return fuelPump.Get(level); // draw it empty
73+
yield return env.Timeout(TimeSpan.FromSeconds(level / RefuelingSpeed));
7374
yield return fuelPump.Get(litersRequired - level); // wait for the rest
75+
yield return env.Timeout(TimeSpan.FromSeconds((litersRequired - level) / RefuelingSpeed));
7476
} else {
7577
yield return fuelPump.Get(litersRequired);
78+
yield return env.Timeout(TimeSpan.FromSeconds(litersRequired / RefuelingSpeed));
7679
}
77-
78-
// The "actual" refueling process takes some time
79-
yield return env.Timeout(TimeSpan.FromSeconds(litersRequired / RefuelingSpeed));
80-
8180
env.Log("{0} finished refueling in {1} seconds.", name, (env.Now - start).TotalSeconds);
8281
}
8382
}
@@ -101,9 +100,8 @@ private IEnumerable<Event> TankTruck(Simulation env, Container fuelPump) {
101100
yield return env.Timeout(TankTruckTime);
102101
env.Log("Tank truck arriving at time {0}", env.Now);
103102
var amount = fuelPump.Capacity - fuelPump.Level;
104-
env.Log("Tank truck refuelling {0} liters.", amount);
105103
yield return fuelPump.Put(amount);
106-
env.Log("Tank truck finished at time {0}.", env.Now);
104+
env.Log("Tank truck finished refuelling {0} liters at time {1}.", amount, env.Now);
107105
}
108106

109107
private IEnumerable<Event> CarGenerator(Simulation env, Resource gasStation, Container fuelPump) {
@@ -121,13 +119,23 @@ public void Simulate(int rseed = RandomSeed) {
121119
// Create environment and start processes
122120
var env = new Simulation(DateTime.Now.Date, rseed);
123121
env.Log("== Gas Station refuelling ==");
124-
var gasStation = new Resource(env, 2);
125-
var fuelPump = new Container(env, GasStationSize, GasStationSize);
122+
var gasStation = new Resource(env, 2) {
123+
QueueLength = new TimeSeriesMonitor(env, name: "Waiting cars", collect: true),
124+
WaitingTime = new SampleMonitor(name: "Waiting time", collect: true),
125+
Utilization = new TimeSeriesMonitor(env, name: "Station utilization"),
126+
};
127+
var fuelPump = new Container(env, GasStationSize, GasStationSize) {
128+
Fillrate = new TimeSeriesMonitor(env, name: "Tank fill rate")
129+
};
126130
env.Process(GasStationControl(env, fuelPump));
127131
env.Process(CarGenerator(env, gasStation, fuelPump));
128132

129133
// Execute!
130134
env.Run(SimTime);
135+
env.Log(gasStation.QueueLength.Summarize());
136+
env.Log(gasStation.WaitingTime.Summarize());
137+
env.Log(gasStation.Utilization.Summarize());
138+
env.Log(fuelPump.Fillrate.Summarize());
131139
}
132140
}
133141
}

Samples/KanbanControl.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,15 @@ public void Simulate() {
5959
env.Log("== Kanban controlled production system ==");
6060
kanban = new Resource(env, capacity: 15);
6161
server = new Resource(env, capacity: 1);
62-
stockStat = new TimeSeriesMonitor(env);
62+
// In this sample stockStat is tracked manually in the process
63+
// it would also possible to track kanban's utilization and obtain
64+
// the stock as capacity * (1 - util.mean)
65+
stockStat = new TimeSeriesMonitor(env, name: "Kanbans in stock", collect: true);
6366
env.Process(Source());
6467
env.Run(TimeSpan.FromDays(180));
65-
Console.WriteLine("Stock: {0} ; {1:F3}±{2:F3} ; {3} (Min;Mean±StdDev;Max) kanbans ", stockStat.Min, stockStat.Mean, stockStat.StdDev, stockStat.Max);
68+
Console.WriteLine("Kanbans in stock: {0} ; {1:F1}±{2:F1} ; {3} (Min;Mean±StdDev;Max) kanbans ", stockStat.Min, stockStat.Mean, stockStat.StdDev, stockStat.Max);
6669
Console.WriteLine("Produced kanbans: {0:N0}", completedOrders);
70+
Console.WriteLine(stockStat.Summarize(histInterval: 1));
6771
}
6872
}
6973
}

Samples/MM1Queueing.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ public void Simulate() {
4747
var analyticWaitingtime = rho / (mu - lambda);
4848

4949
var env = new Simulation(randomSeed: 1, defaultStep: TimeSpan.FromMinutes(1));
50-
var utilization = new TimeSeriesMonitor(env);
51-
var wip = new TimeSeriesMonitor(env, collect: true);
52-
var leadtime = new SampleMonitor(collect: true);
53-
var waitingtime = new SampleMonitor(collect: true);
50+
var utilization = new TimeSeriesMonitor(env, name: "Utilization");
51+
var wip = new TimeSeriesMonitor(env, name: "WIP", collect: true);
52+
var leadtime = new SampleMonitor(name: "Lead time", collect: true);
53+
var waitingtime = new SampleMonitor(name: "Waiting time", collect: true);
5454

5555
env.Log("Analytical results of this system:");
5656
env.Log("\tUtilization.Mean\tWIP.Mean\tLeadtime.Mean\tWaitingTime.Mean");
@@ -75,6 +75,7 @@ public void Simulate() {
7575
.SetOutput(env.Logger)
7676
.SetSeparator("\t")
7777
.SetFinalUpdate(withHeaders: false) // creates a summary of the means at the end
78+
.SetTimeAPI(useDApi: true)
7879
.Build();
7980

8081
env.Log("Simulated results of this system:");
@@ -101,10 +102,10 @@ public void Simulate() {
101102
env.Log("");
102103
env.Log("Detailed results from the last run:");
103104
env.Log("");
104-
env.Log(utilization.Print("Utilization"));
105-
env.Log(wip.Print("WIP"));
106-
env.Log(leadtime.Print("Lead time"));
107-
env.Log(waitingtime.Print("Waiting time"));
105+
env.Log(utilization.Summarize());
106+
env.Log(wip.Summarize(maxBins: 10, histInterval: 2));
107+
env.Log(leadtime.Summarize(maxBins: 10, histInterval: 5));
108+
env.Log(waitingtime.Summarize(maxBins: 10, histInterval: 4)); ;
108109
}
109110
}
110111
}

Samples/RunAllSamples.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,22 @@ namespace SimSharp.Samples {
2222
class RunAllSamples {
2323
public static void Main(string[] args) {
2424
// Run all samples one after another
25-
//new BankRenege().Simulate();
26-
//Console.WriteLine();
27-
//new GasStationRefueling().Simulate();
28-
//Console.WriteLine();
29-
//new MachineShop().Simulate();
30-
//Console.WriteLine();
31-
//new ProcessCommunication().Simulate();
32-
//Console.WriteLine();
33-
//new SteelFactory().Simulate();
34-
//Console.WriteLine();
35-
//new MachineShopSpecialist().Simulate();
36-
//Console.WriteLine();
37-
//new SimpleShop().Simulate();
38-
//Console.WriteLine();
39-
//new KanbanControl().Simulate();
40-
//Console.WriteLine();
25+
new BankRenege().Simulate();
26+
Console.WriteLine();
27+
new GasStationRefueling().Simulate();
28+
Console.WriteLine();
29+
new MachineShop().Simulate();
30+
Console.WriteLine();
31+
new ProcessCommunication().Simulate();
32+
Console.WriteLine();
33+
new SteelFactory().Simulate();
34+
Console.WriteLine();
35+
new MachineShopSpecialist().Simulate();
36+
Console.WriteLine();
37+
new SimpleShop().Simulate();
38+
Console.WriteLine();
39+
new KanbanControl().Simulate();
40+
Console.WriteLine();
4141
new MM1Queueing().Simulate();
4242
}
4343
}

SimSharp/Analysis/IMonitor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public interface IMonitor {
3131

3232
double GetMedian();
3333
double GetPercentile(double p);
34+
35+
string Summarize();
3436

3537
event EventHandler Updated;
3638
}

SimSharp/Analysis/SampleMonitor.cs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public sealed class SampleMonitor : ISampleMonitor {
3737
/// </summary>
3838
public bool Collect { get; }
3939

40+
/// <summary>
41+
/// The name of the variable that is being monitored.
42+
/// Used for output in <see cref="Summarize(bool, int, double?, double?)"/>.
43+
/// </summary>
44+
public string Name { get; set; }
45+
4046
public int Count { get; private set; }
4147

4248
public double Min { get; private set; }
@@ -97,7 +103,8 @@ private static double GetPercentile(IList<double> s, double p) {
97103
return s.OrderBy(x => x).Skip(k - 1).Take(2).Average();
98104
}
99105

100-
public SampleMonitor(bool collect = false) {
106+
public SampleMonitor(string name = null, bool collect = false) {
107+
Name = name;
101108
Collect = collect;
102109
if (collect) samples = new List<double>(64);
103110
}
@@ -137,16 +144,45 @@ private void OnUpdated() {
137144
Updated?.Invoke(this, EventArgs.Empty);
138145
}
139146

140-
public string Print(string name = null, double? histMin = null, double? histInterval = null) {
147+
string IMonitor.Summarize() {
148+
return Summarize();
149+
}
150+
151+
/// <summary>
152+
/// Provides a summary of the statistics in a certain format.
153+
/// If the monitor is configured to collect data, it may also print a histogram.
154+
/// </summary>
155+
/// <param name="withHistogram">Whether to suppress the histogram.
156+
/// This is only effective if <see cref="Collect"/> was set to true, otherwise
157+
/// the data to produce the histogram is not available in the first place.</param>
158+
/// <param name="maxBins">The maximum number of bins that should be used.
159+
/// Note that the bin width and thus the number of bins is also governed by
160+
/// <paramref name="histInterval"/> if it is defined.
161+
/// This is only effective if <see cref="Collect"/> and <paramref name="withHistogram"/>
162+
/// was set to true, otherwise the data to produce the histogram is not available
163+
/// in the first place.</param>
164+
/// <param name="histMin">The minimum for the histogram to start at or the sample
165+
/// minimum in case the default (null) is given.
166+
/// This is only effective if <see cref="Collect"/> and <paramref name="withHistogram"/>
167+
/// was set to true, otherwise the data to produce the histogram is not available
168+
/// in the first place.</param>
169+
/// <param name="histInterval">The interval for the bins of the histogram or the
170+
/// range (<see cref="Max"/> - <see cref="Min"/>) divided by the number of bins
171+
/// (<paramref name="maxBins"/>) in case the default value (null) is given.
172+
/// This is only effective if <see cref="Collect"/> and <paramref name="withHistogram"/>
173+
/// was set to true, otherwise the data to produce the histogram is not available
174+
/// in the first place.</param>
175+
/// <returns>A formatted string that provides a summary of the statistics.</returns>
176+
public string Summarize(bool withHistogram = true, int maxBins = 20, double? histMin = null, double? histInterval = null) {
141177
var nozero = Collect ? samples.Where(x => x != 0).ToList() : new List<double>();
142178
var nozeromin = nozero.Count > 0 ? nozero.Min() : double.NaN;
143179
var nozeromax = nozero.Count > 0 ? nozero.Max() : double.NaN;
144180
var nozeromean = nozero.Count > 1 ? nozero.Average() : double.NaN;
145181
var nozerostdev = nozero.Count > 2 ? Math.Sqrt(nozero.Sum(x => (x - nozeromean) * (x - nozeromean)) / (nozero.Count - 1.0)) : double.NaN;
146182
var sb = new StringBuilder();
147183
sb.Append("Statistics");
148-
if (!string.IsNullOrEmpty(name))
149-
sb.Append(" of " + name);
184+
if (!string.IsNullOrEmpty(Name))
185+
sb.Append(" of " + Name);
150186
sb.AppendLine();
151187
sb.AppendLine(" all excl.zero zero ");
152188
sb.AppendLine("--------------- --------------- --------------- ---------------");
@@ -162,19 +198,20 @@ public string Print(string name = null, double? histMin = null, double? histInte
162198
}
163199
sb.AppendLine(string.Format("{0,15} {1,15} {2,15}", "Maximum", Formatter.Format15(Max), Formatter.Format15(nozeromax)));
164200

165-
if (Collect) {
201+
if (Collect && withHistogram) {
166202
var min = histMin ?? Min;
167-
var interval = histInterval ?? (Max - Min) / 20.0;
168-
var histData = samples.GroupBy(x => (int)Math.Floor(Math.Max(x - min + interval, 0) / interval))
169-
.ToDictionary(x => x.Key, x => x.Count());
203+
var interval = histInterval ?? (Max - Min) / maxBins;
204+
var histData = samples.GroupBy(x => x <= min ? 0 : (int)Math.Floor(Math.Min((x - min + interval) / interval, maxBins)))
205+
.Select(x => new { Key = x.Key, Value = x.Count() })
206+
.OrderBy(x => x.Key);
170207
sb.AppendLine();
171208
sb.AppendLine("Histogram");
172209
sb.AppendLine("<= count % cum% ");
173210
sb.AppendLine("--------------- ---------- ----- ------");
174211
var cumul = 0.0;
175212
var totStars = 0;
176213
var last = -1;
177-
foreach (var kvp in histData.OrderBy(x => x.Key)) {
214+
foreach (var kvp in histData) {
178215
while (kvp.Key > last + 1) {
179216
last++;
180217
var tmp = "|".PadLeft(totStars + 1);
@@ -189,7 +226,9 @@ public string Print(string name = null, double? histMin = null, double? histInte
189226
var stars = string.Join("", Enumerable.Repeat("*", numstars));
190227
totStars += numstars;
191228
var cumulbar = "|".PadLeft(totStars + 1 - numstars);
192-
sb.AppendLine(string.Format("{0,15} {1,10} {2,5:F1} {3,5:F1} {4}{5}", Formatter.Format15(min + kvp.Key * interval), kvp.Value, prob * 100, cumul * 100, stars, cumulbar));
229+
sb.AppendLine(string.Format("{0,15} {1,10} {2,5:F1} {3,5:F1} {4}{5}",
230+
(kvp.Key == maxBins && min + kvp.Key * interval < Max) ? "inf" : Formatter.Format15(min + kvp.Key * interval),
231+
kvp.Value, prob * 100, cumul * 100, stars, cumulbar));
193232
last = kvp.Key;
194233
}
195234
}

0 commit comments

Comments
 (0)