Example #1
0
/**************************************************************************
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;
}
Example #2
0
/*
 * Write the molecule counts to the file.
 *
 * XXX: Update this code to be smarter about the tradeoff between large slab
 * size (= large mem usage) and small slab size (= worse cache usage).
 */
static int produce_mol_counts(struct volume *wrld, FILE *out_file,
                              struct volume_output_item *vo) {
  struct volume_molecule *curmol;
  int *counters, *countersptr;
  int k, u, v;
  double z = vo->location.z, y = vo->location.y, x = vo->location.x;
  double x_lim = x + vo->voxel_size.x * (double)vo->nvoxels_x;
  double y_lim = y + vo->voxel_size.y * (double)vo->nvoxels_y;
  struct subvolume *cur_partition_z;
  double z_lim_part;

  /* Allocate memory for counters. */
  counters =
      CHECKED_MALLOC_ARRAY(int, vo->nvoxels_x * vo->nvoxels_y, "voxel slab");

  cur_partition_z = find_subvolume(wrld, &vo->location, NULL);
  if (cur_partition_z == NULL) {
    free(counters);
    mcell_internal_error(
        "While counting at [%g, %g, %g]: point isn't within a partition.", x, y,
        z);
    /*return 1;*/
  }

  z_lim_part = wrld->z_fineparts[cur_partition_z->urb.z];

  /* For each slab: */
  double r_voxsz_x = 1.0 / vo->voxel_size.x;
  double r_voxsz_y = 1.0 / vo->voxel_size.y;
  for (k = 0; k < vo->nvoxels_z; ++k) {
    double z_lim_slab = z + vo->voxel_size.z;
    struct subvolume *cur_partition_y;

    /* reset counters for this slab */
    memset(counters, 0, sizeof(int) * vo->nvoxels_x * vo->nvoxels_y);

  /* Loop over relevant partitions */
  keep_counting:
    cur_partition_y = cur_partition_z;
    while (cur_partition_y != NULL &&
           wrld->y_fineparts[cur_partition_y->llf.y] < y_lim) {
      struct subvolume *cur_partition = cur_partition_y;
      while (cur_partition != NULL &&
             wrld->x_fineparts[cur_partition->llf.x] < x_lim) {
        /* Count molecules */
        struct per_species_list *psl;
        int check_nonreacting = 0;
        int i = 0;
        for (i = 0; i < vo->num_molecules; ++i) {
          if (!(vo->molecules[i]->flags & CAN_VOLVOL)) {
            check_nonreacting = 1;
            break;
          }
        }
        for (psl = cur_partition->species_head; psl != NULL; psl = psl->next) {
          if (psl->properties == NULL) {
            if (!check_nonreacting)
              continue;
            else {
              for (curmol = psl->head; curmol != NULL;
                   curmol = curmol->next_v) {
                /* See if we're interested in this molecule */
                if (vo->num_molecules == 1) {
                  if (*vo->molecules != curmol->properties)
                    continue;
                } else {
                  if ((find_species_in_array(vo->molecules, vo->num_molecules,
                                             curmol->properties)) == -1)
                    continue;
                }

                /* Skip molecules not in our slab */
                if (curmol->pos.z < z || curmol->pos.z >= z_lim_slab)
                  continue;

                /* Skip molecules outside our domain */
                if (curmol->pos.x < x || curmol->pos.x >= x_lim ||
                    curmol->pos.y < y || curmol->pos.y >= y_lim)
                  continue;

                /* We've got a winner!  Add one to the appropriate voxel. */
                ++counters[((int)floor((curmol->pos.y - y) * r_voxsz_y)) *
                               vo->nvoxels_x +
                           (int)floor((curmol->pos.x - x) * r_voxsz_x)];
              }
            }
          } else {
            /* See if we're interested in this molecule */
            if (vo->num_molecules == 1) {
              if (*vo->molecules != psl->properties)
                continue;
            } else {
              if ((find_species_in_array(vo->molecules, vo->num_molecules,
                                         psl->properties)) == -1)
                continue;
            }

            for (curmol = psl->head; curmol != NULL; curmol = curmol->next_v) {
              /* Skip molecules not in our slab */
              if (curmol->pos.z < z || curmol->pos.z >= z_lim_slab)
                continue;

              /* Skip molecules outside our domain */
              if (curmol->pos.x < x || curmol->pos.x >= x_lim ||
                  curmol->pos.y < y || curmol->pos.y >= y_lim)
                continue;

              /* We've got a winner!  Add one to the appropriate voxel. */
              ++counters[((int)floor((curmol->pos.y - y) * r_voxsz_y)) *
                             vo->nvoxels_x +
                         (int)floor((curmol->pos.x - x) * r_voxsz_x)];
            }
          }
        }

        /* Advance to next x-partition */
        cur_partition =
            traverse_subvol(cur_partition, NULL, X_POS, wrld->ny_parts,
                            wrld->nz_parts);
      }

      /* Advance to next y-partition */
      cur_partition_y =
          traverse_subvol(cur_partition_y, NULL, Y_POS, wrld->ny_parts,
                          wrld->nz_parts);
    }

    /* If the slab crosses a Z boundary, keep on truckin' */
    if (z_lim_slab >= z_lim_part) {
      /* If we can get to the next partition, don't update slab and don't
       * spill!
       */
      cur_partition_z =
          traverse_subvol(cur_partition_z, NULL, Z_POS, wrld->ny_parts,
                          wrld->nz_parts);

      if (cur_partition_z != NULL) {
        z_lim_part = wrld->z_fineparts[cur_partition_z->urb.z];
        goto keep_counting;
      }
    } else {
      z = z_lim_slab;
      /* z_lim_slab will be updated on the next pass through the loop. */
    }

    /* Spill our counts */
    countersptr = counters;
    for (u = 0; u < vo->nvoxels_y; ++u) {
      for (v = 0; v < vo->nvoxels_x; ++v)
        fprintf(out_file, "%d ", *countersptr++);
      fprintf(out_file, "\n");
    }

    /* Extra newline to put visual separation between slabs */
    fprintf(out_file, "\n");
  }

  free(counters);
  return 0;
}
Example #3
0
/*************************************************************************
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;
  }
}
Example #4
0
/*************************************************************************
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;
}
Example #5
0
/*************************************************************************
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;
}