/* * Adding an agent means placing them into our PQ. If agent * remove ts is < PQ->min, then must update LP remove timer. * * Check time for stage change. Place in queue by minimum * stage change time or remove time. Set timer for stage * change time (and event) or remove time (and event). */ void epi_agent_add(epi_state * state, tw_bf * bf, tw_memory * buf, tw_lp * lp) { epi_agent *a; a = tw_memory_data(buf); if(5 == a->id && 0) printf("%lld: added %d at %lf \n", lp->gid, a->id && 0, tw_now(lp)); // Test to make sure this location is this lp if (lp->gid != a->loc[a->curr]) tw_error(TW_LOC, "epi_agent_add adding agent to lp %d but loc[%d] is %d", lp->gid, a->curr, a->loc[a->curr]); // if agent is contagious, increment number of contagious agents here epi_agent_contagious(state, buf, +1); a->ts_remove = tw_now(lp) + a->dur[a->curr]; epi_agent_update_ts(state, bf, a, lp); pq_enqueue(g_epi_pq[lp->id], buf); if(pq_get_size(g_epi_pq[lp->id]) > state->max_queue_size) state->max_queue_size = pq_get_size(g_epi_pq[lp->id]); // If not at home, if agent is a worried well, if there are already enough symtomatic // people here then starting tomorrow, this agent will stay home for the number // of days remaining. days_remaining is checked in agent_remove(). if(!a->days_remaining && a->nloc > 1 && a->loc[a->curr] != a->loc[0] && (a->behavior_flags & EPI_AGENT_WORRIED_WELL)) { if ((float) state->ncontagious_tot / (float) state->max_queue_size > g_epi_ww_threshold) { a->days_remaining = g_epi_ww_duration; a->behavior_flags = 0; g_epi_hospital_ww[state->hospital][0]++; } } }
int main( int argc, char** argv ) { uint64_t i; // pointers for casting pq_op_create *op_create; pq_op_destroy *op_destroy; pq_op_clear *op_clear; pq_op_get_key *op_get_key; pq_op_get_item *op_get_item; pq_op_get_size *op_get_size; pq_op_insert *op_insert; pq_op_find_min *op_find_min; pq_op_delete *op_delete; pq_op_delete_min *op_delete_min; pq_op_decrease_key *op_decrease_key; //pq_op_meld *op_meld; pq_op_empty *op_empty; // temp dummies for readability pq_type *q;//, *r; pq_node_type *n; if( argc < 2 ) exit( -1 ); int trace_file = open( argv[1], O_RDONLY ); if( trace_file < 0 ) { fprintf( stderr, "Could not open file.\n" ); return -1; } pq_trace_header header; pq_trace_read_header( trace_file, &header ); close( trace_file ); //printf("Header: (%llu,%lu,%lu)\n",header.op_count,header.pq_ids, // header.node_ids); pq_op_blank *ops = calloc( MIN( header.op_count, CHUNK_SIZE ), sizeof( pq_op_blank ) ); pq_type **pq_index = calloc( header.pq_ids, sizeof( pq_type* ) ); pq_node_type **node_index = calloc( header.node_ids, sizeof( pq_node_type* ) ); if( ops == NULL || pq_index == NULL || node_index == NULL ) { fprintf( stderr, "Calloc fail.\n" ); return -1; } #ifdef USE_QUAKE mem_capacities[0] = header.node_ids << 2; #else mem_capacities[0] = header.node_ids; #endif #ifdef USE_EAGER mem_map *map = mm_create( mem_types, mem_sizes, mem_capacities ); #else mem_map *map = mm_create( mem_types, mem_sizes ); #endif uint64_t op_remaining, op_chunk; int status; struct timeval t0, t1; uint32_t iterations = 0; uint32_t total_time = 0; key_type k; //pq_node_type *min; #ifndef CACHEGRIND while( iterations < 5 || total_time < PQ_MIN_USEC ) { mm_clear( map ); iterations++; #endif trace_file = open( argv[1], O_RDONLY ); if( trace_file < 0 ) { fprintf( stderr, "Could not open file.\n" ); return -1; } pq_trace_read_header( trace_file, &header ); op_remaining = header.op_count; while( op_remaining > 0 ) { op_chunk = MIN( CHUNK_SIZE, op_remaining ); op_remaining -= op_chunk; for( i = 0; i < op_chunk; i++ ) { status = pq_trace_read_op( trace_file, ops + i ); if( status == -1 ) { fprintf( stderr, "Invalid operation!" ); return -1; } } #ifndef CACHEGRIND gettimeofday(&t0, NULL); #endif for( i = 0; i < op_chunk; i++ ) { switch( ops[i].code ) { case PQ_OP_CREATE: op_create = (pq_op_create*) ( ops + i ); //printf("pq_create(%d)\n", op_create->pq_id); pq_index[op_create->pq_id] = pq_create( map ); break; case PQ_OP_DESTROY: op_destroy = (pq_op_destroy*) ( ops + i ); //printf("pq_destroy(%d)\n", op_destroy->pq_id); q = pq_index[op_destroy->pq_id]; pq_destroy( q ); pq_index[op_destroy->pq_id] = NULL; break; case PQ_OP_CLEAR: op_clear = (pq_op_clear*) ( ops + i ); //printf("pq_clear(%d)\n", op_clear->pq_id ); q = pq_index[op_clear->pq_id]; pq_clear( q ); break; case PQ_OP_GET_KEY: op_get_key = (pq_op_get_key*) ( ops + i ); //printf("pq_get_key(%d,%d)\n", op_get_key->pq_id, // op_get_key->node_id ); q = pq_index[op_get_key->pq_id]; n = node_index[op_get_key->node_id]; pq_get_key( q, n ); break; case PQ_OP_GET_ITEM: op_get_item = (pq_op_get_item*) ( ops + i ); //printf("pq_get_item(%d,%d)\n", op_get_item->pq_id, // op_get_item->node_id); q = pq_index[op_get_item->pq_id]; n = node_index[op_get_item->node_id]; pq_get_item( q, n ); break; case PQ_OP_GET_SIZE: op_get_size = (pq_op_get_size*) ( ops + i ); //printf("pq_get_size(%d)\n", op_get_size->pq_id); q = pq_index[op_get_size->pq_id]; pq_get_size( q ); break; case PQ_OP_INSERT: op_insert = (pq_op_insert*) ( ops + i ); //printf("pq_insert(%d,%d,%llu,%d)\n", op_insert->pq_id, // op_insert->node_id, op_insert->key, op_insert->item ); q = pq_index[op_insert->pq_id]; node_index[op_insert->node_id] = pq_insert( q, op_insert->item, op_insert->key ); break; case PQ_OP_FIND_MIN: op_find_min = (pq_op_find_min*) ( ops + i ); //printf("pq_find_min(%d)\n", op_find_min->pq_id ); q = pq_index[op_find_min->pq_id]; pq_find_min( q ); break; case PQ_OP_DELETE: op_delete = (pq_op_delete*) ( ops + i ); //printf("pq_delete(%d,%d)\n", op_delete->pq_id, // op_delete->node_id ); q = pq_index[op_delete->pq_id]; n = node_index[op_delete->node_id]; pq_delete( q, n ); break; case PQ_OP_DELETE_MIN: op_delete_min = (pq_op_delete_min*) ( ops + i ); //printf("pq_delete_min(%d)\n", op_delete_min->pq_id); q = pq_index[op_delete_min->pq_id]; //min = pq_find_min( q ); k = pq_delete_min( q ); #ifdef CACHEGRIND if( argc > 2 ) printf("%llu\n",k); #endif break; case PQ_OP_DECREASE_KEY: op_decrease_key = (pq_op_decrease_key*) ( ops + i ); //printf("pq_decrease_key(%d,%d,%llu)\n", op_decrease_key->pq_id, // op_decrease_key->node_id, op_decrease_key->key); q = pq_index[op_decrease_key->pq_id]; n = node_index[op_decrease_key->node_id]; pq_decrease_key( q, n, op_decrease_key->key ); break; /*case PQ_OP_MELD: printf("Meld.\n"); op_meld = (pq_op_meld*) ( ops + i ); q = pq_index[op_meld->pq_src1_id]; r = pq_index[op_meld->pq_src2_id]; pq_index[op_meld->pq_dst_id] = pq_meld( q, r ); break;*/ case PQ_OP_EMPTY: op_empty = (pq_op_empty*) ( ops + i ); //printf("pq_empty(%d)\n", op_empty->pq_id); q = pq_index[op_empty->pq_id]; pq_empty( q ); break; default: break; } //verify_queue( pq_index[0], header.node_ids ); } #ifndef CACHEGRIND gettimeofday(&t1, NULL); total_time += (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - t0.tv_usec); #endif } close( trace_file ); #ifndef CACHEGRIND } #endif for( i = 0; i < header.pq_ids; i++ ) { if( pq_index[i] != NULL ) pq_destroy( pq_index[i] ); } mm_destroy( map ); free( pq_index ); free( node_index ); free( ops ); #ifndef CACHEGRIND printf( "%d\n", total_time / iterations ); #endif return 0; }
/* * epi_seir_compute: compute SEIR model for all agents in location LPn * * Assume that the probability of catching the disease is constant over time. * If the probability of catching the disease in one hour is P, then the * geometric distribution gives the number of hours before the disease is * caught. We do a draw on the random number generator from the geometric * distribution and if the result is less than the time interval, then the * agent gets sick at that time. */ void epi_seir_compute(epi_state * state, tw_bf * bf, epi_agent * in_a, tw_lp * lp) { /* * For each susceptible agent, for each contagious agent, determine * the susceptible agent caught the disease. */ double draw, delta_t; epi_agent *a, *ai; epi_ic_stage *s, *sn; unsigned int queue_size; unsigned int i, j; void *pq; queue_size = pq_get_size(state->pq); if (queue_size <= 1) return; delta_t = tw_now(lp) - state->last_event; //printf(" lp %2d SEIR delta_t %f at %f\n", (int) lp->id, delta_t, tw_now(lp)); if (delta_t < EPSILON) // already done an seir check at this time. return; //printf("SEIR Check %f\n", tw_now(lp)); pq = state->pq; //printf( "Queue size: %d ", queue_size); for ( i = 0; i < queue_size; i++) { a = (epi_agent *) pq_next( pq, i); //printf("\ta: %d, stage: %d\n ", a->id, a->stage); if (a->stage == EPI_SUSCEPTIBLE) { for (j = 0; j < queue_size; j++) { if ( j == i ) continue; ai = (epi_agent *) pq_next(pq, j); s = &g_epi_stages[ai->stage]; //printf("a: %d, ai: %d, ln_multiplier: %f\n", a->id, ai->id, s->ln_multiplier); if (abs(s->ln_multiplier) > EPSILON) { // tw_geometric draws from uniform until the draw is greater than P. // tw_geometric then returns the number of draws. // For large P (which is 1.0 - multiplier) this could be very large. // Also note that tw_geometric always returns an integer of 1 or // greater. draw = tw_rand_unif(lp->id); draw = log(draw); draw /= g_epi_stages[a->stage].ln_multiplier; //draw = tw_rand_geometric(lp->id, (s->start_multiplier + s->stop_multiplier)/2); //state->stats->s_ndraws += draw; state->stats->s_nchecked++; //printf("SEIR agent %d, stage %d, agent %d, stage %d, draw: %g\n", // a->id, a->stage, ai->id, ai->stage, draw); if (draw <= delta_t) //Make agent sick { #if EPI_DEBUG printf("%7.3f, lp, %7d, agent_infect, , , , %7d, %5.3f, %5.2f\n", tw_now(lp), (int) lp->id, a->id, delta_t, draw); #endif a->stage = EPI_INCUBATING; g_epi_ct[a->ct][0]--; g_epi_ct[a->ct][1]++; if (g_epi_position_f) fprintf(g_epi_position_f, "%.3f,%d,%d,%d\n", tw_now(lp), a->id, (int) lp->id, a->stage); sn = &g_epi_stages[a->stage]; a->ts_infected = tw_now(lp); a->ts_stage_tran = tw_rand_unif(lp->id)*(sn->max_duration - sn->min_duration) + sn->min_duration + tw_now(lp); a->ts_last_tran = tw_now(lp); // collect statistics state->stats->s_ninfected++; break; } #if EPI_DEBUG else { printf("%7.3f, lp, %7d, agent_not_infect, , , , %7d, %5.3f, %5.2f\n", tw_now(lp), (int) lp->id, a->id, delta_t, draw); } #endif } } } } //printf("SEIR compputed at %f ", state->last_event); }
/* * epi_init - initialize node LPs state variables * * state - our node LP state space * lp - our node LP */ void epi_init(epi_state * state, tw_lp * lp) { tw_memory *b; tw_memory *agent = NULL; tw_stime ts_min_tran = DBL_MAX; epi_agent *a; epi_pathogen *p; epi_ic_stage *s; double x; int i; int j; int sz; // hard-coded, single hospital LP if(lp->id == g_tw_nlp-1) { fprintf(g_epi_hospital_f, "0 0 %u %u %u\n", g_epi_hospital[0][0], g_epi_hospital_ww[0][0], g_epi_hospital_wws[0][0]); g_epi_hospital[0][0] = 0; g_epi_hospital_ww[0][0] = 0; g_epi_hospital_wws[0][0] = 0; return; } // ID of the hospital I go to (or nearest if office/school) state->hospital = 0; state->stats = tw_calloc(TW_LOC, "", sizeof(epi_statistics), 1); state->ncontagious = tw_calloc(TW_LOC, "", sizeof(unsigned int), g_epi_ndiseases); state->ts_seir = tw_calloc(TW_LOC, "", sizeof(tw_stime), g_epi_ndiseases); for(i = 0; i < g_epi_ndiseases; i++) state->ts_seir[i] = DBL_MAX; // if no agents initially at this location, then we are done! if(0 == (sz = pq_get_size(g_epi_pq[lp->id]))) return; // determine agents initial parameters / configuration for(i = 0; i < sz; i++) { agent = pq_next(g_epi_pq[lp->id], i); a = tw_memory_data(agent); for(j = 0; j < g_epi_ndiseases; j++) { if(!a->id && !j) { // ALLOCATE PATHOGEN AND FILL IT IN b = tw_memory_alloc(lp, g_epi_pathogen_fd); p = tw_memory_data(b); b->next = a->pathogens; a->pathogens = b; p->index = j; p->stage = EPI_SUSCEPTIBLE; p->ts_stage_tran = DBL_MAX; g_epi_regions[a->region][p->index][p->stage]--; p->stage = EPI_EXPOSED; g_epi_regions[a->region][p->index][p->stage]++; g_epi_exposed_today[j]++; // determine if agents start out sick. if(tw_rand_unif(lp->rng) < g_epi_sick_rate) { s = &g_epi_diseases[p->index].stages[p->stage]; x = ((double) tw_rand_integer(lp->rng, 0, INT_MAX) / (double) INT_MAX); p->ts_stage_tran = (s->min_duration + (x * (s->max_duration - s->min_duration))); if(0.0 >= p->ts_stage_tran) tw_error(TW_LOC, "Immediate stage transition?!"); //state->stats->s_ninfected++; state->ncontagious[p->index]++; state->ncontagious_tot++; //g_epi_hospital[0][0]++; g_epi_sick_init_pop[j]++; ts_min_tran = min(ts_min_tran, p->ts_stage_tran); if(!a->id) printf("%ld: agent exposed %d: %s, transition at %lf\n", lp->id, a->id, g_epi_diseases[j].name, p->ts_stage_tran); } } } // reflect ts_next change in PQ ordering if(ts_min_tran < a->ts_next) { a->ts_next = ts_min_tran; pq_delete_any(g_epi_pq[lp->id], &agent); pq_enqueue(g_epi_pq[lp->id], agent); } // determine if agents are a part of worried well population. if(g_epi_ww_rate) { if(tw_rand_unif(lp->rng) < g_epi_ww_rate) { a->behavior_flags = 1; g_epi_hospital_ww[0][0]++; g_epi_ww_init_pop++; } } else if(g_epi_wws_rate) { if(tw_rand_unif(lp->rng) < g_epi_wws_rate) { a->behavior_flags = 2; g_epi_hospital_wws[0][0]++; g_epi_wws_init_pop++; } } // define agent's at work contact population //g_epi_regions[a->region][a->stage]++; } epi_location_timer(state, lp); }
/* * epi_event_handler - main event processing (per node LP) * * state - our node LP state space * bf - a bitfield provided by the simulation ecxecutive * m - the incoming event or message * lp - our node LP */ void epi_event_handler(epi_state * state, tw_bf * bf, epi_message * msg, tw_lp * lp) { tw_memory *buf; tw_memory *b; epi_agent *a; int population; int ncontagious[g_epi_ndiseases]; int today; int pq_clean = 0; int i; // TO DO: need to check for last day to get last day of statistics today = (int) (tw_now(lp) / TWENTY_FOUR_HOURS); population = pq_get_size(g_epi_pq[lp->id]); for(i = 0; i < g_epi_ndiseases; i++) ncontagious[i] = state->ncontagious[i]; if(today > g_epi_day) { // report yesterday's statistics epi_report_census_tract(today); epi_report_hospitals(today); g_epi_day++; printf("%ld: DAY %d \n", lp->id, today); } if(g_epi_complete) return; // may return NULL in case of EPI_REMOVE if(NULL != (buf = tw_event_memory_get(lp))) { a = tw_memory_data(buf); while(NULL != (b = tw_event_memory_get(lp))) { b->next = a->pathogens; a->pathogens = b; } epi_agent_add(state, bf, buf, lp); printf("%ld: ADD %d at %lf \n", lp->id, a->id, tw_now(lp)); } else { epi_agent_remove(state, bf, lp); } // Bring SEIR computation up to date for the agents at this location // Ok to skip if no change to infectious population, // or no change to overall location population // // basically, avoid calling epi_seir_update, like it was the plague, // pun intended. for(i = 0; i < g_epi_ndiseases; i++) { if((population != pq_get_size(g_epi_pq[lp->id]) && state->ncontagious[i]) || ncontagious[i] != state->ncontagious[i]) { epi_seir_compute(state, bf, i, lp); pq_clean = 1; } } if(pq_clean) pq_cleanup(g_epi_pq[lp->id]); epi_location_timer(state, lp); }