Command Line
The desflow command runs a model without a line of Python, and also checks it, compares designs, fits measured times and converts a distribution's parameters. This page uses each command in the order a study uses it, on one shop, and says what each output tells you.
What It Is
desflow is installed with the package: uv tool install desflow, or pip install desflow. Each task is a command of its own, such as desflow run or desflow explain. desflow on its own lists them by task, with examples. The --help of each command, such as desflow run --help, lists its options and ends with examples of that command. CLI gives the help of every command, with every option.
The shop on this page is the model of the experiment example: two packers, an order every 2.5 seconds on average, and 4 seconds of packing on average. Every command runs in one folder and needs no Docker service. desflow fit needs the fit extra: uv tool install "desflow[fit]", or pip install "desflow[fit]".
How to Use It
Get an Example
desflow --version prints the release you installed. desflow examples list, or desflow examples alone, lists every example with the service it needs, such as (no service) or (odctl up kafka-lite):
desflow examples get downloads one example at the release you installed, into a folder named after it:
The model is experiment_shop.yaml, and experiment.yaml is the experiment that compares two designs of it. observed_waits.csv holds the mean wait measured in the real shop. The line before the last runs desflow explain on the model, and the last line is the command that runs the example from its own folder. --api builder or --api low-level downloads the same example in Python.
--dest downloads into another folder, and --ref downloads from another tag or branch, such as main for an example added after your release:
The builder version goes under shop/experiment/builder, and the last line runs it from there. A file that is already there stops the download before anything is written, and --force replaces it instead. This line downloads the YAML version again, over the files of the first download, and prints the same lines as the first download:
Check It Before a Long Run
desflow explain reads the model without running it. It draws the model as a graph and works out each resource's utilisation, the share of its units that the arrivals keep busy:

