Skip to main content

Overview

The Modeler is where a simulation model gets built: a directed graph of components on a canvas, plus the entities, expressions, reference data, and event logic that make it behave like your operation. This page is the working reference for that surface — the controls at the top of the page, every component type and its configuration, the expression language, the left panel, and how a model actually runs.
The Overview panel listing entities and variables on the left, a column of colored component buttons, and a canvas showing a process flow of stations and buffers, with playback controls and an event timeline along the bottom

The Modeler: the Overview panel, the component dock and the canvas

If you haven’t built a model before, walk through Your First Simulation once first. And you never have to drive the canvas by hand: Dexter builds and edits models conversationally, already knows which model you have open, and mixes freely with manual edits.

What discrete event simulation is

Discrete event simulation (DES) represents an operation as a sequence of instants at which something changes — an order arrives, a machine finishes a cycle, a resource frees up. Between events nothing happens, so simulated time jumps from one event to the next; that’s why a model of a month of production runs in seconds. Change a capacity, a distribution, or a buffer and you see the downstream effects immediately, without touching the real system. You build the model visually and ProDex’s simulation engine executes it directly. The canvas is the source of truth — there is no engine code to write.

Building on the Canvas

A model is a directed graph: components are nodes, connections are edges, and entities flow along the edges. Drag components from the dock in the canvas’s bottom-left corner (the arrow button beneath it expands and collapses it), drop them on the canvas, wire each component’s output handle to the next component’s input handle, and click any component to open its settings in the left panel’s Properties view. The minimum viable model is Source → Process → Sink. Everything else — buffers, routers, stations, event logic — earns its place as your questions demand it.

The Top Bar

  • Model picker — switches the open model; its menu also holds New Model, Rename, and Delete.
  • Schedule picker — picks what drives timing: Default (no schedule) or any schedule defined for the model. Its menu also holds New Schedule, plus Edit and Delete for the selected schedule. With a named schedule active, the schedule’s own Start and End Times drive the run.
The ⋯ menu at the right of the bar holds the rest:
  • Duration — shown while Default is the active schedule; sets the run length (DDD:HH:MM:SS).
  • Resource fill rule — how Processes compete for Resources when a request can’t be met yet. See Resource fill rule.

Component Types

Every flow component’s panel also carries an Event Hooks and an Event Listeners section — the reaction logic covered in The Event System below.

Source

Where entities enter the model.
  • Entity type — which entity this Source produces.
  • Arrival logic — the inter-arrival time (time between arrivals, not a rate), drawn from the distribution picker or a DSL expression.
  • Initial attribute values — per attribute, a Strategy: Fixed, DSL Expression, Round Robin, Random (pick from the attribute’s choices), Distribution (sample a distribution), or Weighted (a value/weight table). The dropdown narrows to the strategies valid for the attribute’s type — see the type-by-strategy matrix. Note that a DSL assignment at a Source runs before the entity exists, so it can’t read the new entity’s other attributes.
  • Events — fires entity created per produced entity.
An event-only Source is valid on purpose: leave arrival logic empty and the Source produces nothing on its own, releasing entities only when a release action or a schedule material release triggers it. When a schedule is active, its material releases run in parallel with the Source’s own arrival logic — both fire — so decide deliberately which mechanism (or both) drives each entry point.

Process

A step that takes time and may consume resources.
  • Entity Type — scopes the rest of the panel to one entity type.
  • Processing time — a distribution or DSL expression.
  • Resource requirements — a multi-row table (Add Row) of resource → whole-number quantity. Connecting a Resource node to one of the orange resource handles on the top or bottom of the Process adds a row too; a connection to the Process’s side flow handles does not. Each row’s Hold switch keeps that resource with the entity after this process, so the next process that needs it uses it without a new request. Ask Dexter to make a quantity an expression; a requirement whose quantity evaluates to 0 is skipped for that entity — the mechanism behind cross-training and substitution patterns.
  • Station — optionally places the Process inside a Station.
  • Events — process started, process completed.

Resource

A capacity pool — workers, machines, fixtures — that Processes draw from. Drag a Resource from the dock like any other component, then connect it to an orange resource handle on each Process that uses it (or pick it in the Process’s requirement table).
  • Capacity — number of parallel slots.
  • Allocation Discipline — FIFO, LIFO, or Priority (a DSL key with ascending/descending ordering).
  • Changeover Times — a table of (from process, to process) → duration rows. Same-process pairs are allowed. In the panel each duration is a fixed time; ask Dexter when a changeover should be a distribution or a DSL expression. An expression can read the changeover-only identifiers for the exiting and entering entities — their types and their attributes — so a single changeover time can vary with the (exiting entity, entering entity) pair rather than being keyed only by process.
  • Events — slot freed, slot working. A set capacity action reacting to shift topics is the standard shift scheduling pattern; the same mechanism models breakdowns, maintenance windows, and staffing changes.
