Resource Modelling

Resource Containers

Resources support the comparison operations, such as ==, >=, or <. This derives a Comparison that triggers when the Resources’ levels reaches the desired value.

class usim.Resources(__zero__: Optional[usim._basics.resource.T] = None, **capacity: usim._basics.resource.T)[source]

Supply of named resources which can be temporarily borrowed or produced/consumed

The resources and their initial levels are defined when the resource supply is created. Afterwards, the level of resources can be permanently increase()d or decrease()d as well as temporarily decreased by borrow()ing:

# create an open supply of resources
resources = Resources(cores=8, memory=4000)

# increase the resource supply available
await resources.increase(memory=2000)

# temporarily remove resources
async with resources.borrow(cores=2, memory=6000):
    await computation

# decrease the resource supply available
await resources.decrease(cores=4)

A Capacities guarantees that it is always possible to increase the level of available resources. Once resources are borrow()ed, they can always be returned promptly.

await decrease(**amounts: usim._basics.resource.T)[source]

Decrease the level of resources

Parameters

amounts – resource levels to decrease

Only levels of resources that are already part of these Resources can be decreased. Levels cannot be decreased by negative amounts or below zero. If a resource is not specified, its level remains unchanged.

await increase(**amounts: usim._basics.resource.T)[source]

Increase the level of resources

Parameters

amounts – resource levels to increase

Only levels of resources that are already part of these Resources can be increased. Levels cannot be increased by negative amounts. If a resource is not specified, its level remains unchanged.

await set(**amounts: usim._basics.resource.T)[source]

Set the level of resources

Parameters

amounts – resource levels to set

Only levels of resources that are already part of these Resources can be set. Levels cannot be set below zero. If a resource is not specified, its level remains unchanged.

class usim.Capacities(__zero__: Optional[usim._basics.resource.T] = None, **capacity: usim._basics.resource.T)[source]

Fixed supply of named resources which can be temporarily borrowed

The resources and their maximum capacity are defined when the resource supply is created. Afterwards, it is only possible to temporarily borrow() resources:

# create a limited supply of resources
resources = Capacities(cores=8, memory=16000)

# temporarily remove resources
async with resources.borrow(cores=2, money=4000):
    await computation

A Capacities guarantees that its resources are conserved and cannot be leaked. Once resources are borrow()ed, they can always be returned promptly.

class usim.typing.ResourceLevels(**kwargs: usim._basics._resource_level.T)[source]

Common class for named resource levels

Representation for the levels of multiple named resources. Every set of resources, such as usim.Resources or usim.Capacities, specializes a ResourceLevels subclass with one attribute for each named resource. For example, Resources(a=3, b=4) uses a ResourceLevels with attributes a and b.

from usim import Resources

resources = Resources(a=3, b=4)
print(resources.levels.a)  # 3
print(resources.levels.b)  # 4
print(resources.levels.c)  # raises AttributeError

ResourceLevels subtypes allow no additional attributes other than their initial resources, but their values may be changed. Instantiating a subtype requires resource levels to be specified by keyword; missing resource are set to zero.

Each resource always uses the same ResourceLevels subtype. Binary operators for comparisons and arithmetic can be applied for instances of the same subtype.

levels_a + levels_b
levels_a - levels_b

Elementwise addition/subtraction of values.

levels_a > levels_b
levels_a >= levels_b
levels_a <= levels_b
levels_a < levels_b

Strict elementwise comparison of values. True if the comparison is satisfied by each element pair, False otherwise.

levels_a == levels_b

Total elementwise equality of values. True if each element pair is equal, False otherwise. The inverse of levels_a != levels_b.

levels_a != levels_b

Partial elementwise unequality of values. False if each element pair is equal, True otherwise. The inverse of levels_a == levels_b.

In addition, iteration on a ResourceLevels subtype yields field, value pairs. This is similar to dict.items().

for field, value in levels_a

Iterate over the current field, value pairs.

dict(levels_a)

Create dict of field: value pairs.

exception usim.ResourcesUnavailable(claim: usim._basics.resource.ClaimedResources)[source]

Resources requested from a supply are not available

Resource Transfer

class usim.Pipe(throughput: float)[source]

Shared transport for resources with a limited total throughput

Parameters

throughput – limit of total throughput

The throughput limit of a pipe is defined when a Pipe is created. Afterwards, activities may temporarily request transfers through the Pipe with a maximum throughput. If the sum of throughput from all transfers exceeds the throughput limit, transfers are throttled accordingly.

connection = Pipe(throughput=3)

# transfers below limit
await connection.transfer(total=10, throughput=2)  # takes 5 time units

# transfers above limit
async with Scope() as scope:  # takes 10 time units
    scope.do(connection.transfer(15, throughput=3)
    scope.do(connection.transfer(15, throughput=3)
await transfer(total: float, throughput: Optional[float] = None) None[source]

Wait until some total volume has been transferred

Parameters
  • total – absolute volume to transfer before resuming

  • throughput – maximum throughput of transfer

The effective throughput is bounded by the transfer’s throughput as well as the Pipe’s throughput weighted by all transfers’ throughput. For example, if two transfers each request the entire throughput, each receives only half.

network = Pipe(throughput=64)
await network.transfer(total=50 * 1024, throughput=128)  # transfer with 64

If throughput is not given, it defaults to the Pipe’s throughput limit.

class usim.UnboundedPipe(throughput=inf)[source]

Shared transport for resources with unlimited total throughput

This is a noop variant of the regular Pipe. It serves as a neutral element when a Pipe is required but no throttling should take place.

await transfer(total: float, throughput: Optional[float] = None) None[source]

Wait until some total volume has been transferred

Parameters
  • total – absolute volume to transfer before resuming

  • throughput – maximum throughput of transfer

The effective throughput is bounded by the transfer’s throughput as well as the Pipe’s throughput weighted by all transfers’ throughput. For example, if two transfers each request the entire throughput, each receives only half.

network = Pipe(throughput=64)
await network.transfer(total=50 * 1024, throughput=128)  # transfer with 64

If throughput is not given, it defaults to the Pipe’s throughput limit.