---------------------------------------------------------------------- This is the API documentation for the json2ciw library. ---------------------------------------------------------------------- ## Classes Core classes CiwConverter(model) ## Functions Public functions multiple_replications(network: ciw.network.Network, process_model: 'ProcessModel', num_reps: int = 50, runtime: float = 1000.0, warmup: float = 0.0, n_jobs: int = -1) -> pandas.DataFrame Run multiple replications of a Ciw simulation and collect performance metrics. Executes independent replications of a discrete event simulation, collecting node performance measures including arrivals, waiting times, service times, utilisation, and queue lengths. Activity and resource names from the process model are included in the output. Parameters ---------- network : ciw.Network A configured Ciw network object defining the queueing system structure, service distributions, and routing. process_model : ProcessModel Pydantic model containing activity and resource metadata. Used to map node IDs to human-readable activity and resource names. num_reps : int, default 50 Number of independent replications to run. Each replication uses a the replication number as a random seed. runtime : float, default 1000.0 Simulation time horizon for each replication. Units match the time units used in service and arrival distributions. warmup : float, default 0.0 Warmup period to exclude from statistics. Records with arrival times before this value are filtered out to reduce initialization bias. n_jobs: int, default -1 Number of cores to use for parallel replications. Use -1 for all cores. Returns ------- pd.DataFrame DataFrame with one row per node per replication containing: - rep : int Replication number (0-indexed) - node_id : int Ciw node identifier (1-indexed) - activity_name : str Name of the activity from process model - resource_name : str Name of the resource from process model - resource_capacity : int Number of servers at this node - arrivals : int Number of completed visits to this node - mean_wait : float Mean waiting time (queueing time before service) - mean_service : float Mean service time - utilisation : float Time-averaged server utilisation (fraction busy) - mean_Lq : float Time-averaged mean number of customers in queue summarise_results(df_reps: pandas.DataFrame, metric_name_map: dict[str, str] | None = None, include_resource_in_colname: bool = True) -> pandas.DataFrame Aggregate replication results and transpose so metrics are rows and activity names are columns. Parameters ---------- df_reps : pd.DataFrame Raw tidy-format replication results from multiple_replications(). metric_name_map : dict or None, optional Dictionary mapping internal metric names to friendly names. If None, default friendly names are used. Pass {} to keep original names. include_resource_in_colname : bool, default True If True, column headers include both activity and resource name. tidy_to_wide_format(df_reps: pandas.DataFrame, include_resource_in_colname: bool = False) -> pandas.DataFrame Return replication results in wide format: one row per replication, one column per (metric, activity[/resource]). Parameters ---------- df_reps : pd.DataFrame Tidy replication results from multiple_replications(). include_resource_in_colname : bool, default False If True, wide-format columns include resource names as well. create_user_filtered_hist(results: pandas.DataFrame) -> plotly.graph_objs._figure.Figure Create a Plotly histogram with a dropdown that lets the user choose which metric to display. Parameters ---------- results : pd.DataFrame Wide-format results with one row per replication and one column per KPI. Returns ------- plotly.graph_objects.Figure Interactive histogram figure. render_simulation_app(default_params, model_metadata, valid_process_model=None) Main function to render the simulation UI and execute the model. ---------------------------------------------------------------------- This is the User Guide documentation for the package. ---------------------------------------------------------------------- ### Ciw Call Centre Model from JSON On this page we will create a `ciw` network model from a specification in a JSON file. The model represents a simple urgent care call centre defined in [Monks and Harper (2023)](https://openresearch.nihr.ac.uk/articles/3-48) * A single random arrival process * Two activities for call triage (by an operator resource) and nurse call back (by a nurse). * Only 40% of patients require a nurse call back by default. ![](/img/call_centre_diagram.png){width=80% fig-align="center" fig-alt="Call centre diagram"} ## 1. Imports ### 1.1 `json2ciw` imports **We will use:** * `load_call_centre_model` function that loads the built in urgent care call centre JSON file. * `ProcessModel`: a `pydantic` schema that provides automatic validation of the JSON. * `CiwConverter` that accepts a valid `ProcessModel` that represents a DES model and returns a `ciw` parameter `dict`. * `multiple_replications`:runs the network model and results a `Dataframe` of replication results. * `summarise_results`: provides a formatted table of mean results for each node in the network. * `tidy_to_wide_format`: the results from `multiple_replications` are returned in tidy (stacked row) format. This function converts into wide format i.e. each column is a performance measure. * `create_user_filtered_hist` generates a interactive plotly chart to view replications for each performance measure. It requires replication data in **wide** format. ```{python} from json2ciw.datasets import load_call_centre_model from json2ciw.engine import ( CiwConverter, multiple_replications ) from json2ciw.results import ( summarise_results, create_user_filtered_hist, tidy_to_wide_format ) from json2ciw.schema import ProcessModel ``` ### 1.2 Other imports ```{python} import ciw import statistics from IPython.display import JSON from rich import print ``` ## 2. Load JSON ```{python} json_call_centre = load_call_centre_model() ``` display as collapsible JSON. Expand to view the details. ```{python} JSON(json_call_centre) ``` ## 3. Convert to a ProcessModel A `ProcessModel` is a `pydantic` schema. It is independent of `ciw`. This early version will automatically validate that the JSON file is correct and that all transitions add up to 1.0 when it is created. ```{python} model_instance = ProcessModel(**json_call_centre) ``` The contents of the process model can be manually inspected by a developer as follows: ```{python} print(model_instance) ``` ```{python} model_instance.display_diagram() ``` ```{python} # summary of distribution used model_instance.get_distributions_df() ``` ```{python} # summary of routing percentages model_instance.get_routing_matrix_df() ``` ```{python} # resources included model_instance.get_resources_df() ``` ## 3. Convert to a `ciw` Network Model ```{python} adapter = CiwConverter(model_instance) network_params = adapter.generate_params() ``` The variable `network_params` is a python `dict` that contains all the parameters that `ciw` requires for a very simple queuing model. This can be passed as keyword args to the `ciw.create_network` function. ```{python} network_params ``` ```{python} # we use ciw's native `create_network` method network = ciw.create_network(**network_params) print(type(network)) ``` ```{python} # Run a quick simulation to verify it works without crashing... sim = ciw.Simulation(network) sim.simulate_until_max_time(50) print("Quick simulation run worked!") ``` ## 4. Run the model for multiple replications The `multiple_replications` function returns a `DataFrame` that contains one row per activity per replication. So if there are two nodes there are two rows for each replication. ```{python} # Run replications with activity/resource names df_reps = multiple_replications( network, model_instance, num_reps=100, runtime=2880, warmup=1440, n_jobs=-1 # parallel reps ) df_reps.head() ``` Filter the nodes with the usual `pandas` syntax ```{python} df_reps.loc[df_reps['activity_name'] == "Call Triage"].head(3) ``` ## 5. Summarise results If needed there is a built in function to create a `DataFrame` that contains a mean summary of all metrics across the nodes. ```{python} # Get summary table summary = summarise_results(df_reps) summary.round(1) ``` ```{python} wide = tidy_to_wide_format(df_reps) wide.head() ``` ### Open Jackson Network Model from JSON On this page we will create a `ciw` network model from a specification in a JSON file. The model represents a a classic queuing network problem that can be formulated as an Open Jackson Network. ![](/img/jackson_network_sketch.png){width=80% fig-align="center" fig-alt="Jackson network"} ## 1. Imports ### 1.1 `json2ciw` imports **We will use:** * `load_jackson_network_model` function that loads the built in urgent care call centre JSON file. * `ProcessModel`: a `pydantic` schema that provides automatic validation of the JSON. * `CiwConverter` that accepts a valid `ProcessModel` that represents a DES model and returns a `ciw` parameter `dict`. * `multiple_replications`:runs the network model and results a `Dataframe` of replication results. * `summarise_results`: provides a formatted table of mean results for each node in the network. * `tidy_to_wide_format`: the results from `multiple_replications` are returned in tidy (stacked row) format. This function converts into wide format i.e. each column is a performance measure. * `create_user_filtered_hist` generates a interactive plotly chart to view replications for each performance measure. It requires replication data in **wide** format. ```{python} from json2ciw.datasets import load_jackson_network_model from json2ciw.engine import ( CiwConverter, multiple_replications ) from json2ciw.results import ( summarise_results, create_user_filtered_hist, tidy_to_wide_format ) from json2ciw.schema import ProcessModel ``` ### 1.2 Other imports ```{python} import ciw import statistics from IPython.display import JSON from rich import print ``` ## 2. Load JSON ```{python} json_network = load_jackson_network_model() ``` display as collapsible JSON. Expand to view the details. ```{python} JSON(json_network) ``` ## 3. Convert to a ProcessModel A `ProcessModel` is a `pydantic` schema. It is independent of `ciw`. This early version will automatically validate that the JSON file is correct and that all transitions add up to 1.0 when it is created. ```{python} model_instance = ProcessModel(**json_network) ``` The contents of the process model can be manually inspected by a developer as follows: ```{python} print(model_instance) ``` ```{python} model_instance.display_diagram(include_resources=False) ``` ```{python} model_instance.display_diagram(include_resources=True) ``` ```{python} # summary of distributions model_instance.get_distributions_df() ``` ```{python} # summary of routing percentages model_instance.get_routing_matrix_df() ``` ```{python} model_instance.get_resources_df() ``` ## 3. Convert to a `ciw` Network Model ```{python} adapter = CiwConverter(model_instance) network_params = adapter.generate_params() ``` The variable `network_params` is a python `dict` that contains all the parameters that `ciw` requires for a very simple queuing model. This can be passed as keyword args to the `ciw.create_network` function. ```{python} network_params ``` ```{python} # we use ciw's native `create_network` method network = ciw.create_network(**network_params) print(type(network)) ``` ```{python} # Run a quick simulation to verify it works without crashing... sim = ciw.Simulation(network) sim.simulate_until_max_time(50) print("Quick simulation run worked!") ``` ## 4. Run the model for multiple replications The `multiple_replications` function returns a `DataFrame` that contains one row per activity per replication. So if there are two nodes there are two rows for each replication. ```{python} # Run replications with activity/resource names df_reps = multiple_replications( network, model_instance, num_reps=100, runtime=2880, warmup=1440, n_jobs=-1 # parallel reps ) df_reps.head() ``` Filter the nodes with the usual `pandas` syntax ```{python} df_reps.loc[df_reps['activity_name'] == "Service 1"].head(3) ``` ## 5. Summarise results If needed there is a built in function to create a `DataFrame` that contains a mean summary of all metrics across the nodes. ```{python} # Get summary table summary = summarise_results(df_reps) summary.round(1) ``` ### Open Network with lognormal service times On this page we will create a `ciw` network model from a specification in a JSON file. The model represents a simple three node network problem with lognormal service times. ## 1. Imports ### 1.1 `json2ciw` imports **We will use:** * `load_three_node_network_model` function that loads the built in urgent care call centre JSON file. * `ProcessModel`: a `pydantic` schema that provides automatic validation of the JSON. * `CiwConverter` that accepts a valid `ProcessModel` that represents a DES model and returns a `ciw` parameter `dict`. * `multiple_replications`:runs the network model and results a `Dataframe` of replication results. * `summarise_results`: provides a formatted table of mean results for each node in the network. * `tidy_to_wide_format`: the results from `multiple_replications` are returned in tidy (stacked row) format. This function converts into wide format i.e. each column is a performance measure. * `create_user_filtered_hist` generates a interactive plotly chart to view replications for each performance measure. It requires replication data in **wide** format. ```{python} from json2ciw.datasets import load_three_node_network_model from json2ciw.engine import ( CiwConverter, multiple_replications ) from json2ciw.results import ( summarise_results, ) from json2ciw.schema import ProcessModel ``` ### 1.2 Other imports ```{python} import ciw from IPython.display import JSON from rich import print ``` ## 2. Load JSON ```{python} json_network = load_three_node_network_model() ``` display as collapsible JSON. Expand to view the details. ```{python} JSON(json_network) ``` ## 3. Convert to a ProcessModel A `ProcessModel` is a `pydantic` schema. It is independent of `ciw`. This early version will automatically validate that the JSON file is correct and that all transitions add up to 1.0 when it is created. ```{python} model_instance = ProcessModel(**json_network) ``` The contents of the process model can be manually inspected by a developer as follows: ```{python} print(model_instance) ``` ```{python} model_instance.display_diagram(include_resources=False) ``` ```{python} model_instance.display_diagram(include_resources=True) ``` ```{python} # summary of distributions model_instance.get_distributions_df() ``` ```{python} # summary of routing percentages model_instance.get_routing_matrix_df() ``` ```{python} # resources model_instance.get_resources_df() ``` ## 3. Convert to a `ciw` Network Model ```{python} adapter = CiwConverter(model_instance) network_params = adapter.generate_params() ``` The variable `network_params` is a python `dict` that contains all the parameters that `ciw` requires for a very simple queuing model. This can be passed as keyword args to the `ciw.create_network` function. ```{python} network_params ``` ```{python} # we use ciw's native `create_network` method network = ciw.create_network(**network_params) print(type(network)) ``` ```{python} # Run a quick simulation to verify it works without crashing... sim = ciw.Simulation(network) sim.simulate_until_max_time(50) print("Quick simulation run worked!") ``` ## 4. Run the model for multiple replications The `multiple_replications` function returns a `DataFrame` that contains one row per activity per replication. So if there are two nodes there are two rows for each replication. ```{python} # Run replications with activity/resource names df_reps = multiple_replications( network, model_instance, num_reps=100, runtime=2880, warmup=1440, n_jobs=-1 # parallel reps ) df_reps.head() ``` Filter the nodes with the usual `pandas` syntax ```{python} df_reps.loc[df_reps['activity_name'] == "Service 1"].head(3) ``` ## 5. Summarise results If needed there is a built in function to create a `DataFrame` that contains a mean summary of all metrics across the nodes. ```{python} # Get summary table summary = summarise_results(df_reps) summary.round(1) ``` ### Urgent care call centre with caller hang up (renege) On this page we will create a `ciw` network model from a specification in a JSON file. The model is a modification of the original call centre model to include caller waiting time patience. I.e. they hang up if the call is not answered quickly. ## 1. Imports ### 1.1 `json2ciw` imports **We will use:** * `load_renege_call_model` function that loads the built in urgent care call centre JSON file. * `ProcessModel`: a `pydantic` schema that provides automatic validation of the JSON. * `CiwConverter` that accepts a valid `ProcessModel` that represents a DES model and returns a `ciw` parameter `dict`. * `multiple_replications`:runs the network model and results a `Dataframe` of replication results. * `summarise_results`: provides a formatted table of mean results for each node in the network. * `tidy_to_wide_format`: the results from `multiple_replications` are returned in tidy (stacked row) format. This function converts into wide format i.e. each column is a performance measure. * `create_user_filtered_hist` generates a interactive plotly chart to view replications for each performance measure. It requires replication data in **wide** format. ```{python} from json2ciw.datasets import load_renege_call_model from json2ciw.engine import ( CiwConverter, multiple_replications ) from json2ciw.results import ( summarise_results, ) from json2ciw.schema import ProcessModel ``` ### 1.2 Other imports ```{python} import ciw from IPython.display import JSON from rich import print ``` ## 2. Load JSON ```{python} json_network = load_renege_call_model() ``` Display as collapsible JSON. Expand to view the details. ```{python} JSON(json_network) ``` ## 3. Convert to a ProcessModel A `ProcessModel` is a `pydantic` schema. It is independent of `ciw`. This early version will automatically validate that the JSON file is correct and that all transitions add up to 1.0 when it is created. ```{python} model_instance = ProcessModel(**json_network) ``` The contents of the process model can be manually inspected by a developer as follows: ```{python} print(model_instance) ``` ```{python} model_instance.display_diagram(include_resources=False) ``` ```{python} model_instance.display_diagram(include_resources=True) ``` ```{python} # summary of distributions model_instance.get_distributions_df() ``` ```{python} # summary of routing percentages model_instance.get_routing_matrix_df() ``` ```{python} # resources model_instance.get_resources_df() ``` ## 3. Convert to a `ciw` Network Model ```{python} adapter = CiwConverter(model_instance) network_params = adapter.generate_params() ``` The variable `network_params` is a python `dict` that contains all the parameters that `ciw` requires for a very simple queuing model. This can be passed as keyword args to the `ciw.create_network` function. ```{python} network_params ``` ```{python} # we use ciw's native `create_network` method network = ciw.create_network(**network_params) print(type(network)) ``` ```{python} # Run a quick simulation to verify it works without crashing... sim = ciw.Simulation(network) sim.simulate_until_max_time(50) print("Quick simulation run worked!") ``` ## 4. Run the model for multiple replications The `multiple_replications` function returns a `DataFrame` that contains one row per activity per replication. So if there are two nodes there are two rows for each replication. ```{python} # Run replications with activity/resource names df_reps = multiple_replications( network, model_instance, num_reps=100, runtime=2880, warmup=1440, n_jobs=-1 # parallel reps ) df_reps.head() ``` Filter the nodes with the usual `pandas` syntax ```{python} df_reps.loc[df_reps['activity_name'] == "Service 1"].head(3) ``` ## 5. Summarise results If needed there is a built in function to create a `DataFrame` that contains a mean summary of all metrics across the nodes. ```{python} # Get summary table summary = summarise_results(df_reps) summary.round(1) ```