Changeover keys are processes, not entity types. A resource that handles two products through the same process incurs no changeover; switching between two processes (even on the same entity type) does. If your changeover model is keyed by entity type today, rework it around the processes that actually demand the setup.

Resource fill rule

A Process acquires everything it needs at once: it starts only when every resource in its requirement table has a free slot, and while it waits it ties up nothing beyond resources the entity is already holding from an earlier process. Each Resource ranks the waiting requests by its Allocation Discipline. The Resource fill rule in the ⋯ menu decides what happens when the request at the front of the line can’t be met yet:
  • Allow bypass (pass) — the default. A request that can get all its resources may go ahead of a blocked one.
  • Preserve precedence (no pass) — blocked requests keep their place in the queue, even if that leaves free capacity idle.
The rule is saved with the model. When the model runs as the top-level model, it also applies to every sub-model inside it. Changing it can change waiting times and throughput, so compare runs under both settings if it matters to your decision.

Buffer

A waiting area between components.
  • Entity Type, and Capacity with an explicit Infinite toggle.
  • Queue Discipline — FIFO, LIFO, or Priority.
  • Release Mode — Push (auto-release) sends entities downstream as soon as there’s room; Hold (manual release) keeps them until a release action fires — the kanban/pull pattern.
  • Events — entity entered, entity exited, contents changed. Buffers and Stations are the components that emit the per-entity entered/exited pair; Processes signal through process started/completed instead.
Hold-mode Buffers are the canonical batching recipe. A contents changed hook whose condition inspects the queue and fires release with a quantity (and optional per-entity filter) often beats a Combiner + Separator sandwich, because it preserves per-entity lineage. See Combiners, Separators & Transformers.

Router

Splits entity flow across outgoing connections based on its Logic Type: Routers fire entity routed per entity. Each Conditional or Probabilistic rule row has a See expression / AI explanation toggle that flips between the raw DSL and a plain-English explanation — the fastest way to confirm a rule does what you think it does.

Combiner, Separator, and Transformer

The batching components, covered in depth in Combiners, Separators & Transformers:
  • Combiner — merges multiple inputs into one output (assembly). Batches form either from static per-input quantities or from a dynamic batch expression evaluated on each arrival. Output attribute assignments run in multi-entity context, so aggregations apply — SUM, MEAN, MAX, MIN, COUNT, ANY, ALL, MODE, N_UNIQUE over the batch (e.g. SUM(weight), MAX(priority), ALL(qc_passed)). An aggregated attribute must exist on every input type — give a default to inputs that don’t naturally carry it rather than leaving it absent.
  • Separator — splits one input into several outputs: an Output Batch table pairs each output entity type with a count expression, with per-output attribute assignments (Fixed or DSL).
  • Transformer — one in, one out. Changing the entity type is only half its job: configuring the same input and output type is the standard way to mutate attributes mid-flow (incrementing a rework counter, stamping a QC result).
All three are typically placed inside a Station rather than standalone.

Station

A capacity-constrained container representing a physical space or work center. Capacity is a per-entity-type table (e.g., Tube = 500, Tray = 100), so one station can hold different quantities of different items at once; leave a type unbounded when the station isn’t the constraint. Stations also group related components, which keeps large models navigable — and emit entity entered, entity exited, and contents changed (multi-entity context over the current contents, the usual seat of WIP gauges).
Station capacity is tracked by entity lineage, not throughput. A slot is tied to the entry entity and freed only when it and all its descendants have exited — a Separator producing three outputs per input holds the parent’s slot until every output leaves, so a station can be fully blocked at what looks like low utilization. Capacity represents physical space, not flow rate.

Sink

The exit point. Entity Type is required — only matching entities may enter — and entities that arrive are consumed, counted, and fire entity terminated.

Model Node

Embeds another model from the same factory as a single component — the key to hierarchical modeling. First import the model: in the left panel’s Sub-models section, click + (or Import Model) and pick another model in the factory. Imported models then appear under the dock’s Sub-model button; open it and drag a model onto the canvas. Each instance maps its outer connections to the sub-model’s internal sources and sinks, and can alias resources, topics, and state variables per instance; nested components are addressed with parent::child qualified paths. See Model Nodes & Hierarchical Modeling.

