forked from salabim/salabim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
changelog.txt
4696 lines (3790 loc) · 197 KB
/
changelog.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
salabim changelog
version 21.1.5 2021-
==========================
Changed functionality / bug fix (0)
-----------------------------------
The Monitor.percentile method has been completely rewritten, to fix bugs and
introduce the interpolation parameter.
For non weighted monitors, the method is now exactly equivalent to the numpy percentile
function. So it is now possible to use the interpolation parameters as follows:
This optional parameter specifies the interpolation method to use when the
desired percentile lies between two data points i < j:
‘linear’: i + (j - i) * fraction, where fraction is the fractional part of the index surrounded by i and j
‘lower’: i.
‘higher’: j.
‘nearest’: i or j, whichever is nearest.
‘midpoint’: (i + j) / 2.
For weighted and level monitors the behaviour is quite different (there's no equivalent in numpy),
as only tallied values will be returned, apart from when the value is undetermined,
i.e. on change of value. In that case, the interpolation parameter will be applied as follows:
‘linear’, 'midpoint': mean of the two tallied values
‘lower’: lower tallied value
‘higher’: highed tallie value
In all cases, interpolation defaults to 'linear'.
The test_monitor.py file has been updated to test the new functionality.
Added functionality (0)
-----------------------
The Monitor.median method now also supports the interpolation parameter
(see above description of percentile)
Added functionality (1)
-----------------------
The classes
AnimateCircle, AnimateImage, AnimateLine, AnimatePolygon, AnimateRectangle, AnimateText,
Animate3dBar, Animate3dBox, Animate3dGrid, Animate3dLine, Animate3dObj, Animate3dRectangle, Animate3dSphere and
AnimateQueue
now have an additional method: add_attr
This is useful to add attribute(s) to an animation object, without having to assign the object a name and
assign the attribute(s) in seperate line(s).
So, instead of
for xx in range(10):
an = sim.AnimateRectangle(spec=lambda arg,t: (arg.xx, 0, arg.xx + 10,arg.xx + t * 10))
an.xx = xx * 20
we can now say
for xx in range(10):
sim.AnimateRectangle(spec=lambda arg,t: (arg.xx, 0, arg.xx + 10, arg.xx + t * 10)).add_attr(xx=xx * 20)
It is possible to assign several attributes at once, like:
sim.AnimateRectangle(spec=lambda arg,t: (arg.xx, arg.yy,arg.xx + 10,arg.xx + t * 10)).add_attr(xx=xx * 20, yy=xx * 5)
, but also like
sim.AnimateRectangle(spec=lambda arg,t: (arg.xx, arg.yy,arg.xx + 10, arg.xx + t * 10)).add_attr(xx=xx * 20).add_attr(yy=xx * 5)
The method add_attr just returns the "calling" object.
Note that it is not allowed to add any attributes that are already defined. So
sim.AnimateRectangle(spec=(0, 0, 10, 10).add_attr(x=5)
is not allowed as x is already defined by salabim.
Added functionality (2)
-----------------------
The method env.animate() now can use '?', to enable animation, if possible.
If not possible, animation is ignored.
The method env.animate3d() now can use '?', to enable 3D-animation, if possible.
If not possible, 3D-animation is ignored.
Both functionalities are also available via env.animation_parameters().
Bug fix (0)
-----------
The offsetx and offsety parameters of all animation objects were always in screen_coordinates, even
if screen_coordinates == False. Fixed.
Documentation bug fix (0)
-------------------------
Docstring of AnimateMonitor and Monitor.animate() updated.
Bug fix (1)
-----------
Under Pythonista, 3D animation was just ignored, instead of issueing an error message
(as Pythonista does not support OpenGL). Fixed.
version 21.1.4 2021-07-18
==========================
New functionality (0)
---------------------
Introduced Environment.insert_frame() that can insert a PIL image or file (specified as str or pathlib.Path)
into the video stream.
This is particularly useful to add an introduction and explanatory frames.
Note that each frame is 1/30 seconds and you can specify the number of frames.
The image is automatically scaled to fit the current video resolution.
New functionality (1)
---------------------
The method Environment.snapshot() now has an extra parameter 'video_mode' that can be "2d" (the default),
"3d" or "screen".
Note that "3d" is only available if animate3d is True.
The resulting image is never scaled.
Changed functionality (0)
-------------------------
The method Environment.Animate3dObj now postpones the actual loading of the file till the moment it is
required. That makes it possible to call Animate3dObj even if no 3d animation is activated and
even possible.
Also, the filename and show_warnings may now be changed dynamically.
Internal changes (0)
--------------------
Changed the internals of _save_frame (now via insert_frame) and _capture_image.
Internal changes (1)
--------------------
If ImageGrab is not available on a platform, video_mode 'screen' is not supported
for video and snapshot and properly detected.
version 21.1.3 2021-07-07
==========================
New functionality (0)
---------------------
From this version on, it is possible to select the "video_mode" of a video.
You can choose from
- "2d" to film the normal tkinter window
- "3d" to film the OpenGL 3d animation window (only if animate3d is True)
- "screen" to film the full screen (screen capture). This is particularly handy to make a video of both
the 2d and 3d windows.
The method Environment.video_mode can be used to specify the video_mode. The default is "2d".
Alternatively, the video_mode parameter of Environment.animation_parameters may be used as well.
It is possible to change the video_mode during a recording.
New functionality (1)
---------------------
It is now possible to specify the resolution of the to be recorded video.
This can be done with Environment.video_width() and Environment.video_height().
Alternatively, the video_width and video_height parameters of Environment.animation_parameters may be used as well.
If the video_width/video_height is "auto" (the default), the resolution is taken from the video_mode at the time
video recording was started.
The captured images are always scaled to the given resolution, which might lead to horizontal or vertical
black bars to pad the image.
Added functionality (0)
-----------------------
The camera control keys in 3d animation now work also when the OpenGL window has the focus.
version 21.1.2 2021-07-03
==========================
Bugfix (0)
----------
Version 21.1.1 tried erroneously to import ycecream. This dependency has been removed.
BTW, my ycecream package is a very useful tool for debugging and benchmarking any Python program,
including salabim models.
See www.github.com/salabim/ycecream , if you are interested.
version 21.1.1 2021-07-02
==========================
New functionality (0)
---------------------
Introduced Animate3dSphere to animate a sphere.
Enhanced functionality (0)
--------------------------
Blind animation now also supports 3D animations. Note that OpenGL needs to be installed, though.
Functional change (0)
---------------------
The way the mode monitor was implemented made that the garbage collector could never remove a component,
thus causing a memory leak.
In order to change the behaviour, it won't be possible to set the mode of a component with
Component.mode.value = ...
anymore (as was mentioned in the 20.0.4 release notes).
You have to set the mode via one of the process control functions or with set_mode().
Bugfix (0)
----------
A bug in version 21.1.0 required to have OpenGL installed, even when no 3D animation was used. Fixed.
This bugfix also makes startup of non 3D animation models faster.
Bugfix (1)
----------
Under certain conditions, the file/linenumber in the trace was not shown correctly in the trace. Fixed.
Sample models update (0)
------------------------
Updated several models in the sample models folder.
Documentation update (0)
------------------------
A section on how to install OpenGL under Windows has been added to the documentation.
Here's the same text (slightly differently formatted):
If, after
pip install PyOpenGL
you get a runtime error that glutInit is not defined, try to install from the
Unofficial Windows Binaries for Python Extension Packages site at
https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl
Find the right 3.1.5 or later version for you
(e.g. for Python 3.7, 64 bits you use PyOpenGL-3.1.5-cp37-cp37m-win_amd64.whl) and download that.
Then issue
pip install wheelfile
,like (for the above package)
pip install PyOpenGL-3.1.5-cp37-cp37m-win_amd64.whl
and it should work.
It is reecommended to install pyopengl_accelerate in the same way, like
pip install PyOpenGL_accelerate-3.1.5-cp37-cp37m-win_amd64.whl
version 21.1.0 2021-06-17
==========================
Added functionality (0)
-----------------------
This version introduces 3D animation. That's a very powerful feature that
requires users at least to know how to do 2D animation.
Note that the API is still work in progress and might change.
Note that the documetation is not properly updated. Only the reference section
contains is now up-to-date.
I intend to update the documentation soon. Volunteers to help with that are welcome!
The sample 3d models folder on GitHub contains a couple of demo models, that
could be used as a start.
Changed functionality (0)
-------------------------
When Stop is pressed from the menu, the program now correctly exits, instead of returning to main.
Changed functionality (1)
-------------------------
Color name 'lightgrey' added, as this is present in the X11 color name standard, but was absent in salabim.
Color name 'sandybrown' updated, to be in line with the X11 color name standard
Bugfix (0)
----------
If video specified an extension with a coded, like .mp4+h264, the output file extension incorrectly
contained the codec information.
From this version on, the codec information is skipped in the resulting filename.
version 21.0.4 2021-05-01
==========================
Bugfix (0)
-----------
When a non-level monitor was sliced, the stop value was included in the slice,
so causing an overlap with a slice that started at that value.
Fixed.
Bugfix (1)
-----------
When run on a Chromebook (at least on a Lenovo Duet), the menu buttons were far too big. Fixed.
Bugfix (2)
-----------
On Linux platforms fonts not having the .ttf extension (particularly .TTF) were not found. Fixed.
Bugfix (3)
-----------
PeriodMonitors now get the stats_only flag from the parent monitor.
PeriodMonitors now correctly tally the weight.
version 21.0.3 2021-03-20
==========================
Added functionality (0)
-----------------------
Environment.snapshot() now also supports the .ico image file format.
This may be useful when (mis)using salabim as an icon generator.
Bug fix (0)
----------
Bug in initializing system monitors at init of a component (not setting env to self.env). Fixed.
Bug fix (1)
-----------
When producing videos with blind_animation=True (to allow running without tkinter installed),
the Environment.animation_pre_tick_sys() method was not called prior to saving a frame,
causing problems with animating queues. Fixed.
version 21.0.2 2021-01-25
==========================
Improved functionality (0)
--------------------------
The probabilities parameter of sim.Pdf can now be any iterable, so not just list or tuple.
This can be useful when using the keys or values of a dict as probabilities.
Improved functionality (1)
--------------------------
The spec parameter of sim.Pdf can now be a dict, where the keys are the x-values and the values
are the probabilities. In that case, the probabilities parameter can't be used.
E.g.
d = sim.Pdf({1:10, 2:70, 3:20})
d = sim.Pdf(dict(red=45, yellow=10, green=45))
Change in distribution (0)
--------------------------
The test scripts are now also available on GitHub (in the /test folder).
Bug fix (0)
-----------
Slicing of a merged level monitor didn't work properly, because the attribute start of the merged
monitor was not set correctly. Fixed.
version 21.0.1 2021-01-14
==========================
Bug fix (0)
-----------
Bug in Component._remove() fixed.
version 21.0.0 2021-01-14
==========================
New functionality (0)
---------------------
Added Environment.title()
With this, the title of the canvas window can be set. The title is set to "", the title will be completely suppressed.
Added Environment.show_menu_buttons()
With this, the menu buttons can be hidden
Added parameter title and show_menu_buttons to Environment.animation_parameters
The title of the canvas window now defaults to salabim, instead of tk. This can be overruled with Environment.title()
version 20.0.6 2020-12-18
==========================
New functionality (0)
---------------------
Monitors can now optionally collect statistics only.
This minimizes memory usage as individual tallies will not be stored in that case.
But beware that some important functionality is not available in stats_only monitors (see below).
You can define the stats_only status at creation time of the monitor, like:
m = sim.Monitor('m', stats_only=True)
But, it is also possible to reset a monitor with a different stats_only value.
This can be useful if you want a system monitor, like Component.mode or Component.status to not keep individual
tallied values:
class Car(sim.Component):
def setup(self):
self.mode.reset(stats_only=True)
When stats_only is active, values are always forced to numeric, with a fallback value of 0.
Monitor with stats_only=True support
__call__/get (without arguments), base_name, deregister, duration, duration_zero, maximum, mean,
minimum, monitor, name, number_of_entries, number_of_entries_zero, print_histogram,
print_histograms, print_statistics, register, rename, reset, reset_monitors, sequence_number,
setup, stats_only, std, t, tally, weight, weight_zero
Monitors with stats_only=True do NOT support (these will raise a NotImplementedError):
__call__/get (with an argument), arithmeric operations (+, *, /), animate, bin_duration,
bin_number_of_entries, bin_weight, freeze, histogram_autoscale, median, merge, multiply,
percentile, slice, slicing, to_days, to_hours, to_microseconds, to_milliseconds, to_minutes,
to_seconds, to_time_unit, to_weeks, to_years, tx, value_duration, value_number_of_entries,
value_weight, values, x, xduration, xt, xweight
In line with the above, Queue.reset_monitors, Resource.reset_monitors, State.reset_monitors now
have a stats_only parameter, with which all monitors can set/reset the stats_only status.
So, if you want all eight monitors belonging to a resource, but the requesters' to be stats_only, you can write
r.reset_monitors(stats_only=True)
r.requesters.monitors(stats_only=False)
The current stats_only mode can be queried with Monitor.stats_only() .
New functionality (1)
---------------------
Queue.all_monitors() returns all (2) monitors associated with the queue
Resource.all_monitors() returns all (8) monitors associated with the resource
State.all_monitors() returns all (3) monitors associated with the state
Change of functionality (0)
---------------------------
Monitor.t is no longer a property, but a method. That means that to get the last tally time and value
of monitor m, you can write
last_tally_t = m.t()
last_tally = m() # or last_tally = m.get()
Bug fix (0)
-----------
Non-level monitors did not always calculate the ex0=True mean and ex0=True std correctly when weights
other than one were in the monitor. Fixed.
version 20.0.5 2020-11-24
==========================
Added functionality (0)
-----------------------
The methods Component.request() and Component.wait() now also support the urgent and priority parameters, which
make it possible to fine-tune race conditions.
Added functionality (1)
-----------------------
ComponentGenerator can now 'propagate' keyword parameters to the components generated.
In line with this, the name parameter is changed to generator_name, in order to make it possible to
define the name of the generated components.
Example:
import salabim as sim
class Worker(sim.Component):
def process(self, collar):
print(f"I am {self.name()} and I have a {collar} collar")
env = sim.Environment(trace=True)
sim.ComponentGenerator(Worker, "technician generator", iat=sim.Uniform(2,4), name="technician.", collar="blue")
sim.ComponentGenerator(Worker, "manager generator", iat=sim.Uniform(3,6), name="manager.", collar="white")
env.run(10)
Added functionality (2)
-----------------------
sim.Environment() has now an extra parameter, blind_animation that can be used to create videos
without showing the animation during production.
This is particularly useful when running a simulation on a platform where tkinter is not supported,
such a server.
This functionality can also be used to slightly increase the performance of video production.
Example usage:
try:
import tkinter
blind_animation = False
except ImportError:
blind_animation = True
env = sim.Environment(blind_animation = blind_animation)
Improved functionality (0)
--------------------------
Monitor.print_histogram() can now show a user specified collection of values. This can be done by giving
an iterable to the values parameter. If not all values present in the monitor are given, a <rest> value
will be shown at the bottom. Like:
x0.status.print_histogram(values=["scheduled", "current", "passive"])
may result in
Histogram of x.0.status
duration 50
value duration %
scheduled 10.100 20.2 ****************
current 0 0
passive 31.600 63.2 **************************************************
<rest> 8.300 16.6 *************
By default, the values are shown in the given order, but can also be sorted on value by specifying
sort_on_value=True.
Improved functionality (1)
--------------------------
Monitor.print_histogram() with values now supports sorting on weight (=number of entries if
all weights are 1) (for non-level monitors) and sorting on duration (for level monitors).
Therefore, two parameters have been added to print_histogram(): sort_on_weight and sort_on_duration.
So,
x0.status.print_histogram(values=["scheduled", "current", "passive"], sort_on_duration=True)
may result in
Histogram of x.0.status
duration 50
value duration %
passive 31.600 63.2 **************************************************
scheduled 10.100 20.2 ****************
current 0 0
<rest> 8.300 16.6 *************
Improved functionality (2)
--------------------------
Monitor.values() now supports sorting on weight (=number of entries if all weights are 1) (for non-level monitors) and sorting on duration (for level monitors).
Therefore, two parameters have been added to values(): sort_on_weight and sort_on_duration.
So, in the above example,
print(x0.status.values())
may result in
['data', 'interrupted', 'passive', 'scheduled']
, whereas
print(x0.status.values(sort_on_duration))
may result in
['passive', 'scheduled', data', 'interrupted']
Internal change (0)
-------------------
The Component.__del__ now explicitly checks for the existence of an _animation_children attribute to prevent
an error when deleting a component which is not completely initialized yet.
version 20.0.4 2020-10-26
==========================
New functionality (0)
---------------------
A component now supports a level monitor 'status' that keeps track of all the statuses a component has been in.
For instance, component.status.print_histogram(values=True) will print something like
Histogram of x.1.status
duration 40
value duration %
data 4.300 10.8 ********
interrupted 4 10 ********
passive 11.500 28.7 ***********************
requesting 10.100 25.2 ********************
scheduled 10.100 25.3 ********************
And of course, it is possible to get the duration a component was in a certain status, like
passive_time = component.status.value_duration(sim.passive).
You can even get the status of the component at a moment in the past, with e.g.
status_4_units_ago = c1.status(env.now() - 4)
In line with this change, the various statutes (passive, scheduled, ...) are no longer functions,
but just strings. So now, therefore it is also possible to say
passive_time = component.status.value_duration("passive")
This will be quite transparent from the user. Only if the text representation of a status was required,
status() had to be called, like
print(f"car status={car.status()()}")
From this version on, this should read
print(f"car status={car.status()}")
, which is also more intuitive.
Alternatively, you can now also write
print(f"car status={car.status.value}")
This makes the methods Component.ispassive(), Component.isscheduled, etc. less required as
component.status() == "passive" and
component.status() == "scheduled"
are arguably easier to understand.
The package salabim now has a function statuses() that returns a tuple with all statuses a component can be in.
So
print(sim.statuses())
will print
('current', 'data', 'interrupted', 'passive', 'requesting', 'scheduled', 'standby', 'waiting')
New functionality (1)
---------------------
Component.mode is now a monitor. That makes it possible to get an overview of all modes a component
has been in, either as a histogram or an animated monitor.
And it is possible to get the duration a component was in a certain mode, with e.g.
red_time = c1.mode.value_duration("red")
It is even possible to get the mode a component was in at a given moment, like
mode_4_units_ago = c1.mode(env.now() - 4)
Because of this you can't use mode(x) anymore to set the mode directly. In order to do that, you
have to use the new method set_mode:
c1.set_mode("green")
or
c1.mode.value = "green"
Both options do store the modetime correctly.
Please observe that the initial value for mode is now the null string and not None.
New_functionality (3)
---------------------
New method: Monitor.values() to assess all values in a monitor.
The values returned will be alphabetically sorted (not case sensitive), just as in Monitor.print_histogram().
The method supports both ex0 and force_numeric parameter flags.
Example (with same monitor as above):
print(x1.status.values())
will print
['data', 'interrupted', 'passive', 'requesting', 'scheduled']
New functionality (4)
---------------------
The values parameter of print_histogram() can now also be an iterable (i.e. tuple, list or set).
In that case, the statistics of only these values will be shown.
Example (with same monitor as above):
x1.status.print_histogram(values = sim.statuses())
will print
Histogram of x.1.status
duration 40
value duration %
current 0 0
data 4.300 10.8 ********
interrupted 4 10 ********
passive 11.500 28.7 ***********************
requesting 10.100 25.2 ********************
scheduled 10.100 25.3 ********************
standby 0 0
waiting 0 0
Any not shown values will be shown at the bottom as '<rest>'.
So, again with the same monitor as above:
x1.status.print_histogram(values = ("passive", "requesting", "error"))
will print
Histogram of x.1.status
duration 40
value duration %
passive 11.500 28.7 ***********************
requesting 10.100 25.2 ********************
error 0 0
<rest> 18.400 46.0 ************************************
New functionality (5)
---------------------
AnimateMonitor and Monitor.animate has got a new parameter: vertical_map.
This parameter should be a function that accepts one argument (the value to be plotted). By default
vertical_map is float.
The function vertical_map should result in a float or raise a TypeError or ValueError.
E.g. to map "red" to the value 1, "blue" to 2, etc., you could provide a mapping function like:
vertical_map = "unknown red blue green yellow".split().index
Note that in this example any value other than red, blue, green or yellow would map to 0
(via a ValueError exception).
This vertical_map function can also be used to map to a logarithmic scale:
vertical_map = lambda value_y: math.log(value_y) * 10
New functionality (6)
---------------------
AnimateMonitor and Monitor.animate can now show labels and corresponding lines.
There is full control of the colours, linewidth, fonts, placement, etc. See the docstring or
documentation for details.
There is a sample model (demo animation of labeled monitors.py) in the sample model folder
to illustrate the usage of this new functionality with the new Component.status and
Component.mode monitors.
Changed functionality (0)
-------------------------
The tests for which value(s) to include in
Montitor.value_number_of_entries()
Monitor.value_duration()
Monitor.value_weight()
are now direct and do not involve string conversion anymore. Normally, this won't cause any compatibility issues.
Distribution change (0)
-----------------------
The setup information (used in PyPI) now includes a set of classifiers as well as requires_python information.
Compatibility change (0)
------------------------
Salabim requires Python 3.6 from now on.
Internal change (0)
-------------------
The statuses requesting and waiting were internally stored as scheduled and 'translated' in the status()
method. Now, these statuses are natively stored as such (in the status monitor).
Version 20.0.3 2020-08-06
==========================
New functionality (0)
-----------------------
The specification of sim.Distribution now also accepts a time_unit parameter.
Note that if the specification string contains a time_unit parameter as well, the time_unit parameter
of Distribution is ignored.
Examples
d = sim.Distribution('uniform(1, 2)', time_unit='minutes')) # 1-2 minutes
d = sim.Distribution('uniform(1, 2, time_unit='hours')')) # 1-2 hours, same as before
d = sim.Distribution('uniform(1, 2, time_unit='hours')', time_unit='minutes')) # 1-2 hours, ignore minutes
New functionality (1)
-----------------------
Monitor.freeze() returns a 'frozen' monitor that can be used to store the results not
depending on the current environment.
This is particularly useful for pickling a monitor.
E.g. use
with open("mon.pickle", "wb") as f:
pickle.dump(f, mon.freeze())
to save the monitor mon, and
with open("mon.pickle", "rb") as f:
mon_retrieved = pickle.load(f)
to retrieve the monitor, later.
Both level and non-level monitors are supported.
Frozen monitors get the name of the original monitor padded with '.frozen' unless specified differently.
New functionality (2)
---------------------
All Component methods that support urgent to schedule a component now also support a priority parameter.
With this it is possible to sort a component before or after other components, scheduled for the same time.
Note that urgent only applies to components scheduled with the same time and same priority.
The priority is 0 by default.
This is particularly useful for race conditions. It is possible to change the priority of a component
by cancelling it prior to activating it with another priority.
The priority can be accessed with the new Component.scheduled_priority() method.
Improved functionality (0)
--------------------------
Video production can now be done with a context manager, thus making an explicit final call to video_close
obsolete:
with env.video('myvideo.mp4'):
...
env.run(10)
This will automatically close the file myvideo.mp4 upon leaving the with block.
Change in functionality (0)
---------------------------
In sim.reset(), which is always called at startup, random_seed() will be called without any parameters, causing
the random_seed to be set to 1234567.
That makes reproducibility even possible when calling
env = sim.Environment(random_seed="") # no change in seed
If 'real' random behaviour (dependent on clock ticks) is required, just do:
env = sim.Environment(random_seed="*")
Changes in functionality (1)
----------------------------
Queue.extend() does return None now instead of a copy of self, for consistency reasons.
Now queues support all comparisons, i.e. ==, !=, <, <=, >, >=
These comparsons are on membership only, i.e they ignore, the order, priorities and name.
It is possible to compare a quueu with any object supporting the iter protocol, most notably
sets, lists and tuples.
Internal changes /change in functionality (0)
---------------------------------------------
Upon termination of a process, salabim did check a list to see whether there were any animation objects
that had this component as it parent. Particularly with many animation objects defined that could take a long
time, as reported by Hugo Huges.
From this version on, a component itself has a set of its 'animation children'.
Also changed is the moment the animation child is removed automatically. When a parent (component)
is no longer accessible, it will remove all of its animation children if any.
That means we now rely on the automatic garbage collection, which can be slightly delayed.
This change means that an animation object with a parent that terminates its process is not
necessarily removed, as it can be still in a queue, or even just referenced by a variable.
If you use the parent parameter in an Animate, AnimateQueue or AnimateMonitor this might change the
behaviour.
Added support files (0)
-----------------------
For the development there's is now a set of pytest files that will result in a more stable product. Indeed
several bugs (see below) were detected during this test development.
Future versions will include more test scripts.
Bug fix (0)
-----------
Minor bug in 'install salabim from github.py' and 'install salabim.py' fixed.
Bug fix (1)
-----------
Bug in Component.wait() fixed. Thank you, Alexander Kaiblinger, for detecting the bug and proposing the solution.
Bug fix (2)
-----------
Upon honouring an anonymous resource, the statistics of the available_quantity, claimed_quantity and occupancy
were not updated. Bug fixed. Thank you, Lukas Hollenstein, for detecting the bug and proposing the solution.
Bug fix (3)
-----------
The algorithm used to calculate weighted percentiles in Monitor.percentile() from a stackoverflow.com article
was found to be incorrect, thus sometimes resulting in wrong percentiles.
Bug fixed.
Bug fix (4)
-----------
Minor changes in Queue.__iter__ and Queue.__reversed__ to make iterating when components are added or removed
during the iteration more consistent.
version 20.0.2 2020-05-18
==========================
New functionality (0)
---------------------
Component.request() has a new parameter 'oneof'.
If oneof=True, the request has to be honoured by just one of the given resources.
So, this essentially an or condition.
Note that the order of the mentioned resources is the order in which the request will be honoured.
It is possible to check which resource has claimed with Component.claimers()
Example:
c.request(r1, r2, r3, oneof=True)
The request will be honoured if either r1, r2 OR r3 has at least a quantity of one available.
This contrast to
c.request(r1, r2, r3)
, which will be honoured if r1, r2 AND r3 have at least a quantity of one available.
The trace of request and honouring request has been updated accordingly.
Changes in video production (0)
-------------------------------
With this version, animated PNGs are supported as well. In contrast to animated GIFs, the background
can be transparent (i.e. have an alpha <255).
For the production of individual video frames in jpg, png, bmp, tiff or gif format, the filename
now has to contain one asterisk (*), which will be replaced by a 6 digit zero-padded serial number
at run time, for each frame.
E.g.
video('.\videos\car*.jpg)
Filenames without an asteriks are only allowed for real videos or animated gif/png files:
videos('.videos\car.png')
Note that the directory is now automatically created.
Because of the changes, the Environment.delete_video() is now deprecated.
Internally, the video production is now more consistent and easier to maintain.
Improved functionality (0)
--------------------------
Under Linux and Android many more fonts will be available because salabim now searches (recursively)
in /usr/share/fonts and /system/fonts for .ttf files as well.
As a reminder: it is possible to show all available fonts with
sim.show_fonts()
Utility files (0)
-----------------
The utility 'install.py' is now called 'install salabim.py' and is now fully compatible with
pip uninstall and pip update, because a salabim-<version>.dist-info directory is written correctly
to the site-packages folder.
New is 'install salabim from github.py' which installs salabim directly from github instead of PyPI.
Support for iPadOS/iOS PyTo (0)
-------------------------------
When running under PyTo, a NotImplementedError was raised. Now, salabim can be run on this platform, albeit
animation is not supported.
Compatibility (0)
-----------------
From now on, only Python >=3.4 is supported, particularly because salabim now uses pathlib internally.
Bugfix (0)
-----------
In a number of methods, e.g. Queue.__init__(), the environment of an implied Monitor was set to the
default environment instead of the overruled environment.
Bugfix (1)
-----------
PeriodMonitor lacked an env parameter. Fixed.
Bugfix (2)
-----------
Under certain conditions, an animated GIF was not written correctly as a result of a bug in the optimization
of PIL save. From now on, salabim disables the optimization, possibly resulting in slightly larger
.GIF files. This change does not apply to Pythonista, which uses another technique to save animated GIFs.
Bugfix (3)
-----------
Under Pythonista, video production (animated gif only) did not start at the right time.
Fixed.
Internal changes (0)
--------------------
In order to support the new oneof parameter of request, the data structure of _requests and _claims is now
collections.OrderedDict instead of collections.defaultdict.
version 20.0.1 2020-02-27
==========================
New functionality (0)
---------------------
A new class ComponentGenerator is introduced.
With this, component can be generated according to a given inter arrival time (distribution) or a random spread
over a given interval.
Examples:
sim.ComponentGenerator(Car, iat=sim.Exponential(2))
# generates Cars according to a Poisson arrival
sim.ComponentGenerator(Car, iat=sim.Exponential(2), at=10, till=30, number=10)
# generates maximum 10 Cars according to a Poisson arrival from t=10 to t=30
sim.ComponentGenerator(Car, iat=sim.Exponential(2), at=10, till=30, number=10, force_at=True)
# generates maximum 10 Cars according to a Poisson arrival from t=10 to t=30, first arrival at t=10
sim.ComponentGenerator(sim.Pdf((Car, 0.7, Bus, 0.3)), iat=sim.Uniform(20,40), number=20)
# generates maximum 20 vehicles (70% Car, 30% Bus) according to a uniform inter arrival time
sim.ComponentGenerator(Car, duration=100, n=20)
# generates exactly 20 Cars, random spread over t=now and t=now+100
ComponentGenerator is a subclass of Component and therefore has all the normal properties and methods of an
ordinary component, altough it is not recommended to use any the process methods, apart from cancel.
Added functionality (0)
-----------------------
It is now possible to suppress line numbers in the trace.
Particularly when the trace output is written to a file, this can result in dramatic (up to 50 times!)
performance increase, because the calculation of line numbers is very demanding.
Now, therefore a method Environment.suppress_trace_linenumbers() has been introduced.
Like:
env = sim.Environment(trace=True)
env.suppress_trace_linenumbers(True)
By default, line numbers are not suppressed in the trace.
The current status can be queried with
print(env.suppress_trace_linenumbers())
version 20.0.0 2020-02-10
==========================
Announcement (0)
----------------
Salabim now runs fully also on Android platforms under the excellent Pydroid3 app
<https://play.google.com/store/apps/details?id=ru.iiec.pydroid3&hl=en>.
In order to use animation on that platform, it is required to import tkinter in the main program
, otherwise you will get a salabim error message if you try and start an animation.
With the PyDroid3 Premium version, your can also produce animation videos, albeit only .avi files.
AnimateButton does not properly yet and therefore the animation navigation buttons are not
placed correctly and in fact not usable. We plan a fix for this in a future version of salabim.
New functionality (0)
---------------------
An AnimateQueue object now has a queue() method that returns the queue it refers to.
This is useful in animation_objects() that want to check the queue (in the id parameter).
Improved functionality (0)
--------------------------
Normal video production (i.e. not .gif, .jpg, .bmp, .tiff, .png) is now done via a temporary file.
That has the effect that if the video production is not closed properly, the video is not written at all,
thus making the production process more stable.
It is necessary to always close the video production with
env.video("")
Changed functionality (0)
-------------------------
If a video name with extension .avi is to be produced (with env.video()), the default codec is now MJPG.
For all other extensions, the default codec remains MP4V.
Utility update (0)
------------------
The install.py utility is changed internally. Also, it shows now the location where salabim was installed.
Documentation (0)
-----------------
The width parameter of AnimateImage was not documented. Fixed.
Documentation (1)
-----------------
Closing a video is now documented (see also above).
Bug fix (0)
-----------
Minor bug in video production, when animation was turned off and on during the simulation. Fixed.
version 19.0.10 2019-12-18
===========================
New functionality (0)
---------------------
Map distribution allows sampled values from a distribution to be mapped to
a given function. E.g.
round_normal = sim.Map(sim.Normal(10, 4), lambda x: round(x))
or, equivalently:
round_normal = sim.Map(sim.Normal(10, 4), round)
The sampled values from the normal distribution will be rounded.
Another example:
positive_normal = sim.Map(sim.Normal(10, 4), lambda x: x if x > 0 else 0)
Negative sampled values will be set to zero, positive values are not affected.
Note that this is different from
sim.Bounded(sim.Normal(10, 4), lowerbound=0)
as in the latter case resampling will be done when a negative value is sampled,
in other words, virtually no zero samples.
New functionality (1)
---------------------
Introduced an AnimateCombined class with which it is possible to combine several instances of
AnimateText, AnimateCircle, AnimateRectangle, AnimatePolygon, AnimateLine, AnimatePoints,
AnimateImage or another AnimateCombined.
AnimateCombined works as a list, so can be filled by initializing with a list of Animatexxx instances, like
a = sim.AnimateCombined((a0, a1, a2))
, but can also be build up with
a.append(a3)
a.extend((a4, a5))
a += a6
etc.
It is then possible to set an attribute of the combined animation objects, like
a.x = 100
This will set the x for all animation objects to 100.
It is required that each of animation objects in an AnimatCombined instance supports the attribute
to be set! That means that you cannot set the radius for an AnimateCombined instance with an` AnimateCircle
and an AnimateRectangle, as the latter does not support radius.
If an attribute of an AnimateCombined instance is queried, the value for the first animation object will be returned.
It is possible to put AnimateCombined instances in another AnimateCombined object.
The remove method applied to AnimateCombined objects will remove all the included animation objects.
Changed functionality (0
--------------------------
In version 19.0.6 the following functionality was introduced:
If an error occurred in the make_pil_image method during an animation, it was sometimes difficult to
find out the reason for that error. From this version on, the line number (and the filename)
where the animation object was created will be added to the error message, which makes debugging
hopefully easier.
However, this feature sometimes appears to significantly slow down animations.
Therefore, this feature is now disabled by default.
If required (particularly when finding a complicated error), it is possible to enable the source location
tracking. The method Environment.animation_parameters() has therefore now a new parameter: animate_debug,
which is False by default.
Alternatively, the method Environment.animate_debug() can be used.
Changed functionality (1)
-------------------------
sim.Environment() now automatically resets the simulation environment when run under Pythonista.
A parameter 'do_reset' to sim.Environment() allows the user to force a reset as well, or to indicate
that no reset should be executed under Pythonista.
Internal change (0)
-------------------
The install.py file has been internally changed to support other single source
packages. No functional changes.
version 19.0.9 2019-10-08
==========================
New functionality (0)