Start many entities from one arrival, each with its own key and its own values: ten tills that report their takings every minute, and three warehouses named in a list.
Before You Start
The commands run from the root of a clone of the repository, where the files are in docs/snippets/generate-data/. The Full Files block of this page holds the same files, for a reader who saves them by hand.
First Simulation explains arrivals, tasks and the records a run writes.
Arrivals and Services lists the keys of an arrival, and entity lists the generator that reads an entity's key.
Steps
Start Entities with a Count
count: 10 starts ten entities from the one arrival, and each reports every 60 seconds. key is a generator read once for each entity, and sequence with format: "till-{}" names them till-1 to till-10.
# A blueprint's `count: 10` is ten arrivals here, each with the key of its tillfornumberinrange(1,11):app.add_arrival(f"tills{number}","constant",value=60,spawn="report",key=lambdan,number=number:f"till-{number}",)
A Builder arrival has no count, so the file adds one arrival for each till, each with its own key.
sampler=Sampler(seed=42)takings=sampler.generator("tasks.report.payload.takings")tills_gap=DistributionConfig(dist="constant",value=60)# The random sequence the blueprint's draw usestills_gap.stream="arrival.tills"sent={"report":0}
A Low-level script starts one process for each till.
Read the Entity's Key
entity writes the entity's key into the payload, next to a takings figure that each till draws for itself. The key is also the record key, so a Kafka topic keeps all the records of one till in one partition, in order.
report_payload=Template({"till":{"gen":"entity"},"takings":{"gen":"normal","mean":400,"std":60,"decimals":2},},name="tasks.report",)@app.task(name="report")defreport(task_id):"""Gives one report record."""returnreport_payload
keys is a list of names instead of a count, and it starts one entity for each name. Three warehouses report the orders waiting every 2 seconds under their own names.
# A blueprint's `keys` is one arrival for each name here, each with its own keyfornamein["north","south","east"]:app.add_arrival(f"warehouses-{name}","constant",value=2,spawn="report",key=lambdan,name=name:name,)
As with count, a Builder arrival has no keys, so the file adds one arrival for each name.
report_payload=Template({"warehouse":{"gen":"entity"},"orders_waiting":{"gen":"integer","bounds":[0,40]},},name="tasks.report",)@app.task(name="report")defreport(task_id):"""Gives one report record."""returnreport_payload
sampler=Sampler(seed=42)waiting=sampler.generator("tasks.report.payload.orders_waiting")warehouses_gap=DistributionConfig(dist="constant",value=2)# The random sequence the blueprint's draw useswarehouses_gap.stream="arrival.warehouses"sent={"report":0}
Run the first model for 300 simulated seconds and print its first 3 records. All ten tills report at 60 seconds, each under its own key, and again at 120, 180 and 240 seconds, which makes 40 records. The run ends at 300 seconds before the reports due at that time.
# Many entities: ten tills, each with its own key, report their takings every minute.## `count: 10` starts ten entities from the one arrival, and `key` gives each its own# key, till-1 to till-10, which the task reads with `entity`. The key is the record key# too, so a Kafka topic puts each till's records in one partition, in order.## Needs: nothing.# Run it with: desflow run docs/snippets/generate-data/many_entities.yamlsimulation:sim_id:Shopfactor:0random_seed:42logical_start_time:2026-01-01T00:00:00egress:-{type:Console}arrivals:tills:dist:constantvalue:60spawn:reportcount:10key:{gen:sequence, start:1, format:"till-{}"}tasks:report:payload:till:{gen:entity}takings:{gen:normal, mean:400, std:60, decimals:2}run:{until:300}
# Many entities, by name: one entity for each key in a list.## `keys` starts one entity for each name, here three warehouses, and each reports the# orders waiting every 2 seconds under its own name.## Needs: nothing.# Run it with: desflow run docs/snippets/generate-data/many_entities_named.yamlsimulation:sim_id:Shopfactor:0random_seed:42logical_start_time:2026-01-01T00:00:00egress:-{type:Console}arrivals:warehouses:{dist:constant, value:2, spawn:report, keys:[north,south,east]}tasks:report:payload:warehouse:{gen:entity}orders_waiting:{gen:integer, bounds:[0,40]}run:{until:10}
"""Many entities: ten tills, each with its own key, builder API.The builder version of `many_entities.yaml`: ten tills, each with its own key, reporttheir takings every minute. Each till is its own arrival, because `add_arrival` has no`count`.Needs: nothing.Run it with: python docs/snippets/generate-data/many_entities_builder.py"""importloggingfromdatetimeimportdatetime,timezonefromdesflowimportConsoleEgress,SimulationContextfromdesflow.valuesimportTemplateUNTIL=300defbuild()->SimulationContext:"""Builds the model."""app=SimulationContext(sim_id="Shop",factor=0,random_seed=42,logical_start_time=datetime(2026,1,1,tzinfo=timezone.utc),)app.add_egress(ConsoleEgress())# A blueprint's `count: 10` is ten arrivals here, each with the key of its tillfornumberinrange(1,11):app.add_arrival(f"tills{number}","constant",value=60,spawn="report",key=lambdan,number=number:f"till-{number}",)report_payload=Template({"till":{"gen":"entity"},"takings":{"gen":"normal","mean":400,"std":60,"decimals":2},},name="tasks.report",)@app.task(name="report")defreport(task_id):"""Gives one report record."""returnreport_payloadreturnappif__name__=="__main__":logging.basicConfig(level=logging.INFO,format="%(levelname)s%(message)s")build().run(until=UNTIL)
"""Many entities, by name: one entity for each key in a list, builder API.The builder version of `many_entities_named.yaml`: three warehouses, each with its ownname, report the orders waiting every 2 seconds. Each warehouse is its own arrival,because `add_arrival` has no `keys`.Needs: nothing.Run it with: python docs/snippets/generate-data/many_entities_named_builder.py"""importloggingfromdatetimeimportdatetime,timezonefromdesflowimportConsoleEgress,SimulationContextfromdesflow.valuesimportTemplateUNTIL=10defbuild()->SimulationContext:"""Builds the model."""app=SimulationContext(sim_id="Shop",factor=0,random_seed=42,logical_start_time=datetime(2026,1,1,tzinfo=timezone.utc),)app.add_egress(ConsoleEgress())# A blueprint's `keys` is one arrival for each name here, each with its own keyfornamein["north","south","east"]:app.add_arrival(f"warehouses-{name}","constant",value=2,spawn="report",key=lambdan,name=name:name,)report_payload=Template({"warehouse":{"gen":"entity"},"orders_waiting":{"gen":"integer","bounds":[0,40]},},name="tasks.report",)@app.task(name="report")defreport(task_id):"""Gives one report record."""returnreport_payloadreturnappif__name__=="__main__":logging.basicConfig(level=logging.INFO,format="%(levelname)s%(message)s")build().run(until=UNTIL)
"""Many entities: ten tills, each with its own key, low-level API.The low-level version of `many_entities.yaml`: plain SimPy on a`DynamicRealtimeEnvironment`, which publishes each record with `publish_event`. Eachtill is one process with its own key. Each draw is made with `Sampler`, from therandom sequence the blueprint's generator at that place uses, so the draws are theblueprint's, and the ten tills share that one sequence.Needs: nothing.Run it with: python docs/snippets/generate-data/many_entities_low_level.py"""importloggingfromdatetimeimportdatetime,timezonefromdesflowimport(ConsoleEgress,DistributionConfig,DynamicRealtimeEnvironment,Sampler,)UNTIL=300START=datetime(2026,1,1,tzinfo=timezone.utc)ENVIRONMENT:dict={"logical_start_time":START,"timezone":"UTC"}defbuild(env:DynamicRealtimeEnvironment)->None:"""Starts the ten tills on `env`."""sampler=Sampler(seed=42)takings=sampler.generator("tasks.report.payload.takings")tills_gap=DistributionConfig(dist="constant",value=60)# The random sequence the blueprint's draw usestills_gap.stream="arrival.tills"sent={"report":0}deftill(key):whileTrue:yieldenv.timeout(sampler.sample(tills_gap))payload={"till":key,"takings":round(float(takings.normal(400,60)),2),}env.publish_event(key,payload,task="report",instance=sent["report"])sent["report"]+=1fornumberinrange(1,11):env.process(till(f"till-{number}"))defrun()->None:"""Runs the model unpaced into the console."""env=DynamicRealtimeEnvironment(factor=0,**ENVIRONMENT)env.setup_egress([ConsoleEgress()])build(env)try:env.run(until=UNTIL)finally:env.teardown()if__name__=="__main__":logging.basicConfig(level=logging.INFO,format="%(levelname)s%(message)s")run()
"""Many entities, by name: one entity for each key in a list, low-level API.The low-level version of `many_entities_named.yaml`: plain SimPy on a`DynamicRealtimeEnvironment`, which publishes each record with `publish_event`. Eachwarehouse is one process with its own name. Each draw is made with `Sampler`, from therandom sequence the blueprint's generator at that place uses, so the draws are theblueprint's, and the three warehouses share that one sequence.Needs: nothing.Run it with: python docs/snippets/generate-data/many_entities_named_low_level.py"""importloggingfromdatetimeimportdatetime,timezonefromdesflowimport(ConsoleEgress,DistributionConfig,DynamicRealtimeEnvironment,Sampler,)UNTIL=10START=datetime(2026,1,1,tzinfo=timezone.utc)ENVIRONMENT:dict={"logical_start_time":START,"timezone":"UTC"}defbuild(env:DynamicRealtimeEnvironment)->None:"""Starts the three warehouses on `env`."""sampler=Sampler(seed=42)waiting=sampler.generator("tasks.report.payload.orders_waiting")warehouses_gap=DistributionConfig(dist="constant",value=2)# The random sequence the blueprint's draw useswarehouses_gap.stream="arrival.warehouses"sent={"report":0}defwarehouse(key):whileTrue:yieldenv.timeout(sampler.sample(warehouses_gap))payload={"warehouse":key,"orders_waiting":int(waiting.integers(0,40+1)),}env.publish_event(key,payload,task="report",instance=sent["report"])sent["report"]+=1forkeyin["north","south","east"]:env.process(warehouse(key))defrun()->None:"""Runs the model unpaced into the console."""env=DynamicRealtimeEnvironment(factor=0,**ENVIRONMENT)env.setup_egress([ConsoleEgress()])build(env)try:env.run(until=UNTIL)finally:env.teardown()if__name__=="__main__":logging.basicConfig(level=logging.INFO,format="%(levelname)s%(message)s")run()