Connections

Drag from a component’s output handle to another’s input handle to define flow. Entities move along a connection when the downstream component can accept them — or, for Hold-mode Buffers, when explicitly released. Two topology rules worth knowing up front: connections can’t start or end at a Station (wire the components inside it instead), and a Router’s rules attach to its outgoing connections. Validation enforces the full ruleset as you edit.

The Event System

Flow connections describe how entities move; the event system describes how the model reacts. Two primitives, both configured on every flow component’s panel:
  • Event Hooks fire on the component’s own lifecycle events — the entity created / process completed / contents changed events listed per component above. The hook’s expression context follows the event: single-entity for per-entity lifecycles, multi-entity for batch and contents-changed events, no-entity for resource slot events.
  • Event Listeners fire when a subscribed topic is emitted anywhere in the model — and always run in no-entity context: a listener can read state variables, constants, lookups, and component queries, but never the emitting entity’s attributes. When cross-component logic needs per-entity data, the pattern is: a hook writes what it needs into state and emits; the listener reads state.
Both take an optional boolean condition and a list of actions: assign (write a state variable), emit (publish to a topic), release (Sources and Buffers only), pause / resume (Sources and Buffers only), and set capacity (Resources only). This is how shift logic, kanban, backpressure, and every other behavior that spans components gets wired — see Event System for the full per-component event and action contract.

Expressions and the DSL

Most numeric and logical fields accept either a literal or an expression in ProDex’s DSL, which is how models become dynamic and data-driven: routing conditions over attributes, per-entity resource needs, durations that depend on state. Identifiers available everywhere: SIM_TIME (current simulation time) and SIM_DURATION (the configured run length, not the time remaining). In entity contexts: ENTITY_TYPE (the entity’s type slug) and ENTITY_AGE. In hooks and listeners: SELF, the hosting component’s id — which makes rules copy-pasteable across components. Component queries, usable in every context: BUFFER_LEVEL(id), BUFFER_CAPACITY(id), RESOURCE_AVAILABLE(id), RESOURCE_CAPACITY(id), STATION_WIP(id) — and LOOKUP(table, key1, key2, ..., default:=value) reads a lookup table by one or more keys. Which fields run in which context — and the full catalog of functions and aggregations — lives in The Expression Language.
Time fields use a DDD:HH:MM:SS widget by default — typing 5 in the day box means 5 days, not 5 minutes. The </> toggle next to a field flips it into the DSL editor. That extends into distribution parameters too: each parameter accepts a literal number or a DSL expression directly, so a computed value doesn’t need any special wrapper. And every DSL-mode value has the See expression / AI explanation toggle, so you can audit any expression in plain English.
Distributions are picked from a ten-option dropdown (Fixed, Normal, Exponential, Uniform, Lognormal, Weibull, Triangular, Erlang, Beta, Gamma), not written as DSL calls. Every parameter of every distribution accepts a literal number or a per-sample DSL expression, so any of them can vary by entity or state. See Distributions for parameter conventions and which to reach for.

The Left Panel

The panel to the left of the canvas has two views:
  • Overview — what the model uses, in six sections: Entities, Variable, Topics, Constants, Lookup tables, and Sub-models. Each section’s header pencil (View and manage all) opens the full management dialog, and the + Add … row at the bottom of a section adds an existing item to the model or creates a new one (Sub-models use the header + instead). A row’s × removes the item from this model without deleting it; it’s disabled while something in the model still references the item. Below the sections, an Events list fills in as a simulation runs, and during replay the Variable rows show live values.
  • Properties — the settings of the component you’ve selected on the canvas. The back arrow returns to Overview.
Entities, constants, lookup tables, and sub-models belong to the factory: each is defined once, and every model adds the ones it uses. State variables and topics belong to the model itself.

Entities

The items that flow through your simulation: raw materials, WIP, finished goods. An entity type must be added to the model before a Source, Transformer, or other component can use it — use + Add entity in the Entities section, or open the pencil to create, edit, and browse entity types and click Add to model. Each entity type carries typed attributes (Boolean, Number, Text, Text List, Number List), referenced by bare name in single-entity contexts — no self. prefix. Attributes persist with the entity through the whole flow. Note that the two list types are discrete choice sets — despite the name, each holds a single member of its declared choices, not a collection. They accept only a fixed or sampled value; a DSL-computed assignment is rejected in validation. If a downstream node needs to compute or thread the value, make the attribute a scalar Number or Text instead.
Entities are factory-scoped, not model-scoped. Renaming, retyping, or deleting an entity affects every model in the factory that references it — including expressions that name its slug. Need a one-off variant for one model? Create a new entity rather than mutating a shared one.

