Skip to main content

Overview

An Entity is an item that flows through a simulation, a work order, a finished good, a pallet, a batch, a patient, a truck. Every simulation revolves around entities being created, routed, processed, and terminated. What makes entities powerful in ProDex isn’t the concept of “things flowing through a graph”: it’s that entities carry typed attributes with them, and expressions across the model can read and act on those attributes. An entity isn’t just an anonymous token; it’s a bundle of typed data that components inspect, route on, time-process differently, and aggregate. This is the mechanism that turns a static flow diagram into a model that responds to the specific work being done. Entity types are one of the first things you define when building a model — see Key concepts for where they sit in the Factory > Models hierarchy, and the Simulation overview for the modeling workflow around them.

Attribute Types

Each entity type defines a set of named attributes. The schema has five canonical types (boolean, number, text, text_list, number_list); the UI splits Number into Integer and Real, so the attribute-type dropdown shows six options. Under the hood that split isn’t a sixth type: a Number attribute carries a domain field ("int" or "real", defaulting to real), and the two dropdown entries surface it. The List types are enums, not lists. A Text List attribute doesn’t hold multiple strings: it holds one string, picked from a fixed set of allowed values declared on the entity type. Same for Number List: the attribute’s value is one number chosen from a predefined set. Think of them as constrained categorical values, not arrays. Use Text List or Number List when you want an attribute to be categorical and you want the platform to enforce valid values. Use Text or Number when the attribute is open-ended. Attribute types are fixed on the entity type: you can’t store a number in a Text attribute. The expression language respects these types when you reference attributes by name.

How Attributes Flow

Attributes set on an entity persist with it through the entire flow. Once assigned, downstream components can read them without any explicit propagation.
  • Create / assign: attributes are assigned any time a component produces a new entity. That’s on Sources (creating initial entities), Combiners (output entity of a batch), Separators (output entities of a split), Transformers (output entity of a type change), and material releases on either a Source or a Buffer in a schedule. In the model definition, Source/Combiner/Transformer carry a singular attribute_assignment; a Separator carries attribute_assignments (plural), keyed by output entity-type slug, because one split can emit several output types.
  • Read: any downstream expression in a single-entity context can reference the attribute by bare name (priority, weight).
  • Change identity: a Transformer produces a new entity of a different type with its own attribute assignments. It doesn’t mutate the input entity’s attributes in place: it emits a different entity, typed differently, with its own attribute values.
  • Aggregate: when entities are combined, the output entity’s attributes are computed from the set of inputs using aggregation functions like SUM(weight) or MAX(priority) in the Combiner’s multi-entity context.
Transformer attribute carryover is not automatic. When a Transformer emits an output entity, none of the input entity’s attributes are copied across by default. Every attribute you want on the output must be explicitly assigned, most commonly as a DSL expression referencing the input attribute by bare name. Skip an assignment and the value simply isn’t carried — silent data loss. Separator outputs behave the same way.
The DSL doesn’t require a prefix to read attributes: just use the bare name. priority and product_class are valid identifiers in any expression that has an entity in scope.

Which context each assignment runs in

The single biggest authoring trap with attributes is forgetting which expression context an assignment evaluates in: Attribute names also can’t collide with the DSL’s reserved keywords — the validator rejects an entity type whose attribute shadows one.

Assignment Strategies

Whenever a component assigns an attribute on a new entity (on a Source, Combiner, Separator, Transformer, or schedule material release), it uses one of six strategies. Not all six are valid for every attribute type; the dropdown filters to the strategies that work for the attribute you’re configuring. (The per-type restrictions are enforced by the validator and surfaced by the UI — the schema itself is looser, so hand-authored JSON can express combinations that will then fail validation.)
Random Choice and Random are different strategies. Random Choice picks uniformly from a categorical choices array (boolean / text list / number list). Random samples from a probability distribution (number only). The dropdown filters them out based on attribute type, so you’ll only see the ones that apply.

Strategy Details

Fixed: Same value on every entity. Useful for attributes that don’t vary across items, or as a placeholder before you wire in real variability. On Text List / Number List attributes the fixed value must be one of the declared choices. DSL Expression: The value is the result of a DSL expression evaluated when the entity is created. Use when the value depends on simulation state, other attributes, or the current time: IF(SIM_TIME < SHIFT_1_END, "day", "night") or LOOKUP(priority_by_class, product_class). Not valid for Text List or Number List: for dynamic categorical selection use Weighted with DSL-expression weights, or restructure to Random Choice. Round Robin: The first entity gets value 1, second gets value 2, third gets value 3, fourth cycles back to value 1. Useful for evenly distributing entities across categories without randomness. Round Robin carries no choices of its own — it cycles through the choices declared on the attribute. Random Choice: Uniform random pick from the entity type’s declared choices. Every choice has equal probability. For Boolean attributes the choice set is the implicit {true, false}. Random: Samples from a distribution (normal, exponential, triangular, etc.). Useful for natural variability: entity weights drawn from a normal, service-time requirements drawn from triangular, or arrival intervals drawn from exponential. Weighted: Pick from the entity type’s choices with per-choice weights. Specify a weight for each option; relative weights determine selection probability. Weights can themselves be DSL expressions for dynamic distributions.