The same output as text:
The packers are at utilisation 0.8. An order arrives every 2.5 seconds on average (24 a minute, rate 0.4/s in the graph, λ in the table). Each order needs 4 seconds of a packer, and 2 packers share the work. μ is the rate one packer serves, 0.25 orders a second (4 seconds each), and ρ is the utilisation. Wq is the mean wait in the queue and L the mean number of orders in the shop, shown as - when no formula applies. The status is OK below 0.85, CONGESTED from 0.85 and UNSTABLE from 1, where the queue grows for as long as the run lasts. The table by units shows that one packer would be UNSTABLE. The hints give the units and the arrival rate that reach the target of 0.85. A hint of more units than the resource's max_cap also says to raise max_cap, because a current_cap above max_cap is clamped to it. The formulas give no wait for two packers with gamma packing times, so the line under Not estimated says to measure it. Queues and Capacity works the same numbers out by hand.
--verify measures it. It runs the model unpaced and compares each statistic with the analysis, as Check Against Theory shows. With --replications, it runs the model that many times from --seed, each to --until, and compares with each measure's 95% interval. --target sets the utilisation the hints aim for:
The measured utilisation of 0.7953 and throughput of 0.4002 agree with the analysis, so the model does what the formulas say. The wait has no formula here, so the runs only measure it: 4.505 seconds. With a target of 0.9, the hints say that two packers reach it at an order every 2.22 seconds. --json prints the same explanation as one JSON object, for a script to read. Its schema_version names the shape, which the Explanation class describes:
Run It
desflow run --sample prints the first events as JSON lines. It runs the model unpaced, with no ingress, and with no sinks of the file's own, so a model that writes to Kafka or a database needs no service:
Each order publishes queued when it joins the packers' queue and started when a packer takes it. The first two orders find a packer free, so each starts at the moment it queues. Without --sample, desflow run runs the model with its own settings and sinks.
One run is one sample, so measure the wait with replications: the same model with ten seeds, each run for one hour:
The mean wait is 4.505 seconds, and its half-width of 0.6483 says that the true mean lies within about 0.65 seconds of it, with 95% confidence. The packers are busy 79.53% of the time, close to the 0.8 that explain worked out. The last line repeats any one replication, with its events. This shop has no sinks. A model with a sink, such as type: Console, also sends the result to it as telemetry: one reading for each replication and measure. A Console sink therefore prints those lines before the table, as What the Sinks Receive explains.
--measure names a statistic to summarise, and repeats for more. --level sets the confidence level of the intervals. --workers runs that many replications at once, each in its own process, and gives the same results, because each replication keeps its seed:
The means are the same as before. At 99%, the half-width of the mean wait grows from 0.6483 to 0.9314 seconds, because more confidence needs a wider interval.
--stdout prints the run's records as JSON lines, instead of writing to the file's sinks. --drain stops the arrivals at the end time and runs on until every order has left. This run stops the arrivals at 20 seconds, and its last record is the ninth order packed, at 24.789 seconds:
Compare Designs
desflow experiment runs an experiment: here two packers against three, 20 replications of each with the same seeds, with a check that three packers keep the mean wait under 2 seconds. --output names the folder for its files, here results/packers in the current folder. Without it, they go to the folder the file's output names, beside the file:
The check passed: the upper end of the interval of the mean wait with three packers is 0.5919 seconds, under the bound of 2. The command exits with status 1 when a check fails, so a CI job that runs it fails too. The six files in results/packers hold every value, and report.md compares the designs, as Experiment Results describes. After editing experiment.json by hand, desflow report builds the report again from the files, without running anything:
Fit Measured Times
A packing time in a model comes from measured times. The fitting example holds 40 packing times measured at a bench, for a shop of its own, and desflow fit fits every distribution family to them:
The first line counts the values. Each row is one family: fitted with its AIC, where lower is better, or the reason it was not fitted. The triangular has the lowest AIC, 56.68, and the gamma is within 0.11 of it, so the data cannot tell the two apart. Fitting the Inputs checks what the choice changes.
The other options shape the fit, as Input Fitting describes:
--unitnames the data's unit,ms,s,minorh, and the settings are written in seconds.--upperis the highest plausible value, and a family that can draw above it is marked.--candidatesnames the families to try.--binsoffers the empirical distribution as a histogram with that many bins.--min-countis the fewest values a family is fitted to.--toleranceis how far a fitted mean and standard deviation may be from the data's, as a share of the data's.--drop-invalidleaves out values that are not numbers or lie outside the bounds, instead of stopping.--outwrites the best fit as YAML that a blueprint includes, and--plotdraws the data against it as a PNG.
The empirical row is now a histogram of 8 bins, and the gamma row is marked because a gamma can draw above 10 seconds. fitting/yaml/packing_fit.yaml is the file the fitting example's blueprint includes, so fitting/yaml/fitting.yaml now draws its packing times from this triangular.
Convert Parameters
A gamma takes a shape and a scale, not a mean and a standard deviation. desflow dist converts the measured times' mean of 1.5 seconds and standard deviation of 0.4832 seconds into them:
The shape and scale are close to the gamma that desflow fit found from the 40 values. The last two lines are the service in a blueprint and in the builder.
Given a family's own parameters, desflow dist converts the other way. Each of these lines prints the family's mean and standard deviation, as in the output above, for each kind of parameter: a rate, a constant's value, a low and a high, and a shape and a scale:
The shop's arrivals, rate: 0.4, are 2.5 seconds apart on average. A triangular also takes a mode, its most likely value. The one desflow fit found has a mean of 1.53 seconds, close to the measured 1.5:
desflow dist alone prints a table of every family with its parameters and a one-line summary, and Choosing Inputs explains the variability line:
Set Up an Editor
desflow schema prints the JSON Schema of a blueprint, for the release you installed. An editor that reads it offers the keys as you type and marks a misspelt one:
Set Up Your Editor names the file in the editor's settings.
Stability and Limits
- Exit statuses. A command exits with status 0 when it succeeds.
desflow runanddesflow explainexit with status 1 for a file that does not load, and print each error with its file and line.desflow experimentexits with status 1 when a check fails, after writing every file, and with status 2 when the experiment is invalid.desflow fitexits with status 1 when no family is usable.desflow distexits with status 1 for a family with no formula from a mean and a standard deviation, such asweibull. A usage error exits with status 2: a missing file or folder, an option value that is not valid such as--until abc, an unknowndistfamily or a parameter that breaks its rule, and adesflow fitcolumn with a value that is not a number, unless--drop-invaliddrops it. - Files, not Python objects.
desflow runreads a blueprint.desflow explainalso reads a builder Python file that defines aSimulationContextat module level.desflow experimentreads an experiment file, anddesflow fita CSV file. A builder or low-level script runs with Python, andreplicateandrun_experimentrun its replications and experiments, as Replications shows. - JSON output. The shape of
desflow explain --jsonis experimental through 1.x, and itsschema_versionchanges when the shape does. - Examples need the network.
desflow examples getdownloads from GitHub, at the tag of the installed release.--refnames another tag or branch.