Constants

Named, factory-scoped parameters — Text, Number, or Boolean — referenced by bare name in any expression: PROCESSING_TIME_MINUTES * SHIFT_EFFICIENCY_FACTOR. Names must be SCREAMING_SNAKE_CASE and can’t reuse a reserved word or the name of another constant or lookup table. Constants are the right home for any value that appears in several places or that you’ll vary between runs: change it once, and every referencing field updates.

Lookup Tables

Keyed tables queryable from any expression — LOOKUP(CYCLE_TIMES, ENTITY_TYPE) — with SCREAMING_SNAKE_CASE names, one or more key columns and Text/Number/Boolean values. A missing key returns the default:= you pass, or a type-specific zero (0, "", FALSE) if you don’t. Typical uses: cycle time by product, yield by material, routing weight by order class. See Constants & Lookup Tables.

State Variables

Model-level mutable values that persist across entities: current shift, an inventory level, a rolling average. Read them by bare name in any context; write them with an assign action on a hook, listener, or scheduled action. Names follow the same SCREAMING_SNAKE_CASE rule. Use a state variable when the value belongs to the system; use an entity attribute for per-entity data.

Topics

Named pub/sub channels: a component publishes with an emit action, and any component whose Event Listener subscribes to the topic reacts — coordination without a flow connection between them. Constants, Lookup Tables, State Variables, and Topics share one four-tab Lookups dialog; the pencil on the matching Overview section opens it on that tab.

Schedules

A model runs in abstract time until you pair it with a schedule, which anchors execution to wall-clock time: material releases inject specific entities at specific moments (in parallel with Source arrival logic, not instead of it), and scheduled actions fire assign and emit at specific times — the component-control actions remain hook-only. A model can hold multiple schedules — baseline week, stress scenario, historical replay — and you pick the active one in the schedule picker at the top of the page.

Running a Simulation

The run controls sit at the bottom of the canvas:
  • Run simulation (play button) — executes the model to completion.
  • Fast forward to end (skip-forward icon) — jumps the on-canvas playback straight to the end of the run; available once a run has produced data to replay.
  • Playback speed — controls the on-canvas animation only. The engine always runs at full speed.
The play button doubles as the validation indicator: green means ready; amber means the model has warnings but still runs; red means a specific blocker that stops the run. Hover an amber or red button for the count, the verbatim messages, and a Fix with Dexter button that sends them to Dexter to fix. The Modeler autosaves as you work, so there’s no manual save step: validation re-runs against that saved state after each edit — checking DSL typing, entity-flow compatibility, hooks, schedules, and nested models — and the play button always reflects the current model. Below the canvas, a horizontal timeline shows every material release, shift transition, and scheduled action. With Default selected the axis follows the Duration setting; with a schedule active it switches to wall-clock dates spanning the schedule’s Start and End Times. Scan it before running to confirm the schedule lines up with what you expect. Runs execute against the live working model — tweak, run, repeat. To compare configurations, run each variant and compare the runs; to measure variability, replicate a run with several seeds. Each execution creates a run whose KPIs and charts compute in a second pass after the simulation finishes — a run showing pending isn’t broken, just still computing. Read the output in Results and Analytics. The bottom-right of the canvas holds the view controls: Zoom in / Zoom out, Fit view, Auto format layout (re-arranges nodes), Find node (⌘F / Ctrl+F), and 3D.

3D view

3D (or the V key) switches the canvas to a 3D shop-floor view of the same model; 2D view, or V again, switches back. The dock, run controls, and component selection all work in 3D. Left-drag to orbit, hold Space or right-drag to pan, scroll to zoom, and Shift-drag to select several components. The Modeler remembers which view you last used for each model.

Tips and Best Practices

  • Start simple. Source → Process → Sink validates your setup before you add complexity.
  • Use constants for anything you’ll change between runs — scenario sweeps become one-field edits.
  • Name everything in the operator’s terms. Component names surface in results, event logs, and charts; good names pay off every time you read output.
  • Use stations to group related components — large models get hard to navigate without structure.
  • Watch the play button. Green means valid; red names a specific blocker, and Fix with Dexter hands it to Dexter.
  • Copy before big changes. Ask Dexter to copy the model at milestones, so you can still run the earlier configuration to compare against.