42.5 Mid-cycle Processing Using Python
The example we have used for most of this Chapter has focused on using Global Trackers and Stop nodes to control access to a limited treament resource. However, a limitation of this approach is that resources are allocated to trials in trial number order, without regard to priority and/or wait time.
If you need more complex rules to control the allocation of the limited resource, then you can use Python scripts run mid-cycle at the Stop node to allocate the resource to specific trials. We will start by using Python scripts to achieve the identical results as the prior model, but using Python to set trackers for trials that receive the resource after the Stop node.
Open the Healthcare tutorial example tree, Parallel Trials - Queue - Python.trex, which has the same structure as the Global Tracker model. The model is exactly the same in regards to progression of trials and, as we will show, yields identical results. The main differences are:
-
Global Trackers are not used as logic to control access to the resource.
-
Tracker t_resource_needed is set to 1 when a trial needs a resource.
-
Tracker t_resource_needed is set to 0 when a trial no longer needs a resource.
-
Tracker t_resource_held is set to 1 when a trial takes a resource.
-
Tracker t_resource_held is set to 0 when a trial releases a resource.
-
The use of the keyword _monte_stop_cycle_eval executes the Python script PythonAllocateResources mid-cycle when some trials have finished the cycle while others may be stopped at a Stop node waiting for a resource.
-
The Python script PythonAllocateResources allocates the resources by setting t_resource_needed to 2 for those receiving the resource. The logical expression then checks for t_resource_needed = 2 to allocate the treatment resource.
Let's review the processing for parallel trials with a focus on the scripts.
-
Trial set begins.
-
Start cycle.
-
Run pre-cycle script via special keyword _monte_pre_cycle_eval.
-
Run all trials in numerical order for this cycle until each trial reaches either a Terminal node or a Stop node.
-
Run stop cycle script via special keyword _monte_stop_cycle_eval.
-
Finish cycle for each trial held at a Stop node until they all reach a Terminal node.
-
Run post-cycle script via special keyword _monte_post_cycle_eval.
-
-
Repeat step 2 for all cycles.
-
Trial set is complete.
-
Generate reports by trial and by trial set.
Step 2.1 above using the special keyword _monte_stop_cycle_eval provides the opportunity to execute a Python script to determine who gets the resource as is done in this example model. Everything else is the same as the Global Trackers model.
The figure below shows the structure for the Healthy Health state in the model. There is no visible execution of the _monte_stop_cycle_eval, but it runs. The branches of the stop node use any updated Tracker values as updated by the Python script. Note that the second Stop node uses the same logic.
We can examine the updates to the trackers in the model at the Stop Node "Get Sick, Need Treatment" and its branches:
-
Get Sick, Need Treatment: The trial needs the treatment resource, so tracker updates needed are:
-
t_resource_needed= 1
-
-
Get Resources: If the trial is getting the resource, then the logic is true for t_resource_needed = 2. The tracker updates are:
-
t_resource_held = 1
-
t_resource_needed = 0
-
The trackers are updated for resource allocation within the Python script, PythonAllocateResources, which is executed by the special keyword _monte_stop_cycle_eval. Note the following root node variable definition.
-
_monte_stop_cycle_eval = DebugContext(6) + DebugWriteForce("Executing _monte_stop_cycle_eval") + PythonAllocateResources(g_resources_available; _stage; _trial_size) + DebugContext(6)
Much of the definition is related to debug output, but the critical piece is the call to the Python function PythonAllocateResources. The basic functions of the Python code are presented below.
-
Receive the current number of available resources as a function argument as derived from a separate script (g_resources_available = PythonGetResourcesAvailable(GlobalTrkGet("gt_TotalResources"); _stage; _trial_size)).
-
Get access to all the parallel trials in the model.
-
Loop through all the trials while there are still resources available.
-
If a resource is available and this trial needs a resource, allocate it to that trial by setting t_resource_needed = 2.
-
When all available resources are assigned, exit the loop.
While this section does not go into details of how to set up the Python script, there are certain commands which are useful to know to get details from the TreeAge Pro model into Python. These can be found in the section Python and access to model data. The example model script is heavily commented and there are additional extensive Python resources available online.
The model runs in the same way as the parallel trials model with Global Trackers. Select Microsimulation and then enter the number of trials and begin. Note that you will get the same results from this model because the Python function also allocates resources in trial number order.
Python Example with Priority Queue
The Python script model described above simply allocates the treatment resources in trial number order just as in the Global Tracker model. However, using Python allows for more complex processing via programming code.
The Healthcare tutorial example tree Parallel Trials - Priority Queue - Python.trex includes additional logic to allocate resources based on a priority score sampled from a distribution. There are comments in the Python code describing how this is done.