State
A state is a value the model remembers. Use one when what happens next depends on something that happened earlier.
What it is
A state is a named, typed value. Think of a patient's acuity, a part's rework count, or a running total of orders. If you've used attributes or variables elsewhere, states do the same job. You define each state once, on the Model editor's States tab.
The question it helps answer
Does the work behave differently depending on what it is or what's happened to it? Urgent cases jumping the queue, parts that fail twice being scrapped, and orders of different sizes all need a value that travels with the work.
How it behaves
Component Type sets who holds the value. It's the most important choice you make for a state, because each type behaves differently.
| Component Type | Who owns the value | When it's created | What can change it | What can read it |
|---|---|---|---|---|
| entity | Each entity has its own copy. Every entity type gets every entity state. | When the entity is created, at Initial Value. | A generator's States tab, a schedule row, Assign State, a Delay, a connector, and Split, Create and Join for the new entity. The change lands on the entity running that step. | Conditions, Expressions, Queue Ranking and Match State. |
| model | One value, shared by the whole model. | At the start of each replication. | All of the places above, from any entity. | Conditions and Expressions, and the States option in animation. Queue Ranking and Match State can't read it. |
| activity | Each activity has its own copy. | At the start of each replication. | Assign State or a Delay on an activity. The change lands on that activity's own copy. | Nothing in the model. Only your results show it. |
| resource | Each resource has its own copy. | At the start of each replication. | A change on any of the places above, where you pick which resource it lands on. | Nothing in the model. Only your results show it. |
Data types
Data Type sets what a state can hold and what you can do with it.
| Data Type | Default Initial Value | Operations | Expression | Comparisons |
|---|---|---|---|---|
| number | 0 | assign, sample, add, subtract, multiply, divide | Yes | equal, not_equal, greater_than, greater_equal, less_than, less_equal |
| string | Blank | assign | No | equal, not_equal. Text must match exactly, including capitals. |
| boolean | false | assign, sample | Yes | equal, not_equal |
| category | The first value in the list | assign, sample | No | equal, not_equal |
A number state has no minimum or maximum, and it can go negative. It keeps fractions, so adding 0.5 to 0 gives 0.5. A category state can only hold a value from its list.
Changing a state
A change picks a State, an Operation, and a value. You can add changes in these places:
- The States tab of a generator, applied as each new entity is created.
- A schedule row on a generator, applied after the States tab.
- An Assign State action, or the State Modifications of a Delay, on an activity. These take no time. On a Delay, they apply once the resource is held, before the time passes.
- Split, Create and Join actions. Entity states change on the new entity.
- On departure and On arrival on a connector.
Changes run in the order listed, and each sees the one before. Set qty to 7, then set total to qty * 2, and total is 14.
| Operation | Works on | What it does |
|---|---|---|
| assign | All data types | Sets the value. |
| add, subtract, multiply | number | Does the arithmetic on the current value. |
| divide | number | Divides the current value. Dividing by 0 stops the run, with a message naming the step, the state and the time. The form and validation both accept it, because a value can reach 0 during the run. |
| sample | number, boolean, category | Draws a new value at random each time the step runs. Use it for "30% of patients are urgent". |
Reading a state
- A State Condition on an action skips that action unless the test passes.
- State Condition routing on connectors picks a path.
- Queue Ranking on an activity serves entities by a number state.
- Join groups entities by a Match State.
- An Expression can use a state's value in a calculation.
Queue Ranking and Match State read the entity's own states. A condition reads an entity state or a model state, and so does an Expression. A condition on an activity or resource state is refused, because validation stops the model first.
Conditions on a model state
A model state is one value for the whole model, so a condition can use it as a switch that every entity sees. This works in all three places a condition appears: a connector's State Condition, an action's State Condition (Only if… in Recipe) and a Branch action.
- It reads the value at that moment. The condition looks at the model state when it is checked, not at the value the model started with. In a test, an action guarded on a model state of 0 being equal to 1 was skipped, and a Branch on a model state of 2 took its true path.
- It needs no entity states. In a test, a model with only a model state routed all 10 entities by it.
- A change mid-run switches later entities. In a test, a connector sent entities to path A when
modewas above 1 and to path B otherwise. Twenty entities left the routing activity one a minute, andmodestarted at 0. At 10.5 minutes another entity setmodeto 2. The 11 entities that left before it went to B, and the 9 after it went to A. - A route is chosen once. A connector is chosen when the entity finishes its steps. If the entity then waits, blocked because the next activity is full, it stays on the connector it chose, even if the model state changes while it waits. It does not choose again. In a test with a single 10-minute slot on path A and no queue room, two entities blocked after choosing A. The state then changed so that later entities chose B. The two blocked entities still went to A, so A took 3 entities and B took 1.
- The same moment is not guaranteed. If a model state changes at the very minute an entity is routed, the order of the two is set by the engine's event order, not by your model. In a test, the change and an entity's routing fell on the same minute, and the change had been scheduled first. The entity saw the new value. Treat that as today's behavior, not a promise, and leave a gap between a change and the entities that depend on it.
- Only entity and model states. A condition on an activity or resource state is refused: <owner> has a condition on <scope>-scoped state '<name>'. Conditions read an entity State or a model State. Use one of those. In a test, a condition on an entity or model state in any of the three places passed validation, and one on an activity or resource state was refused.
When the warm-up ends
Each replication starts every state fresh at its Initial Value. At the end of a warm-up, a state's statistics restart from the value it holds. The value itself stays, so routing and queues carry on as they were.
For model, activity and resource states, tick Reset value at warm-up end to also put the value back to Initial Value. In a test with a model state adding 1 every 6 minutes, a 2-hour run and a 1-hour warm-up, the final value was 20 without the reset and 10 with it. The reset itself doesn't count as a change. Use it for running tallies. If a state drives a condition, the reset changes what entities read after the warm-up: they see the starting value again, not the value the run had set.
Entity states never reset. An entity in flight keeps its value, and its statistics count from the end of the warm-up. An entity that left before then is left out. With no warm-up, the reset does nothing.
In your results
Each state with Collect statistics on gets a row in the results. They cover the time after any warm-up, across replications. Add a card from the States group on a dashboard, such as Final, Max or Min. In a study, the Tradeoff View can plot them too.
| Result | Shown for | What it means |
|---|---|---|
| Number of changes | All data types | How often the value changed. Setting a state to the value it already holds doesn't count. For an entity state, it's the average per entity. |
| Final value | number | The value at the end of the run. For an entity state, it's the average across entities, each at the moment it left or at the end of the run. |
| Time-weighted average | number | The average value over time. For an entity state, an entity that held a value longer counts for more. |
| Lowest value, Highest value | number | The extremes reached. The Initial Value counts only if the state held it for some time, so a value a generator sets at creation replaces it. After a warm-up, the value at the end of the warm-up counts. |
| Most common value | category | The value held for the most time. |
| Percent of time true | boolean | The share of time the state was true, from 0 to 100. |
A string state shows only its number of changes. Entity states report one row per entity type, so you can compare a state across types. Activity and resource states report one row for each activity or resource, even one that never changed it. Model states report one row, named Model.
Changing a starting value in a what-if
A what-if can change a state's Initial Value, and you can make it a lever. This lets you ask "what if the mix, the mode or the starting stock were different?" without editing the state.
- Which states. Model and entity states of Data Type number, boolean or category. Activity and resource states show Only model and entity States can be changed by a scenario for now. String states show Text States can't be changed by a scenario.
- Routing on a state. Connector conditions read an entity state or a model state. Say a connector sends entities down path A when
modeis above 1 and down path B otherwise. Makemodea model state, and a what-if that changes its starting value switches the route for every entity. This is the recommended way to compare routing policies: one model state used as a switch, with its starting value varied per scenario. An entity state works the same way when nothing sets it as the entity is created. In a test, the model's own value of 0 sent every entity to path B, and a what-if setting it to 2 sent every entity to path A. A category state worked the same way, on a model state as on an entity state. - When it has no effect. An entity state's starting value is only used where nothing sets the state as the entity is created. A generator's States tab, a Create, Split or Join action, and an activity that creates entities all set states on the new entity. If every source of entities does that, the what-if shows No effect with the reason Every source of entities sets <State> when it creates them, so its starting value is never used. If only some do, it reads <names> set <State> when they create entities; only other entities start at the new value. An Assign State step later in the run doesn't count, because the entity has started by then.
- After a warm-up. A model state with Reset value at warm-up end ticked goes back to the scenario's starting value at the end of the warm-up, not the value saved in the model. The tick has no effect on an entity state.
- What it holds. A number state keeps fractions, so 1.5 stays 1.5. A boolean state accepts only true or false.
A value reached for no time still counts as a Lowest or Highest value: going to 20 and straight to 10 at the same moment shows a Highest value of 20. A category value never held isn't listed, not even at 0%. If every entity leaves before the warm-up ends, its entity state has no row, because no entity was there to measure. A divide leaves a decimal behind, so 6 divided by 2 then plus 1 holds 4.0, not 4. It reads the same in results.
A number state holds only numbers, so true or false is refused, as is a sample on a string state, because there's no distribution of text.
Properties
What you can edit depends on the complexity view. A view only changes what you can edit, never what runs.
| Where | Lowest view |
|---|---|
| States tab on the Model editor | Intermediate |
| States tab on a generator | Intermediate |
| Assign State action, and State Modifications on a Delay | Intermediate |
| State Condition on an action | Intermediate |
| State Condition routing, On departure and On arrival | Intermediate |
| Queue Ranking | Advanced |
| Split, Create and Join actions | Advanced |
| Schedule rows on a generator | Advanced |
States tab
With nothing selected on your diagram, open the Model editor and choose States. The tab says: State variables track custom numeric or text values on simulation objects. Use them for conditional routing, tracking entity attributes, or counting occurrences. Boolean and category states work too.
| Property | Where to find it | What it does |
|---|---|---|
| Filter | Top of the tab. | Shows All Components, or one type, such as entity Only. It also sets the Component Type a new state starts with. |
| Add State | Top of the tab. | Opens the form. With no states yet, the tab says No states defined yet. Click "Add State" to create your first. |
| State row | The list. | Shows the name, Component Type, Data Type and initial value, such as initial: 0. |
| Edit | Pencil on the row. | Opens the same form. See Changing a state later. |
| Delete | Trash icon on the row. | Opens a box under the row. See Deleting a state. |
Add State form
Add State opens this form. The pencil opens the same form, headed Edit State.
| Property | Where to find it | What it does |
|---|---|---|
| Name | Form. | Required. Starts with a letter or underscore, then letters, digits and underscores only, so no spaces. Capitals matter. The form shows Name is required, or Name must start with a letter or underscore and contain only letters, digits, and underscores. Names must be unique across the whole model, whatever the Component Type, or you'll see Another state named "x" already exists. |
| Component Type | Form. | model, entity, resource or activity. Starts on the filter's type, or model when the filter shows all. |
| Data Type | Form. | number, string, boolean or category. Default number. Changing it resets Initial Value to that type's default. Leaving category clears Category Values. |
| Category Values | Form. category only. | The allowed values, one per row, with spaces trimmed and blank rows ignored. At least 2, or you'll see CATEGORY type requires at least 2 values (e.g. "low, high"). All different, or duplicates are marked and you'll see CATEGORY values must be unique. + Add value or Enter on the last row adds a row. Pasting a comma-separated list splits it into rows. The × removes a row, and it's disabled at 2 rows. |
| Initial Value | Form. | The value at the start. A box for number, which treats blank as 0 and has no limits. A box for string. true or false for boolean. A list for category, showing (no values) until you've added some. If you remove the value it's set to, it moves to the first in the list. |
| Description | Form. | Optional note. Shows only in this form. The run doesn't use it. |
| Collect statistics | Form. | Default on. When off, the state has no row in your results, and the animation's States option doesn't follow its changes. The value still changes, and conditions and Expressions still read it. |
| Reset value at warm-up end | Form. Not for entity states. | Default off. See When the warm-up ends. The form says: The history always restarts at the end of the warm-up; tick this to also put the value back to its initial value — for running tallies, not for flags that drive routing. An entity state saved with it ticked shows it ticked and disabled, because it has no effect. |
In the Intermediate view, the form also shows a Lever tick for a model or entity state that a what-if can change. See State starting values as levers.
Validation also warns about a state named now, abs, min, max or round. It works everywhere except inside an Expression. Words such as if, class and not have the same limit, with no warning until an Expression uses them.
Add State Modification form
Add modification opens this form from any of the places above. It's disabled with no states in the model. The list then shows each change, with edit and delete buttons.
| Property | Where to find it | What it does |
|---|---|---|
| State | Form. | The state to change, listed with its type, such as qty (entity · number). Every state in the model is listed. Starts on the first. Changing it resets the value. |
| Resource | Form. Resource states only. | Which resource the change lands on. Each resource has its own copy of the state. You must pick one before saving. If you later delete that resource, the change stays and shows it as missing, and the model won't run until you pick another or remove the change. In the Recipe view this is the on choice in the sentence, which starts on the first resource. |
| Operation | Form. | The operations that state's Data Type allows. Default assign. A sample saved on a string state by an older model stays listed, disabled, and blocks saving until you choose assign. |
| Value or Expression | Form. number and boolean only, not for sample. | Choose Value to type a literal, or Expression to calculate one each time the step runs. See Expressions. Default Value. |
| Value | Form. | Matches the Data Type: a box for number, a box for string, true or false, or a list of category values. A blank number box counts as 0. A category state starts on its first value. |
| Sample distribution | Form. sample only. | Where the random value comes from. See Sample fields. |
Assign State also has a Recipe view, with sentences such as Increase qty by 1. See Activity. Recipe has no Expression or sample. Those show there, and open through edit in Classic.
Sample fields
| Property | Where to find it | What it does |
|---|---|---|
| Distribution | number states. | Constant, Uniform, Normal, Triangular or Exponential. Default Constant, with Value 0. Each choice starts with its own values, shown below. The units are whatever your state means. |
| Min, Max | Uniform, starting at 0 and 1. | Uniform: min must be ≤ max. |
| Mean, Std | Normal, starting at 0 and 1. | Normal: std must be > 0. A Normal draw can be negative. |
| Min, Mode, Max | Triangular, starting at 0, 0.5 and 1. | Triangular: min ≤ mode ≤ max. |
| Scale | Exponential, starting at 1. | Exponential: scale must be > 0. |
| P(true) | boolean states. | The chance of true, from 0 to 1, with P(false) below it. The box shows 0.5, but nothing is set yet, so the form shows p must be between 0 and 1 until you change the box. |
| Probabilities | category states. | One box per value, from 0 to 1, with its percentage. A new sample starts with every value on the same share. The sum must reach 1, to within 0.000001, and the form shows Probabilities must sum to 1.000 (currently 0.900) until it does. Normalize scales your numbers to total 1. Equalize gives every value the same share. |
Expressions
Enter a formula in Expression value, such as qty * unit_price. It gives a list of readable states as you type. On add, subtract, multiply or divide, the result is the amount: adding qty * 2 with qty at 4 adds 8.
Under the box it says: Operators: + - * / , comparisons (chainable: 0 < qty < 10), and/or/not, A if cond else B. Functions: abs(), max(), min(), now(), round(). Reference a state by typing its name.
- What it reads. Entity and model states, by name. An entity state comes from the entity running the step, or the new entity on a generator, Split, Create or Join. Text and category states can be compared, as in
5 if tier == "gold" else 1. Activity and resource states can't be read. - What it can produce. The result must match the state being changed: a number for a number state, true or false for a boolean state. Expressions can't set string or category states.
- Functions.
abstakes one value.roundtakes one or two, as inround(qty / 3, 2).minandmaxtake two or more.now()takes none, and gives the time since the run started, in the model's Clock Unit. - What isn't allowed.
**,%and//, such as modulo ('%') is not allowed. Words Python reserves, such asclass, can't be read as state names, so avoid them. Other functions, such assqrt, get a message listing the ones you can use. - A number too large to hold. Going past the largest number a state can hold stops the run, for an Expression or for plain arithmetic.
- Dividing by zero. Stops the run when it happens.
qty / countpasses validation, so guard it withqty / count if count != 0 else 0.
The form checks as you type, and won't save with an error, such as Unknown state 'x', Expression mixes incompatible types, or Expression produces boolean, but 'qty' is number. Validation checks it again. A few names get past the form, such as a state called class, and validation then stops the model.
State Condition fields
Actions and connectors share this editor. On an action, open the State Condition section, or Only if… in Recipe. It reads State Condition (active) once set. An action that fails its test is skipped. For connectors, see State Condition fields.
Press Add state condition to start. It fills in the first entity state, or the first model state if there is no entity state, with equal and that state's default value, so check all three fields. With no states, the card says Define states first — switch to the States tab. With states but none on entities or the model, it says Define a State first.
| Property | Where to find it | What it does |
|---|---|---|
| State | Editor. | Entity states are listed first, then model states, each with its scope and data type. A condition saved on an activity or resource state stays listed, disabled, with Conditions read an entity State or a model State, so this one would stop the run. Choose one of those. Validation stops the model until you do. A connector's condition only counts while its source routes by State Condition. Changing the state resets Value, and resets Comparison to equal if the new type doesn't offer it. A deleted state shows as (missing state: …). |
| Comparison | Editor. | Every data type has equal and not_equal. Number states add greater_than, greater_equal, less_than and less_equal. |
| Value | Editor. | What to compare against. A box for a number or text state, true or false, or a list of category values. A value no longer on the list shows as (missing value: …). The test still runs, with no warning: equal is never true and not_equal is always true. A category condition with no value shows (choose a value). |
| Clear condition | Editor. | Removes the condition. The action then always runs. |
Deleting a state
The trash icon opens a box headed Delete State: "qty"?, with Delete State and Cancel. It ends with This action cannot be undone. Here's what happens to what used the state.
| What used the state | What the delete does |
|---|---|
| Changes to it | Removed, on generators, activities and connectors, including inside Branch and Loop. |
| A State Condition on an action | Removed, so the action now always runs. On a Branch, the true path always runs. The box tells you how many Branches this affects, and validation warns about each one until you choose a new condition. |
| A State Condition on a connector | Removed. That connector is then never taken. See Connector. |
| Queue Ranking | Removed. The queue goes back to first come, first served. |
| Inherit States, Split Index State, Join Count State | Cleared. |
| Match State | Cleared. A Join needs one, so the model can't run until you choose another. |
| An Expression naming it | Left as it is. Validation stops the model until you fix it. |
| A schedule row with a value for it | Cleared from that row. The box tells you how many rows this affects. |
The box counts the shapes it will change: 1 place — activities, generators, or connectors — uses this state directly and will be updated automatically. In Lucidchart it can't count shapes or Branches, so it says Activity and generator steps that use this state directly will have that reference removed automatically., and that an If-then step testing it always takes its true path.
It lists each Expression, with the state it sets, under 1 expression references this state inside a formula and cannot be fixed automatically:, and adds Edit these yourself first — the engine rejects a formula that names a state which no longer exists. You can still delete.
Changing a state later
Edit a state from its row. A new name carries through to everything that uses it. Other edits don't, so the form warns you before you change a type, and validation flags whatever no longer fits.
| Change | What happens |
|---|---|
| Rename it | Everything follows the new name: changes, conditions, Queue Ranking, Expressions, schedule rows, Inherit States, Split Index State, Join Count State and Match State. The one exception is a script, which is free text. Update those yourself. |
| Change Data Type | Existing changes stay. Validation stops the model at any that no longer fit, such as an add on a state that's now text, and at Queue Ranking on a state that's no longer a number. |
| Change Component Type | Existing changes and conditions stay. Validation stops the model at a condition on a state that is no longer an entity or model state, and at a change that can't say which component it lands on. |
| Remove a category value | Validation stops the model at a change, sample or schedule row that uses it. Reopening a sample and saving drops the removed value. A condition testing it is never true, and nothing flags that. |
| Add a category value | Validation stops the model at a sample that doesn't list it. Reopen the sample and set the new value's share. |
Example
In an emergency department, urgent patients should jump the queue. Add an entity state named acuity with Data Type number. Use one generator for urgent arrivals and one for routine arrivals. On each generator's States tab, add a modification that assigns acuity: 1 for urgent and 2 for routine. Then, in the Advanced view, set Queue Ranking on Triage to Serve by acuity, with Order set to Lowest value first. Your results now show how much the routine patients pay for that.
To count urgent arrivals as well, add a model state named urgent_count. On the urgent generator's States tab, add a second modification that adds 1 to it. Its Final value is the average count per replication. If your model has a warm-up, tick Reset value at warm-up end so the count covers the same window as the rest of your results.
Common mistakes
- Choosing model when you meant entity. Add State starts on model unless you've filtered the list. A model state can drive a condition, but not Queue Ranking or Join. It is also one value for every entity, so use an entity state when each entity needs its own.
- Ranking a queue when every entity has the same value. A new number state starts every entity at 0. Set different values, or the queue stays in arrival order.
- Changing the value after the choice you meant to affect. Actions run in order, and routing runs after them, so an action at this activity can drive this activity's routing, but not its queue.
- Deleting a state used in a schedule or a formula. Quodsi removes direct uses, but not these. Fix them first, or the model will be rejected.
- Forgetting that a deleted condition means "always". A step that ran only for urgent patients now runs for everyone.
- Using a state where an entity type would do. If two kinds of work need their own results, make them separate entity types.