/** * Responds to a trace result by ordering quarantine. * * @param self the model. * @param units the list of units. * @param event a trace result event. */ void handle_trace_result_event (struct adsm_module_t_ *self, UNT_unit_list_t * units, EVT_trace_result_event_t * event, EVT_event_queue_t * queue) { local_data_t *local_data; UNT_unit_t *unit; #if DEBUG g_debug ("----- ENTER handle_trace_result_event (%s)", MODEL_NAME); #endif local_data = (local_data_t *) (self->model_data); if (event->traced) { if (event->direction == ADSM_TraceForwardOrOut) unit = event->exposed_unit; else unit = event->exposing_unit; #if DEBUG g_debug ("requesting quarantine of unit \"%s\"", unit->official_id); #endif UNT_quarantine (unit); EVT_event_enqueue (queue, EVT_new_quarantine_event (unit, event->day)); } #if DEBUG g_debug ("----- EXIT handle_trace_result_event (%s)", MODEL_NAME); #endif return; }
/** * Responds to a trace result by ordering a zone focus to be established. * * @param self the model. * @param event a trace result event. * @param queue for any new events the model creates. */ void handle_trace_result_event (struct naadsm_model_t_ *self, EVT_trace_result_event_t * event, EVT_event_queue_t * queue) { local_data_t *local_data; HRD_herd_t *herd; #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- ENTER trace_result_event (%s)", MODEL_NAME); #endif local_data = (local_data_t *) (self->model_data); herd = event->exposed_herd; /* Check whether the herd is a production type we're interested in. */ if (local_data->production_type[herd->production_type] == FALSE) goto end; /* Check whether the trace is for a contact type we're interested in. */ if (event->contact_type != local_data->contact_type) goto end; #if DEBUG g_debug ("ordering a zone focus around unit \"%s\"", herd->official_id); #endif EVT_event_enqueue (queue, EVT_new_request_for_zone_focus_event (herd, event->day, "trace out")); end: #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- EXIT handle_trace_result_event (%s)", MODEL_NAME); #endif return; }
/** * Responds to a detection by requesting traces. * * @param self the model. * @param herds the list of herds. * @param event a detection event. * @param rng a random number generator. * @param queue for any new events the model creates. */ void handle_detection_event (struct naadsm_model_t_ *self, HRD_herd_list_t * herds, EVT_detection_event_t * event, RAN_gen_t * rng, EVT_event_queue_t * queue) { local_data_t *local_data; HRD_herd_t *herd; #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- ENTER handle_detection_event (%s)", MODEL_NAME); #endif local_data = (local_data_t *) (self->model_data); herd = event->herd; if (local_data->production_type[herd->production_type] == TRUE) { #if DEBUG g_debug ("unit \"%s\" request to %s %ss", herd->official_id, NAADSM_trace_direction_name[local_data->direction], NAADSM_contact_type_name[local_data->contact_type]); #endif EVT_event_enqueue (queue, EVT_new_attempt_to_trace_event (herd, event->day, local_data->contact_type, local_data->direction, local_data->trace_period)); } #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- EXIT handle_detection_event (%s)", MODEL_NAME); #endif }
/** * Before any simulations, this module announces the output variables it is * recording. * * @param self this module. * @param queue for any new events this function creates. */ void handle_before_any_simulations_event (struct naadsm_model_t_ *self, EVT_event_queue_t *queue) { unsigned int n, i; RPT_reporting_t *output; GPtrArray *outputs = NULL; n = self->outputs->len; for (i = 0; i < n; i++) { output = (RPT_reporting_t *) g_ptr_array_index (self->outputs, i); if (output->frequency != RPT_never) { if (outputs == NULL) outputs = g_ptr_array_new(); g_ptr_array_add (outputs, output); } } if (outputs != NULL) EVT_event_enqueue (queue, EVT_new_declaration_of_outputs_event (outputs)); /* We don't free the pointer array, that will be done when the event is freed * after all interested modules have processed it. */ return; }
/** * Responds to a detection by ordering destruction actions. * * @param self the model. * @param herds the list of herds. * @param event a report event. * @param queue for any new events the model creates. */ void handle_detection_event (struct naadsm_model_t_ *self, HRD_herd_list_t * herds, EVT_detection_event_t * event, EVT_event_queue_t * queue) { local_data_t *local_data; HRD_herd_t *herd; #if DEBUG g_debug ("----- ENTER handle_detection_event (%s)", MODEL_NAME); #endif local_data = (local_data_t *) (self->model_data); herd = event->herd; /* Check whether the herd is a production type we're interested in, and that * it is not already destroyed. We can "detect" a destroyed herd because of * test result delays -- if a test comes back positive and the herd has been * pre-emptively destroyed in the meantime, that is still a "detection". * * In the experimental version 'Riverton', "naturally immune" units have * died out and no longer exist, so they don't need to be destroyed. */ if ( local_data->production_type[herd->production_type] == TRUE && herd->status != Destroyed #ifdef RIVERTON && herd->status != NaturallyImmune #endif ) { #if DEBUG g_debug ("ordering unit \"%s\" destroyed", event->herd->official_id); #endif EVT_event_enqueue (queue, EVT_new_request_for_destruction_event (event->herd, event->day, "Det", local_data->priority)); } #if DEBUG g_debug ("----- EXIT handle_detection_event (%s)", MODEL_NAME); #endif return; }
/** * Before any simulations, this module declares all the reasons for which it * may request a destruction. * * @param queue for any new events the model creates. */ void handle_before_any_simulations_event (EVT_event_queue_t * queue) { GPtrArray *reasons; #if DEBUG g_debug ("----- ENTER handle_before_any_simulations_event (%s)", MODEL_NAME); #endif reasons = g_ptr_array_sized_new (1); g_ptr_array_add (reasons, "Det"); EVT_event_enqueue (queue, EVT_new_declaration_of_destruction_reasons_event (reasons)); /* Note that we don't clean up the GPtrArray. It will be freed along with * the declaration event after all interested sub-models have processed the * event. */ #if DEBUG g_debug ("----- EXIT handle_before_any_simulations_event (%s)", MODEL_NAME); #endif return; }
/** * Responds to a request for infection causes by declaring all the causes which * this model may state for an infection. * * @param self the model. * @param queue for any new events the model creates. */ void handle_request_for_infection_causes_event (struct ergadm_model_t_ *self, EVT_event_queue_t * queue) { GPtrArray *causes; #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- ENTER handle_request_for_infection_causes_event (%s)", MODEL_NAME); #endif causes = g_ptr_array_sized_new (1); g_ptr_array_add (causes, "airborne spread"); EVT_event_enqueue (queue, EVT_new_declaration_of_infection_causes_event (causes)); /* Note that we don't clean up the GPtrArray. It will be freed along with * the declaration event after all interested sub-models have processed the * event. */ #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- EXIT handle_request_for_infection_causes_event (%s)", MODEL_NAME); #endif return; }
/** * Responds to a new day event by releasing any pending infections and * stochastically generating infections. * * @param self the model. * @param herds a list of herds. * @param event a new day event. * @param rng a random number generator. * @param queue for any new events the model creates. */ void handle_new_day_event (struct ergadm_model_t_ *self, HRD_herd_list_t * herds, EVT_new_day_event_t * event, RAN_gen_t * rng, EVT_event_queue_t * queue) { local_data_t *local_data; HRD_herd_t *herd1; unsigned int nherds; /* number of herds */ unsigned int herd1_index, herd2_index; gboolean herd1_can_be_source; double distance; GQueue *q; EVT_event_t *pending_event; struct Rect search_rect; /* for narrowing down radius searches using the R-tree (spatial index) */ double mult; /* to account for latitude */ callback_t callback_data; #if DEBUG GString *s; #endif #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- ENTER handle_new_day_event (%s)", MODEL_NAME); #endif local_data = (local_data_t *) (self->model_data); /* Release any pending (due to airborne transport delays) events. */ local_data->rotating_index = (local_data->rotating_index + 1) % local_data->pending_infections->len; q = (GQueue *) g_ptr_array_index (local_data->pending_infections, local_data->rotating_index); while (!g_queue_is_empty (q)) { /* Remove the event from this model's internal queue and place it in the * simulation's event queue. */ pending_event = (EVT_event_t *) g_queue_pop_head (q); EVT_event_enqueue (queue, pending_event); local_data->npending_infections--; } if ( #if defined(USE_RTREE) && USE_RTREE == 1 /* For debugging purposes, you can #define USE_RTREE to 0 to never use * the spatial index, or 1 to always use it. */ TRUE || #endif local_data->use_rtree_index) { /* Initialize a data structure used by the callback function. */ callback_data.self = self; callback_data.herds = herds; callback_data.event = event; callback_data.rng = rng; callback_data.queue = queue; } nherds = HRD_herd_list_length (herds); for (herd1_index = 0; herd1_index < nherds; herd1_index++) { herd1 = HRD_herd_list_get (herds, herd1_index); /* Can this herd be the source of an exposure? */ #if DEBUG s = g_string_new (NULL); g_string_sprintf (s, "unit \"%s\" is %s, state is %s: ", herd1->official_id, herd1->production_type_name, HRD_status_name[herd1->status]); #endif herd1_can_be_source = local_data->param_block[herd1->production_type] != NULL && (herd1->status == InfectiousSubclinical || herd1->status == InfectiousClinical); #if DEBUG g_string_sprintfa (s, "%s be source", herd1_can_be_source ? "can" : "cannot"); g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s", s->str); g_string_free (s, TRUE); #endif if (!herd1_can_be_source) continue; if (local_data->use_rtree_index[herd1->production_type]) { distance = local_data->max_spread[herd1->production_type] / GIS_DEGREE_DISTANCE; mult = 1.0 / MIN (cos (DEG2RAD * (herd1->lat + distance)), cos(DEG2RAD * (herd1->lat - distance))); search_rect.boundary[0] = herd1->lon - (distance * mult) - EPSILON; search_rect.boundary[1] = herd1->lat - distance - EPSILON; search_rect.boundary[2] = herd1->lon + (distance * mult) + EPSILON; search_rect.boundary[3] = herd1->lat + distance + EPSILON; callback_data.herd1 = herd1; RTreeSearch (herds->spatial_index, &search_rect, callback, &callback_data); } else for (herd2_index = 0; herd2_index < nherds; herd2_index++) check_and_infect (self, herds, herd1, HRD_herd_list_get (herds, herd2_index), event, rng, queue); } #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- EXIT handle_new_day_event (%s)", MODEL_NAME); #endif return; }
/** * Check whether herd 1 can infect herd 2 and if so, attempt to infect herd 2. * This function is used by both the naive search and the R-tree (spatial * index) search. */ void check_and_infect (struct ergadm_model_t_ *self, HRD_herd_list_t * herds, HRD_herd_t * herd1, HRD_herd_t * herd2, EVT_new_day_event_t * event, RAN_gen_t * rng, EVT_event_queue_t * queue) { local_data_t *local_data; gboolean herd2_can_be_target; param_block_t *param_block; double max_spread, distance, heading; double distance_factor, herd1_size_factor, herd2_size_factor; EVT_event_t *attempt_to_infect; double r, P; int delay; int delay_index; GQueue *q; HRD_expose_t exposure_update; #if DEBUG GString *s; #endif #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- ENTER check_and_infect (%s)", MODEL_NAME); #endif /* Are herd 1 and herd 2 the same? */ if (herd1 == herd2) goto end; local_data = (local_data_t *) (self->model_data); /* Can herd 2 be the target of an exposure? */ #if DEBUG s = g_string_new (NULL); g_string_sprintf (s, "unit \"%s\" is %s, state is %s: ", herd2->official_id, herd2->production_type_name, HRD_status_name[herd2->status]); #endif param_block = local_data->param_block[herd1->production_type][herd2->production_type]; herd2_can_be_target = (param_block != NULL && herd2->status != Destroyed); #if DEBUG g_string_sprintfa (s, "%s be target", herd2_can_be_target ? "can" : "cannot"); g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s", s->str); g_string_free (s, TRUE); #endif if (!herd2_can_be_target) goto end; /* Is herd 2 close enough to herd 1? */ distance = GIS_local_distance (herd1->lat, herd1->lon, herd2->lat, herd2->lon); max_spread = param_block->max_spread; if (distance - max_spread > EPSILON) { #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " unit \"%s\" too far (%g > %g)", herd2->official_id, distance, max_spread); #endif goto end; } #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " unit \"%s\" within range (%g <= %g)", herd2->official_id, distance, max_spread); #endif /* Is herd 2 within the wind direction range? */ heading = GIS_local_heading (herd1->lat, herd1->lon, herd2->lat, herd2->lon); if (param_block->wind_range_crosses_0 ? (param_block->wind_dir_start - heading > EPSILON && heading - param_block->wind_dir_end >= EPSILON) : (param_block->wind_dir_start - heading > EPSILON || heading - param_block->wind_dir_end >= EPSILON)) { #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " unit \"%s\" outside wind angles (%g)", herd2->official_id, heading); #endif goto end; } #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " unit \"%s\" within wind angles (%g)", herd2->official_id, heading); #endif distance_factor = (max_spread - distance) / (max_spread - 1); herd1_size_factor = local_data->herd_size_factor[herd1->index]; herd2_size_factor = local_data->herd_size_factor[herd2->index]; #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " P = %g * %g * %g * %g * %g", herd1_size_factor, herd1->prevalence, distance_factor, param_block->prob_spread_1km, herd2_size_factor); #endif P = herd1_size_factor * herd1->prevalence * distance_factor * param_block->prob_spread_1km * herd2_size_factor; r = RAN_num (rng); if (r < P && herd2->status == Susceptible) { if (NULL != guilib_record_exposure) { exposure_update.dest_index = herd2->index; exposure_update.dest_status = herd2->status; exposure_update.src_index = herd1->index; exposure_update.src_status = herd1->status; exposure_update.exposure_method = "A"; exposure_update.success = -1; guilib_record_exposure (exposure_update); } attempt_to_infect = EVT_new_attempt_to_infect_event (herd1, herd2, event->day, "airborne spread"); delay = (int) round (PDF_random (param_block->delay, rng)); if (delay <= 0) { EVT_event_enqueue (queue, attempt_to_infect); #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " r (%g) < P (%g), target herd infected", r, P); #endif } else { attempt_to_infect->u.attempt_to_infect.day = event->day + delay; if (delay > local_data->pending_infections->len) ergadm_extend_rotating_array (local_data->pending_infections, delay, local_data->rotating_index); delay_index = (local_data->rotating_index + delay) % local_data->pending_infections->len; q = (GQueue *) g_ptr_array_index (local_data->pending_infections, delay_index); g_queue_push_tail (q, attempt_to_infect); local_data->npending_infections++; #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " r (%g) < P (%g), target unit will be infected on day %i", r, P, event->day + delay); #endif } } else { if (NULL != guilib_record_exposure) { exposure_update.dest_index = herd2->index; exposure_update.dest_status = herd2->status; exposure_update.src_index = herd1->index; exposure_update.src_status = herd1->status; exposure_update.exposure_method = "A"; exposure_update.success = 0; guilib_record_exposure (exposure_update); } #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " r (%g) >= P (%g), target unit not infected", r, P); #endif ; } end: #if DEBUG g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "----- EXIT check_and_infect (%s)", MODEL_NAME); #endif return; }