├── go.mod ├── .DS_Store ├── examples ├── .DS_Store ├── example1 │ ├── example1 │ ├── .DS_Store │ └── example1.go ├── example0 │ ├── .DS_Store │ └── example0.go ├── example3 │ └── example3.go ├── example5 │ └── example5.go ├── example4 │ └── example4.go ├── example6 │ └── example6.go ├── example2 │ └── example2.go └── example7 │ └── example7.go ├── LICENSE ├── controls.go ├── queue.go ├── randgen.go ├── runner.go ├── util.go ├── model.go └── README.md /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/agoussia/godes 2 | 3 | go 1.22.4 4 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agoussia/godes/HEAD/.DS_Store -------------------------------------------------------------------------------- /examples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agoussia/godes/HEAD/examples/.DS_Store -------------------------------------------------------------------------------- /examples/example1/example1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agoussia/godes/HEAD/examples/example1/example1 -------------------------------------------------------------------------------- /examples/example0/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agoussia/godes/HEAD/examples/example0/.DS_Store -------------------------------------------------------------------------------- /examples/example1/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agoussia/godes/HEAD/examples/example1/.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alex Goussiatiner agoussia@yahoo.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/example0/example0.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/agoussia/godes" 11 | ) 12 | 13 | // the arrival and service are two random number generators for the uniform distribution 14 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 15 | 16 | // the Visitor is a Runner 17 | // any type of the Runner should be defined as struct 18 | // with the *godes.Runner as anonimous field 19 | type Visitor struct { 20 | *godes.Runner 21 | number int 22 | } 23 | 24 | var visitorsCount int = 0 25 | 26 | func (vst *Visitor) Run() { // Any runner should have the Run method 27 | fmt.Printf(" %-6.3f \t Visitor # %v arrives \n", godes.GetSystemTime(), vst.number) 28 | } 29 | func main() { 30 | var shutdown_time float64 = 8 * 60 31 | godes.Run() 32 | for { 33 | //godes.Stime is the current simulation time 34 | if godes.GetSystemTime() < shutdown_time { 35 | //the function acivates the Runner 36 | godes.AddRunner(&Visitor{&godes.Runner{}, visitorsCount}) 37 | //this advance the system time 38 | godes.Advance(arrival.Get(0, 70)) 39 | visitorsCount++ 40 | } else { 41 | break 42 | } 43 | } 44 | // waits for all the runners to finish the Run() 45 | godes.WaitUntilDone() 46 | } 47 | 48 | /* OUTPUT: 49 | 0.000 Visitor # 0 arrives 50 | 37.486 Visitor # 1 arrives 51 | 98.737 Visitor # 2 arrives 52 | 107.468 Visitor # 3 arrives 53 | 149.471 Visitor # 4 arrives 54 | 207.523 Visitor # 5 arrives 55 | 230.922 Visitor # 6 arrives 56 | 261.770 Visitor # 7 arrives 57 | 269.668 Visitor # 8 arrives 58 | 310.261 Visitor # 9 arrives 59 | 338.323 Visitor # 10 arrives 60 | 397.720 Visitor # 11 arrives 61 | 409.123 Visitor # 12 arrives 62 | 436.817 Visitor # 13 arrives 63 | 447.731 Visitor # 14 arrives 64 | */ 65 | -------------------------------------------------------------------------------- /examples/example3/example3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/agoussia/godes" 10 | ) 11 | 12 | // the arrival and service are two random number generators for the uniform distribution 13 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 14 | var service *godes.UniformDistr = godes.NewUniformDistr(true) 15 | 16 | // true when waiter should act 17 | var waitersSwt *godes.BooleanControl = godes.NewBooleanControl() 18 | 19 | // FIFO Queue for the arrived 20 | var visitorArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("0") 21 | 22 | // the Visitor is a Passive Object 23 | type Visitor struct { 24 | id int 25 | } 26 | 27 | // the Waiter is a Runner 28 | type Waiter struct { 29 | *godes.Runner 30 | id int 31 | } 32 | 33 | var visitorsCount int = 0 34 | var shutdown_time float64 = 4 * 60 35 | 36 | func (waiter *Waiter) Run() { 37 | for { 38 | waitersSwt.Wait(true) 39 | if visitorArrivalQueue.Len() > 0 { 40 | visitorArrivalQueue.Get() 41 | if visitorArrivalQueue.Len() == 0 { 42 | waitersSwt.Set(false) 43 | } 44 | godes.Advance(service.Get(10, 60)) //advance the simulation time by the visitor service time 45 | 46 | } 47 | if godes.GetSystemTime() > shutdown_time && visitorArrivalQueue.Len() == 0 { 48 | break 49 | } 50 | 51 | } 52 | } 53 | 54 | func main() { 55 | for runs := 0; runs < 5; runs++ { 56 | for i := 0; i < 2; i++ { 57 | godes.AddRunner(&Waiter{&godes.Runner{}, i}) 58 | } 59 | godes.Run() 60 | for { 61 | visitorArrivalQueue.Place(Visitor{visitorsCount}) 62 | waitersSwt.Set(true) 63 | godes.Advance(arrival.Get(0, 30)) 64 | visitorsCount++ 65 | if godes.GetSystemTime() > shutdown_time { 66 | break 67 | } 68 | } 69 | waitersSwt.Set(true) 70 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 71 | fmt.Printf(" Run # %v \t Average Time in Queue=%6.3f \n", runs, visitorArrivalQueue.GetAverageTime()) 72 | //clear after each run 73 | waitersSwt.Clear() 74 | visitorArrivalQueue.Clear() 75 | godes.Clear() 76 | 77 | } 78 | } 79 | 80 | /* OUTPUT 81 | 82 | Run # 0 Average Time in Queue=15.016 83 | Run # 1 Average Time in Queue=17.741 84 | Run # 2 Average Time in Queue=49.046 85 | Run # 3 Average Time in Queue=30.696 86 | Run # 4 Average Time in Queue=14.777 87 | */ 88 | -------------------------------------------------------------------------------- /controls.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Godes is the general-purpose simulation library 6 | // which includes the simulation engine and building blocks 7 | // for modeling a wide variety of systems at varying levels of details. 8 | // 9 | // Godes Main Features: 10 | // 11 | //1.Active Objects: 12 | //All active objects in Godes shall implement the RunnerInterface 13 | // 14 | //2.Random Generators: 15 | //Godes contains set of built-in functions for generating random numbers for commonly used probability distributions. 16 | //Each of the distrubutions in Godes has one or more parameter values associated with it:Uniform (Min, Max), Normal (Mean and Standard Deviation), Exponential (Lambda), Triangular(Min, Mode, Max) 17 | // 18 | //3.Queues: 19 | //Godes implements operations with FIFO and LIFO queues 20 | // 21 | //4.BooleanControl : 22 | //Godes uses BooleanControl variables as a locks for 23 | //syncronizing execution of multiple runners 24 | // 25 | //5.StatCollector: 26 | //The object calculates and prints statistical parameters for set of samples collected during the simulation. 27 | // 28 | //See examples for usage. 29 | package godes 30 | 31 | 32 | // BooleanControl is a boolean control variable 33 | type BooleanControl struct { 34 | state bool 35 | } 36 | 37 | // NewBooleanControl constructs a BooleanControl 38 | func NewBooleanControl() *BooleanControl { 39 | return &BooleanControl{state: false} 40 | } 41 | 42 | //Wait stops the runner untill the BooleanControll bc is set to true 43 | func (bc *BooleanControl) Wait(b bool) { 44 | if bc.state == b { 45 | //do nothing 46 | } else { 47 | modl.booleanControlWait(bc, b) 48 | } 49 | } 50 | 51 | //Wait stops the runner untill the BooleanControll bc is set to true or timeout 52 | func (bc *BooleanControl) WaitAndTimeout(b bool, timeOut float64) { 53 | if bc.state == b { 54 | //do nothing 55 | } else { 56 | modl.booleanControlWaitAndTimeout(bc, b, timeOut) 57 | } 58 | } 59 | 60 | // Set changes the value of bc 61 | func (bc *BooleanControl) Set(b bool) { 62 | 63 | if bc.state == b { 64 | //do nothing 65 | } else { 66 | bc.state = b 67 | } 68 | } 69 | 70 | // getState returns value of bc 71 | func (bc *BooleanControl) GetState() bool { 72 | return bc.state 73 | } 74 | 75 | // Clear sets the bc value to default false 76 | func (bc *BooleanControl) Clear() { 77 | bc.state = false 78 | } 79 | -------------------------------------------------------------------------------- /examples/example5/example5.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/agoussia/godes" 10 | ) 11 | 12 | const NEW_CUSTOMERS = 5 // Total number of customers 13 | const INTERVAL_CUSTOMERS = 12.00 // Generate new customers roughly every x minites 14 | const SERVICE_TIME = 12.0 15 | const MIN_PATIENCE = 1 // Min. customer patience 16 | const MAX_PATIENCE = 3 // Max. customer patience 17 | 18 | // random generator for the arrival interval - expovariate distribution 19 | var arrivalGen *godes.ExpDistr = godes.NewExpDistr(true) 20 | 21 | // random generator for the patience time time - uniform distribution 22 | var patienceGen *godes.UniformDistr = godes.NewUniformDistr(true) 23 | 24 | // random generator for the service time - expovariate distribution 25 | var serviceGen *godes.ExpDistr = godes.NewExpDistr(true) 26 | 27 | // true when Counter 28 | var counterAvailable *godes.BooleanControl = godes.NewBooleanControl() 29 | 30 | type Customer struct { 31 | *godes.Runner 32 | name int 33 | } 34 | 35 | func (customer *Customer) Run() { 36 | 37 | arrivalTime := godes.GetSystemTime() 38 | patience := patienceGen.Get(MIN_PATIENCE, MAX_PATIENCE) 39 | fmt.Printf(" %6.3f Customer %v : Here I am My patience=%6.3f \n", godes.GetSystemTime(), customer.name, patience) 40 | 41 | counterAvailable.WaitAndTimeout(true, patience) 42 | if !counterAvailable.GetState() { 43 | fmt.Printf(" %6.3f Customer %v : Reneged after %6.3f \n", godes.GetSystemTime(), customer.name, godes.GetSystemTime()-arrivalTime) 44 | } else { 45 | counterAvailable.Set(false) 46 | 47 | fmt.Printf(" %6.3f Customer %v : Waited %6.3f \n", godes.GetSystemTime(), customer.name, godes.GetSystemTime()-arrivalTime) 48 | godes.Advance(serviceGen.Get(1 / SERVICE_TIME)) 49 | fmt.Printf(" %6.3f Customer %v : Finished \n", godes.GetSystemTime(), customer.name) 50 | counterAvailable.Set(true) 51 | } 52 | 53 | } 54 | 55 | func main() { 56 | counterAvailable.Set(true) 57 | godes.Run() 58 | for i := 0; i < NEW_CUSTOMERS; i++ { 59 | godes.AddRunner(&Customer{&godes.Runner{}, i}) 60 | godes.Advance(arrivalGen.Get(1 / INTERVAL_CUSTOMERS)) 61 | } 62 | 63 | godes.WaitUntilDone() 64 | 65 | } 66 | 67 | /* OUTPUT 68 | 0.000 Customer 0 : Here I am My patience= 1.135 69 | 0.000 Customer 0 : Waited 0.000 70 | 5.536 Customer 0 : Finished 71 | 11.141 Customer 1 : Here I am My patience= 1.523 72 | 11.141 Customer 1 : Waited 0.000 73 | 34.482 Customer 2 : Here I am My patience= 2.523 74 | 36.123 Customer 1 : Finished 75 | 36.123 Customer 2 : Waited 1.641 76 | 38.869 Customer 2 : Finished 77 | 39.943 Customer 3 : Here I am My patience= 1.592 78 | 39.943 Customer 3 : Waited 0.000 79 | 49.113 Customer 4 : Here I am My patience= 1.224 80 | 50.337 Customer 4 : Reneged after 1.224 81 | 59.948 Customer 3 : Finished 82 | */ 83 | -------------------------------------------------------------------------------- /examples/example4/example4.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/agoussia/godes" 10 | ) 11 | 12 | const PT_MEAN = 10.0 // Avg. processing time in minutes 13 | const PT_SIGMA = 2.0 // Sigma of processing time 14 | const MTTF = 300.0 // Mean time to failure in minutes 15 | const REPAIR_TIME = 30.0 // Time it takes to repair a machine in minutes 16 | const REPAIR_TIME_SIGMA = 1.0 // Sigma of repair time 17 | 18 | const NUM_MACHINES = 10 19 | const SHUT_DOWN_TIME = 4 * 7 * 24 * 60 20 | 21 | // random generator for the processing time - normal distribution 22 | var processingGen *godes.NormalDistr = godes.NewNormalDistr(true) 23 | 24 | // random generator for the time until the next failure for a machine - exponential distribution 25 | var breaksGen *godes.ExpDistr = godes.NewExpDistr(true) 26 | 27 | // true when repairman is available for carrying a repair 28 | var repairManAvailableSwt *godes.BooleanControl = godes.NewBooleanControl() 29 | 30 | type Machine struct { 31 | *godes.Runner 32 | partsCount int 33 | number int 34 | finished bool 35 | } 36 | 37 | func (machine *Machine) Run() { 38 | for { 39 | godes.Advance(processingGen.Get(PT_MEAN, PT_SIGMA)) 40 | machine.partsCount++ 41 | if godes.GetSystemTime() > SHUT_DOWN_TIME { 42 | machine.finished = true 43 | break 44 | } 45 | 46 | } 47 | fmt.Printf(" Machine # %v %v \n", machine.number, machine.partsCount) 48 | } 49 | 50 | type MachineRepair struct { 51 | *godes.Runner 52 | machine *Machine 53 | } 54 | 55 | func (machineRepair *MachineRepair) Run() { 56 | machine := machineRepair.machine 57 | for { 58 | godes.Advance(breaksGen.Get(1 / MTTF)) 59 | if machine.finished { 60 | break 61 | } 62 | 63 | interrupted := godes.GetSystemTime() 64 | godes.Interrupt(machine) 65 | repairManAvailableSwt.Wait(true) 66 | if machine.finished { 67 | break 68 | } 69 | repairManAvailableSwt.Set(false) 70 | godes.Advance(processingGen.Get(REPAIR_TIME, REPAIR_TIME_SIGMA)) 71 | if machine.finished { 72 | break 73 | } 74 | //release repairman 75 | repairManAvailableSwt.Set(true) 76 | //resume machine and change the scheduled time to compensate delay 77 | godes.Resume(machine, godes.GetSystemTime()-interrupted) 78 | 79 | } 80 | 81 | } 82 | 83 | func main() { 84 | 85 | godes.Run() 86 | repairManAvailableSwt.Set(true) 87 | var m *Machine 88 | for i := 0; i < NUM_MACHINES; i++ { 89 | m = &Machine{&godes.Runner{}, 0, i, false} 90 | godes.AddRunner(m) 91 | godes.AddRunner(&MachineRepair{&godes.Runner{}, m}) 92 | } 93 | godes.WaitUntilDone() 94 | } 95 | 96 | /* OUTPUT 97 | Machine # 4 3599 98 | Machine # 5 3625 99 | Machine # 7 3580 100 | Machine # 2 3724 101 | Machine # 1 3554 102 | Machine # 9 3581 103 | Machine # 0 3585 104 | Machine # 6 3604 105 | Machine # 3 3639 106 | Machine # 8 3625 107 | */ 108 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Godes is the general-purpose simulation library 6 | // which includes the simulation engine and building blocks 7 | // for modeling a wide variety of systems at varying levels of details. 8 | // 9 | 10 | package godes 11 | 12 | import ( 13 | "container/list" 14 | ) 15 | 16 | // Queue represents a FIFO or LIFO queue 17 | type Queue struct { 18 | id string 19 | fifo bool 20 | sumTime float64 21 | count int64 22 | qList *list.List 23 | qTime *list.List 24 | startTime float64 25 | } 26 | 27 | // FIFOQueue represents a FIFO queue 28 | type FIFOQueue struct { 29 | Queue 30 | } 31 | 32 | // LIFOQueue represents a LIFO queue 33 | type LIFOQueue struct { 34 | Queue 35 | } 36 | 37 | // GetAverageTime is average elapsed time for an object in the queue 38 | func (q *Queue) GetAverageTime() float64 { 39 | return q.sumTime / float64(q.count) 40 | } 41 | 42 | // Len returns number of objects in the queue 43 | func (q *Queue) Len() int { 44 | return q.qList.Len() 45 | } 46 | 47 | // GetAverageTime is average elapsed time for an object in the queue 48 | func (q *Queue) GetAverageNumber() float64 { 49 | return q.sumTime / (stime - q.startTime) 50 | } 51 | 52 | // Place adds an object to the queue 53 | func (q *Queue) Place(entity interface{}) { 54 | q.qList.PushFront(entity) 55 | q.qTime.PushFront(stime) 56 | if q.startTime == 0 { 57 | q.startTime = stime 58 | } 59 | } 60 | 61 | // Get returns an object and removes it from the queue 62 | func (q *Queue) Get() interface{} { 63 | 64 | var entity interface{} 65 | var timeIn float64 66 | if q.fifo { 67 | entity = q.qList.Back().Value 68 | timeIn = q.qTime.Back().Value.(float64) 69 | q.qList.Remove(q.qList.Back()) 70 | q.qTime.Remove(q.qTime.Back()) 71 | } else { 72 | entity = q.qList.Front().Value 73 | timeIn = q.qTime.Front().Value.(float64) 74 | q.qList.Remove(q.qList.Front()) 75 | q.qTime.Remove(q.qTime.Front()) 76 | } 77 | 78 | q.sumTime = q.sumTime + stime - timeIn 79 | q.count++ 80 | 81 | return entity 82 | } 83 | 84 | // GetHead returns the head object (doesn't remove it from the queue) 85 | func (q *Queue) GetHead() interface{} { 86 | var entity interface{} 87 | if q.fifo { 88 | entity = q.qList.Back().Value 89 | } else { 90 | entity = q.qList.Front().Value 91 | } 92 | return entity 93 | } 94 | 95 | // NewFIFOQueue itializes the FIFO queue 96 | func NewFIFOQueue(mid string) *FIFOQueue { 97 | return &FIFOQueue{Queue{fifo: true, id: mid, qList: list.New(), qTime: list.New()}} 98 | } 99 | 100 | // NewLIFOQueue itializes the LIFO queue 101 | func NewLIFOQueue(mid string) *LIFOQueue { 102 | return &LIFOQueue{Queue{fifo: false, id: mid, qList: list.New(), qTime: list.New()}} 103 | } 104 | 105 | // Clear reinitiates the queue 106 | func (q *Queue) Clear() { 107 | q.sumTime = 0 108 | q.count = 0 109 | q.qList.Init() 110 | q.qTime.Init() 111 | } 112 | 113 | // NEW: Get List 114 | func (q *Queue) GetSlice() []any { 115 | slice := []any{} 116 | l := q.qList 117 | for e := l.Front(); e != nil; e = e.Next() { 118 | slice = append(slice, e.Value) 119 | } 120 | return slice 121 | } 122 | -------------------------------------------------------------------------------- /examples/example6/example6.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | /* 7 | Procces Description: 8 | ==================== 9 | A bank employs three tellers and the customers form a queue for all three tellers. 10 | The doors of the bank close after eight hours. 11 | The simulation is ended when the last customer has been served. 12 | */ 13 | 14 | import ( 15 | "fmt" 16 | 17 | "github.com/agoussia/godes" 18 | ) 19 | 20 | // Input Parameters 21 | const ( 22 | ARRIVAL_INTERVAL = 0.5 23 | SERVICE_TIME = 1.3 24 | SHUTDOWN_TIME = 8 * 60. 25 | ) 26 | 27 | // the arrival and service are two random number generators for the exponential distribution 28 | var arrival *godes.ExpDistr = godes.NewExpDistr(true) 29 | var service *godes.ExpDistr = godes.NewExpDistr(true) 30 | 31 | // true when any counter is available 32 | var counterSwt *godes.BooleanControl = godes.NewBooleanControl() 33 | 34 | // FIFO Queue for the arrived customers 35 | var customerArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("0") 36 | 37 | var tellers *Tellers 38 | var measures [][]float64 39 | var titles = []string{ 40 | "Elapsed Time", 41 | "Queue Length", 42 | "Queueing Time", 43 | "Service Time", 44 | } 45 | 46 | var availableTellers int = 0 47 | 48 | // the Tellers is a Passive Object represebting resource 49 | type Tellers struct { 50 | max int 51 | } 52 | 53 | func (tellers *Tellers) Catch(customer *Customer) { 54 | for { 55 | counterSwt.Wait(true) 56 | if customerArrivalQueue.GetHead().(*Customer).id == customer.id { 57 | break 58 | } else { 59 | godes.Yield() 60 | } 61 | } 62 | availableTellers++ 63 | if availableTellers == tellers.max { 64 | counterSwt.Set(false) 65 | } 66 | } 67 | 68 | func (tellers *Tellers) Release() { 69 | availableTellers-- 70 | counterSwt.Set(true) 71 | } 72 | 73 | // the Customer is a Runner 74 | type Customer struct { 75 | *godes.Runner 76 | id int 77 | } 78 | 79 | func (customer *Customer) Run() { 80 | a0 := godes.GetSystemTime() 81 | tellers.Catch(customer) 82 | a1 := godes.GetSystemTime() 83 | customerArrivalQueue.Get() 84 | qlength := float64(customerArrivalQueue.Len()) 85 | godes.Advance(service.Get(1. / SERVICE_TIME)) 86 | a2 := godes.GetSystemTime() 87 | tellers.Release() 88 | collectionArray := []float64{a2 - a0, qlength, a1 - a0, a2 - a1} 89 | measures = append(measures, collectionArray) 90 | } 91 | func main() { 92 | measures = [][]float64{} 93 | tellers = &Tellers{3} 94 | godes.Run() 95 | counterSwt.Set(true) 96 | count := 0 97 | for { 98 | customer := &Customer{&godes.Runner{}, count} 99 | customerArrivalQueue.Place(customer) 100 | godes.AddRunner(customer) 101 | godes.Advance(arrival.Get(1. / ARRIVAL_INTERVAL)) 102 | if godes.GetSystemTime() > SHUTDOWN_TIME { 103 | break 104 | } 105 | count++ 106 | } 107 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 108 | collector := godes.NewStatCollector(titles, measures) 109 | collector.PrintStat() 110 | fmt.Printf("Finished \n") 111 | } 112 | 113 | /* OUTPUT 114 | Variable # Average Std Dev L-Bound U-Bound Minimum Maximum 115 | Elapsed Time 944 2.591 1.959 2.466 2.716 0.005 11.189 116 | Queue Length 944 2.411 3.069 2.215 2.607 0.000 13.000 117 | Queueing Time 944 1.293 1.533 1.195 1.391 0.000 6.994 118 | Service Time 944 1.298 1.247 1.219 1.378 0.003 7.824 119 | */ 120 | -------------------------------------------------------------------------------- /examples/example1/example1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/agoussia/godes" 10 | ) 11 | 12 | // the arrival and service are two random number generators for the uniform distribution 13 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 14 | var service *godes.UniformDistr = godes.NewUniformDistr(true) 15 | 16 | // tableBusy is the boolean control variable than can be accessed and changed by number of Runners 17 | var tableBusy *godes.BooleanControl = godes.NewBooleanControl() 18 | 19 | // the Visitor is a Runner 20 | // any type of the Runner should be defined as struct // with the *godes.Runner as anonimous field 21 | type Visitor struct { 22 | *godes.Runner 23 | number int 24 | } 25 | 26 | var visitorsCount int = 0 27 | 28 | func (vst Visitor) Run() { // Any runner should have the Run method 29 | fmt.Printf("%-6.3f \t Visitor %v arrives \n", godes.GetSystemTime(), vst.number) 30 | tableBusy.Wait(false) // this will wait till the tableBusy control becomes false 31 | tableBusy.Set(true) // sets the tableBusy control to true - the table is busy 32 | fmt.Printf("%-6.3f \t Visitor %v gets the table \n", godes.GetSystemTime(), vst.number) 33 | godes.Advance(service.Get(10, 60)) //the function advance the simulation time by the value in the argument 34 | tableBusy.Set(false) // sets the tableBusy control to false - the table is idle 35 | fmt.Printf("%-6.3f \t Visitor %v leaves \n", godes.GetSystemTime(), vst.number) 36 | } 37 | func main() { 38 | var shutdown_time float64 = 8 * 60 39 | godes.Run() 40 | for { 41 | //godes.GetSystemTime() is the current simulation time 42 | if godes.GetSystemTime() < shutdown_time { 43 | //the function acivates the Runner 44 | godes.AddRunner(Visitor{&godes.Runner{}, visitorsCount}) 45 | godes.Advance(arrival.Get(0, 70)) 46 | visitorsCount++ 47 | } else { 48 | break 49 | } 50 | } 51 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 52 | } 53 | 54 | /* OUTPUT 55 | 0.000 Visitor 0 arrives 56 | 0.000 Visitor 0 gets the table 57 | 13.374 Visitor 0 leaves 58 | 37.486 Visitor 1 arrives 59 | 37.486 Visitor 1 gets the table 60 | 60.558 Visitor 1 leaves 61 | 98.737 Visitor 2 arrives 62 | 98.737 Visitor 2 gets the table 63 | 107.468 Visitor 3 arrives 64 | 146.824 Visitor 2 leaves 65 | 146.824 Visitor 3 gets the table 66 | 149.471 Visitor 4 arrives 67 | 171.623 Visitor 3 leaves 68 | 171.623 Visitor 4 gets the table 69 | 187.234 Visitor 4 leaves 70 | 207.523 Visitor 5 arrives 71 | 207.523 Visitor 5 gets the table 72 | 230.922 Visitor 6 arrives 73 | 245.859 Visitor 5 leaves 74 | 245.859 Visitor 6 gets the table 75 | 261.770 Visitor 7 arrives 76 | 269.668 Visitor 8 arrives 77 | 272.368 Visitor 6 leaves 78 | 272.368 Visitor 7 gets the table 79 | 290.484 Visitor 7 leaves 80 | 290.484 Visitor 8 gets the table 81 | 310.261 Visitor 9 arrives 82 | 333.570 Visitor 8 leaves 83 | 333.570 Visitor 9 gets the table 84 | 338.323 Visitor 10 arrives 85 | 354.874 Visitor 9 leaves 86 | 354.874 Visitor 10 gets the table 87 | 393.826 Visitor 10 leaves 88 | 397.720 Visitor 11 arrives 89 | 397.720 Visitor 11 gets the table 90 | 409.123 Visitor 12 arrives 91 | 436.817 Visitor 13 arrives 92 | 447.731 Visitor 14 arrives 93 | 455.705 Visitor 11 leaves 94 | 455.705 Visitor 13 gets the table 95 | 482.955 Visitor 13 leaves 96 | 482.955 Visitor 12 gets the table 97 | 496.034 Visitor 12 leaves 98 | 496.034 Visitor 14 gets the table 99 | 555.822 Visitor 14 leaves 100 | */ 101 | -------------------------------------------------------------------------------- /randgen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Godes is the general-purpose simulation library 6 | // which includes the simulation engine and building blocks 7 | // for modeling a wide variety of systems at varying levels of details. 8 | // 9 | // Godes contains set of built-in functions for generating random numbers 10 | // for commonly used probability distributions (see examples for the usage). 11 | // Each of the distrubutions in Godes has one or more parameter values associated with it: 12 | // Uniform: Min, Max 13 | // Normal: Mean and Standard Deviation 14 | // Exponential: Lambda 15 | // Triangular: Min, Mode, Max 16 | 17 | package godes 18 | 19 | import ( 20 | "math" 21 | "math/rand" 22 | //"fmt" 23 | ) 24 | 25 | var seedCount int64 = 100000 26 | 27 | type distribution struct { 28 | generator *rand.Rand 29 | } 30 | 31 | 32 | //UniformDistr represents the generator for the uniform distribution 33 | type UniformDistr struct { 34 | distribution 35 | } 36 | 37 | //NewUniformDistr initiats the generator for the uniform distribution 38 | func NewUniformDistr(repetion bool) *UniformDistr { 39 | if repetion { 40 | seedCount++ 41 | return &UniformDistr{distribution{rand.New(rand.NewSource(seedCount))}} 42 | } else { 43 | return &UniformDistr{distribution{rand.New(rand.NewSource(GetCurComputerTime()))}} 44 | } 45 | } 46 | 47 | // Get returns new radom value from the uniform distribution generator 48 | func (b *UniformDistr) Get(min float64, max float64) float64 { 49 | return b.generator.Float64()*(max-min) + min 50 | } 51 | 52 | // NormalDistr represents the generator for the normal distribution 53 | type NormalDistr struct { 54 | distribution 55 | } 56 | 57 | // NewNormalDistr initiats the generator for the normal distribution 58 | func NewNormalDistr(repetion bool) *NormalDistr { 59 | if repetion { 60 | seedCount++ 61 | return &NormalDistr{distribution{rand.New(rand.NewSource(seedCount))}} 62 | } else { 63 | return &NormalDistr{distribution{rand.New(rand.NewSource(GetCurComputerTime()))}} 64 | } 65 | } 66 | 67 | // Get returns new radom value from the normal distribution generator 68 | func (b *NormalDistr) Get(mean float64, sigma float64) float64 { 69 | return b.generator.NormFloat64()*sigma + mean 70 | } 71 | 72 | //ExpDistr represents the generator for the exponential distribution 73 | type ExpDistr struct { 74 | distribution 75 | } 76 | 77 | //NewExpDistr initiats the generator for the exponential distribution 78 | // If repetition flag is true, the generator will generate the same sequences for every execution 79 | func NewExpDistr(repetion bool) *ExpDistr { 80 | 81 | if repetion { 82 | seedCount++ 83 | return &ExpDistr{distribution{rand.New(rand.NewSource(seedCount))}} 84 | } else { 85 | return &ExpDistr{distribution{rand.New(rand.NewSource(GetCurComputerTime()))}} 86 | } 87 | 88 | } 89 | 90 | // Get returns new radom value from the exponential distribution generator 91 | func (b *ExpDistr) Get(lambda float64) float64 { 92 | return b.generator.ExpFloat64() / lambda 93 | } 94 | 95 | //TriangularDistr represents the generator for the triangular distribution 96 | type TriangularDistr struct { 97 | distribution 98 | } 99 | 100 | //NewTriangularDistr initiats the generator for the triangular distribution 101 | // If repetition flag is true, the generator will generate the same sequences for every execution 102 | func NewTriangularDistr(repetion bool) *TriangularDistr { 103 | if repetion { 104 | seedCount++ 105 | return &TriangularDistr{distribution{rand.New(rand.NewSource(seedCount))}} 106 | } else { 107 | return &TriangularDistr{distribution{rand.New(rand.NewSource(GetCurComputerTime()))}} 108 | } 109 | } 110 | 111 | // Get returns new radom value from the triangular distribution generator 112 | func (bd *TriangularDistr) Get(a float64, b float64, c float64) float64 { 113 | u := bd.generator.Float64() 114 | f := (c - a) / (b - a) 115 | if u < f { 116 | return a + math.Sqrt(u*(b-a)*(c-a)) 117 | } else { 118 | return b - math.Sqrt((1.-u)*(b-a)*(b-c)) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /examples/example2/example2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/agoussia/godes" 10 | ) 11 | 12 | // the arrival and service are two random number generators for the uniform distribution 13 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 14 | var service *godes.UniformDistr = godes.NewUniformDistr(true) 15 | 16 | // true when waiter should act 17 | var waitersSwt *godes.BooleanControl = godes.NewBooleanControl() 18 | 19 | // FIFO Queue for the arrived 20 | var visitorArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("arrivalQueue") 21 | 22 | // the Visitor is a Passive Object 23 | type Visitor struct { 24 | id int 25 | } 26 | 27 | // the Waiter is a Runner 28 | type Waiter struct { 29 | *godes.Runner 30 | id int 31 | } 32 | 33 | var visitorsCount int = 0 34 | var shutdown_time float64 = 4 * 60 35 | 36 | func (waiter *Waiter) Run() { 37 | 38 | for { 39 | waitersSwt.Wait(true) 40 | if visitorArrivalQueue.Len() > 0 { 41 | visitor := visitorArrivalQueue.Get() 42 | if visitorArrivalQueue.Len() == 0 { 43 | waitersSwt.Set(false) 44 | } 45 | fmt.Printf("%-6.3f \t Visitor %v is invited by waiter %v \n", godes.GetSystemTime(), visitor.(Visitor).id, waiter.id) 46 | godes.Advance(service.Get(10, 60)) //advance the simulation time by the visitor service time 47 | fmt.Printf("%-6.3f \t Visitor %v leaves \n", godes.GetSystemTime(), visitor.(Visitor).id) 48 | 49 | } 50 | if godes.GetSystemTime() > shutdown_time && visitorArrivalQueue.Len() == 0 { 51 | fmt.Printf("%-6.3f \t Waiter %v ends the work \n", godes.GetSystemTime(), waiter.id) 52 | break 53 | } 54 | } 55 | } 56 | 57 | func main() { 58 | 59 | for i := 0; i < 2; i++ { 60 | godes.AddRunner(&Waiter{&godes.Runner{}, i}) 61 | } 62 | godes.Run() 63 | for { 64 | 65 | visitorArrivalQueue.Place(Visitor{visitorsCount}) 66 | fmt.Printf("%-6.3f \t Visitor %v arrives \n", godes.GetSystemTime(), visitorsCount) 67 | waitersSwt.Set(true) 68 | godes.Advance(arrival.Get(0, 30)) 69 | visitorsCount++ 70 | if godes.GetSystemTime() > shutdown_time { 71 | break 72 | } 73 | } 74 | waitersSwt.Set(true) 75 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 76 | fmt.Printf("Average Waiting Time %6.3f \n", visitorArrivalQueue.GetAverageTime()) 77 | } 78 | 79 | /* OUTPUT 80 | 0.000 Visitor 0 arrives 81 | 0.000 Visitor 0 is invited by waiter 0 82 | 13.374 Visitor 0 leaves 83 | 16.066 Visitor 1 arrives 84 | 16.066 Visitor 1 is invited by waiter 1 85 | 39.137 Visitor 1 leaves 86 | 42.316 Visitor 2 arrives 87 | 42.316 Visitor 2 is invited by waiter 1 88 | 46.058 Visitor 3 arrives 89 | 46.058 Visitor 3 is invited by waiter 0 90 | 64.059 Visitor 4 arrives 91 | 70.857 Visitor 3 leaves 92 | 70.857 Visitor 4 is invited by waiter 0 93 | 86.468 Visitor 4 leaves 94 | 88.938 Visitor 5 arrives 95 | 88.938 Visitor 5 is invited by waiter 0 96 | 90.403 Visitor 2 leaves 97 | 98.966 Visitor 6 arrives 98 | 98.966 Visitor 6 is invited by waiter 1 99 | 112.187 Visitor 7 arrives 100 | 115.572 Visitor 8 arrives 101 | 125.475 Visitor 6 leaves 102 | 125.475 Visitor 7 is invited by waiter 1 103 | 127.275 Visitor 5 leaves 104 | 127.275 Visitor 8 is invited by waiter 0 105 | 132.969 Visitor 9 arrives 106 | 143.591 Visitor 7 leaves 107 | 143.591 Visitor 9 is invited by waiter 1 108 | 144.995 Visitor 10 arrives 109 | 164.895 Visitor 9 leaves 110 | 164.895 Visitor 10 is invited by waiter 1 111 | 170.361 Visitor 8 leaves 112 | 170.451 Visitor 11 arrives 113 | 170.451 Visitor 11 is invited by waiter 0 114 | 175.338 Visitor 12 arrives 115 | 187.207 Visitor 13 arrives 116 | 191.885 Visitor 14 arrives 117 | 203.848 Visitor 10 leaves 118 | 203.848 Visitor 12 is invited by waiter 1 119 | 213.596 Visitor 15 arrives 120 | 228.436 Visitor 11 leaves 121 | 228.436 Visitor 13 is invited by waiter 0 122 | 231.098 Visitor 12 leaves 123 | 231.098 Visitor 14 is invited by waiter 1 124 | 231.769 Visitor 16 arrives 125 | 241.515 Visitor 13 leaves 126 | 241.515 Visitor 15 is invited by waiter 0 127 | 287.864 Visitor 15 leaves 128 | 287.864 Visitor 16 is invited by waiter 0 129 | 290.886 Visitor 14 leaves 130 | 290.886 Waiter 1 ends the work 131 | 330.903 Visitor 16 leaves 132 | 330.903 Waiter 0 ends the work 133 | Average Waiting Time 15.016 134 | */ 135 | -------------------------------------------------------------------------------- /runner.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Godes is the general-purpose simulation library 6 | // which includes the simulation engine and building blocks 7 | // for modeling a wide variety of systems at varying levels of details. 8 | // 9 | // All active objects in Godes shall implement the RunnerInterface 10 | // See examples for the usage. 11 | // 12 | 13 | package godes 14 | 15 | import ( 16 | "fmt" 17 | "time" 18 | ) 19 | 20 | type RunnerInterface interface { 21 | Run() 22 | setState(i int) 23 | getState() int 24 | setChannel(c chan int) 25 | getChannel() chan int 26 | setInternalId(id int) 27 | getInternalId() int 28 | setMovingTime(m float64) 29 | getMovingTime() float64 30 | setMarkTime(m time.Time) 31 | getMarkTime() time.Time 32 | setPriority(p int) 33 | getPriority() int 34 | setWaitingForBool(p bool) 35 | getWaitingForBool() bool 36 | setWaitingForBoolControl(p *BooleanControl) 37 | getWaitingForBoolControl() *BooleanControl 38 | setWaitingForBoolControlTimeoutId(id int) 39 | getWaitingForBoolControlTimeoutId() int 40 | } 41 | 42 | type Runner struct { 43 | state int 44 | channel chan int 45 | internalId int 46 | movingTime float64 47 | markTime time.Time 48 | priority int 49 | waitingForBool bool 50 | waitingForBoolControl *BooleanControl 51 | waitingForBoolControlTimeoutId int 52 | //schedulledTime float64 53 | } 54 | 55 | type TimeoutRunner struct { 56 | *Runner 57 | original RunnerInterface 58 | timeoutPeriod float64 59 | } 60 | 61 | func (timeOut *TimeoutRunner) Run() { 62 | Advance(timeOut.timeoutPeriod) 63 | if timeOut.original.getWaitingForBoolControl() != nil && timeOut.original.getWaitingForBoolControlTimeoutId() == timeOut.internalId { 64 | timeOut.original.setState(rUNNER_STATE_READY) 65 | timeOut.original.setWaitingForBoolControl(nil) 66 | modl.addToMovingList(timeOut.original) 67 | delete(modl.waitingConditionMap, timeOut.original.getInternalId()) 68 | } 69 | 70 | } 71 | 72 | func newRunner() *Runner { 73 | return &Runner{} 74 | } 75 | 76 | func (b *Runner) Run() { 77 | fmt.Println("Run Run Run Run") 78 | } 79 | 80 | func (b *Runner) setState(i int) { 81 | b.state = i 82 | } 83 | 84 | func (b *Runner) getState() int { 85 | return b.state 86 | } 87 | 88 | func (b *Runner) setChannel(c chan int) { 89 | b.channel = c 90 | } 91 | 92 | func (b *Runner) getChannel() chan int { 93 | return b.channel 94 | } 95 | 96 | func (b *Runner) setInternalId(i int) { 97 | b.internalId = i 98 | 99 | } 100 | func (b *Runner) getInternalId() int { 101 | return b.internalId 102 | } 103 | 104 | func (b *Runner) setMovingTime(m float64) { 105 | b.movingTime = m 106 | 107 | } 108 | func (b *Runner) getMovingTime() float64 { 109 | return b.movingTime 110 | } 111 | 112 | func (b *Runner) setMarkTime(m time.Time) { 113 | b.markTime = m 114 | 115 | } 116 | func (b *Runner) getMarkTime() time.Time { 117 | return b.markTime 118 | } 119 | 120 | func (b *Runner) setPriority(p int) { 121 | b.priority = p 122 | } 123 | func (b *Runner) getPriority() int { 124 | return b.priority 125 | } 126 | 127 | func (b *Runner) setWaitingForBool(p bool) { 128 | b.waitingForBool = p 129 | 130 | } 131 | 132 | func (b *Runner) getWaitingForBool() bool { 133 | return b.waitingForBool 134 | 135 | } 136 | 137 | func (b *Runner) setWaitingForBoolControl(p *BooleanControl) { 138 | b.waitingForBoolControl = p 139 | 140 | } 141 | 142 | func (b *Runner) getWaitingForBoolControl() *BooleanControl { 143 | return b.waitingForBoolControl 144 | } 145 | 146 | func (b *Runner) setWaitingForBoolControlTimeoutId(p int) { 147 | b.waitingForBoolControlTimeoutId = p 148 | 149 | } 150 | 151 | func (b *Runner) getWaitingForBoolControlTimeoutId() int { 152 | return b.waitingForBoolControlTimeoutId 153 | } 154 | 155 | func (b *Runner) IsShedulled() bool { 156 | if b.state == rUNNER_STATE_SCHEDULED { 157 | return true 158 | } 159 | return false 160 | } 161 | 162 | func (b *Runner) GetMovingTime() float64 { 163 | if b.state == rUNNER_STATE_SCHEDULED { 164 | return b.movingTime 165 | } else { 166 | panic("Runner is Not Shedulled ") 167 | } 168 | } 169 | 170 | func (b *Runner) String() string { 171 | 172 | var st = "" 173 | 174 | switch b.state { 175 | case rUNNER_STATE_READY: 176 | st = "READY" 177 | case rUNNER_STATE_ACTIVE: 178 | st = "ACTIVE" 179 | case rUNNER_STATE_WAITING_COND: 180 | st = "WAITING_COND" 181 | case rUNNER_STATE_SCHEDULED: 182 | st = "SCHEDULED" 183 | case rUNNER_STATE_INTERRUPTED: 184 | st = "INTERRUPTED" 185 | case rUNNER_STATE_TERMINATED: 186 | st = "TERMINATED" 187 | 188 | default: 189 | panic("Unknown state") 190 | } 191 | return fmt.Sprintf(" st=%v ch=%v id=%v mt=%v mk=%v pr=%v", st, b.channel, b.internalId, b.movingTime, b.markTime, b.priority) 192 | } 193 | -------------------------------------------------------------------------------- /examples/example7/example7.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | package main 5 | 6 | /* 7 | Procces Description: 8 | =================== 9 | A bank employs three tellers and the customers form a queue for all three tellers. 10 | The doors of the bank close after eight hours. 11 | The simulation is ended when the last customer has been served. 12 | 13 | Task 14 | ==== 15 | Execute multiple simulation runs, calculate Average, Standard Deviation, 16 | confidence intervall lower and upper bounds,minimum and Maximum for the 17 | following performance measures: 18 | total elapsed time, 19 | queue length, 20 | queueing time 21 | service time. 22 | 23 | Model Features: 24 | =============== 25 | 1. FIFO Queue 26 | The customer object is placed in the FIFO arrival queue as soon as the customer is created. 27 | 28 | 2. Parallel Resources 29 | The application constructs Tellers object to model tellers as a set of resources. 30 | The object 'provides' tellers to the customer located in the Queue head and "releases" the teller when customer is serviced. 31 | Maximum 3 tellers can be provided simultaneously. 32 | The interlocking between catching request is performed using godes BooleanControl object. 33 | 34 | 3. Collection and processing of statistics 35 | While finishing a customer run the application creates data arrays for each measure. At the end of simulation, the application creates StatCollection object and performs descriptive statistical analysis. The following statistical parameters are calculated for each measure array: 36 | #Observ - number of observations 37 | Average - average (mean) value 38 | Std Dev- standard deviation 39 | L-Bound-lower bound of the confidence interval with 95% probability 40 | U-Bound-upper bound of the confidence interval with 95% probability 41 | Minimum- minimum value 42 | Maximum- maximum value 43 | */ 44 | 45 | import ( 46 | "fmt" 47 | 48 | "github.com/agoussia/godes" 49 | ) 50 | 51 | // Input Parameters 52 | const ( 53 | ARRIVAL_INTERVAL = 0.5 54 | SERVICE_TIME = 1.3 55 | SHUTDOWN_TIME = 8 * 60. 56 | INDEPENDENT_RUNS = 100 57 | ) 58 | 59 | // the arrival and service are two random number generators for the exponential distribution 60 | var arrival *godes.ExpDistr = godes.NewExpDistr(true) 61 | var service *godes.ExpDistr = godes.NewExpDistr(true) 62 | 63 | // true when any counter is available 64 | var counterSwt *godes.BooleanControl = godes.NewBooleanControl() 65 | 66 | // FIFO Queue for the arrived customers 67 | var customerArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("0") 68 | 69 | var tellers *Tellers 70 | var statistics [][]float64 71 | var replicationStats [][]float64 72 | var titles = []string{ 73 | "Elapsed Time", 74 | "Queue Length", 75 | "Queueing Time", 76 | "Service Time", 77 | } 78 | 79 | var availableTellers int = 0 80 | 81 | // the Tellers is a Passive Object represebting resource 82 | type Tellers struct { 83 | max int 84 | } 85 | 86 | func (tellers *Tellers) Catch(customer *Customer) { 87 | for { 88 | counterSwt.Wait(true) 89 | if customerArrivalQueue.GetHead().(*Customer).GetId() == customer.GetId() { 90 | break 91 | } else { 92 | godes.Yield() 93 | } 94 | } 95 | availableTellers++ 96 | if availableTellers == tellers.max { 97 | counterSwt.Set(false) 98 | } 99 | } 100 | 101 | func (tellers *Tellers) Release() { 102 | availableTellers-- 103 | counterSwt.Set(true) 104 | } 105 | 106 | // the Customer is a Runner 107 | type Customer struct { 108 | *godes.Runner 109 | id int 110 | } 111 | 112 | func (customer *Customer) Run() { 113 | a0 := godes.GetSystemTime() 114 | tellers.Catch(customer) 115 | a1 := godes.GetSystemTime() 116 | customerArrivalQueue.Get() 117 | qlength := float64(customerArrivalQueue.Len()) 118 | godes.Advance(service.Get(1. / SERVICE_TIME)) 119 | a2 := godes.GetSystemTime() 120 | tellers.Release() 121 | collectionArray := []float64{a2 - a0, qlength, a1 - a0, a2 - a1} 122 | replicationStats = append(replicationStats, collectionArray) 123 | } 124 | 125 | func (customer *Customer) GetId() int { 126 | return customer.id 127 | } 128 | 129 | func main() { 130 | statistics = [][]float64{} 131 | 132 | tellers = &Tellers{3} 133 | for i := 0; i < INDEPENDENT_RUNS; i++ { 134 | replicationStats = [][]float64{} 135 | godes.Run() 136 | counterSwt.Set(true) 137 | customerArrivalQueue.Clear() 138 | count := 0 139 | for { 140 | customer := &Customer{&godes.Runner{}, count} 141 | customerArrivalQueue.Place(customer) 142 | godes.AddRunner(customer) 143 | godes.Advance(arrival.Get(1. / ARRIVAL_INTERVAL)) 144 | if godes.GetSystemTime() > SHUTDOWN_TIME { 145 | break 146 | } 147 | count++ 148 | } 149 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 150 | godes.Clear() 151 | replicationCollector := godes.NewStatCollector(titles, replicationStats) 152 | 153 | collectionArray := []float64{ 154 | replicationCollector.GetAverage(0), 155 | replicationCollector.GetAverage(1), 156 | replicationCollector.GetAverage(2), 157 | replicationCollector.GetAverage(3), 158 | } 159 | statistics = append(statistics, collectionArray) 160 | } 161 | 162 | collector := godes.NewStatCollector(titles, statistics) 163 | collector.PrintStat() 164 | fmt.Printf("Finished \n") 165 | } 166 | 167 | /* OUTPUT 168 | Variable # Average Std Dev L-Bound U-Bound Minimum Maximum 169 | Elapsed Time 100 3.672 1.217 3.433 3.910 1.980 8.722 170 | Queue Length 100 4.684 2.484 4.197 5.171 1.539 14.615 171 | Queueing Time 100 2.368 1.194 2.134 2.602 0.810 7.350 172 | Service Time 100 1.304 0.044 1.295 1.312 1.170 1.432 173 | Finished 174 | */ 175 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Package godes is the general-purpose simulation library 6 | // which includes the simulation engine and building blocks 7 | // for modeling a wide variety of systems at varying levels of details. 8 | 9 | package godes 10 | 11 | import ( 12 | "bufio" 13 | "fmt" 14 | "math" 15 | "os" 16 | "text/tabwriter" 17 | "time" 18 | ) 19 | const mAX_NUMBER_OF_SAMPLES = 100 20 | const mAX_NUMBER_OF_PARAMETERS = 6 21 | 22 | var curTime int64 23 | 24 | func GetCurComputerTime() int64 { 25 | ct := time.Now().UnixNano() 26 | if ct > curTime { 27 | curTime = ct 28 | return ct 29 | } else if ct == curTime { 30 | curTime = ct + 1 31 | return curTime 32 | } else { 33 | curTime++ 34 | return curTime 35 | } 36 | } 37 | 38 | //NewStatCollector creates a wrapper for samples data 39 | func NewStatCollector(measures []string, samples [][]float64) *StatCollector { 40 | 41 | if measures == nil { 42 | panic("null measures array") 43 | } 44 | 45 | if samples == nil { 46 | panic("null samples array") 47 | } 48 | 49 | if len(measures) != len(samples[0]) { 50 | panic("invalid measures/samples arrays") 51 | } 52 | 53 | return &StatCollector{measures, samples} 54 | } 55 | //StatCollector is a wrapper which contains set of samples for statistical analyses 56 | type StatCollector struct { 57 | measures []string 58 | samples [][]float64 59 | } 60 | 61 | //Print calculates statistical parameters and output them to *bufio.Writer 62 | // parameters flags are allowed 63 | func (collector *StatCollector) Print(statWriter *bufio.Writer, measuresSwt bool, avgSwt bool, stdDevSwt bool, lBooundSwt bool, uBoundSwt bool, minSwt bool, maxSwt bool) (err error) { 64 | 65 | var results [mAX_NUMBER_OF_PARAMETERS][mAX_NUMBER_OF_SAMPLES]float64 66 | if statWriter == nil { 67 | panic("startWrite equal nil") 68 | } 69 | if measuresSwt { 70 | fmt.Fprintf(statWriter, " Replication\t") 71 | for i := 0; i < len(collector.measures); i++ { 72 | fmt.Fprintf(statWriter, "%v\t", collector.measures[i]) 73 | } 74 | fmt.Fprintf(statWriter, "\n") 75 | } 76 | 77 | //Results 78 | fmt.Fprintf(statWriter, "\n") 79 | 80 | for i := 0; i < len(collector.measures); i++ { 81 | _, avg, std, lb, ub, min, max := collector.GetStat(i) 82 | results[0][i] = avg 83 | results[1][i] = std 84 | results[2][i] = lb 85 | results[3][i] = ub 86 | results[4][i] = min 87 | results[5][i] = max 88 | } 89 | if avgSwt { 90 | fmt.Fprintf(statWriter, "Avg. \t") 91 | for i := 0; i < len(collector.measures); i++ { 92 | fmt.Fprintf(statWriter, "%6.3f \t", results[0][i]) 93 | } 94 | fmt.Fprintf(statWriter, "\n") 95 | } 96 | 97 | if stdDevSwt { 98 | fmt.Fprintf(statWriter, "StdDev\t") 99 | for i := 0; i < len(collector.measures); i++ { 100 | fmt.Fprintf(statWriter, "%6.3f \t", results[1][i]) 101 | } 102 | fmt.Fprintf(statWriter, "\n") 103 | } 104 | 105 | if lBooundSwt { 106 | fmt.Fprintf(statWriter, "L-Bound\t") 107 | for i := 0; i < len(collector.measures); i++ { 108 | fmt.Fprintf(statWriter, "%6.3f \t", results[2][i]) 109 | } 110 | fmt.Fprintf(statWriter, "\n") 111 | } 112 | 113 | if uBoundSwt { 114 | fmt.Fprintf(statWriter, "U-Bound\t") 115 | for i := 0; i < len(collector.measures); i++ { 116 | fmt.Fprintf(statWriter, "%6.3f \t", results[3][i]) 117 | } 118 | fmt.Fprintf(statWriter, "\n") 119 | } 120 | 121 | if minSwt { 122 | fmt.Fprintf(statWriter, "Minimum\t") 123 | for i := 0; i < len(collector.measures); i++ { 124 | fmt.Fprintf(statWriter, "%6.3f \t", results[4][i]) 125 | } 126 | fmt.Fprintf(statWriter, "\n") 127 | } 128 | 129 | if maxSwt { 130 | fmt.Fprintf(statWriter, "Maximum\t") 131 | for i := 0; i < len(collector.measures); i++ { 132 | fmt.Fprintf(statWriter, "%6.3f \t", results[5][i]) 133 | } 134 | fmt.Fprintf(statWriter, "\n") 135 | } 136 | return 137 | } 138 | 139 | // PrintStat calculates and prints statistical parameters 140 | func (collector *StatCollector) PrintStat() { 141 | w := new(tabwriter.Writer) 142 | w.Init(os.Stdout, 0, 10, 0, '\t', 0) 143 | fmt.Fprintln(w, "Variable\t#\tAverage\tStd Dev\tL-Bound\tU-Bound\tMinimum\tMaximum") 144 | for i := 0; i < len(collector.measures); i++ { 145 | obs, avg, std, lb, ub, min, max := collector.GetStat(i) 146 | fmt.Fprintf(w, "%s\t%d\t%6.3f\t%6.3f\t%6.3f\t%6.3f\t%6.3f\t%6.3f\n", collector.measures[i], obs, avg, std, lb, ub, min, max) 147 | } 148 | w.Flush() 149 | return 150 | } 151 | 152 | //GetStat returns size of the sample, average, standard deviation, low bound and uppe bounds of confidence inteval, minimum and maximum values 153 | func (collector *StatCollector) GetStat(measureInd int) (int64, float64, float64, float64, float64, float64, float64) { 154 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 155 | panic("invalid index") 156 | } 157 | avg := 0. 158 | std := 0. 159 | lb := 0. 160 | ub := 0. 161 | repl := int64(len(collector.samples)) 162 | slice := []float64{} 163 | for i := 0; i < int(repl); i++ { 164 | slice = append(slice, collector.samples[i][measureInd]) 165 | } 166 | avg = Mean(slice) 167 | std = StandardDeviation(slice) 168 | lb, ub = NormalConfidenceInterval(slice) 169 | min, max := MinMax(slice) 170 | return repl, avg, std, lb, ub, min, max 171 | 172 | } 173 | 174 | //GetSize returns size of a sample 175 | func (collector *StatCollector) GetSize(measureInd int) int { 176 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 177 | panic("invalid index") 178 | } 179 | size := int(len(collector.samples)) 180 | return size 181 | } 182 | 183 | //GetAverage returns average of a sample 184 | func (collector *StatCollector) GetAverage(measureInd int) float64 { 185 | 186 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 187 | panic("invalid index") 188 | } 189 | avg := 0. 190 | slice := []float64{} 191 | size := collector.GetSize(measureInd) 192 | for i := 0; i < size; i++ { 193 | slice = append(slice, collector.samples[i][measureInd]) 194 | } 195 | avg = Mean(slice) 196 | return avg 197 | } 198 | 199 | //GetStandardDeviation returns standard deviation of a sample 200 | func (collector *StatCollector) GetStandardDeviation(measureInd int) float64 { 201 | 202 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 203 | panic("invalid index") 204 | } 205 | std := 0. 206 | slice := []float64{} 207 | size := collector.GetSize(measureInd) 208 | for i := 0; i < size; i++ { 209 | slice = append(slice, collector.samples[i][measureInd]) 210 | } 211 | std = StandardDeviation(slice) 212 | return std 213 | } 214 | 215 | //GetLowBoundCI returns low bound of confidence interval for sample 216 | func (collector *StatCollector) GetLowBoundCI(measureInd int) float64 { 217 | 218 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 219 | panic("invalid index") 220 | } 221 | lb := 0. 222 | slice := []float64{} 223 | size := collector.GetSize(measureInd) 224 | for i := 0; i < size; i++ { 225 | slice = append(slice, collector.samples[i][measureInd]) 226 | } 227 | lb, _ = NormalConfidenceInterval(slice) 228 | return lb 229 | } 230 | 231 | //GetUpperBoundCI returns upper bound of confidence interval for sample 232 | func (collector *StatCollector) GetUpperBoundCI(measureInd int) float64 { 233 | 234 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 235 | panic("invalid index") 236 | } 237 | ub := 0. 238 | slice := []float64{} 239 | size := collector.GetSize(measureInd) 240 | for i := 0; i < size; i++ { 241 | slice = append(slice, collector.samples[i][measureInd]) 242 | } 243 | _, ub = NormalConfidenceInterval(slice) 244 | return ub 245 | } 246 | 247 | //GetMinimum returns minimum value for a sample 248 | func (collector *StatCollector) GetMinimum(measureInd int) float64 { 249 | 250 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 251 | panic("invalid index") 252 | } 253 | min := 0. 254 | slice := []float64{} 255 | size := collector.GetSize(measureInd) 256 | for i := 0; i < size; i++ { 257 | slice = append(slice, collector.samples[i][measureInd]) 258 | } 259 | min, _ = MinMax(slice) 260 | return min 261 | } 262 | 263 | //GetMaximum returns maximum value for a sample 264 | func (collector *StatCollector) GetMaximum(measureInd int) float64 { 265 | 266 | if measureInd < 0 || measureInd > len(collector.measures)-1 { 267 | panic("invalid index") 268 | } 269 | max := 0. 270 | slice := []float64{} 271 | size := collector.GetSize(measureInd) 272 | for i := 0; i < size; i++ { 273 | slice = append(slice, collector.samples[i][measureInd]) 274 | } 275 | _, max = MinMax(slice) 276 | return max 277 | } 278 | 279 | // Mean returns float mean value 280 | func Mean(nums []float64) (mean float64) { 281 | if len(nums) == 0 { 282 | return 0.0 283 | } 284 | for _, n := range nums { 285 | mean += n 286 | } 287 | return mean / float64(len(nums)) 288 | } 289 | 290 | // StandardDeviation returns STD 291 | func StandardDeviation(nums []float64) (dev float64) { 292 | if len(nums) == 0 { 293 | return 0.0 294 | } 295 | 296 | m := Mean(nums) 297 | for _, n := range nums { 298 | dev += (n - m) * (n - m) 299 | } 300 | dev = math.Pow(dev/float64(len(nums)-1.), 0.5) 301 | 302 | return dev 303 | } 304 | 305 | // NormalConfidenceInterval returns Confidence Interval 306 | func NormalConfidenceInterval(nums []float64) (lower float64, upper float64) { 307 | conf := 1.95996 // 95% confidence for the mean, http://bit.ly/Mm05eZ 308 | mean := Mean(nums) 309 | dev := StandardDeviation(nums) / math.Sqrt(float64(len(nums))) 310 | return mean - dev*conf, mean + dev*conf 311 | } 312 | 313 | // MinMax returns minimum and maximum values amongst sample 314 | func MinMax(nums []float64) (minimum float64, maximum float64) { 315 | 316 | min := nums[0] 317 | max := nums[0] 318 | 319 | for i := 0; i < len(nums); i++ { 320 | if nums[i] < min { 321 | min = nums[i] 322 | } 323 | if nums[i] > max { 324 | max = nums[i] 325 | } 326 | } 327 | return min, max 328 | } 329 | -------------------------------------------------------------------------------- /model.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Alex Goussiatiner. All rights reserved. 2 | // Use of this source code is governed by a MIT 3 | // license that can be found in the LICENSE file. 4 | // 5 | // Godes is the general-purpose simulation library 6 | // which includes the simulation engine and building blocks 7 | // for modeling a wide variety of systems at varying levels of details. 8 | // 9 | // Godes model controls the runnners 10 | // See examples for the usage. 11 | // 12 | 13 | package godes 14 | 15 | import ( 16 | "container/list" 17 | "fmt" 18 | //"sync" 19 | "time" 20 | ) 21 | 22 | const simulationSecondScale = 100 23 | const rUNNER_STATE_READY = 0 24 | const rUNNER_STATE_ACTIVE = 1 25 | const rUNNER_STATE_WAITING_COND = 2 26 | const rUNNER_STATE_SCHEDULED = 3 27 | const rUNNER_STATE_INTERRUPTED = 4 28 | const rUNNER_STATE_TERMINATED = 5 29 | 30 | var modl *model 31 | var stime float64 = 0 32 | 33 | // WaitUntilDone stops the main goroutine and waits 34 | // until all the runners finished executing the Run() 35 | func WaitUntilDone() { 36 | if modl == nil { 37 | panic(" not initilized") 38 | } 39 | modl.waitUntillDone() 40 | } 41 | 42 | //AddRunner adds the runner obejct into model 43 | func AddRunner(runner RunnerInterface) { 44 | if runner == nil { 45 | panic("runner is nil") 46 | } 47 | if modl == nil { 48 | createModel(false) 49 | } 50 | modl.add(runner) 51 | } 52 | 53 | //Interrupt holds the runner execution 54 | func Interrupt(runner RunnerInterface) { 55 | if runner == nil { 56 | panic("runner is nil") 57 | } 58 | if modl == nil { 59 | panic("model is nil") 60 | } 61 | modl.interrupt(runner) 62 | } 63 | 64 | //Resume restarts the runner execution 65 | func Resume(runner RunnerInterface, timeChange float64) { 66 | if runner == nil { 67 | panic("runner is nil") 68 | } 69 | if modl == nil { 70 | panic("model is nil") 71 | } 72 | modl.resume(runner, timeChange) 73 | } 74 | 75 | //Run starts the simulation model. 76 | // Must be called explicitly. 77 | func Run() { 78 | if modl == nil { 79 | createModel(false) 80 | } 81 | //assuming that it comes from the main go routine 82 | if modl.activeRunner == nil { 83 | panic("runner is nil") 84 | } 85 | 86 | if modl.activeRunner.getInternalId() != 0 { 87 | panic("it comes from not from the main go routine") 88 | } 89 | 90 | modl.simulationActive = true 91 | modl.control() 92 | 93 | } 94 | 95 | //Advance the simulation time 96 | func Advance(interval float64) { 97 | if modl == nil { 98 | createModel(false) 99 | } 100 | modl.advance(interval) 101 | } 102 | 103 | // Verbose sets the model in the verbose mode 104 | func Verbose(v bool) { 105 | if modl == nil { 106 | createModel(v) 107 | } 108 | modl.DEBUG = v 109 | } 110 | 111 | // Clear the model between the runs 112 | func Clear() { 113 | if modl == nil { 114 | panic(" No model exist") 115 | } else { 116 | 117 | stime = 0 118 | modl = newModel(modl.DEBUG) 119 | //model.simulationActive = true 120 | //model.control() 121 | } 122 | } 123 | 124 | // GetSystemTime retuns the current simulation time 125 | func GetSystemTime() float64 { 126 | return stime 127 | } 128 | 129 | // Yield stops the runner for short time 130 | func Yield() { 131 | Advance(0.01) 132 | } 133 | 134 | // createModel 135 | func createModel(verbose bool) { 136 | if modl != nil { 137 | panic("model is already active") 138 | } 139 | stime = 0 140 | modl = newModel(verbose) 141 | //model.simulationActive = true 142 | //model.control() 143 | //assuming that it comes from the main go routine 144 | } 145 | 146 | type model struct { 147 | //mu sync.RWMutex 148 | activeRunner RunnerInterface 149 | movingList *list.List 150 | scheduledList *list.List 151 | waitingList *list.List 152 | waitingConditionMap map[int]RunnerInterface 153 | interruptedMap map[int]RunnerInterface 154 | terminatedList *list.List 155 | currentId int 156 | controlChannel chan int 157 | simulationActive bool 158 | DEBUG bool 159 | } 160 | 161 | //newModel initilizes the model 162 | func newModel(verbose bool) *model { 163 | 164 | var ball *Runner = newRunner() 165 | ball.channel = make(chan int) 166 | ball.markTime = time.Now() 167 | ball.internalId = 0 168 | ball.state = rUNNER_STATE_ACTIVE //that is bypassing READY 169 | ball.priority = 100 170 | ball.setMarkTime(time.Now()) 171 | var runner RunnerInterface = ball 172 | mdl := model{activeRunner: runner, controlChannel: make(chan int), DEBUG: verbose, simulationActive: false} 173 | mdl.addToMovingList(runner) 174 | return &mdl 175 | } 176 | 177 | func (mdl *model) advance(interval float64) bool { 178 | 179 | ch := mdl.activeRunner.getChannel() 180 | mdl.activeRunner.setMovingTime(stime + interval) 181 | mdl.activeRunner.setState(rUNNER_STATE_SCHEDULED) 182 | mdl.removeFromMovingList(mdl.activeRunner) 183 | mdl.addToSchedulledList(mdl.activeRunner) 184 | //restart control channel and freez 185 | mdl.controlChannel <- 100 186 | <-ch 187 | return true 188 | } 189 | 190 | func (mdl *model) waitUntillDone() { 191 | 192 | if mdl.activeRunner.getInternalId() != 0 { 193 | panic("waitUntillDone initiated for not main ball") 194 | } 195 | 196 | mdl.removeFromMovingList(mdl.activeRunner) 197 | mdl.controlChannel <- 100 198 | for { 199 | 200 | if !modl.simulationActive { 201 | break 202 | } else { 203 | if mdl.DEBUG { 204 | fmt.Println("waiting", mdl.movingList.Len()) 205 | } 206 | time.Sleep(time.Millisecond * simulationSecondScale) 207 | } 208 | } 209 | } 210 | 211 | func (mdl *model) add(runner RunnerInterface) bool { 212 | 213 | mdl.currentId++ 214 | runner.setChannel(make(chan int)) 215 | runner.setMovingTime(stime) 216 | runner.setInternalId(mdl.currentId) 217 | runner.setState(rUNNER_STATE_READY) 218 | mdl.addToMovingList(runner) 219 | 220 | go func() { 221 | <-runner.getChannel() 222 | runner.setMarkTime(time.Now()) 223 | runner.Run() 224 | if mdl.activeRunner == nil { 225 | panic("remove: activeRunner == nil") 226 | } 227 | mdl.removeFromMovingList(mdl.activeRunner) 228 | mdl.activeRunner.setState(rUNNER_STATE_TERMINATED) 229 | mdl.activeRunner = nil 230 | mdl.controlChannel <- 100 231 | }() 232 | return true 233 | 234 | } 235 | 236 | func (mdl *model) interrupt(runner RunnerInterface) { 237 | 238 | if runner.getState() != rUNNER_STATE_SCHEDULED { 239 | panic("It is not rUNNER_STATE_SCHEDULED") 240 | } 241 | mdl.removeFromSchedulledList(runner) 242 | runner.setState(rUNNER_STATE_INTERRUPTED) 243 | mdl.addToInterruptedMap(runner) 244 | 245 | } 246 | 247 | func (mdl *model) resume(runner RunnerInterface, timeChange float64) { 248 | if runner.getState() != rUNNER_STATE_INTERRUPTED { 249 | panic("It is not rUNNER_STATE_INTERRUPTED") 250 | } 251 | mdl.removeFromInterruptedMap(runner) 252 | runner.setState(rUNNER_STATE_SCHEDULED) 253 | runner.setMovingTime(runner.getMovingTime() + timeChange) 254 | //mdl.addToMovingList(runner) 255 | mdl.addToSchedulledList(runner) 256 | 257 | } 258 | 259 | 260 | func (mdl *model) booleanControlWait(b *BooleanControl, val bool) { 261 | 262 | ch := mdl.activeRunner.getChannel() 263 | if mdl.activeRunner == nil { 264 | panic("booleanControlWait - no runner") 265 | } 266 | 267 | mdl.removeFromMovingList(mdl.activeRunner) 268 | 269 | mdl.activeRunner.setState(rUNNER_STATE_WAITING_COND) 270 | mdl.activeRunner.setWaitingForBool(val) 271 | mdl.activeRunner.setWaitingForBoolControl(b) 272 | 273 | mdl.addToWaitingConditionMap(mdl.activeRunner) 274 | mdl.controlChannel <- 100 275 | <-ch 276 | 277 | } 278 | 279 | func (mdl *model) booleanControlWaitAndTimeout(b *BooleanControl, val bool, timeout float64) { 280 | 281 | ri := &TimeoutRunner{&Runner{}, mdl.activeRunner, timeout} 282 | AddRunner(ri) 283 | mdl.activeRunner.setWaitingForBoolControlTimeoutId(ri.getInternalId()) 284 | mdl.booleanControlWait(b, val) 285 | 286 | } 287 | 288 | func (mdl *model) booleanControlSet(b *BooleanControl) { 289 | ch := mdl.activeRunner.getChannel() 290 | if mdl.activeRunner == nil { 291 | panic("booleanControlSet - no runner") 292 | } 293 | mdl.controlChannel <- 100 294 | <-ch 295 | 296 | } 297 | 298 | func (mdl *model) control() bool { 299 | 300 | if mdl.activeRunner == nil { 301 | panic("control: activeBall == nil") 302 | } 303 | 304 | go func() { 305 | var runner RunnerInterface 306 | for { 307 | <-mdl.controlChannel 308 | if mdl.waitingConditionMap != nil && len(mdl.waitingConditionMap) > 0 { 309 | for key, temp := range mdl.waitingConditionMap { 310 | if temp.getWaitingForBoolControl() == nil { 311 | panic(" no BoolControl") 312 | } 313 | if temp.getWaitingForBool() == temp.getWaitingForBoolControl().GetState() { 314 | temp.setState(rUNNER_STATE_READY) 315 | temp.setWaitingForBoolControl(nil) 316 | temp.setWaitingForBoolControlTimeoutId(-1) 317 | mdl.addToMovingList(temp) 318 | delete(mdl.waitingConditionMap, key) 319 | break 320 | } 321 | } 322 | } 323 | 324 | //finding new runner 325 | runner = nil 326 | if mdl.movingList != nil && mdl.movingList.Len() > 0 { 327 | runner = mdl.getFromMovingList() 328 | } 329 | if runner == nil && mdl.scheduledList != nil && mdl.scheduledList.Len() > 0 { 330 | runner = mdl.getFromSchedulledList() 331 | if runner.getMovingTime() < stime { 332 | panic("control is seting simulation time in the past") 333 | } else { 334 | stime = runner.getMovingTime() 335 | } 336 | mdl.addToMovingList(runner) 337 | } 338 | if runner == nil { 339 | break 340 | } 341 | //restarting 342 | mdl.activeRunner = runner 343 | mdl.activeRunner.setState(rUNNER_STATE_ACTIVE) 344 | runner.setWaitingForBoolControl(nil) 345 | mdl.activeRunner.getChannel() <- -1 346 | 347 | } 348 | if mdl.DEBUG { 349 | fmt.Println("Finished") 350 | } 351 | mdl.simulationActive = false 352 | }() 353 | 354 | return true 355 | 356 | } 357 | 358 | /* 359 | MovingList 360 | This list is sorted in descending order according to the value of the ball priority attribute. 361 | Balls with identical priority values are sorted according to the FIFO principle. 362 | 363 | | 364 | | | 365 | | | 366 | | | | 367 | <----- | | | | 368 | */ 369 | func (mdl *model) addToMovingList(runner RunnerInterface) bool { 370 | 371 | if mdl.DEBUG { 372 | fmt.Printf("addToMovingList %v\n", runner) 373 | } 374 | 375 | if mdl.movingList == nil { 376 | mdl.movingList = list.New() 377 | mdl.movingList.PushFront(runner) 378 | return true 379 | } 380 | 381 | insertedSwt := false 382 | for element := mdl.movingList.Front(); element != nil; element = element.Next() { 383 | if runner.getPriority() > element.Value.(RunnerInterface).getPriority() { 384 | mdl.movingList.InsertBefore(runner, element) 385 | insertedSwt = true 386 | break 387 | } 388 | } 389 | if !insertedSwt { 390 | mdl.movingList.PushBack(runner) 391 | } 392 | return true 393 | } 394 | 395 | func (mdl *model) getFromMovingList() RunnerInterface { 396 | 397 | if mdl.movingList == nil { 398 | panic("MovingList was not initilized") 399 | } 400 | if mdl.DEBUG { 401 | runner := mdl.movingList.Front().Value.(RunnerInterface) 402 | fmt.Printf("getFromMovingList %v\n", runner) 403 | } 404 | runner := mdl.movingList.Front().Value.(RunnerInterface) 405 | mdl.movingList.Remove(mdl.movingList.Front()) 406 | return runner 407 | 408 | } 409 | 410 | func (mdl *model) removeFromMovingList(runner RunnerInterface) { 411 | 412 | if mdl.movingList == nil { 413 | panic("MovingList was not initilized") 414 | } 415 | 416 | if mdl.DEBUG { 417 | fmt.Printf("removeFromMovingList %v\n", runner) 418 | } 419 | var found bool 420 | for e := mdl.movingList.Front(); e != nil; e = e.Next() { 421 | if e.Value == runner { 422 | mdl.movingList.Remove(e) 423 | found = true 424 | break 425 | } 426 | } 427 | 428 | if !found { 429 | //panic("not found in MovingList") 430 | } 431 | } 432 | 433 | /* 434 | SchedulledList 435 | This list is sorted in descending order according to the schedulled time 436 | Priorites are not used 437 | 438 | | 439 | | | 440 | | | 441 | | | | 442 | | | | |---> 443 | 444 | */ 445 | func (mdl *model) addToSchedulledList(runner RunnerInterface) bool { 446 | 447 | if mdl.scheduledList == nil { 448 | mdl.scheduledList = list.New() 449 | mdl.scheduledList.PushFront(runner) 450 | return true 451 | } 452 | insertedSwt := false 453 | for element := mdl.scheduledList.Back(); element != nil; element = element.Prev() { 454 | if runner.getMovingTime() < element.Value.(RunnerInterface).getMovingTime() { 455 | mdl.scheduledList.InsertAfter(runner, element) 456 | insertedSwt = true 457 | break 458 | } 459 | } 460 | if !insertedSwt { 461 | mdl.scheduledList.PushFront(runner) 462 | } 463 | 464 | if mdl.DEBUG { 465 | fmt.Println("===") 466 | fmt.Printf("addToSchedulledList %v\n", runner) 467 | for element := mdl.scheduledList.Front(); element != nil; element = element.Next() { 468 | fmt.Printf("elem %v\n", element.Value.(RunnerInterface)) 469 | } 470 | fmt.Println("===") 471 | } 472 | return true 473 | } 474 | 475 | func (mdl *model) getFromSchedulledList() RunnerInterface { 476 | if mdl.scheduledList == nil { 477 | panic(" SchedulledList was not initilized") 478 | } 479 | if mdl.DEBUG { 480 | runner := mdl.scheduledList.Back().Value.(RunnerInterface) 481 | fmt.Printf("getFromSchedulledList %v\n", runner) 482 | } 483 | runner := mdl.scheduledList.Back().Value.(RunnerInterface) 484 | mdl.scheduledList.Remove(mdl.scheduledList.Back()) 485 | return runner 486 | } 487 | 488 | func (mdl *model) removeFromSchedulledList(runner RunnerInterface) { 489 | if mdl.scheduledList == nil { 490 | panic("schedulledList was not initilized") 491 | } 492 | if modl.DEBUG { 493 | fmt.Printf("removeFrom schedulledListt %v\n", runner) 494 | } 495 | var found bool 496 | for e := mdl.scheduledList.Front(); e != nil; e = e.Next() { 497 | if e.Value == runner { 498 | mdl.scheduledList.Remove(e) 499 | found = true 500 | break 501 | } 502 | } 503 | if !found { 504 | panic("not found in scheduledList") 505 | } 506 | return 507 | } 508 | 509 | func (mdl *model) addToWaitingConditionMap(runner RunnerInterface) bool { 510 | 511 | if runner.getWaitingForBoolControl() == nil { 512 | panic(" addToWaitingConditionMap - no control ") 513 | } 514 | 515 | if mdl.DEBUG { 516 | fmt.Printf("addToWaitingConditionMap %v\n", runner) 517 | } 518 | 519 | if mdl.waitingConditionMap == nil { 520 | mdl.waitingConditionMap = make(map[int]RunnerInterface) 521 | 522 | } 523 | mdl.waitingConditionMap[runner.getInternalId()] = runner 524 | return true 525 | } 526 | 527 | func (mdl *model) addToInterruptedMap(runner RunnerInterface) bool { 528 | 529 | if mdl.DEBUG { 530 | fmt.Printf("addToInterruptedMap %v\n", runner) 531 | } 532 | if mdl.interruptedMap == nil { 533 | mdl.interruptedMap = make(map[int]RunnerInterface) 534 | } 535 | 536 | mdl.interruptedMap[runner.getInternalId()] = runner 537 | return true 538 | } 539 | 540 | func (mdl *model) removeFromInterruptedMap(runner RunnerInterface) bool { 541 | 542 | if mdl.DEBUG { 543 | fmt.Printf("removeFromInterruptedMap %v\n", runner) 544 | } 545 | 546 | _, ok := mdl.interruptedMap[runner.getInternalId()] 547 | 548 | if ok { 549 | delete(mdl.interruptedMap, runner.getInternalId()) 550 | } else { 551 | panic("not found in interruptedMap") 552 | } 553 | return true 554 | } 555 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Godes 2 | 3 | Open Source Library to Build Discrete Event Simulation Models in Go/Golang (http://golang.org/) 4 | 5 | Copyright (c) 2013-2025 Alex Goussiatiner agoussia@yahoo.com 6 | 7 | ### Features 8 | Godes is the general-purpose simulation library which includes the simulation engine and building blocks for modeling a wide variety of systems at varying levels of details. 9 | 10 | ###### Active Objects 11 | All active objects shall implement the RunnerInterface and have Run() method. For each active object Godes creates a goroutine - lightweight thread. 12 | 13 | ###### Random Generators 14 | Godes contains set of built-in functions for generating random numbers for commonly used probability distributions. 15 | Each of the distrubutions in Godes has one or more parameter values associated with it: Uniform (Min, Max), Normal (Mean and Standard Deviation), Exponential (Lambda), Triangular(Min, Mode, Max) 16 | 17 | ###### Queues 18 | Godes implements operations with FIFO and LIFO queues 19 | 20 | ###### BooleanControl 21 | Godes uses BooleanControl variable as a lock for 22 | synchronizing execution of multiple runners 23 | 24 | ###### StatCollector 25 | The Object calculates and prints statistical parameters for set of samples collected during the simulation. 26 | 27 | 28 | 29 | ### Library Docs 30 | [![GoDoc](https://godoc.org/github.com/agoussia/godes?status.svg)](https://godoc.org/github.com/agoussia/godes) 31 | 32 | ### Advantages 33 | * Godes is easy to learn for the people familiar with the Go and the elementary simulation concept. 34 | * Godes model executes fast as Go compiles to machine code. Its performace is similar to C++ in performance. 35 | * Godes model is multiplatform as Go compiler targets the Linux, Mac OS X, FreeBSD, Microsoft Windows,etc. 36 | * Godes model can be embedded in various computer systems and over the network. 37 | * Speed of the Godes model compilation is high. 38 | * Variety of the IDE with debuggers are available for Go and Godes as well. 39 | * The Godes sumulation model can use all of the GO's features and libraries. 40 | * Code Security - the Godes includes the source code for the library and Go is an open source project supported by Google. 41 | * Godes is free open source software under MIT license. 42 | 43 | ### Installation 44 | 45 | ``` 46 | $ go get github.com/agoussia/godes 47 | ``` 48 | 49 | ### Examples 50 | 51 | #### Example 0. Restaurant.Godes Basics 52 | 53 | ###### Proces Description 54 | During the working day the visitors are entering the restaurant at random intervals and immediately get the table. 55 | The inter arrival interval is the random variable with uniform distribution from 0 to 70 minutes. 56 | The last visitor gets admitted not later than 8 hours after the opening. 57 | The simulation itself is terminated when the last visitors enters the restaurant. 58 | 59 | ```go 60 | package main 61 | 62 | import ( 63 | "fmt" 64 | "github.com/godes" 65 | ) 66 | 67 | // the arrival and service are two random number generators for the uniform distribution 68 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 69 | 70 | // the Visitor is a Runner 71 | // any type of the Runner should be defined as struct 72 | // with the *godes.Runner as anonimous field 73 | type Visitor struct { 74 | *godes.Runner 75 | number int 76 | } 77 | 78 | var visitorsCount int = 0 79 | 80 | func (vst *Visitor) Run() { // Any runner should have the Run method 81 | fmt.Printf(" %-6.3f \t Visitor # %v arrives \n", godes.GetSystemTime(), vst.number) 82 | } 83 | func main() { 84 | var shutdown_time float64 = 8 * 60 85 | godes.Run() 86 | for { 87 | //godes.Stime is the current simulation time 88 | if godes.GetSystemTime() < shutdown_time { 89 | //the function acivates the Runner 90 | godes.AddRunner(&Visitor{&godes.Runner{}, visitorsCount}) 91 | //this advance the system time 92 | godes.Advance(arrival.Get(0, 70)) 93 | visitorsCount++ 94 | } else { 95 | break 96 | } 97 | } 98 | // waits for all the runners to finish the Run() 99 | godes.WaitUntilDone() 100 | } 101 | /* OUTPUT: 102 | 0.000 Visitor # 0 arrives 103 | 37.486 Visitor # 1 arrives 104 | 98.737 Visitor # 2 arrives 105 | 107.468 Visitor # 3 arrives 106 | 149.471 Visitor # 4 arrives 107 | 207.523 Visitor # 5 arrives 108 | 230.922 Visitor # 6 arrives 109 | 261.770 Visitor # 7 arrives 110 | 269.668 Visitor # 8 arrives 111 | 310.261 Visitor # 9 arrives 112 | 338.323 Visitor # 10 arrives 113 | 397.720 Visitor # 11 arrives 114 | 409.123 Visitor # 12 arrives 115 | 436.817 Visitor # 13 arrives 116 | 447.731 Visitor # 14 arrives 117 | */ 118 | ``` 119 | *** 120 | 121 | #### Example 1. Restaurant. Godes Boolean Controls 122 | ###### Proces Description 123 | The restaurant has only one table to sit on. During the working day the visitors are entering the restaurant at random intervals 124 | and wait for the table to be available. The inter arrival interval is the random variable with uniform distribution from 0 to 70 minutes.The time spent in the restaurant is the random variable with uniform distribution from 10 to 60 minutes. 125 | The last visitor gets admitted not later than 8 hours after the opening. 126 | The simulation itself is terminated when the last visitors has left the restaurant. 127 | ```go 128 | package main 129 | 130 | import ( 131 | "fmt" 132 | "github.com/godes" 133 | ) 134 | 135 | // the arrival and service are two random number generators for the uniform distribution 136 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 137 | var service *godes.UniformDistr = godes.NewUniformDistr(true) 138 | 139 | // tableBusy is the boolean control variable than can be accessed and changed by number of Runners 140 | var tableBusy *godes.BooleanControl = godes.NewBooleanControl() 141 | 142 | // the Visitor is a Runner 143 | // any type of the Runner should be defined as struct // with the *godes.Runner as anonimous field 144 | type Visitor struct { 145 | *godes.Runner 146 | number int 147 | } 148 | 149 | var visitorsCount int = 0 150 | 151 | func (vst Visitor) Run() { // Any runner should have the Run method 152 | fmt.Printf("%-6.3f \t Visitor %v arrives \n", godes.GetSystemTime(), vst.number) 153 | tableBusy.Wait(false) // this will wait till the tableBusy control becomes false 154 | tableBusy.Set(true) // sets the tableBusy control to true - the table is busy 155 | fmt.Printf("%-6.3f \t Visitor %v gets the table \n", godes.GetSystemTime(), vst.number) 156 | godes.Advance(service.Get(10, 60)) //the function advance the simulation time by the value in the argument 157 | tableBusy.Set(false) // sets the tableBusy control to false - the table is idle 158 | fmt.Printf("%-6.3f \t Visitor %v leaves \n", godes.GetSystemTime(), vst.number) 159 | } 160 | func main() { 161 | var shutdown_time float64 = 8 * 60 162 | godes.Run() 163 | for { 164 | //godes.GetSystemTime() is the current simulation time 165 | if godes.GetSystemTime() < shutdown_time { 166 | //the function acivates the Runner 167 | godes.AddRunner(Visitor{&godes.Runner{}, visitorsCount}) 168 | godes.Advance(arrival.Get(0, 70)) 169 | visitorsCount++ 170 | } else { 171 | break 172 | } 173 | } 174 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 175 | } 176 | /* OUTPUT 177 | 0.000 Visitor 0 arrives 178 | 0.000 Visitor 0 gets the table 179 | 13.374 Visitor 0 leaves 180 | 37.486 Visitor 1 arrives 181 | 37.486 Visitor 1 gets the table 182 | 60.558 Visitor 1 leaves 183 | 98.737 Visitor 2 arrives 184 | 98.737 Visitor 2 gets the table 185 | 107.468 Visitor 3 arrives 186 | 146.824 Visitor 2 leaves 187 | 146.824 Visitor 3 gets the table 188 | 149.471 Visitor 4 arrives 189 | 171.623 Visitor 3 leaves 190 | 171.623 Visitor 4 gets the table 191 | 187.234 Visitor 4 leaves 192 | 207.523 Visitor 5 arrives 193 | 207.523 Visitor 5 gets the table 194 | 230.922 Visitor 6 arrives 195 | 245.859 Visitor 5 leaves 196 | 245.859 Visitor 6 gets the table 197 | 261.770 Visitor 7 arrives 198 | 269.668 Visitor 8 arrives 199 | 272.368 Visitor 6 leaves 200 | 272.368 Visitor 7 gets the table 201 | 290.484 Visitor 7 leaves 202 | 290.484 Visitor 8 gets the table 203 | 310.261 Visitor 9 arrives 204 | 333.570 Visitor 8 leaves 205 | 333.570 Visitor 9 gets the table 206 | 338.323 Visitor 10 arrives 207 | 354.874 Visitor 9 leaves 208 | 354.874 Visitor 10 gets the table 209 | 393.826 Visitor 10 leaves 210 | 397.720 Visitor 11 arrives 211 | 397.720 Visitor 11 gets the table 212 | 409.123 Visitor 12 arrives 213 | 436.817 Visitor 13 arrives 214 | 447.731 Visitor 14 arrives 215 | 455.705 Visitor 11 leaves 216 | 455.705 Visitor 13 gets the table 217 | 482.955 Visitor 13 leaves 218 | 482.955 Visitor 12 gets the table 219 | 496.034 Visitor 12 leaves 220 | 496.034 Visitor 14 gets the table 221 | 555.822 Visitor 14 leaves 222 | */ 223 | ``` 224 | *** 225 | 226 | #### Example 2. Restaurant. Godes Queues 227 | ###### Proces Description 228 | During the four working hours the visitors are entering the restaurant at random intervals and form the arrival queue. 229 | The inter arrival interval is the random variable with uniform distribution from 0 to 30 minutes. The restaurant employs two waiters who are servicing one visitor in a time. The service time is the random variable with uniform distribution from 10 to 60 minutes. 230 | The simulation itself is terminated when 231 | * Simulation time passes the four hours 232 | * Both waiters have finished servicing 233 | * There are no visitors in the arrival queue. 234 | 235 | The model calculates the average (arithmetic mean) of the visitors waiting time 236 | ```go 237 | package main 238 | 239 | import ( 240 | "fmt" 241 | "github.com/godes" 242 | ) 243 | 244 | // the arrival and service are two random number generators for the uniform distribution 245 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 246 | var service *godes.UniformDistr = godes.NewUniformDistr(true) 247 | 248 | // true when waiter should act 249 | var waitersSwt *godes.BooleanControl = godes.NewBooleanControl() 250 | 251 | // FIFO Queue for the arrived 252 | var visitorArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("arrivalQueue") 253 | 254 | // the Visitor is a Passive Object 255 | type Visitor struct { 256 | id int 257 | } 258 | 259 | // the Waiter is a Runner 260 | type Waiter struct { 261 | *godes.Runner 262 | id int 263 | } 264 | 265 | var visitorsCount int = 0 266 | var shutdown_time float64 = 4 * 60 267 | 268 | func (waiter *Waiter) Run() { 269 | 270 | for { 271 | waitersSwt.Wait(true) 272 | if visitorArrivalQueue.Len() > 0 { 273 | visitor := visitorArrivalQueue.Get() 274 | if visitorArrivalQueue.Len() == 0 { 275 | waitersSwt.Set(false) 276 | } 277 | fmt.Printf("%-6.3f \t Visitor %v is invited by waiter %v \n", godes.GetSystemTime(), visitor.(Visitor).id, waiter.id) 278 | godes.Advance(service.Get(10, 60)) //advance the simulation time by the visitor service time 279 | fmt.Printf("%-6.3f \t Visitor %v leaves \n", godes.GetSystemTime(), visitor.(Visitor).id) 280 | 281 | } 282 | if godes.GetSystemTime() > shutdown_time && visitorArrivalQueue.Len() == 0 { 283 | fmt.Printf("%-6.3f \t Waiter %v ends the work \n", godes.GetSystemTime(), waiter.id) 284 | break 285 | } 286 | } 287 | } 288 | 289 | func main() { 290 | 291 | for i := 0; i < 2; i++ { 292 | godes.AddRunner(&Waiter{&godes.Runner{}, i}) 293 | } 294 | godes.Run() 295 | for { 296 | 297 | visitorArrivalQueue.Place(Visitor{visitorsCount}) 298 | fmt.Printf("%-6.3f \t Visitor %v arrives \n", godes.GetSystemTime(), visitorsCount) 299 | waitersSwt.Set(true) 300 | godes.Advance(arrival.Get(0, 30)) 301 | visitorsCount++ 302 | if godes.GetSystemTime() > shutdown_time { 303 | break 304 | } 305 | } 306 | waitersSwt.Set(true) 307 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 308 | fmt.Printf("Average Waiting Time %6.3f \n", visitorArrivalQueue.GetAverageTime()) 309 | } 310 | /* OUTPUT 311 | 0.000 Visitor 0 arrives 312 | 0.000 Visitor 0 is invited by waiter 0 313 | 13.374 Visitor 0 leaves 314 | 16.066 Visitor 1 arrives 315 | 16.066 Visitor 1 is invited by waiter 1 316 | 39.137 Visitor 1 leaves 317 | 42.316 Visitor 2 arrives 318 | 42.316 Visitor 2 is invited by waiter 1 319 | 46.058 Visitor 3 arrives 320 | 46.058 Visitor 3 is invited by waiter 0 321 | 64.059 Visitor 4 arrives 322 | 70.857 Visitor 3 leaves 323 | 70.857 Visitor 4 is invited by waiter 0 324 | 86.468 Visitor 4 leaves 325 | 88.938 Visitor 5 arrives 326 | 88.938 Visitor 5 is invited by waiter 0 327 | 90.403 Visitor 2 leaves 328 | 98.966 Visitor 6 arrives 329 | 98.966 Visitor 6 is invited by waiter 1 330 | 112.187 Visitor 7 arrives 331 | 115.572 Visitor 8 arrives 332 | 125.475 Visitor 6 leaves 333 | 125.475 Visitor 7 is invited by waiter 1 334 | 127.275 Visitor 5 leaves 335 | 127.275 Visitor 8 is invited by waiter 0 336 | 132.969 Visitor 9 arrives 337 | 143.591 Visitor 7 leaves 338 | 143.591 Visitor 9 is invited by waiter 1 339 | 144.995 Visitor 10 arrives 340 | 164.895 Visitor 9 leaves 341 | 164.895 Visitor 10 is invited by waiter 1 342 | 170.361 Visitor 8 leaves 343 | 170.451 Visitor 11 arrives 344 | 170.451 Visitor 11 is invited by waiter 0 345 | 175.338 Visitor 12 arrives 346 | 187.207 Visitor 13 arrives 347 | 191.885 Visitor 14 arrives 348 | 203.848 Visitor 10 leaves 349 | 203.848 Visitor 12 is invited by waiter 1 350 | 213.596 Visitor 15 arrives 351 | 228.436 Visitor 11 leaves 352 | 228.436 Visitor 13 is invited by waiter 0 353 | 231.098 Visitor 12 leaves 354 | 231.098 Visitor 14 is invited by waiter 1 355 | 231.769 Visitor 16 arrives 356 | 241.515 Visitor 13 leaves 357 | 241.515 Visitor 15 is invited by waiter 0 358 | 287.864 Visitor 15 leaves 359 | 287.864 Visitor 16 is invited by waiter 0 360 | 290.886 Visitor 14 leaves 361 | 290.886 Waiter 1 ends the work 362 | 330.903 Visitor 16 leaves 363 | 330.903 Waiter 0 ends the work 364 | Average Waiting Time 15.016 365 | */ 366 | ``` 367 | *** 368 | 369 | #### Example 3. Restaurant. Multiple Runs 370 | ###### Proces Description 371 | This is the same process as in Example 2. Simulation is repeated 5 times. 372 | ```go 373 | package main 374 | 375 | import ( 376 | "fmt" 377 | "github.com/godes" 378 | ) 379 | 380 | // the arrival and service are two random number generators for the uniform distribution 381 | var arrival *godes.UniformDistr = godes.NewUniformDistr(true) 382 | var service *godes.UniformDistr = godes.NewUniformDistr(true) 383 | 384 | // true when waiter should act 385 | var waitersSwt *godes.BooleanControl = godes.NewBooleanControl() 386 | 387 | // FIFO Queue for the arrived 388 | var visitorArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("0") 389 | 390 | // the Visitor is a Passive Object 391 | type Visitor struct { 392 | id int 393 | } 394 | 395 | // the Waiter is a Runner 396 | type Waiter struct { 397 | *godes.Runner 398 | id int 399 | } 400 | 401 | var visitorsCount int = 0 402 | var shutdown_time float64 = 4 * 60 403 | 404 | func (waiter *Waiter) Run() { 405 | for { 406 | waitersSwt.Wait(true) 407 | if visitorArrivalQueue.Len() > 0 { 408 | visitorArrivalQueue.Get() 409 | if visitorArrivalQueue.Len() == 0 { 410 | waitersSwt.Set(false) 411 | } 412 | godes.Advance(service.Get(10, 60)) //advance the simulation time by the visitor service time 413 | 414 | } 415 | if godes.GetSystemTime() > shutdown_time && visitorArrivalQueue.Len() == 0 { 416 | break 417 | } 418 | 419 | } 420 | } 421 | 422 | func main() { 423 | for runs := 0; runs < 5; runs++ { 424 | for i := 0; i < 2; i++ { 425 | godes.AddRunner(&Waiter{&godes.Runner{}, i}) 426 | } 427 | godes.Run() 428 | for { 429 | visitorArrivalQueue.Place(Visitor{visitorsCount}) 430 | waitersSwt.Set(true) 431 | godes.Advance(arrival.Get(0, 30)) 432 | visitorsCount++ 433 | if godes.GetSystemTime() > shutdown_time { 434 | break 435 | } 436 | } 437 | waitersSwt.Set(true) 438 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 439 | fmt.Printf(" Run # %v \t Average Time in Queue=%6.3f \n", runs, visitorArrivalQueue.GetAverageTime()) 440 | //clear after each run 441 | waitersSwt.Clear() 442 | visitorArrivalQueue.Clear() 443 | godes.Clear() 444 | 445 | } 446 | } 447 | /* OUTPUT 448 | 449 | Run # 0 Average Time in Queue=15.016 450 | Run # 1 Average Time in Queue=17.741 451 | Run # 2 Average Time in Queue=49.046 452 | Run # 3 Average Time in Queue=30.696 453 | Run # 4 Average Time in Queue=14.777 454 | */ 455 | 456 | ``` 457 | *** 458 | #### Example 4. Machine Shop. Godes Interrupt and Resume Feature. 459 | ###### Proces Description 460 | A workshop has *n* identical machines. A stream of jobs (enough to 461 | keep the machines busy) arrives. Each machine breaks down 462 | periodically. Repairs are carried out by one repairman. 463 | The repairman continues them when he is done 464 | with the machine repair. The workshop works continuously. 465 | ```go 466 | package main 467 | 468 | import ( 469 | "fmt" 470 | "github.com/godes" 471 | ) 472 | 473 | const PT_MEAN = 10.0 // Avg. processing time in minutes 474 | const PT_SIGMA = 2.0 // Sigma of processing time 475 | const MTTF = 300.0 // Mean time to failure in minutes 476 | const REPAIR_TIME = 30.0 // Time it takes to repair a machine in minutes 477 | const REPAIR_TIME_SIGMA = 1.0 // Sigma of repair time 478 | 479 | const NUM_MACHINES = 10 480 | const SHUT_DOWN_TIME = 4 * 7 * 24 * 60 481 | 482 | // random generator for the processing time - normal distribution 483 | var processingGen *godes.NormalDistr = godes.NewNormalDistr(true) 484 | 485 | // random generator for the time until the next failure for a machine - exponential distribution 486 | var breaksGen *godes.ExpDistr = godes.NewExpDistr(true) 487 | 488 | // true when repairman is available for carrying a repair 489 | var repairManAvailableSwt *godes.BooleanControl = godes.NewBooleanControl() 490 | 491 | type Machine struct { 492 | *godes.Runner 493 | partsCount int 494 | number int 495 | finished bool 496 | } 497 | 498 | func (machine *Machine) Run() { 499 | for { 500 | godes.Advance(processingGen.Get(PT_MEAN, PT_SIGMA)) 501 | machine.partsCount++ 502 | if godes.GetSystemTime() > SHUT_DOWN_TIME { 503 | machine.finished = true 504 | break 505 | } 506 | 507 | } 508 | fmt.Printf(" Machine # %v %v \n", machine.number, machine.partsCount) 509 | } 510 | 511 | type MachineRepair struct { 512 | *godes.Runner 513 | machine *Machine 514 | } 515 | 516 | func (machineRepair *MachineRepair) Run() { 517 | machine := machineRepair.machine 518 | for { 519 | godes.Advance(breaksGen.Get(1 / MTTF)) 520 | if machine.finished { 521 | break 522 | } 523 | 524 | interrupted := godes.GetSystemTime() 525 | godes.Interrupt(machine) 526 | repairManAvailableSwt.Wait(true) 527 | if machine.finished { 528 | break 529 | } 530 | repairManAvailableSwt.Set(false) 531 | godes.Advance(processingGen.Get(REPAIR_TIME, REPAIR_TIME_SIGMA)) 532 | if machine.finished { 533 | break 534 | } 535 | //release repairman 536 | repairManAvailableSwt.Set(true) 537 | //resume machine and change the scheduled time to compensate delay 538 | godes.Resume(machine, godes.GetSystemTime()-interrupted) 539 | 540 | } 541 | 542 | } 543 | 544 | func main() { 545 | 546 | godes.Run() 547 | repairManAvailableSwt.Set(true) 548 | var m *Machine 549 | for i := 0; i < NUM_MACHINES; i++ { 550 | m = &Machine{&godes.Runner{}, 0, i, false} 551 | godes.AddRunner(m) 552 | godes.AddRunner(&MachineRepair{&godes.Runner{}, m}) 553 | } 554 | godes.WaitUntilDone() 555 | } 556 | /* OUTPUT 557 | Machine # 1 3382 558 | Machine # 7 3255 559 | Machine # 4 3343 560 | Machine # 5 3336 561 | Machine # 6 3248 562 | Machine # 0 3369 563 | Machine # 2 3378 564 | Machine # 9 3342 565 | Machine # 8 3362 566 | Machine # 3 3213 567 | */ 568 | ``` 569 | *** 570 | 571 | #### Example 5. Bank Counter. Godes Wait with Timeout Feature. 572 | ###### Proces Description 573 | This example models a bank counter and customers arriving at random times. Each customer has a certain patience. It waits to get to the counter until she’s at the end of her tether. If she gets to the counter, she uses it for a while before releasing it. 574 | 575 | ```go 576 | package main 577 | 578 | import ( 579 | "fmt" 580 | "github.com/godes" 581 | ) 582 | 583 | const NEW_CUSTOMERS = 5 // Total number of customers 584 | const INTERVAL_CUSTOMERS = 12.00 // Generate new customers roughly every x minites 585 | const SERVICE_TIME = 12.0 586 | const MIN_PATIENCE = 1 // Min. customer patience 587 | const MAX_PATIENCE = 3 // Max. customer patience 588 | 589 | // random generator for the arrival interval - expovariate distribution 590 | var arrivalGen *godes.ExpDistr = godes.NewExpDistr(true) 591 | 592 | // random generator for the patience time time - uniform distribution 593 | var patienceGen *godes.UniformDistr = godes.NewUniformDistr(true) 594 | 595 | // random generator for the service time - expovariate distribution 596 | var serviceGen *godes.ExpDistr = godes.NewExpDistr(true) 597 | 598 | // true when Counter 599 | var counterAvailable *godes.BooleanControl = godes.NewBooleanControl() 600 | 601 | type Customer struct { 602 | *godes.Runner 603 | name int 604 | } 605 | 606 | func (customer *Customer) Run() { 607 | 608 | arrivalTime := godes.GetSystemTime() 609 | patience := patienceGen.Get(MIN_PATIENCE, MAX_PATIENCE) 610 | fmt.Printf(" %6.3f Customer %v : Here I am My patience=%6.3f \n", godes.GetSystemTime(), customer.name, patience) 611 | 612 | counterAvailable.WaitAndTimeout(true, patience) 613 | if !counterAvailable.GetState() { 614 | fmt.Printf(" %6.3f Customer %v : Reneged after %6.3f \n", godes.GetSystemTime(), customer.name, godes.GetSystemTime()-arrivalTime) 615 | } else { 616 | counterAvailable.Set(false) 617 | 618 | fmt.Printf(" %6.3f Customer %v : Waited %6.3f \n", godes.GetSystemTime(), customer.name, godes.GetSystemTime()-arrivalTime) 619 | godes.Advance(serviceGen.Get(1 / SERVICE_TIME)) 620 | fmt.Printf(" %6.3f Customer %v : Finished \n", godes.GetSystemTime(), customer.name) 621 | counterAvailable.Set(true) 622 | } 623 | 624 | } 625 | 626 | func main() { 627 | counterAvailable.Set(true) 628 | godes.Run() 629 | for i := 0; i < NEW_CUSTOMERS; i++ { 630 | godes.AddRunner(&Customer{&godes.Runner{}, i}) 631 | godes.Advance(arrivalGen.Get(1 / INTERVAL_CUSTOMERS)) 632 | } 633 | 634 | godes.WaitUntilDone() 635 | 636 | } 637 | /* OUTPUT 638 | 0.000 Customer 0 : Here I am My patience= 1.135 639 | 0.000 Customer 0 : Waited 0.000 640 | 5.536 Customer 0 : Finished 641 | 11.141 Customer 1 : Here I am My patience= 1.523 642 | 11.141 Customer 1 : Waited 0.000 643 | 34.482 Customer 2 : Here I am My patience= 2.523 644 | 36.123 Customer 1 : Finished 645 | 36.123 Customer 2 : Waited 1.641 646 | 38.869 Customer 2 : Finished 647 | 39.943 Customer 3 : Here I am My patience= 1.592 648 | 39.943 Customer 3 : Waited 0.000 649 | 49.113 Customer 4 : Here I am My patience= 1.224 650 | 50.337 Customer 4 : Reneged after 1.224 651 | 59.948 Customer 3 : Finished 652 | */ 653 | ``` 654 | *** 655 | #### Example 6. Bank. Sngle Run, FIFO Queue, Parallel Resources, StatCollector 656 | ###### Proces Description 657 | A bank employs three tellers and the customers form a queue for all three tellers. The doors of the bank close after eight hours. The simulation is ended when the last customer has been served. 658 | ###### Task 659 | Execute single simulation run, calculate average, standard deviation, 660 | confidence interval, lower and upper bounds, minimum and maximum values for the 661 | following performance measures: total elapsed time, queue length, queueing time 662 | service time. 663 | ###### Model Features 664 | * **FIFO Queue.** The customer object is placed in the FIFO arrival queue as soon as the customer is created. 665 | * **Parallel Resources.** The application constructs Tellers object to model tellers as a set of resources. 666 | The object 'provides' tellers to the customer located in the Queue head and "releases" the teller when customer is serviced. 667 | Maximum 3 tellers can be provided simultaneously. The interlocking between catching request is performed using godes BooleanControl object. 668 | * **Collection and processing of statistics.** While finishing a customer run the application creates data arrays for each measure. At the end of simulation, the application creates StatCollector object and performs descriptive statistical analysis. The following statistical parameters are calculated for each measure array: 669 | Observ - number of observations, Average - average (mean) value, Std Dev- standard deviation, L-Bound-lower bound of the confidence interval with 95% probability, U-Bound-upper bound of the confidence interval with 95% probability, 670 | Minimum value,Maximum value 671 | ```go 672 | package main 673 | 674 | import ( 675 | "fmt" 676 | "github.com/godes" 677 | ) 678 | //Input Parameters 679 | const ( 680 | ARRIVAL_INTERVAL = 0.5 681 | SERVICE_TIME = 1.3 682 | SHUTDOWN_TIME = 8 * 60. 683 | ) 684 | // the arrival and service are two random number generators for the exponential distribution 685 | var arrival *godes.ExpDistr = godes.NewExpDistr(true) 686 | var service *godes.ExpDistr = godes.NewExpDistr(true) 687 | // true when any counter is available 688 | var counterSwt *godes.BooleanControl = godes.NewBooleanControl() 689 | // FIFO Queue for the arrived customers 690 | var customerArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("0") 691 | 692 | 693 | var tellers *Tellers 694 | var measures [][]float64 695 | var titles = []string{ 696 | "Elapsed Time", 697 | "Queue Length", 698 | "Queueing Time", 699 | "Service Time", 700 | } 701 | 702 | var availableTellers int = 0; 703 | // the Tellers is a Passive Object represebting resource 704 | type Tellers struct { 705 | max int 706 | } 707 | 708 | func (tellers *Tellers) Catch(customer *Customer) { 709 | for { 710 | counterSwt.Wait(true) 711 | if customerArrivalQueue.GetHead().(*Customer).id == customer.id { 712 | break 713 | } else { 714 | godes.Yield() 715 | } 716 | } 717 | availableTellers++ 718 | if availableTellers == tellers.max { 719 | counterSwt.Set(false) 720 | } 721 | } 722 | 723 | func (tellers *Tellers) Release() { 724 | availableTellers-- 725 | counterSwt.Set(true) 726 | } 727 | 728 | // the Customer is a Runner 729 | type Customer struct { 730 | *godes.Runner 731 | id int 732 | } 733 | 734 | func (customer *Customer) Run() { 735 | a0 := godes.GetSystemTime() 736 | tellers.Catch(customer) 737 | a1 := godes.GetSystemTime() 738 | customerArrivalQueue.Get() 739 | qlength := float64(customerArrivalQueue.Len()) 740 | godes.Advance(service.Get(1. / SERVICE_TIME)) 741 | a2 := godes.GetSystemTime() 742 | tellers.Release() 743 | collectionArray := []float64{a2 - a0, qlength, a1 - a0, a2 - a1} 744 | measures = append(measures, collectionArray) 745 | } 746 | func main() { 747 | measures = [][]float64{} 748 | tellers = &Tellers{3} 749 | godes.Run() 750 | counterSwt.Set(true) 751 | count := 0 752 | for { 753 | customer := &Customer{&godes.Runner{}, count} 754 | customerArrivalQueue.Place(customer) 755 | godes.AddRunner(customer) 756 | godes.Advance(arrival.Get(1. / ARRIVAL_INTERVAL)) 757 | if godes.GetSystemTime() > SHUTDOWN_TIME { 758 | break 759 | } 760 | count++ 761 | } 762 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 763 | collector := godes.NewStatCollector(titles, measures) 764 | collector.PrintStat() 765 | fmt.Printf("Finished \n") 766 | } 767 | /* OUTPUT 768 | Variable # Average Std Dev L-Bound U-Bound Minimum Maximum 769 | Elapsed Time 944 2.591 1.959 2.466 2.716 0.005 11.189 770 | Queue Length 944 2.411 3.069 2.215 2.607 0.000 13.000 771 | Queueing Time 944 1.293 1.533 1.195 1.391 0.000 6.994 772 | Service Time 944 1.298 1.247 1.219 1.378 0.003 7.824 773 | */ 774 | ``` 775 | #### Example 7. Bank. Multiple Runs, FIFO Queue, Parallel Resources, StatCollector 776 | ###### Procces Description 777 | See example 6. 778 | ###### Task 779 | Execute multiple simulation runs, calculate Average, Standard Deviation, 780 | confidence intervall lower and upper bounds,minimu and maximum for the 781 | following performance measures: total elapsed time, queue length, queueing time, service time. 782 | ```go 783 | package main 784 | import ( 785 | "fmt" 786 | "github.com/godes" 787 | 788 | ) 789 | 790 | //Input Parameters 791 | const ( 792 | ARRIVAL_INTERVAL = 0.5 793 | SERVICE_TIME = 1.3 794 | SHUTDOWN_TIME = 8 * 60. 795 | INDEPENDENT_RUNS = 100 796 | ) 797 | 798 | // the arrival and service are two random number generators for the exponential distribution 799 | var arrival *godes.ExpDistr = godes.NewExpDistr(true) 800 | var service *godes.ExpDistr = godes.NewExpDistr(true) 801 | 802 | // true when any counter is available 803 | var counterSwt *godes.BooleanControl = godes.NewBooleanControl() 804 | 805 | // FIFO Queue for the arrived customers 806 | var customerArrivalQueue *godes.FIFOQueue = godes.NewFIFOQueue("0") 807 | 808 | var tellers *Tellers 809 | var statistics [][]float64 810 | var replicationStats [][]float64 811 | var titles = []string{ 812 | "Elapsed Time", 813 | "Queue Length", 814 | "Queueing Time", 815 | "Service Time", 816 | } 817 | 818 | var availableTellers int = 0 819 | 820 | // the Tellers is a Passive Object represebting resource 821 | type Tellers struct { 822 | max int 823 | } 824 | 825 | func (tellers *Tellers) Catch(customer *Customer) { 826 | for { 827 | counterSwt.Wait(true) 828 | if customerArrivalQueue.GetHead().(*Customer).GetId() == customer.GetId() { 829 | break 830 | } else { 831 | godes.Yield() 832 | } 833 | } 834 | availableTellers++ 835 | if availableTellers == tellers.max { 836 | counterSwt.Set(false) 837 | } 838 | } 839 | 840 | func (tellers *Tellers) Release() { 841 | availableTellers-- 842 | counterSwt.Set(true) 843 | } 844 | 845 | // the Customer is a Runner 846 | type Customer struct { 847 | *godes.Runner 848 | id int 849 | } 850 | 851 | func (customer *Customer) Run() { 852 | a0 := godes.GetSystemTime() 853 | tellers.Catch(customer) 854 | a1 := godes.GetSystemTime() 855 | customerArrivalQueue.Get() 856 | qlength := float64(customerArrivalQueue.Len()) 857 | godes.Advance(service.Get(1. / SERVICE_TIME)) 858 | a2 := godes.GetSystemTime() 859 | tellers.Release() 860 | collectionArray := []float64{a2 - a0, qlength, a1 - a0, a2 - a1} 861 | replicationStats = append(replicationStats, collectionArray) 862 | } 863 | 864 | func (customer *Customer) GetId() int{ 865 | return customer.id 866 | } 867 | 868 | 869 | 870 | func main() { 871 | statistics = [][]float64{} 872 | 873 | tellers = &Tellers{3} 874 | for i := 0; i < INDEPENDENT_RUNS; i++ { 875 | replicationStats = [][]float64{} 876 | godes.Run() 877 | counterSwt.Set(true) 878 | customerArrivalQueue.Clear() 879 | count := 0 880 | for { 881 | customer := &Customer{&godes.Runner{}, count} 882 | customerArrivalQueue.Place(customer) 883 | godes.AddRunner(customer) 884 | godes.Advance(arrival.Get(1. / ARRIVAL_INTERVAL)) 885 | if godes.GetSystemTime() > SHUTDOWN_TIME { 886 | break 887 | } 888 | count++ 889 | } 890 | godes.WaitUntilDone() // waits for all the runners to finish the Run() 891 | godes.Clear() 892 | replicationCollector := godes.NewStatCollector(titles, replicationStats) 893 | 894 | collectionArray := []float64{ 895 | replicationCollector.GetAverage(0), 896 | replicationCollector.GetAverage(1), 897 | replicationCollector.GetAverage(2), 898 | replicationCollector.GetAverage(3), 899 | } 900 | statistics = append(statistics, collectionArray) 901 | } 902 | 903 | collector := godes.NewStatCollector(titles, statistics) 904 | collector.PrintStat() 905 | fmt.Printf("Finished \n") 906 | } 907 | /* OUTPUT 908 | Variable # Average Std Dev L-Bound U-Bound Minimum Maximum 909 | Elapsed Time 100 3.672 1.217 3.433 3.910 1.980 8.722 910 | Queue Length 100 4.684 2.484 4.197 5.171 1.539 14.615 911 | Queueing Time 100 2.368 1.194 2.134 2.602 0.810 7.350 912 | Service Time 100 1.304 0.044 1.295 1.312 1.170 1.432 913 | Finished 914 | */ 915 | ``` 916 | --------------------------------------------------------------------------------