/* * Reschedule a volume output item, if necessary. */ static int reschedule_volume_output_item(struct volume *wrld, struct volume_output_item *vo) { /* Find the next time */ if (vo->timer_type == OUTPUT_BY_STEP) vo->t += (vo->step_time / wrld->time_unit); else { double time_scale = 0.0; /* Check if we're done */ if (vo->next_time == vo->times + vo->num_times) { free(vo->filename_prefix); free(vo->molecules); free(vo->times); free(vo); return 0; } /* Compute the next time and advance the next_time ptr */ if (vo->timer_type == OUTPUT_BY_ITERATION_LIST) time_scale = 1.0; else time_scale = 1.0 / wrld->time_unit; vo->t = (*vo->next_time++) * time_scale; } switch (wrld->notify->volume_output_report) { case NOTIFY_NONE: case NOTIFY_BRIEF: break; case NOTIFY_FULL: mcell_log(" Next output scheduled for time %.15g.", vo->t * wrld->time_unit); break; default: UNHANDLED_CASE(wrld->notify->volume_output_report); } /* Add to the schedule */ if (schedule_add(wrld->volume_output_scheduler, vo)) mcell_allocfailed("Failed to add volume output request to scheduler."); return 0; }
/* * Output a block of volume data as requested by the 'vo' object. */ int update_volume_output(struct volume *wrld, struct volume_output_item *vo) { int failure = 0; char *filename; switch (wrld->notify->volume_output_report) { case NOTIFY_NONE: break; case NOTIFY_BRIEF: case NOTIFY_FULL: mcell_log("Updating volume output '%s' scheduled at time %.15g on " "iteration %lld.", vo->filename_prefix, vo->t, wrld->current_iterations); break; default: UNHANDLED_CASE(wrld->notify->volume_output_report); } /* build the filename */ filename = CHECKED_SPRINTF("%s.%lld.dat", vo->filename_prefix, wrld->current_iterations); /* Try to make the directory if it doesn't exist */ if (make_parent_dir(filename)) { free(filename); return 1; } /* Output the volume item */ failure = output_volume_output_item(wrld, filename, vo); free(filename); /* Reschedule this volume item, if appropriate */ if (!failure) failure = reschedule_volume_output_item(wrld, vo); /* Should we return failure if we can't create the file? Doing so will bring * down the entire sim... */ return failure; }
/************************************************************************ * * function for printing a string * * XXX: This is a temporary hack to be able to print in mcell.c * since mcell disables regular printf * ************************************************************************/ void mcell_print(const char *message) { mcell_log("%s", message); }
/************************************************************************** write_reaction_output: In: the output_set we want to write to disk the flag that signals an end to the scheduled reaction outputs Out: 0 on success, 1 on failure. The reaction output buffer is flushed and written to disk. Indices are not reset; that's the job of the calling function. **************************************************************************/ int write_reaction_output(struct volume *world, struct output_set *set) { FILE *fp; struct output_column *column; char *mode; u_int n_output; u_int i; switch (set->file_flags) { case FILE_OVERWRITE: case FILE_CREATE: if (set->chunk_count == 0) mode = "w"; else mode = "a"; break; case FILE_SUBSTITUTE: if (world->chkpt_seq_num == 1 && set->chunk_count == 0) mode = "w"; else mode = "a"; break; case FILE_APPEND: case FILE_APPEND_HEADER: mode = "a"; break; default: mcell_internal_error( "Bad file output code %d for reaction data output file '%s'.", set->file_flags, set->outfile_name); } fp = open_file(set->outfile_name, mode); if (fp == NULL) return 1; /*int idx = set->block->buf_index;*/ if (set->column_head->buffer[0].data_type != COUNT_TRIG_STRUCT) { n_output = set->block->buffersize; if (set->block->buf_index < set->block->buffersize) n_output = set->block->buf_index; if (world->notify->file_writes == NOTIFY_FULL) mcell_log("Writing %d lines to output file %s.", n_output, set->outfile_name); /* Write headers */ if (set->chunk_count == 0 && set->header_comment != NULL && set->file_flags != FILE_APPEND && (world->chkpt_seq_num == 1 || set->file_flags == FILE_APPEND_HEADER || set->file_flags == FILE_CREATE || set->file_flags == FILE_OVERWRITE)) { if (set->block->timer_type == OUTPUT_BY_ITERATION_LIST) fprintf(fp, "%sIteration_#", set->header_comment); else fprintf(fp, "%sSeconds", set->header_comment); for (column = set->column_head; column != NULL; column = column->next) { if (column->expr->title == NULL) fprintf(fp, " untitled"); else fprintf(fp, " %s", column->expr->title); } fprintf(fp, "\n"); } /* Write data */ for (i = 0; i < n_output; i++) { fprintf(fp, "%.15g", set->block->time_array[i]); for (column = set->column_head; column != NULL; column = column->next) { switch (column->buffer[i].data_type) { case COUNT_INT: fprintf(fp, " %d", (column->buffer[i].val.ival)); break; case COUNT_DBL: fprintf(fp, " %.9g", (column->buffer[i].val.dval)); break; case COUNT_UNSET: fprintf(fp, " X"); break; case COUNT_TRIG_STRUCT: default: if (column->expr->title != NULL) mcell_warn( "Unexpected data type in column titled '%s' -- skipping.", column->expr->title); else mcell_warn("Unexpected data type in untitled column -- skipping."); break; } } fprintf(fp, "\n"); } } else /* Write accumulated trigger data */ { struct output_trigger_data *trig; char event_time_string[1024]; /* Wouldn't run out of space even if we printed out DBL_MAX in non-exponential notation! */ n_output = (u_int)set->column_head->initial_value; for (i = 0; i < n_output; i++) { trig = set->column_head->buffer[i].val.tval; if (set->exact_time_flag) sprintf(event_time_string, "%.12g ", trig->event_time); else strcpy(event_time_string, ""); if (trig->flags & TRIG_IS_RXN) /* Just need time, pos, name */ { fprintf(fp, "%.15g %s%.9g %.9g %.9g %s\n", trig->t_iteration, event_time_string, trig->loc.x, trig->loc.y, trig->loc.z, (trig->name == NULL) ? "" : trig->name); } else if (trig->flags & TRIG_IS_HIT) /* Need orientation also */ { fprintf(fp, "%.15g %s%.9g %.9g %.9g %d %s\n", trig->t_iteration, event_time_string, trig->loc.x, trig->loc.y, trig->loc.z, trig->orient, (trig->name == NULL) ? "" : trig->name); } else /* Molecule count -- need both number and orientation */ { fprintf(fp, "%.15g %s%.9g %.9g %.9g %d %d %s %lu\n", trig->t_iteration, event_time_string, trig->loc.x, trig->loc.y, trig->loc.z, trig->orient, trig->how_many, (trig->name == NULL) ? "" : trig->name, trig->id); } } } set->chunk_count++; fclose(fp); return 0; }
/************************************************************************** update_reaction_output: In: the output_block we want to update Out: 0 on success, 1 on failure. The counters in this block are updated, and the block is rescheduled for the next output time. The counters are saved to an internal buffer, and written out when full. **************************************************************************/ int update_reaction_output(struct volume *world, struct output_block *block) { int report_as_non_trigger = 1; int i = block->buf_index; if (block->data_set_head != NULL && block->data_set_head->column_head != NULL && block->data_set_head->column_head->buffer[i].data_type == COUNT_TRIG_STRUCT) report_as_non_trigger = 0; if (report_as_non_trigger) { switch (world->notify->reaction_output_report) { case NOTIFY_NONE: break; case NOTIFY_BRIEF: mcell_log( "Updating reaction output scheduled at time %.15g on iteration %lld.", block->t, world->current_iterations); break; case NOTIFY_FULL: mcell_log("Updating reaction output scheduled at time %.15g on iteration" " %lld.\n Buffer fill level is at %u/%u.", block->t, world->current_iterations, block->buf_index, block->buffersize); break; default: UNHANDLED_CASE(world->notify->reaction_output_report); } } /* update all counters */ block->t /= (1. + EPS_C); if (world->chkpt_seq_num == 1) { if (block->timer_type == OUTPUT_BY_ITERATION_LIST) block->time_array[i] = block->t; else block->time_array[i] = block->t * world->time_unit; } else { if (block->timer_type == OUTPUT_BY_ITERATION_LIST) { block->time_array[i] = block->t; } else if (block->timer_type == OUTPUT_BY_TIME_LIST) { if (block->time_now == NULL) { return 0; } else { block->time_array[i] = block->time_now->value; } } else { /* OUTPUT_BY_STEP */ block->time_array[i] = convert_iterations_to_seconds( world->start_iterations, world->time_unit, world->simulation_start_seconds, block->t); } } struct output_set *set; struct output_column *column; // Each file for (set = block->data_set_head; set != NULL; set = set->next) { if (report_as_non_trigger) { if (world->notify->reaction_output_report == NOTIFY_FULL) mcell_log(" Processing reaction output file '%s'.", set->outfile_name); } // Each column for (column = set->column_head; column != NULL; column = column->next) { if (column->buffer[i].data_type != COUNT_TRIG_STRUCT) { eval_oexpr_tree(column->expr, 1); switch (column->buffer[i].data_type) { case COUNT_INT: column->buffer[i].val.ival = (int)column->expr->value; break; case COUNT_DBL: column->buffer[i].val.dval = (double)column->expr->value; break; case COUNT_UNSET: column->buffer[i].val.cval = 'X'; break; case COUNT_TRIG_STRUCT: default: UNHANDLED_CASE(column->buffer[i].data_type); } } } } block->buf_index++; int final_chunk_flag = 0; // flag signaling an end to the scheduled // reaction outputs. Takes values {0,1}. // 0 - end not reached yet, // 1 - end reached. /* Pick time of next output, if any */ if (block->timer_type == OUTPUT_BY_STEP) block->t += block->step_time / world->time_unit; else if (block->time_now != NULL) { block->time_now = block->time_now->next; if (block->time_now == NULL) final_chunk_flag = 1; else { if (block->timer_type == OUTPUT_BY_ITERATION_LIST) block->t = block->time_now->value; else { /* OUTPUT_BY_TIME_LIST */ if (world->chkpt_seq_num == 1) { block->t = block->time_now->value / world->time_unit; } else { block->t = world->start_iterations + (block->time_now->value - world->simulation_start_seconds) / world->time_unit; } } } } else final_chunk_flag = 1; /* Schedule next output event--even if we're at the end, since triggers may * not yet be written */ double actual_t; if (final_chunk_flag == 1) { actual_t = block->t; block->t = FOREVER; } else actual_t = -1; block->t *= (1. + EPS_C); if (schedule_add(world->count_scheduler, block)) { mcell_allocfailed_nodie("Failed to add count to scheduler."); return 1; } if (distinguishable(actual_t, -1, EPS_C)) block->t = actual_t; /* Fix time for output */ if (report_as_non_trigger && world->notify->reaction_output_report == NOTIFY_FULL) { mcell_log(" Next output for this block scheduled at time %.15g.", block->t); } if (block->t >= world->iterations + 1) final_chunk_flag = 1; /* write data to outfile */ if (block->buf_index == block->buffersize || final_chunk_flag) { for (set = block->data_set_head; set != NULL; set = set->next) { if (set->column_head->buffer[i].data_type == COUNT_TRIG_STRUCT) continue; if (write_reaction_output(world, set)) { mcell_error_nodie("Failed to write reaction output to file '%s'.", set->outfile_name); return 1; } } block->buf_index = 0; no_printf("Done updating reaction output\n"); } if (distinguishable(actual_t, -1, EPS_C)) block->t = FOREVER; /* Back to infinity if we're done */ return 0; }