/************************************************************************* test_many_bimolecular: In: an array of reactions we're testing scaling coefficients depending on how many timesteps we've moved at once (1.0 means one timestep) and/or missing interaction areas local probability factor for the corresponding reactions the number of elements in the array of reactions placeholder for the chosen pathway in the reaction (works as return value) a flag to indicate if Out: RX_NO_RX if no reaction occurs index in the reaction array corresponding to which reaction occurs if one does occur Note: If this reaction does not return RX_NO_RX, then we update counters appropriately assuming that the reaction does take place. Note: this uses only one call to get a random double, so you can't effectively sample events that happen less than 10^-9 of the time (for 32 bit random number). NOTE: This function was merged with test_many_bimolecular_all_neighbors. These two functions were almost identical, and the behavior of the "all_neighbors" version is preserved with a flag that can be passed in. For reactions between two surface molecules, set this flag to 1. For such reactions (local_prob_factor > 0) *************************************************************************/ int test_many_bimolecular(struct rxn **rx, double *scaling, double local_prob_factor, int n, int *chosen_pathway, struct abstract_molecule **complexes, int *complex_limits, struct rng_state *rng, int all_neighbors_flag) { double rxp[2 * n]; /* array of cumulative rxn probabilities */ struct rxn *my_rx; int i; /* index in the array of reactions - return value */ int m, M; double p, f; int has_coop_rate = 0; int nmax; if (all_neighbors_flag && local_prob_factor <= 0) mcell_internal_error("Local probability factor = %g in the function " "'test_many_bimolecular_all_neighbors().", local_prob_factor); if (n == 1) { if (all_neighbors_flag) return test_bimolecular(rx[0], scaling[0], local_prob_factor, complexes[0], NULL, rng); else return test_bimolecular(rx[0], 0, scaling[0], complexes[0], NULL, rng); } /* Note: lots of division here, if we're CPU-bound,could invert the definition of scaling_coefficients */ if (rx[0]->rates) has_coop_rate = 1; if (all_neighbors_flag && local_prob_factor > 0) { rxp[0] = (rx[0]->max_fixed_p) * local_prob_factor / scaling[0]; } else { rxp[0] = rx[0]->max_fixed_p / scaling[0]; } for (i = 1; i < n; i++) { if (all_neighbors_flag && local_prob_factor > 0) { rxp[i] = rxp[i - 1] + (rx[i]->max_fixed_p) * local_prob_factor / scaling[i]; } else { rxp[i] = rxp[i - 1] + rx[i]->max_fixed_p / scaling[i]; } if (rx[i]->rates) has_coop_rate = 1; } if (has_coop_rate) { for (; i < 2 * n; ++i) { if (all_neighbors_flag && local_prob_factor > 0) { rxp[i] = rxp[i - 1] + (rx[i - n]->min_noreaction_p - rx[i - n]->max_fixed_p) * local_prob_factor / scaling[i]; } else { rxp[i] = rxp[i - 1] + (rx[i - n]->min_noreaction_p - rx[i - n]->max_fixed_p) / scaling[i]; } } } nmax = i; if (has_coop_rate) { p = rng_dbl(rng); /* Easy out - definitely no reaction */ if (p > rxp[nmax - 1]) return RX_NO_RX; /* Might we have missed any? */ if (rxp[nmax - 1] > 1.0) { double deficit = 0.0; int cxNo = 0; for (i = n; i < 2 * n; ++i) { if (i - n >= complex_limits[cxNo]) ++cxNo; for (int n_path = 0; n_path < rx[i]->n_pathways; ++n_path) { if (rx[i]->rates[n_path] == NULL) continue; deficit += macro_lookup_rate(rx[i]->rates[n_path], complexes[cxNo], scaling[i - n] * rx[i]->pb_factor); } rxp[n] -= deficit; } /* Ok, did we REALLY miss any? */ if (rxp[nmax - 1] > 1.0) { f = rxp[nmax - 1] - 1.0; /* Number of failed reactions */ for (i = 0; i < n; i++) /* Distribute failures */ { if (all_neighbors_flag && local_prob_factor > 0) { rx[i]->n_skipped += f * ((rx[i]->max_fixed_p) * local_prob_factor + rxp[n + i] - rxp[n + i - 1]) / rxp[n - 1]; } else { rx[i]->n_skipped += f * (rx[i]->max_fixed_p + rxp[n + i] - rxp[n + i - 1]) / rxp[n - 1]; } } p *= rxp[nmax - 1]; } /* Was there any reaction? */ if (p > rxp[nmax - 1]) return RX_NO_RX; /* Pick the reaction that happens. Note that the binary search is over * 2*n items, not n. The first n are the fixed rate pathways of each of * the n reactions, and the next n are the cooperative pathways. */ i = binary_search_double(rxp, p, nmax - 1, 1); if (i > 0) p = (p - rxp[i - 1]); /* If it was a varying rate... */ if (i >= n) { i -= n; p = p * scaling[i]; cxNo = 0; while (i >= complex_limits[cxNo]) ++cxNo; for (int n_path = 0; n_path < rx[i]->n_pathways; ++n_path) { if (rx[i]->rates[n_path] == NULL) continue; double prob = macro_lookup_rate(rx[i]->rates[n_path], complexes[cxNo], scaling[i] * rx[i]->pb_factor); if (p > prob) p -= prob; else { *chosen_pathway = n_path; return i; } } return RX_NO_RX; } /* else it was a fixed rate... */ else { p = p * scaling[i]; /* Now pick the pathway within that reaction */ my_rx = rx[i]; M = my_rx->n_pathways - 1; if (all_neighbors_flag && local_prob_factor > 0) m = binary_search_double(my_rx->cum_probs, p, M, local_prob_factor); else m = binary_search_double(my_rx->cum_probs, p, M, 1); *chosen_pathway = m; return i; } } /* We didn't miss any reactions and also don't need to consult the varying * probabilities */ else if (p <= rxp[n - 1]) { /* Pick the reaction that happens */ i = binary_search_double(rxp, p, n - 1, 1); my_rx = rx[i]; if (i > 0) p = (p - rxp[i - 1]); p = p * scaling[i]; /* Now pick the pathway within that reaction */ M = my_rx->n_pathways - 1; if (all_neighbors_flag && local_prob_factor > 0) m = binary_search_double(my_rx->cum_probs, p, M, local_prob_factor); else m = binary_search_double(my_rx->cum_probs, p, M, 1); *chosen_pathway = m; return i; } /* The hard way. We're in the cooperativity region of probability space * and will need to examine the varying probabilities. */ else { p -= rxp[n - 1]; int cxNo = 0; for (i = n; i < 2 * n; ++i) { if (i - n >= complex_limits[cxNo]) ++cxNo; for (int n_path = 0; n_path < rx[i]->n_pathways; ++n_path) { if (rx[i]->rates[n_path] == NULL) continue; double prob = macro_lookup_rate(rx[i]->rates[n_path], complexes[cxNo], scaling[i - n] * rx[i]->pb_factor); if (p > prob) p -= prob; else { *chosen_pathway = n_path; return i - n; } } } return RX_NO_RX; } mcell_internal_error("Should never reach this point in the code."); return RX_NO_RX; } else { if (rxp[n - 1] > 1.0) { f = rxp[n - 1] - 1.0; /* Number of failed reactions */ for (i = 0; i < n; i++) /* Distribute failures */ { if (all_neighbors_flag && local_prob_factor > 0) { rx[i]->n_skipped += f * ((rx[i]->cum_probs[rx[i]->n_pathways - 1]) * local_prob_factor) / rxp[n - 1]; } else { rx[i]->n_skipped += f * (rx[i]->cum_probs[rx[i]->n_pathways - 1]) / rxp[n - 1]; } } p = rng_dbl(rng) * rxp[n - 1]; } else { p = rng_dbl(rng); if (p > rxp[n - 1]) return RX_NO_RX; } /* Pick the reaction that happens */ i = binary_search_double(rxp, p, n - 1, 1); my_rx = rx[i]; if (i > 0) p = (p - rxp[i - 1]); p = p * scaling[i]; /* Now pick the pathway within that reaction */ M = my_rx->n_pathways - 1; if (all_neighbors_flag && local_prob_factor > 0) m = binary_search_double(my_rx->cum_probs, p, M, local_prob_factor); else m = binary_search_double(my_rx->cum_probs, p, M, 1); *chosen_pathway = m; return i; } }
/************************************************************************* test_many_reactions_all_neighbors: In: an array of reactions we're testing an array of scaling coefficients depending on how many timesteps we've moved at once (1.0 means one timestep) and/or missing interaction areas an array of local probability factors for the corresponding reactions the number of elements in the array of reactions placeholder for the chosen pathway in the reaction (works as return value) Out: RX_NO_RX if no reaction occurs index in the reaction array corresponding to which reaction occurs if one does occur Note: If this reaction does not return RX_NO_RX, then we update counters appropriately assuming that the reaction does take place. Note: this uses only one call to get a random double, so you can't effectively sample events that happen less than 10^-9 of the time (for 32 bit random number). NOTE: This function should be used for now only for the reactions between three surface molecules. *************************************************************************/ int test_many_reactions_all_neighbors(struct rxn **rx, double *scaling, double *local_prob_factor, int n, int *chosen_pathway, struct rng_state *rng) { if (local_prob_factor == NULL) mcell_internal_error("There is no local probability factor information in " "the function 'test_many_reactions_all_neighbors()."); if (n == 1) return test_bimolecular(rx[0], scaling[0], local_prob_factor[0], NULL, NULL, rng); double rxp[n]; /* array of cumulative rxn probabilities */ if (local_prob_factor[0] > 0) { rxp[0] = (rx[0]->max_fixed_p) * local_prob_factor[0] / scaling[0]; } else { rxp[0] = rx[0]->max_fixed_p / scaling[0]; } // i: index in the array of reactions - return value for (int i = 1; i < n; i++) { if (local_prob_factor[i] > 0) { rxp[i] = rxp[i - 1] + (rx[i]->max_fixed_p) * local_prob_factor[i] / scaling[i]; } else { rxp[i] = rxp[i - 1] + rx[i]->max_fixed_p / scaling[i]; } } double p; if (rxp[n - 1] > 1.0) { double f = rxp[n - 1] - 1.0; /* Number of failed reactions */ for (int i = 0; i < n; i++) /* Distribute failures */ { if (local_prob_factor[i] > 0) { rx[i]->n_skipped += f * ((rx[i]->cum_probs[rx[i]->n_pathways - 1]) * local_prob_factor[i]) / rxp[n - 1]; } else { rx[i]->n_skipped += f * (rx[i]->cum_probs[rx[i]->n_pathways - 1]) / rxp[n - 1]; } } p = rng_dbl(rng) * rxp[n - 1]; } else { p = rng_dbl(rng); if (p > rxp[n - 1]) return RX_NO_RX; } /* Pick the reaction that happens */ int i = binary_search_double(rxp, p, n - 1, 1); struct rxn *my_rx = rx[i]; double my_local_prob_factor = local_prob_factor[i]; if (i > 0) p = (p - rxp[i - 1]); p = p * scaling[i]; /* Now pick the pathway within that reaction */ int M = my_rx->n_pathways - 1; if (my_local_prob_factor > 0) { *chosen_pathway = binary_search_double(my_rx->cum_probs, p, M, my_local_prob_factor); } else { *chosen_pathway = binary_search_double(my_rx->cum_probs, p, M, 1); } return i; }
/************************************************************************* test_many_bimolecular: In: an array of reactions we're testing scaling coefficients depending on how many timesteps we've moved at once (1.0 means one timestep) and/or missing interaction areas local probability factor for the corresponding reactions the number of elements in the array of reactions placeholder for the chosen pathway in the reaction (works as return value) a flag to indicate if Out: RX_NO_RX if no reaction occurs index in the reaction array corresponding to which reaction occurs if one does occur Note: If this reaction does not return RX_NO_RX, then we update counters appropriately assuming that the reaction does take place. Note: this uses only one call to get a random double, so you can't effectively sample events that happen less than 10^-9 of the time (for 32 bit random number). NOTE: This function was merged with test_many_bimolecular_all_neighbors. These two functions were almost identical, and the behavior of the "all_neighbors" version is preserved with a flag that can be passed in. For reactions between two surface molecules, set this flag to 1. For such reactions local_prob_factor > 0. *************************************************************************/ int test_many_bimolecular(struct rxn **rx, double *scaling, double local_prob_factor, int n, int *chosen_pathway, struct rng_state *rng, int all_neighbors_flag) { double rxp[2 * n]; /* array of cumulative rxn probabilities */ struct rxn *my_rx; int i; /* index in the array of reactions - return value */ int m, M; double p, f; if (all_neighbors_flag && local_prob_factor <= 0) mcell_internal_error("Local probability factor = %g in the function " "'test_many_bimolecular_all_neighbors().", local_prob_factor); if (n == 1) { if (all_neighbors_flag) return test_bimolecular(rx[0], scaling[0], local_prob_factor, NULL, NULL, rng); else return test_bimolecular(rx[0], 0, scaling[0], NULL, NULL, rng); } /* Note: lots of division here, if we're CPU-bound,could invert the definition of scaling_coefficients */ if (all_neighbors_flag && local_prob_factor > 0) { rxp[0] = (rx[0]->max_fixed_p) * local_prob_factor / scaling[0]; } else { rxp[0] = rx[0]->max_fixed_p / scaling[0]; } for (i = 1; i < n; i++) { if (all_neighbors_flag && local_prob_factor > 0) { rxp[i] = rxp[i - 1] + (rx[i]->max_fixed_p) * local_prob_factor / scaling[i]; } else { rxp[i] = rxp[i - 1] + rx[i]->max_fixed_p / scaling[i]; } } if (rxp[n - 1] > 1.0) { f = rxp[n - 1] - 1.0; /* Number of failed reactions */ for (i = 0; i < n; i++) /* Distribute failures */ { if (all_neighbors_flag && local_prob_factor > 0) { rx[i]->n_skipped += f * ((rx[i]->cum_probs[rx[i]->n_pathways - 1]) * local_prob_factor) / rxp[n - 1]; } else { rx[i]->n_skipped += f * (rx[i]->cum_probs[rx[i]->n_pathways - 1]) / rxp[n - 1]; } } p = rng_dbl(rng) * rxp[n - 1]; } else { p = rng_dbl(rng); if (p > rxp[n - 1]) return RX_NO_RX; } /* Pick the reaction that happens */ i = binary_search_double(rxp, p, n - 1, 1); my_rx = rx[i]; if (i > 0) p = (p - rxp[i - 1]); p = p * scaling[i]; /* Now pick the pathway within that reaction */ M = my_rx->n_pathways - 1; if (all_neighbors_flag && local_prob_factor > 0) m = binary_search_double(my_rx->cum_probs, p, M, local_prob_factor); else m = binary_search_double(my_rx->cum_probs, p, M, 1); *chosen_pathway = m; return i; }