Defining an Entity Type

Entity types are managed via the Entities button at the bottom of the Modeler’s Library panel: it opens a modal listing every entity type in the factory with Name, Description, Unit, and Attrs columns. Each type has:
  • A name and description
  • A unit — a free-text label (default "EA") that drives quantity labels in reporting and KPIs. It is a label only: ProDex does not perform dimensional conversion between units.
  • A set of typed attributes (with choices arrays declared on Text List and Number List attributes, optional lower_bound/upper_bound/exclude_zero and domain on Number attributes, and optional length on Text)
Once defined, an entity type is available to every model in the factory. Sources, Transformers, BOM nodes, Combiners, and Separators that produce entities of that type share the same attribute schema: if you add an attribute to the type, every place that creates or modifies the entity can now set it. Entity types live in entities/{slug}.json in the factory’s data tree.
The same entity types back your BOMs. An entity type isn’t simulation-only. A BOM references an entity type by its slug (entity_id), and its applies_when conditions read the type’s attributes by name — the two features share one factory-scoped registry, the same entities/{slug}.json files. Define an entity type once and it’s available to both the simulation model and the BOM/planning side; edit its attributes and both see the change.
Renaming an entity slug or attribute propagates widely. Entity slugs are referenced by Source.entity_type, Transformer input_entity/output_entity, Separator input_entity and output batch keys, Combiner.output_entity, schedule material releases (entity_type_id), BOMs (entity_id), and Configuration Templates. Attribute names appear in BOM applies_when conditions and DSL expressions across the model. Rename with a global search, not in isolation.

Authoring Entity Types with Dexter

When Dexter builds entity types for you — typically after profiling your uploaded data — it authors them in code:
The constructors mirror the five schema types (sim.boolean, sim.number, sim.text, sim.text_list, sim.number_list). Ask Dexter to create or extend entity types from a spreadsheet and this is what runs underneath.

Attributes in Results Data

Attributes are queryable after a run, but they live in a dimension table, not inline on every lifecycle row:
  • Each entity_lifecycle row carries an attributes_hash. Join it to the entity_attribute table, which has one row per (attributes_hash, attribute_name).
  • entity_attribute columns: attributes_hash, attribute_name, value_type ("number", "text", or "boolean"), and typed value columns value_number / value_text / value_boolean. Booleans land in value_boolean, never value_number; Text List values land in value_text and Number List values in value_number (there are only three value buckets).
  • Rows are written the first time each distinct attributes_hash is seen and never re-emitted; the table carries no entity identity of its own — the hash on entity_lifecycle is the only route back to entities.
  • entity_lifecycle.attributes_json is populated only on material-release entry rows that carry per-release overrides; it’s null everywhere else.
See available dataframes for the full catalog.

Patterns

Classification attribute driving routing. A product_class Text List attribute set at the Source (one of "rush" or "standard") drives a Router that sends rush items to the express line. Numeric attribute driving processing time. A complexity_factor Number attribute (drawn from a distribution via the Random strategy) multiplies the base processing time on each Process. Type change in a Transformer. A Transformer reassigns the entity to a new type painted_part (from raw_part), with explicit DSL-expression assignments that carry forward every attribute the downstream model cares about. Aggregation in a Combiner. When three components are combined into an assembly, the output entity’s total_weight is SUM(weight), priority is MAX(priority), and qc_passed is ALL(qc_passed). The full aggregation set is SUM, MEAN, COUNT, MAX, MIN, MODE, N_UNIQUE, ANY, and ALLCOUNT and N_UNIQUE always return numbers, ANY and ALL are the boolean aggregations (over boolean attributes), and MAX / MIN / MODE work on numbers or text.

Tips

  • Model the attributes you’ll query. If you care about “cycle time by product class” in your results, product_class has to be an attribute on the entity, otherwise the join isn’t available in the simulation dataframes.
  • Use Text List or Number List for categorical constraints. They’re enums: the platform enforces that the value is one of the declared choices. Prefer these over free-form Text when the attribute has a known finite set of valid values.
  • Prefer entity attributes over state variables for per-entity data. State variables are for facts about the system. Attributes are for facts about a specific entity.
  • For dynamic categorical selection, use Weighted with DSL-expression weights. That’s the only way to drive list-type attributes from expressions, since DSL Expression isn’t valid for list types.
  • Explicit attribute carryover: every output attribute on a Transformer or Separator must be assigned. There’s no auto-copy from the input.
  • Stamp provenance on assignment values. Choices lists, weights, and expressions inside attribute assignments accept the same derived / stated / assumed provenance stamps as everything else Dexter authors — unstamped values surface in an assumptions review.
  • entity_id doesn’t survive transformation. Transformers, Combiners, and Separators emit new entities with new ids. If you need to trace one unit end-to-end in results, carry a stable business identifier (order number, serial) as an attribute.