Skip to content

Commit

Permalink
Release 3.2 (heal-research#17)
Browse files Browse the repository at this point in the history
* Push version to 3.2
* Change license to MIT and restructure repository
* Add appveyor.yml file
  • Loading branch information
abeham authored Sep 1, 2019
1 parent bab14b1 commit af2d930
Show file tree
Hide file tree
Showing 80 changed files with 307 additions and 733 deletions.
31 changes: 31 additions & 0 deletions LICENSE-3RD-PARTY.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
PCGSharp requires to take note of the following copyrights and permissions:
// MIT License
//
// Copyright (c) 2016 Bismur Studios Ltd.
// Copyright (c) 2016 Ioannis Giagkiozis
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.

Daniel "BlueRaja" Pflughoeft requires to take not of the following copyrights and permissions:
/*
The MIT License (MIT)

Copyright (c) 2013 Daniel "BlueRaja" Pflughoeft

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
*/
663 changes: 11 additions & 652 deletions LICENSE.txt

Large diffs are not rendered by default.

30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# Sim# (SimSharp)

A .NET port of SimPy, process-based discrete event simulation framework
A .NET port and extension of SimPy, process-based discrete event simulation framework

[![Build status](https://ci.appveyor.com/api/projects/status/hyn83qegeiga81o2/branch/master?svg=true)](https://ci.appveyor.com/project/abeham/simsharp/branch/master)

---

*Disclaimer: Sim# is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Sim# is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL) as published by the Free Software Foundation, either version 3 of the license, or (at your option) any later version.*
*Disclaimer: Sim# is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Sim# is free software: Sim# can be redistributed and/or modified under the terms of the MIT License.*

---


Sim# aims to port the concepts used in SimPy [1] to the .NET world. Sim# is implemented in C# and is available via Nuget for .NET Framework 4.5 and is also .NET Standard 2.0 compliant. Sim# uses an efficient event queue (adapted from [3]) that allows to compute models very quickly. Simulating 10 years of the MachineShop sample [4], that uses preemptive resources, requires about 1.5s on a Core i7-7 2.7Ghz. This model generates more than 5 million events.
Sim# aims to port the concepts used in SimPy [1] to the .NET world. Sim# is implemented in C# and is available via Nuget for .NET Framework 4.5 and is also .NET Standard 2.0 compliant. Sim# uses an efficient event queue (adapted from [3]) that allows to compute models very quickly. Simulating 10 years of the MachineShop sample [4], that uses preemptive resources, requires less than 1.5s on a Core i7-7 2.7Ghz. This model generates more than 5 million events.

SimPy allows to model processes easily and with little boiler plate code. A process is described as a method that yields events. When an event is yielded, the process waits on it. Processes are themselves events and so it is convenient to spawn sub-processes that can either be waited upon or that run next to each other. There is no need to inherit from classes or understand a complex object oriented
design.
Sim# allows modeling processes easily and with little boiler plate code. A process is described as a method that yields events. When an event is yielded, the process waits on it. Processes are themselves events and so it is convenient to spawn sub-processes that can either be waited upon or that run next to each other. There is no need to inherit from classes or understand a complex object oriented design.

To demonstrate how simple models can be expressed with little code, consider a model of an m/m/1 queuing system as expressed in Sim#:

Expand All @@ -23,8 +22,7 @@ TimeSpan ARRIVAL_TIME = TimeSpan.FromSeconds(...);
TimeSpan PROCESSING_TIME = TimeSpan.FromSeconds(...);
TimeSpan SIMULATION_TIME = TimeSpan.FromHours(...);

IEnumerable<Event> MM1Q(Simulation env) {
var server = new Resource(env, capacity: 1);
IEnumerable<Event> MM1Q(Simulation env, Resource server) {
while (true) {
yield return env.TimeoutExponential(ARRIVAL_TIME);
env.Process(Item(env, server));
Expand All @@ -41,30 +39,36 @@ IEnumerable<Event> Item(Simulation env, Resource server) {

void RunSimulation() {
var env = new Simulation(randomSeed: 42);
env.Process(MM1Q(env));
var server = new Resource(env, capacity: 1) {
QueueLength = new TimeSeriesMonitor(env, collect: true)
}
env.Process(MM1Q(env, server));
env.Run(SIMULATION_TIME);
Console.WriteLine(server.QueueLength.Summarize());
}
```

This model uses the monitoring capabilities introduced with Sim# 3.2. Monitoring allows describing certain variables in the model, e.g. the number of waiting items before the server. The monitor maintains a set of statistical properties such as mean, standard deviation, and can also print histograms. Monitors can be assigned to the variables exposed by resources or they can be used to track other variables.

Sim# tries to be as easy to use as SimPy, but also remains true to the .NET Framework. The most obvious difference between SimPy and Sim# is handling process interruptions. In Sim# a process that can be interrupted needs to call

```csharp
if (Environment.ActiveProcess.HandleFault()) {...}
```
```

after each yield in which an interruption can occur and before continuing to yield further events. This is due to a limitation of the .Net Framework: In Python it is possible to put a try-except block around a yield statement, and an exception can be injected into the iterator. In .Net this is not possible.
after each yield in which an interruption can occur and before continuing to yield further events. This is due to a limitation of the .NET Framework: In Python it is possible to put a try-except block around a yield statement, and an exception can be injected into the iterator. In .NET this is not possible.

Also in Sim# it was decided to base the unit for current time and delays on `DateTime` and `TimeSpan` in the simulation clock. There is however an API, called D-API (short for double-API) that allows you to use doubles as in SimPy, e.g. `env.Now` returns a `DateTime`, `env.NowD` returns a `double`, `env.Timeout(delay`) expects a `TimeSpan` as delay, `env.TimeoutD(delay)` expects a `double`, etc.. It is possible to initialize the Environment with a default timestep in case both APIs are used:

```csharp
var env = new Simulation(defaultStep: TimeSpan.FromMinutes(1));
```
```

In that environment, calling `env.TimeoutD(1)` would be equal to calling the more elaborate normal API `env.Timeout(TimeSpan.FromMinutes(1))`.
In that environment, calling `env.TimeoutD(1)` would be equal to calling the more elaborate standard API `env.Timeout(TimeSpan.FromMinutes(1))`.

## References

1. [Python Simpy Package](https://pypi.python.org/pypi/simpy)
2. [Nuget package](https://www.nuget.org/packages/SimSharp/)
3. [High speed priority queue](https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp)
4. [Machine Shop Example](http://simpy.readthedocs.org/en/latest/examples/machine_shop.html)
4. [Machine Shop Example](src/Samples/MachineShop.cs)
22 changes: 22 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: '{build}'
image: Visual Studio 2017
build_script:
- pwsh: >-
pushd src
dotnet restore
dotnet build --configuration Release
popd
Exit $LASTEXITCODE
test_script:
- pwsh: >-
pushd src
dotnet test Tests --configuration Release
popd
Exit $LASTEXITCODE
150 changes: 150 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Sim# Documentation

The documentation covers the following aspects:

[TOC]

## Introduction

Sim# is available as a [nuget package](https://www.nuget.org/packages/SimSharp/). To use Sim# in your code simply add this package to your .NET project using Visual Studio or the dotnet command line tool:

```
dotnet add package SimSharp
```

It is available for .NET Framework 4.5 and .NET Standard 2.0 and thus can be used with almost any .NET Framework version published since 2012.

## Modeling

Sim# is a _process-based_ discrete event simulation framework. Models are defined by creating *processes* that interact with each other or with shared *resources*. Both processes and resources reference a common *environment* which also contains the simulation time, random number generators, and the event queue.

### Processes

A process is defined by a more or less complex sequence of *events* and is implemented in form of a method. At any time only one process may be active. In this time the process can do calculations and return a next event or finish eventually. A process that returns an event becomes passive and is activated again when the event is processed or when it is interrupted.

The following simple snippet defines a process that outputs the simulation time whenever it is active.

```c#
static IEnumerable<Event> AProcess(Simulation env) {
env.Log("The time is {0}", env.NowD);
yield return env.TimeoutD(3.0);
env.Log("The time is {0}", env.NowD);
yield return env.TimeoutD(3.0);
env.Log("The time is {0}", env.NowD);
}

static void Main(string[] args) {
var env = new Simulation();
env.Process(AProcess(env));
env.Run();
}
// outputs:
//The time is 0
//The time is 3
//The time is 6
```

### Resources

Resources in a model describe shared items that processes may compete for or which are used to achieve communication between processes. The standard resources provided by Sim# can be distinguished into three categories:

* *Spectrum*: Discrete or Continuous
* *Mixture*: Homogeneous or Heterogeneous
* *Contract*: Lease or Consume

Discrete resources usually consider a finite number of entities which are represented by a discrete number or instances of an object. On the other hand, continuous resources usually just capture the total quantity in a continuous number.

Continuous resources implicitly assume a single homogeneous entity of varying size. However, discrete resources may either be homogeneous or heterogeneous. In the later case the entities have further, potentially unique properties, while for a homogeneous resource all entities are exactly alike and only their number is of interest.

Finally, the contract category describes whether a certain amount or quantity of the resource is leased and has to be returned or whether it is consumed. For instance, a worker will usually be modelled as being leased, while a warehouse may be a resource that allows stocking and consuming items.

The following resources are implemented:

* **Resource, PriorityResource, PreemptivePriorityResource** - Discrete, Homogeneous, Lease
* **ResourcePool** - Discrete, Heterogeneous, Lease
* **Store, FilterStore, PriorityStore** - Discrete, Heterogeneous, Consume
* **Container** - Continuous, Homogeneous, Consume

A *Resource* contains a discrete number of homogeneous entities that can be requested and which have to be released back to the resource eventually. It employs a FIFO queue to arrange requests. There exist variants in form of a *PriorityResource* and a *PreemptivePriorityResource* to which requests with a certain priority may be made in both cases. Preemptive resources may additionally retract a processes' lease prematurely. The process that holds the lease is interrupted and has to handle the preemption or fault otherwise.

The *ResourcePool* is similar to the above Resource, but consists of identifiable entities. This type is not part of SimPy and has been introduced in Sim\#. This may be useful, for instance when modeling a pool of employees with their individual characteristics, e.g., qualifications. An entity from the resource pool may only be borrowed for some time and has to be returned. Requests to a ResourcePool may specify a filter to define the properties of the individual to be requested (e.g. some qualification).

A *Store* contains a discrete set of heterogeneous items that can be added to and removed from. Stores may have a maximum capacity. A so called *FilterStore* exists to retrieve items that fulfill some criteria. Store and ResourcePool are very similar, however in a Store the item does not need to return (consume instead of lease). A *PriorityStore* exists in which items have some form of priority and they are consumed in priority order, while still each put and get operation is executed in FIFO order.

A *Container* contains a continuous amount of some substance. Again, the substance may be stocked in the container and consumed.

In any case, it is simple to extend the standard resources given that the code is open source. The classes are of moderate complexity, e.g. the *Resource* class is described in about 200 lines of code.

### Putting it together

Processes that interact with common resources may create highly dynamic behavior which may not be analytically tractable and thus has to be simulated.

It is best to look at the many samples that have been provided along with the Sim# sources to see both simple and more complex models. Here is a short summary:

* [BankRenege](../src/Samples/BankRenege.cs)

This sample uses a single shared resource (bank teller) that customers queue for. The model shows how quitting a queue prematurely is possible, for instance because the customer has run out of patience. It also tracks and prints several statistics.

* [GasStationRefueling](../src/Samples/GasStationRefueling.cs)

This sample uses both a discrete (fuel pump) and a continuous (tank) resource. Cars are queuing at the fuel pump and consume gasoline from the tank. When the tank runs below a certain threshold a truck is dispatched to refill it. This shows how *When*-events of resources can be used. These events have been introduced with Sim# 3.1 (they are ported from the [desmod](https://desmod.readthedocs.io/en/latest/) package - an extension of SimPy).

* [KanbanControl](../src/Samples/KanbanControl.cs)

This samples shows how a simple production system that uses kanban can be modeled. It shows how to use monitors to track a variable of interest (the number of kanbans in stock over time).

* [MachineShop](../src/Samples/MachineShop.cs) and [MachineShopSpecialist](../src/Samples/MachineShopSpecialist.cs)

In this model a production system with machine break-downs is described. It shows how to interrupt processes, e.g. in the case of a break-down the current job is suspended. The -Specialist model additionally shows how to use the ResourcePool to model repairman with different characteristics.

* [MM1Queueing](../src/Samples/MM1Queueing.cs)

The "Hello World" of simulation models. It shows how to perform repetitions and track statistical properties. It also prints the analytical properties of this system - which are known in this case.

* [ProcessCommunication](../src/Samples/ProcessCommunication.cs)

This model describes a simple producer-consumer situation. It shows how processes may interact with each other using a Store resource.

* [SimpleShop](../src/Samples/SimpleShop.cs) and [SteelFactory](../src/Samples/SteelFactory.cs)

These model describe a two-step production. The first step may be blocked by the second. The models should show how one process may obtain a resource, but another processes releases that resource.

## Monitoring

Monitoring is new with Sim# 3.2. Instead of following the SimPy approach which is difficult to translate to .NET. The implementation in Sim# is more akin to [Salabim](https://www.salabim.org).

There are two different kinds of monitors:

1. *SampleMonitor* - is used when the distribution of a variable is characterized by independent samples. For instance, the lead time of a process is such a variable. For each process there is an individual lead time which together form some kind of distribution.
2. *TimeSeriesMonitor* - is used when the variable is a time series, that is, when a variable may change its state over time. For instance, the utilization of a resource may change over time and a weighted average with respect to the duration of each level is required.

Each resource class defines certain variables and the respective type of monitor for that variable. By default no monitoring is tacking place, but such variables may be assigned a monitor and subsequently this will be tracked. In the following snippet a server is created where its *utilization* and the *waiting time* in the queue are tracked:

```c#
var env = new Simulation();
var server = new Resource(env, capacity: 5) {
Utilization = new TimeSeriesMonitor(env),
WaitingTime = new SampleMonitor()
};
```

Monitors may be created with ```collect: true```. This means the monitor will keep the datapoints and thus can compute median, percentiles, and print a histogram of the data. By default collect is false, which still allows computing min, max, mean, standard deviation, count, and sum in a memory efficient way.

### Reports

A report may be defined that summarizes many monitors and prints the selected statistical properties in one line. A report may be directed to a different file and may be performed only at the end, for every update to the statistic or periodically. This enables a quick way to follow some basic statistics in the execution of a model.

Examples of how to use reports are present in the [MM1Queueing](../src/Samples/MM1Queueing.cs) sample:

```c#
var report = Report.CreateBuilder(env)
.Add("Utilization", utilization, Report.Measures.Mean | Report.Measures.StdDev)
.Add("WIP", wip, Report.Measures.Min | Report.Measures.Mean | Report.Measures.Max)
.Add("Leadtime", leadtime, Report.Measures.Min | Report.Measures.Mean | Report.Measures.Max)
.Add("WaitingTime", waitingtime, Report.Measures.Min | Report.Measures.Mean | Report.Measures.Max)
.SetOutput(env.Logger) // use a "new StreamWriter("report.csv")" to direct to a file
.SetSeparator("\t")
.SetPeriodicUpdate(TimeSpan.FromDays(7), withHeaders: true)
.Build();
```

File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions Benchmark/Benchmark.csproj → src/Benchmark/Benchmark.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Authors>Andreas Beham</Authors>
<Version>3.1.1</Version>
<Version>3.2</Version>
<Company>HEAL, FH Upper Austria</Company>
<Product>Sim# (Benchmarks)</Product>
<Description />
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion Samples/Samples.csproj → src/Samples/Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<Version>3.1.1</Version>
<Version>3.2</Version>
<Authors>Andreas Beham</Authors>
<Company>HEAL, FH Upper Austria</Company>
<Product>Sim# (Samples)</Product>
Expand Down
File renamed without changes.
File renamed without changes.
6 changes: 4 additions & 2 deletions SimSharp.sln → src/SimSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5BC56A42-5229-4380-BF91-3A2EB70332C3}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
LICENSE.txt = LICENSE.txt
README.md = README.md
..\appveyor.yml = ..\appveyor.yml
..\LICENSE-3RD-PARTY.txt = ..\LICENSE-3RD-PARTY.txt
..\LICENSE.txt = ..\LICENSE.txt
..\README.md = ..\README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{40F8848F-605E-4CAA-9512-B2812476CC9A}"
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit af2d930

Please sign in to comment.