/************************************************************************** emergency_output_hook: This is an atexit hook to flush reaction output to disk in case an error is occurred. Set emergency_output_hook_enabled to 0 to prevent it from being called (say, on successful exit). In: No arguments. Out: None. **************************************************************************/ static void emergency_output_hook(void) { if (emergency_output_hook_enabled) { /* Disable the emergency output hook in case a signal is received while * producing emergency output. */ emergency_output_hook_enabled = 0; int n_errors = emergency_output(global_state); if (n_errors == 0) mcell_warn("Reaction output was successfully flushed to disk."); else if (n_errors == 1) mcell_warn("An error occurred while flushing reaction output to disk."); else mcell_warn("%d errors occurred while flushing reaction output to disk.", n_errors); } }
/************************************************************************** install_emergency_output_hooks: Installs all relevant hooks for catching invalid program termination and flushing output to disk, where possible. In: No arguments. Out: None. **************************************************************************/ void install_emergency_output_hooks(struct volume *world) { global_state = world; if (atexit(&emergency_output_hook) != 0) mcell_warn("Failed to install emergency output hook."); install_emergency_output_signal_handler( SIGILL); /* not generated on Windows but can be raised manually */ install_emergency_output_signal_handler(SIGABRT); install_emergency_output_signal_handler(SIGFPE); install_emergency_output_signal_handler(SIGSEGV); #ifdef SIGBUS install_emergency_output_signal_handler(SIGBUS); #endif }
/************************************************************************** install_emergency_output_signal_handler: This installs a handler for a single signal which will print out a sensible message, then try to flush as much output data to disk as possible before dying. In: No arguments. Out: None. **************************************************************************/ static void install_emergency_output_signal_handler(int signo) { #ifdef _WIN32 /* fixme: for Windows do a better job than just signal(), need \ to find out what other things the *nix version is doing */ signal(signo, &emergency_output_signal_handler); #else struct sigaction sa, saPrev; sa.sa_sigaction = NULL; sa.sa_handler = &emergency_output_signal_handler; sa.sa_flags = SA_RESTART | SA_RESETHAND | SA_NODEFER; sigfillset(&sa.sa_mask); if (sigaction(signo, &sa, &saPrev) != 0) mcell_warn("Failed to install emergency output signal handler."); #endif }
/************************************************************************* trigger_trimolecular: In: hash values of the three colliding molecules pointers to the species of three colliding molecules (reacA is the moving molecule and reacB and reacC are the targets) orientations of the three molecules array of pointers to the possible reactions Out: number of possible reactions for species reacA, reacB, and reacC Also the first "number" slots in the "matching_rxns" array are filled with pointers to the possible reactions objects. Note: The target molecules are already scheduled and can be destroyed but not rescheduled. Assume we have or will check separately that the moving molecule is not inert! PostNote1: If one of the targets is a surface_molecule - it is reacC, if two of the targets are surface molecules - they are reacB and reacC. *************************************************************************/ int trigger_trimolecular(struct rxn **reaction_hash, int rx_hashsize, u_int hashA, u_int hashB, u_int hashC, struct species *reacA, struct species *reacB, struct species *reacC, int orientA, int orientB, int orientC, struct rxn **matching_rxns) { u_int rawhash = 0; u_int hash = 0; /* index in the reaction hash table */ int num_matching_rxns = 0; /* number of matching reactions */ short geomA = SHRT_MIN, geomB = SHRT_MIN, geomC = SHRT_MIN; struct rxn *inter; int correct_players_flag; int correct_orientation_flag; if (strcmp(reacA->sym->name, reacB->sym->name) < 0) { if (strcmp(reacB->sym->name, reacC->sym->name) < 0) rawhash = (hashA + hashB); else rawhash = (hashA + hashC); } else if (strcmp(reacA->sym->name, reacC->sym->name) < 0) rawhash = (hashB + hashA); else rawhash = (hashB + hashC); hash = rawhash & (rx_hashsize - 1); inter = reaction_hash[hash]; while (inter != NULL) { if (inter->n_reactants == 3) /* Enough reactants? */ { correct_players_flag = 0; correct_orientation_flag = 0; /* Check that we have the right players */ if (reacA == inter->players[0]) { if ((reacB == inter->players[1] && reacC == inter->players[2])) { geomA = inter->geometries[0]; geomB = inter->geometries[1]; geomC = inter->geometries[2]; correct_players_flag = 1; } else if ((reacB == inter->players[2] && reacC == inter->players[1])) { geomA = inter->geometries[0]; geomB = inter->geometries[2]; geomC = inter->geometries[1]; correct_players_flag = 1; } } else if (reacA == inter->players[1]) { if ((reacB == inter->players[0]) && (reacC == inter->players[2])) { geomA = inter->geometries[1]; geomB = inter->geometries[0]; geomC = inter->geometries[2]; correct_players_flag = 1; } else if ((reacB == inter->players[2]) && (reacC == inter->players[0])) { geomA = inter->geometries[1]; geomB = inter->geometries[2]; geomC = inter->geometries[0]; correct_players_flag = 1; } } else if (reacA == inter->players[2]) { if ((reacB == inter->players[0]) && (reacC == inter->players[1])) { geomA = inter->geometries[2]; geomB = inter->geometries[0]; geomC = inter->geometries[1]; correct_players_flag = 1; } else if ((reacB == inter->players[1]) && (reacC == inter->players[0])) { geomA = inter->geometries[2]; geomB = inter->geometries[1]; geomC = inter->geometries[0]; correct_players_flag = 1; } } /* Check to see if orientation classes are zero or different. In such case we do not care about relative orientations of the volume and surface reactants. */ if ((geomA == 0) && (geomB == 0) && (geomC == 0)) { /* all volume molecules */ correct_orientation_flag = 1; } /* two volume and one surface molecule */ /* since geomA = geomB we will test only for geomA */ else if (((reacA->flags & NOT_FREE) == 0) && ((reacB->flags & NOT_FREE) == 0) && ((reacC->flags & ON_GRID) != 0)) { /* different orientation classes */ if ((geomA + geomC) * (geomA - geomC) != 0) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if (orientA != 0 && orientA * orientC * geomA * geomC > 0) { correct_orientation_flag = 1; } } /* (one volume molecule and two surface molecules) or (three surface molecules) */ else { /* different orientation classes for all 3 reactants */ if (((geomA + geomC) * (geomA - geomC) != 0) && ((geomA + geomB) * (geomA - geomB) != 0) && ((geomB + geomC) * (geomB - geomC))) { correct_orientation_flag = 1; } /* two reactants in the zero orientation class */ else if ((geomB == 0) && (geomC == 0) && (orientA != 0) && (geomA != 0)) { correct_orientation_flag = 1; } else if ((geomA == 0) && (geomC == 0) && (orientB != 0) && (geomB != 0)) { correct_orientation_flag = 1; } else if ((geomA == 0) && (geomB == 0) && (orientC != 0) && (geomC != 0)) { correct_orientation_flag = 1; } /* one reactant in the zero orientation class */ else if (geomA == 0) { /* different orientation classes */ if ((geomB + geomC) * (geomB - geomC) != 0) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if (orientB != 0 && orientB * orientC * geomB * geomC > 0) { correct_orientation_flag = 1; } } else if (geomB == 0) { /* different orientation classes */ if ((geomA + geomC) * (geomA - geomC) != 0) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if (orientA != 0 && orientA * orientC * geomA * geomC > 0) { correct_orientation_flag = 1; } } else if (geomC == 0) { /* different orientation classes */ if ((geomA + geomB) * (geomA - geomB) != 0) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if (orientA != 0 && orientA * orientB * geomA * geomB > 0) { correct_orientation_flag = 1; } /* two geometries are the same */ } else if (geomB == geomC) { /* different orientation classes */ if (((geomA + geomB) * (geomA - geomB) != 0) && (orientB == orientC)) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if ((orientA != 0 && orientA * orientB * geomA * geomB > 0) && (orientB == orientC)) { correct_orientation_flag = 1; } } else if (geomA == geomC) { /* different orientation classes */ if (((geomA + geomB) * (geomA - geomB) != 0) && (orientA == orientC)) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if ((orientA != 0 && orientA * orientB * geomA * geomB > 0) && (orientA == orientC)) { correct_orientation_flag = 1; } } else if (geomA == geomB) { /* different orientation classes */ if (((geomA + geomC) * (geomA - geomC) != 0) && (orientA == orientB)) { correct_orientation_flag = 1; } /* Same class, is the orientation correct? */ else if ((orientA != 0 && orientA * orientC * geomA * geomC > 0) && (orientA == orientB)) { correct_orientation_flag = 1; } /* all three geometries are non-zero but the same */ } else if ((geomA == geomB) && (geomA == geomC)) { if ((orientA == orientB) && (orientA == orientC)) { /* Same class, is the orientation correct? */ if (orientA != 0 && orientA * orientC * geomA * geomC > 0 && orientA * orientB * geomA * geomB > 0) { correct_orientation_flag = 1; } } } } if (correct_players_flag && correct_orientation_flag) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; } } inter = inter->next; } if (num_matching_rxns > MAX_MATCHING_RXNS) { mcell_warn("Number of matching reactions exceeds the maximum allowed " "number MAX_MATCHING_RXNS."); } return num_matching_rxns; }
/************************************************************************* trigger_bimolecular: In: hash values of the two colliding molecules pointers to the two colliding molecules orientations of the two colliding molecules both zero away from a surface both nonzero (+-1) at a surface A is the moving molecule and B is the target array of pointers to the possible reactions Out: number of possible reactions for molecules reacA and reacB Also the first 'number' slots in the 'matching_rxns' array are filled with pointers to the possible reactions objects. Note: The target molecule is already scheduled and can be destroyed but not rescheduled. Assume we have or will check separately that the moving molecule is not inert! *************************************************************************/ int trigger_bimolecular(struct rxn **reaction_hash, int rx_hashsize, u_int hashA, u_int hashB, struct abstract_molecule *reacA, struct abstract_molecule *reacB, short orientA, short orientB, struct rxn **matching_rxns) { u_int hash; /* index in the reaction hash table */ int test_wall; /* flag */ int num_matching_rxns = 0; /* number of matching reactions */ short geomA, geomB; struct rxn *inter; struct surf_class_list *scl, *scl2; int need_complex = 0; int right_walls_surf_classes; /* flag to check whether SURFACE_CLASSES of the walls for one or both reactants match the SURFACE_CLASS of the reaction (if needed) */ hash = (hashA + hashB) & (rx_hashsize - 1); /* Check if either reactant belongs to a complex */ if ((reacA->flags | reacB->flags) & COMPLEX_MEMBER) { need_complex = 1; /* If both reactants are subunits, this reaction cannot occur */ if (((reacA->flags ^ reacB->flags) & COMPLEX_MEMBER) == 0) return 0; } for (inter = reaction_hash[hash]; inter != NULL; inter = inter->next) { right_walls_surf_classes = 0; /* Right number of reactants? */ if (inter->n_reactants < 2) continue; else if (inter->n_reactants > 2 && !(inter->players[2]->flags & IS_SURFACE)) continue; /* If it's a complex rxn, make sure one of the molecules is part of a * complex */ if (inter->is_complex) { if (!need_complex) continue; } else { if (need_complex) continue; } /* Do we have the right players? */ if (reacA->properties == reacB->properties) { if ((reacA->properties != inter->players[0] || reacA->properties != inter->players[1])) continue; } else if ((reacA->properties == inter->players[0] && reacB->properties == inter->players[1])) { if (inter->is_complex != NULL) { if (inter->is_complex[0] != ((reacA->flags & COMPLEX_MEMBER) ? 1 : 0)) continue; /* Don't need to check other reactant -- we know we have the right * number of subunits */ } } else if ((reacB->properties == inter->players[0] && reacA->properties == inter->players[1])) { if (inter->is_complex != NULL) { if (inter->is_complex[0] != ((reacB->flags & COMPLEX_MEMBER) ? 1 : 0)) continue; /* Don't need to check other reactant -- we know we have the right * number of subunits */ } } else continue; test_wall = 0; geomA = inter->geometries[0]; geomB = inter->geometries[1]; /* Check to see if orientation classes are zero/different */ if (geomA == 0 || geomB == 0 || (geomA + geomB) * (geomA - geomB) != 0) { if (inter->n_reactants == 2) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; continue; } else { test_wall = 1; } } /* Same class, is the orientation correct? */ else if (orientA != 0 && orientA * orientB * geomA * geomB > 0) { if (inter->n_reactants == 2) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; continue; } else { test_wall = 1; } } /* See if we need to check a wall (fails if we're in free space) */ if (test_wall && orientA != 0) { struct wall *w_A = NULL, *w_B = NULL; short geomW; /* short orientW = 1; Walls always have orientation 1 */ /* If we are oriented, one of us is a surface mol. */ /* For volume molecule wall that matters is the target's wall */ if (((reacA->properties->flags & NOT_FREE) == 0) && (reacB->properties->flags & ON_GRID) != 0) { w_B = (((struct surface_molecule *)reacB)->grid)->surface; } else if (((reacA->properties->flags & ON_GRID) != 0) && (reacB->properties->flags & ON_GRID) != 0) { w_A = (((struct surface_molecule *)reacA)->grid)->surface; w_B = (((struct surface_molecule *)reacB)->grid)->surface; } /* If a wall was found, we keep going to check.... This is a case for reaction between volume and surface molecules */ if ((w_A == NULL) && (w_B != NULL)) { /* Right wall type--either this type or generic type? */ for (scl = w_B->surf_class_head; scl != NULL; scl = scl->next) { if (inter->players[2] == scl->surf_class) { right_walls_surf_classes = 1; break; } } } /* if both reactants are surface molecules they should be on the walls with the same SURFACE_CLASS */ if ((w_A != NULL) && (w_B != NULL)) { for (scl = w_A->surf_class_head; scl != NULL; scl = scl->next) { for (scl2 = w_B->surf_class_head; scl2 != NULL; scl2 = scl->next) { if (scl->surf_class == scl2->surf_class) { if (inter->players[2] == scl->surf_class) { right_walls_surf_classes = 1; break; } } } } } if (right_walls_surf_classes) { geomW = inter->geometries[2]; if (geomW == 0) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; continue; } /* We now care whether A and B correspond to player [0] and [1] or */ /* vice versa, so make sure A==[0] and B==[1] so W can */ /* match with the right one! */ if (reacA->properties != inter->players[0]) { short temp = geomB; geomB = geomA; geomA = temp; } if (geomA == 0 || (geomA + geomW) * (geomA - geomW) != 0) /* W not in A's class */ { if (geomB == 0 || (geomB + geomW) * (geomB - geomW) != 0) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; continue; } if (orientB * geomB * geomW > 0) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; continue; } } else /* W & A in same class */ { if (orientA * geomA * geomW > 0) { if (num_matching_rxns >= MAX_MATCHING_RXNS) break; matching_rxns[num_matching_rxns] = inter; num_matching_rxns++; continue; } } } /* if (right_walls_surf_classes) ... */ } /* end if (test_wall && orientA != NULL) */ } /* end for (inter = reaction_hash[hash]; ...) */ if (num_matching_rxns > MAX_MATCHING_RXNS) { mcell_warn("Number of matching reactions exceeds the maximum allowed " "number MAX_MATCHING_RXNS."); } return num_matching_rxns; }
/************************************************************************** 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; }