Salabim

Latest version: v25.0.8

Safety actively analyzes 723400 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 14 of 28

19.0.9

==========================
New functionality (0)
---------------------
Introduced preemptive resources

It is now possible to specify that a resource is to be preemptive, by adding preemptive=True when the resource
is created.
If a component requests from a preemptive resource, it may bump component(s) that are claiming from
the resource, provided these have a lower priority = higher value).
If component is bumped, it releases the resource and is the activated, thus essentially stopping the current
action (usually hold or passivate).
Therefore, it is necessary that a component claiming from a preemptive resource should check
whether the component is bumped or still claiming at any point where they can be bumped.
This can be done with the method Component.isclaiming which is True if the component is claiming from the resource,
or the opposite (Component.isbumped) which is True is the component is not claiming from te resource.

E.g. if the component has to start all over again (hold(1)) if it is bumped:
def process(self):
prio = sim.Pdf((1,2,3), 1)
while True:
yield self.request((preemptive_resource, 1, prio)
yield self.hold(1)
if self.isclaiming(preemptive_resource):
break
self.release(preemptive_resource)

E.g. if the component just has to 'complete' the hold time:
def process(self):
prio = sim.Pdf((1,2,3), 1)
remaining = 1
while True:
yield self.request((preemptive_resource, 1, prio)
yield self.hold(remaining, mode='')
if self.isclaiming(preemptive_resource):
break
remaining -= (env.now() - self.mode_time())
self.release(preemptive_resource)

Note that a component that hasn't requested at all from a resource, it is considered as bumped, therefore the above
example can be rewritten as:
def process(self):
prio = sim.Pdf((1,2,3), 1)
remaining = 1
while isbumped(preemptive_resource):
yield self.request((preemptive_resource, 1, prio)
yield self.hold(remaining, mode='')
remaining -= (env.now() - self.modetime())
self.release(preemptive_resource)

Finally, if the component is dealing with just one preemptive resource (which is very likely), the isbumped()
and isclaiming() methods can be used without an argument:
prio = sim.Pdf((1,2,3), 1)
remaining = 1
while isbumped():
yield self.request((preemptive_resource, 1, prio)
yield self.hold(remaining, mode='')
remaining -= (env.now() - self.modetime())
self.release(preemptive_resource)

If a request for a preemptive resource is made, it is not possible to combine that request with any other
resource.

The method Resource.ispreemptive() can be used to check the whether a resource is preemptive.

There is an animated demo showing nicely the difference between preemptive and non preemptive resources
See salabim./sample models/Demo preemptive resources animated.py.

New functionality (1)
---------------------
In the trace, the time of rescheduling of Component initialization, activate, hold, request, wait and run
the time was always shown in the information field of the trace as 'scheduled for xxx'.
From this version, also the delta time (if not 0 of inf) is shown as +xxx behind the action, e.g.

75+ 1.000 main current
76 client.0 create
76 client.0 activate scheduled for 1.000 59 process=process
77 client.1 create
77 client.1 activate +1.000 scheduled for 2.000 59 process=process
78 client.2 create
78 client.2 activate +3.000 scheduled for 4.000 59 process=process

New functionality (2)
---------------------
The trace parameter in Environment() and Environment.trace() can now be a file handle.
If so, the trace output will be directed to the file given, provided it is opened for output.
When the trace parameter is not an 'open for write' file handle, but is 'Truthy' (usually True),
the trace output is sent to stdout, as before.
When the parameter is 'Falsy', no trace output will be generated, as before.

Example:
import salabim as sim

class X(sim.Component):
pass

out = open('output.txt', 'w') as out:
env = sim.Environment(trace=out)

env.trace(True)
X()
env.trace(False)
X()
env.trace(out)
X()
env.trace(False)
out.close()

After execution, the file output.txt contains:
line time current component action information
------ ---------- -------------------- ----------------------------------- -------------------------------
line numbers refers to test.py
8 default environment initialize
8 main create
8 0.000 main current
9 x.0 create data component

And the following output is generated:
12 x.1 create data component

Note that by issueing env.trace(False), the file is not closed, so that has to be done explicitely or with
a context manager.


Changed functionality (0)
-------------------------
The default priority of a requested resource is now inf, which means the lowest possible priority.
In practice, this will hardly ever make a difference with the former behaviour that if no priority was given, it was
added to the tail of the requesters

When a request is honoured, the component now enters the claimers queue with priority as it had in the requesters
queue. Again, in practice this will hardly make any difference.

Bugfix (0)
-----------
A bug with autonumbering components, queues, etc. by ending the name with a comma, like
for _ in range(3):
Car(name='car,')
fixed.
Now, the cars are correctly named car.1, car.2 and car.3 .

Bugfix (1)
-----------
On some platforms PIL does not accept a new image with a width or height of 0. Therefore, salabim now
sets the dimensions for a dummy image to (1, 1).

Bugfix (2)
-----------
As a leftover from a test, the seed was printed when an Environment was created. Removed.

19.0.8

==========================
New functionality (0)
---------------------
The time related parameters of the methods methods mentioned below can now be called with a
distribution instead of a float value ('auto sampling ').

Method parameters that can be auto sampled
------------------------ -----------------------------------
sim.Component at, delay
Component.activate at, delay
Component.hold duration, till
Component.request fail_at, fail_delay
Component.wait fail_at, fail_delay
Environment.run duration, till
Environment.reset_now new_now
Environment.years, ... t
Environment.to_time_unit t
Environment.to_years, ... t

If a distribution is given, the distribution will be sampled.
So,
car = Car(delay=sim.Normal(100,10))
is equivalent to
car = Car(delay=sim.Normal(100,10).sample())
and
car = Car(delay=sim.Normal(100,10)())

This makes it possible to intermix floats/ints and distributions, without having to worry about sampling, e.g.
set_up_time = 6
processing_time = Normal(10, 1)
reaction_time = sim.Constant(10)
...
yield self.hold(set_up_time)
yield self.hold(processing_time)
yield self.hold(env.hours(reaction_time))

Note that salabim also supports basic expressions for distributions, so it is even possible to do something like
yield self.hold(2 * setup_time + processing_time)

New functionality (1)
---------------------
Introduced a new distribution, External, that makes it possible to specify an 'external' statistical
distribution from the modules
* random
* numpy.random
* scipy.stats
as were it a salabim distribution.
The class takes at least one parameter (dis) that specifies the distribution to use, like
* random.uniform
* numpy.random.uniform
* scipy.stats.uniform
Next, all postional and keyword parameters are used for sampling. In case of random and numpy.random by calling
the method itself, in case of a scipy.stats distribution by calling rvs().

Examples:
d = sim.External(random.lognormvariate, mu=5, sigma=1)
d = sim.External(numpy.random.laplace, loc=5, scale=1)
d = sim.SciPyDis(SciPy.beta, a=1, loc=4, scale=1)
Then sampling with
d.sample()
or
d()

If the size is given (only for numpy.random and scipy.stats distributions), salabim will return the sampled values successively. This can be useful to increase performance.

The mean() method returns the proper mean for scipy.stats distributions, nan otherwise.

If a time_unit parameter is given, the sampled values will be multiplied by the applicable factor, e.g.
env = sim.Environment(time_unit='seconds')
d = sim.ScyPyDis(SciPy.norm, loc=2, time_unit='minutes')
print(d.mean())
will print out
120

New functionality (2)
---------------------
Bounded distribution now has a time_unit parameter to specify the lowerbound or upperbound.

New functionality (3)
---------------------
IntUniform distribution now also supports a time_unit. If used, the returned value will be scaled accordingly.
E.g.
env = sim.Environment('seconds')
d = sim.IntUniform(1, 2, time_unit='minutes')
print(d.sample())
will print either 60 or 120.

Improvement (0)
---------------
When trying to animate text for a None object, salabim issued an error, about absence of .strip().
Now, None will be handled as the null string ("")

Improvement (1)
---------------
When a distribution with a time unit was specified without an Environment was instantiated, a rather obscure error
message was shown. Now, a clear error message will be shown, in that case.

Implementation note (0)
-----------------------
Provided numpy is installed, now numpy.random is seeded at init of Environment() and when calling random_seed,
unless the set_numpy_random_seed parameter is False.
This is particularly useful when using External distributions with
numpy.random or scypi.stats distributions. Therefore, unless explicitely overriden, also those
distribution sampling will be reproducable.
Related to this is an internal change in the way Environment seeds random.

Implementation note (1)
-----------------------
Change in the way time_units are handled internally.

19.0.7

==========================
Bug fix (0)
-----------
Rare error in multiplication of monitors (and thus Monitor.to_hours, etc) fixed.

19.0.6

==========================
Improved functionality (0)
--------------------------
AnimateMonitor and Monitor.animate() now allow rotation of an animated monitor.
This is particularly useful for animating a monitor from top to bottom (angle = 270),
instead of from left to right (angle=0)
The rotation angle is specified with the angle parameter, which defaults to 0.
Also, offsetx and offsety are now supported for AnimateMonitor and Monitor.animate().

Improved functionality (1)
--------------------------
The spec parameter of AnimateLine, AnimatePoints, AnimateRectangle and AnimatePolygon can now use None to repeat
a previous x or y-coordinate.
The same holds for the line0/line1, rectangle0/rectangle1, polygon0/polygon1 parameters of Animate and
Animate.update().
From now on it is therefore possible to say for instance:
sim.AnimateLine(spec=(100, 0, 900, None, None, 600))
which is equivalent to
sim.AnimateLine(spec=(100, 0, 900, 0, 900, 600))
And it even possible to say:
sim.AnimatePoints(spec=100, 400, 150, None, 200, None, 300, None)
which is equivalent to
sim.AnimatePoints(spec=100, 400, 150, 400, 200, 400, 300, 400)

Improved functionality (2)
--------------------------
Applications can now retrieve the salabim version consistenty with sim.__version__.
Previously, this had to be done with
- sim.__version__ if salabim.py is in the current directory
- sim.salabim.__version__ if salabim.py is not in the current directory
Techinal note: This change is actually in the __init__.py file rather than in salabim.py.
For comparisons of versions, distutils.version.StrictVersion() is recommended.

Improved functionality (3)
--------------------------
If an error occured 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.

Bug fix (0)
-----------
Creating a video with audio did crash sometimes. Fixed.

Bug fix (1)
-----------
Under Pythonista (iOS), animation objects were not properly sorted, thus causing layer values sometimes to be
ignored. Fixed.

Bug fix (2)
-----------
When querying a tallied value from a level monitor, either with mon.get(t) or mon(t), the values were not correct
when a Environment.reset_time() was applied. Fixed.

19.0.5

==========================
New functionality (0)
---------------------
Level monitors have two new properties:
- value, which can be used to get the current value of a monitor, i.e
a = m.value is equivalent to a = m.get() and a = m()
value can also be used to tally a value, i.e.
m.value = 10 is equivalent to m.tally(10)
Combining the getter and the setter is useful in constructs like
m.value += 100, which is equivalent to m.tally(m.get() + 100)
- t, which can be used to get the time the last value was tallied.

Combining these two is for instance useful to calculate the cost of storage over a period, such as
costs += (env.now() - inventory.t) * inventory.value * cost_per_day
inventory.value -= consume
Both value and t are available can be used even if the monitor is turned off.

Note that these two properties are only available for level monitors.

Note that this functionality is also available for the capacity of a resource, which means that increasing
the capacity of a resource by 1 can now be done with
res.set_capacity(res.capacity() + 1)
or
res.capacity.value += 1

The following standard salabim level monitors do support only *getting* the value and (for obvious reasons)
not *setting* the value:
- Queue.length()
- Resource.available_quantity()
- Resource.claimed_quantity()
- Resource.occupancy()
- State.value()


New functionality (1)
---------------------
Anonymous resources can now request for a negative quantity. In that case, the request gets honoured if
the claimed quantity is greater than or equal to minus the requested quantity.
In case of these anonymous resources, it might be easier to think in terms of get and put, like in
SimPy.Container. Therefore, salabim introduces to new methods:
- Component.get(), which is equivalent to Component.request
- Component.put(), which is equivalent to Component.request, but where the quantity of anonymous resources
is negated.
Thus yield self.request((r, -5)) is the same as yield self.put((r, 5)) if r is defined as
r = sim.Resource(name='r', anonymous=True)
Note that in the trace, the text 'request honor' will be shown even for get or request calls.
Also, isrequesting() will be True if a component is waiting for a get or put honor.
The Gas station model in sample models illustrates the get/put functionality.

Note that you can still 'refill' an anonymous resource without checking for exceeding the capacity, with a call to Resource.release(). In many cases that will be sufficient.


New functionality (2)
---------------------
On Windows and Pythonista platforms, an audio track (usually an mp3 file) may be played during animation.
This particularly during the development of lip synchronized videos.
Therefore, a new method Environment.audio() has been introduced.
Alternatively, the new parameter audio of Environment.animation_parameters() can be used to specify which
mp3 to be played.
It is recommended not to use variable bit rate (vbr) mp3 files, as the length can't be detected correctly.
Note that audio is only played when the animation speed is equal to audio_speed, which is 1 by default.
The audio_speed may be changed with Environment.audio_speed() or the new parameter audio_speed of
Environment.animation_parameters().
On other platforms than Windows or Pythonista, the audio functions are ignored.

The duration of an audio file (usually an mp3 file) can be retrieved with sim.audio_duration(filename).
On other platforms than Windows or Pythonista, a length of zero will be returned when sim.audio_duration() is called.

New functionality (3)
---------------------
On Windows platforms, an audio track can now be added to a video animation.
The audio is controlled by Environment.audio() commands (see above).
In order to add audio to a video, ffmpeg must be installed and included in the environment path.
See www.ffmpeg.org for download and installation instructions.


New functionality (4)
---------------------
When animating images, the alpha (transparency channel) can now be specified.
Therefore, Animation() and Animation.update() have two extra arguments: alpha0 and alpha1.
AnimateImage() has an extra parameter alpha.
The alpha value should be between 0 (fully transparent) and 255 (not transparent).
Note that if an image has alpha values other than 255, these are scaled accordingly.
Images with an alpha values of less than 255 are rendered quicker when numpy is installed.


Improved performance (0)
------------------------
Animation objects that are completely out of the frame are suppressed now. This results in better
performance when many animation objects are not visible anyway.


Improved performance (1)
------------------------
Rendering speed of animated text improved, particularly when numpy is installed.


Experimental functionality (0)
------------------------------
Experimental support for retina screens on supported iOS devices (Pythonista only).
By adding retina=True to the Environment() call, iOS devices will double the number of pixels, both
in height and width. As of now, buttons and sliders are not shown in retina mode.


Bug fixes (0)
-------------
In some Ubuntu environments, install.py could not install salabim correctly.
This has been fixed with this release.

19.0.4

==========================
New functionality (0)
---------------------
Queue.extend() has an extra parameter, clear_source. If clear_source = True, the given source will be cleared
after copying the elements to self.
This means that
q0.extend(q1, clear_source=True)
effectively transfers all elements of q1 into q0, prior to emptying q1.
Note that clear_source cannot be applied if source is a list or a tuple.

New functionality (1)
---------------------
non-level monitors can now be filled from a list or tuple, like
m = Monitor("my monitor", level=False, fill=(1,2,5,7,2,3))
which is functionally equivalent to
m = Monitor("my monitor", level=False)
for el in (1,2,5,7,2,3):
m.tally(el)

Bug fix (0)
-----------
Autoscaling of histograms was incorrectly always enabled.
Now histograms are autoscaled only if neither number_of_bins, nor lowerbound nor bin_width is specified.

Bug fix (1)
-----------
Due to a problem with the Black formatter, version 19.0.3 did not run under Python versions prior to 3.6.
This is fixed with this release.

Page 14 of 28

© 2025 Safety CLI Cybersecurity Inc. All Rights Reserved.