Пример #1
0
/*************************************************************************
add_trigger_output:
   In: counter of thing that just happened (trigger of some sort)
       request structure saying who wanted to know that it happened
       number of times that thing happened
   Out: The event is added to the buffer of the requester, and the
        buffer is written and flushed if the buffer is full.
   Note: Hits are assumed to happen with only one molecule; positive
        means the front face was hit, negative means the back was hit.
        This is reported as orientation instead.
*************************************************************************/
void add_trigger_output(struct volume *world, struct counter *c,
                        struct output_request *ear, int n, short flags,
                        u_long id) {

  struct output_column *first_column;
  first_column = ear->requester->column->set->column_head;

  int idx = (int)first_column->initial_value;

  struct output_trigger_data *otd;
  otd = first_column->buffer[idx].val.tval;

  if (first_column->set->block->timer_type == OUTPUT_BY_ITERATION_LIST)
    otd->t_iteration = world->current_iterations;
  else
    otd->t_iteration = world->current_iterations * world->time_unit;

  otd->event_time = c->data.trig.t_event * world->time_unit;
  otd->loc.x = c->data.trig.loc.x * world->length_unit;
  otd->loc.y = c->data.trig.loc.y * world->length_unit;
  otd->loc.z = c->data.trig.loc.z * world->length_unit;
  if (flags & TRIG_IS_HIT) {
    otd->how_many = 1;
    if (n > 0)
      otd->orient = 1;
    else
      otd->orient = -1;
  } else {
    otd->how_many = n;
    otd->orient = c->data.trig.orient;
  }
  otd->flags = flags;
  otd->name = ear->requester->column->expr->title;
  otd->id = id;

  first_column->initial_value += 1.0;
  idx = (int)first_column->initial_value;
  if (idx >= (int)first_column->set->block->trig_bufsize) {
    if (write_reaction_output(world, first_column->set))
      mcell_error("Failed to write triggered count output to file '%s'.",
                  first_column->set->outfile_name);
    first_column->initial_value = 0;
  }
}
Пример #2
0
/*************************************************************************
 *
 * function for computing the probability factor (pb_factor) used to
 * convert reaction rate constants into probabilities
 *
 * in: mcell state
 *     rx to compute pb_factor for
 *     maximum number of expected surface products for this reaction
 *
 * out: pb_factor
 *
 ************************************************************************/
double compute_pb_factor(double time_unit,
                         double length_unit,
                         double grid_density,
                         double rx_radius_3d,
                         struct reaction_flags *rxn_flags,
                         int *create_shared_walls_info_flag,
                         struct rxn *rx,
                         int max_num_surf_products) {
  /* determine the number of volume and surface reactants as well
   * as the number of surfaces */
  int num_vol_reactants = 0;
  int num_surf_reactants = 0;
  int num_surfaces = 0;
  for (unsigned int n_reactant = 0; n_reactant < rx->n_reactants;
       n_reactant++) {
    if ((rx->players[n_reactant]->flags & ON_GRID) != 0) {
      num_surf_reactants++;
    } else if ((rx->players[n_reactant]->flags & NOT_FREE) == 0) {
      num_vol_reactants++;
    } else if (rx->players[n_reactant]->flags & IS_SURFACE) {
      num_surfaces++;
    }
  }

  /* probability for this reaction */
  double pb_factor = 0.0;

  /* determine reaction probability by proper conversion of the reaction
   * rate constant */
  if (rx->n_reactants == 1) {
    pb_factor = time_unit;
    if (max_num_surf_products > 0)
      *create_shared_walls_info_flag = 1;
  } /* end if (rx->reactants == 1) */
  else if (((rx->n_reactants == 2) &&
            (num_surf_reactants >= 1 || num_surfaces == 1)) ||
           ((rx->n_reactants == 3) && (num_surfaces == 1))) {

    if ((num_surf_reactants == 2) && (num_vol_reactants == 0) &&
        (num_surfaces < 2)) {
      /* this is a reaction between two surface molecules */
      /* with an optional SURFACE                         */
      rxn_flags->surf_surf_reaction_flag = 1;
      *create_shared_walls_info_flag = 1;

      if (rx->players[0]->flags & rx->players[1]->flags & CANT_INITIATE)
        mcell_error("Reaction between %s and %s listed, but both are marked "
                    "TARGET_ONLY.",
                    rx->players[0]->sym->name, rx->players[1]->sym->name);
      else if ((rx->players[0]->flags | rx->players[1]->flags) &
               CANT_INITIATE) {
        pb_factor =
            time_unit * grid_density / 3; /* 3 neighbors */
      } else {
        pb_factor = time_unit * grid_density /
                    6; /* 2 molecules, 3 neighbors each */
      }
    } else if ((((rx->players[0]->flags & IS_SURFACE) != 0 &&
                 (rx->players[1]->flags & ON_GRID) != 0) ||
                ((rx->players[1]->flags & IS_SURFACE) != 0 &&
                 (rx->players[0]->flags & ON_GRID) != 0)) &&
               (rx->n_reactants == 2)) {
      /* This is actually a unimolecular reaction in disguise! */
      pb_factor = time_unit;
      if (max_num_surf_products > 0)
        *create_shared_walls_info_flag = 1;
    } else if (((rx->n_reactants == 2) && (num_vol_reactants == 1) &&
                (num_surfaces == 1)) ||
               ((rx->n_reactants == 2) && (num_vol_reactants == 1) &&
                (num_surf_reactants == 1)) ||
               ((rx->n_reactants == 3) && (num_vol_reactants == 1) &&
                (num_surf_reactants == 1) && (num_surfaces == 1))) {
      /* this is a reaction between "vol_mol" and "surf_mol" */
      /* with an optional SURFACE                            */
      /* or reaction between "vol_mol" and SURFACE           */
      if (max_num_surf_products > 0)
        *create_shared_walls_info_flag = 1;
      if (((rx->n_reactants == 2) && (num_vol_reactants == 1) &&
           (num_surfaces == 1))) {
        /* do not take into acccount SPECIAL reactions */
        if (rx->n_pathways > RX_SPECIAL) {
          rxn_flags->vol_wall_reaction_flag = 1;
        }
      } else {
        rxn_flags->vol_surf_reaction_flag = 1;
      }

      double D_tot = 0.0;
      double t_step = 0.0;
      if ((rx->players[0]->flags & NOT_FREE) == 0) {
        D_tot = rx->players[0]->D;
        t_step = rx->players[0]->time_step * time_unit;
      } else if ((rx->players[1]->flags & NOT_FREE) == 0) {
        D_tot = rx->players[1]->D;
        t_step = rx->players[1]->time_step * time_unit;
      } else {
        /* Should never happen. */
        D_tot = 1.0;
        t_step = 1.0;
      }

      if (D_tot <= 0.0)
        pb_factor = 0; /* Reaction can't happen! */
      else
        pb_factor = 1.0e11 * grid_density / (2.0 * N_AV) *
                    sqrt(MY_PI * t_step / D_tot);

      if ((rx->geometries[0] + rx->geometries[1]) *
                  (rx->geometries[0] - rx->geometries[1]) ==
              0 &&
          rx->geometries[0] * rx->geometries[1] != 0) {
        pb_factor *= 2.0;
      }
    } /* end else */
  } else if ((rx->n_reactants == 2) && (num_vol_reactants == 2)) {
    /* This is the reaction between two "vol_mols" */
    rxn_flags->vol_vol_reaction_flag = 1;

    double eff_vel_a = rx->players[0]->space_step / rx->players[0]->time_step;
    double eff_vel_b = rx->players[1]->space_step / rx->players[1]->time_step;
    double eff_vel;

    if (rx->is_complex) {
      if (rx->is_complex[0])
        eff_vel_a = 0;
      if (rx->is_complex[1])
        eff_vel_b = 0;
    }

    if (rx->players[0]->flags & rx->players[1]->flags & CANT_INITIATE)
      mcell_error(
          "Reaction between %s and %s listed, but both are marked TARGET_ONLY.",
          rx->players[0]->sym->name, rx->players[1]->sym->name);
    else if (rx->players[0]->flags & CANT_INITIATE)
      eff_vel_a = 0;
    else if (rx->players[1]->flags & CANT_INITIATE)
      eff_vel_b = 0;

    if (eff_vel_a + eff_vel_b > 0) {
      eff_vel = (eff_vel_a + eff_vel_b) * length_unit /
                time_unit; /* Units=um/sec */
      pb_factor = 1.0 / (2.0 * sqrt(MY_PI) * rx_radius_3d *
                         rx_radius_3d * eff_vel);
      pb_factor *= 1.0e15 / N_AV; /* Convert L/mol.s to um^3/number.s */
    } else
      pb_factor = 0.0; /* No rxn possible */
  } else if ((rx->n_reactants == 3) && (num_vol_reactants == 3)) {
    /* This is the reaction between three "vol_mols" */
    rxn_flags->vol_vol_vol_reaction_flag = 1;

    double eff_dif_a, eff_dif_b, eff_dif_c,
        eff_dif; /* effective diffusion constants*/
    eff_dif_a = rx->players[0]->D;
    eff_dif_b = rx->players[1]->D;
    eff_dif_c = rx->players[2]->D;

    if (rx->players[0]->flags & rx->players[1]->flags & rx->players[2]->flags &
        CANT_INITIATE)
      mcell_error("Reaction between %s and %s and %s listed, but all marked "
                  "TARGET_ONLY.",
                  rx->players[0]->sym->name, rx->players[1]->sym->name,
                  rx->players[2]->sym->name);
    if (rx->players[0]->flags & CANT_INITIATE)
      eff_dif_a = 0;
    if (rx->players[1]->flags & CANT_INITIATE)
      eff_dif_b = 0;
    if (rx->players[2]->flags & CANT_INITIATE)
      eff_dif_c = 0;

    if (eff_dif_a + eff_dif_b + eff_dif_c > 0) {
      eff_dif = (eff_dif_a + eff_dif_b + eff_dif_c) *
                1.0e8; /* convert from cm^2/sec to um^2/sec */

      pb_factor =
          1.0 / (6.0 * (MY_PI) * rx_radius_3d * rx_radius_3d *
                 (MY_PI) * rx_radius_3d * rx_radius_3d * eff_dif);
      pb_factor *=
          1.0e30 / (N_AV * N_AV); /* Convert (L/mol)^2/s to (um^3/number)^2/s */
    } else
      pb_factor = 0.0; /* No rxn possible */

  } else if ((rx->n_reactants == 3) && (num_vol_reactants == 2) &&
             (num_surf_reactants == 1)) {
    /* This is a reaction between 2 volume_molecules and */
    /* one surface_molecule                              */
    rxn_flags->vol_vol_surf_reaction_flag = 1;
    if (max_num_surf_products > 0)
      *create_shared_walls_info_flag = 1;

    /* find out what reactants are volume_molecules */
    /* and what is surface_molecule                 */
    struct species *vol_reactant1 = NULL, *vol_reactant2 = NULL;
    struct species *surf_reactant = NULL;
    /* geometries of the reactants */
    int vol_react1_geom = 0, vol_react2_geom = 0, surf_react_geom = 0;
    if ((rx->players[0]->flags & NOT_FREE) == 0) {
      vol_reactant1 = rx->players[0];
      vol_react1_geom = rx->geometries[0];
      if ((rx->players[1]->flags & NOT_FREE) == 0) {
        vol_reactant2 = rx->players[1];
        vol_react2_geom = rx->geometries[1];
        surf_reactant = rx->players[2];
        surf_react_geom = rx->geometries[2];
      } else if ((rx->players[2]->flags & NOT_FREE) == 0) {
        vol_reactant2 = rx->players[2];
        vol_react2_geom = rx->geometries[2];
        surf_reactant = rx->players[1];
        surf_react_geom = rx->geometries[1];
      }
    } else if ((rx->players[1]->flags & NOT_FREE) == 0) {
      vol_reactant1 = rx->players[1];
      vol_react1_geom = rx->geometries[1];
      if ((rx->players[0]->flags & NOT_FREE) == 0) {
        vol_reactant2 = rx->players[0];
        vol_react2_geom = rx->geometries[0];
        surf_reactant = rx->players[2];
        surf_react_geom = rx->geometries[2];
      } else if ((rx->players[2]->flags & NOT_FREE) == 0) {
        vol_reactant2 = rx->players[2];
        vol_react2_geom = rx->geometries[2];
        surf_reactant = rx->players[0];
        surf_react_geom = rx->geometries[0];
      }
    }

    /* volume reactants should be aligned - it means that
      they should be in the same orientation class and have
      the same sign */

    if (vol_react1_geom != vol_react2_geom)
      mcell_error("In 3-way reaction %s + %s + %s ---> [...] volume reactants "
                  "%s and %s are either in different orientation classes or "
                  "have opposite orientation sign.",
                  rx->players[0]->sym->name, rx->players[1]->sym->name,
                  rx->players[2]->sym->name, vol_reactant1->sym->name,
                  vol_reactant2->sym->name);

    double eff_dif_1, eff_dif_2, eff_dif; /* effective diffusion constants*/
    eff_dif_1 = vol_reactant1->D;
    eff_dif_2 = vol_reactant2->D;

    if (vol_reactant1->flags & vol_reactant2->flags & surf_reactant->flags &
        CANT_INITIATE) {
      mcell_error("Reaction between %s and %s and %s listed, but all marked "
                  "TARGET_ONLY.",
                  vol_reactant1->sym->name, vol_reactant2->sym->name,
                  surf_reactant->sym->name);
    } else if (vol_reactant1->flags & vol_reactant2->flags & CANT_INITIATE) {
      mcell_error("Reaction between %s and %s and %s listed, but both volume "
                  "molecules %s and %s marked TARGET_ONLY.",
                  vol_reactant1->sym->name, vol_reactant2->sym->name,
                  surf_reactant->sym->name, vol_reactant1->sym->name,
                  vol_reactant2->sym->name);
    } else {
      if (vol_reactant1->flags & CANT_INITIATE)
        eff_dif_1 = 0;
      if (vol_reactant2->flags & CANT_INITIATE)
        eff_dif_2 = 0;
    }

    if ((eff_dif_1 + eff_dif_2) > 0) {
      eff_dif = (eff_dif_1 + eff_dif_2) *
                1.0e8; /* convert from cm^2/sec to um^2/sec */

      pb_factor =
          2.0 * grid_density /
          (3.0 * (MY_PI) * rx_radius_3d * rx_radius_3d * eff_dif);
      pb_factor *=
          1.0e30 / (N_AV * N_AV); /* Convert (L/mol)^2/s to (um^3/number)^2/s */
    } else
      pb_factor = 0.0; /* No rxn possible */

    /* The value of pb_factor above is calculated for the case
        when surface_molecule can be hit from either side Otherwise the
        reaction_rate should be doubled. So we check whether both of the
        volume_molecules are in the same orientation class as
        surface_molecule.
    */

    /* flags that show whether volume reactants are in the same
        orientation classes as surface reactant */
    int vol_react1_and_surf_react = 0, vol_react2_and_surf_react = 0;
    if ((vol_react1_geom + surf_react_geom) *
                (vol_react1_geom - surf_react_geom) ==
            0 &&
        vol_react1_geom * surf_react_geom != 0) {
      vol_react1_and_surf_react = 1;
    }
    if ((vol_react2_geom + surf_react_geom) *
                (vol_react2_geom - surf_react_geom) ==
            0 &&
        vol_react2_geom * surf_react_geom != 0) {
      vol_react2_and_surf_react = 1;
    }

    if (vol_react1_and_surf_react && vol_react2_and_surf_react) {
      pb_factor *= 2.0;
    }
  } else if ((rx->n_reactants == 3) && (num_vol_reactants == 1) &&
             (num_surf_reactants == 2)) {
    /* one volume reactant and two surface reactants */
    rxn_flags->vol_surf_surf_reaction_flag = 1;
    *create_shared_walls_info_flag = 1;

    /* find out what reactants are volume_molecules
      and what reactant is a surface_molecule */
    struct species *surf_reactant1 = NULL, *surf_reactant2 = NULL;
    struct species *vol_reactant = NULL;
    /* geometries of the reactants */
    int vol_react_geom = 0, surf_react1_geom = 0, surf_react2_geom = 0;
    if ((rx->players[0]->flags & NOT_FREE) == 0) {
      vol_reactant = rx->players[0];
      vol_react_geom = rx->geometries[0];
      surf_reactant1 = rx->players[1];
      surf_react1_geom = rx->geometries[1];
      surf_reactant2 = rx->players[2];
      surf_react2_geom = rx->geometries[2];
    } else if ((rx->players[1]->flags & NOT_FREE) == 0) {
      vol_reactant = rx->players[1];
      vol_react_geom = rx->geometries[1];
      surf_reactant1 = rx->players[0];
      surf_react1_geom = rx->geometries[0];
      surf_reactant2 = rx->players[2];
      surf_react2_geom = rx->geometries[2];
    } else if ((rx->players[2]->flags & NOT_FREE) == 0) {
      vol_reactant = rx->players[2];
      vol_react_geom = rx->geometries[2];
      surf_reactant1 = rx->players[0];
      surf_react1_geom = rx->geometries[0];
      surf_reactant2 = rx->players[1];
      surf_react2_geom = rx->geometries[1];
    }

    if (vol_reactant->flags & CANT_INITIATE)
      mcell_error("3-way reaction between %s and %s and %s listed, but the "
                  "only volume reactant %s is marked TARGET_ONLY",
                  vol_reactant->sym->name, surf_reactant1->sym->name,
                  surf_reactant2->sym->name, vol_reactant->sym->name);

    double eff_vel = vol_reactant->space_step / vol_reactant->time_step;

    if (eff_vel > 0) {
      eff_vel =
          eff_vel * length_unit / time_unit; /* Units=um/sec */
      pb_factor = (sqrt(MY_PI) * grid_density * grid_density) /
                  (6.0 * eff_vel);

      /* NOTE: the reaction rate should be in units of
        volume * area * #^(-2) * s^(-1) that means
        (um)^5 * #^(-2) * s^(-1),
        otherwise if the reaction rate is in
        (um^2/(M*#*s) units conversion is necessary
        */
    } else
      pb_factor = 0.0; /* No rxn possible */

    /* The value of pb_factor above is calculated for the case
        when surface_molecule can be hit from either side
        Otherwise the reaction_rate should be doubled.
        So we check whether the volume_molecule
        is in the same orientation class as surface_molecules.
    */

    /* flags that show whether volume reactant is in the same
        orientation class as surface reactants */
    int vol_react_and_surf_react1 = 0, vol_react_and_surf_react2 = 0;
    if ((vol_react_geom + surf_react1_geom) *
                (vol_react_geom - surf_react1_geom) ==
            0 &&
        vol_react_geom * surf_react1_geom != 0) {
      vol_react_and_surf_react1 = 1;
    }
    if ((vol_react_geom + surf_react2_geom) *
                (vol_react_geom - surf_react2_geom) ==
            0 &&
        vol_react_geom * surf_react2_geom != 0) {
      vol_react_and_surf_react2 = 1;
    }

    if (vol_react_and_surf_react1 && vol_react_and_surf_react2) {
      pb_factor *= 2.0;
    }
  } else if ((rx->n_reactants == 3) && (num_surf_reactants == 3)) {

    rxn_flags->surf_surf_surf_reaction_flag = 1;
    *create_shared_walls_info_flag = 1;
    int num_active_reactants = 0;

    for (int i = 0; i < 3; i++) {
      if (rx->players[i]->flags & CANT_INITIATE)
        continue;
      else
        num_active_reactants++;
    }

    /* Calculation of pb_factor below should
    account for possible number of outcomes with TARGET_ONLY
    specification.  E.g. when mols A,B,C are all active there are 6
    possible combinations = number of permutations out of 3.  When e.g. C
    mol is TARGET_ONLY there are only 4 combinations (ABC,ACB,BCA,BAC).
    When both B and C mols are TARGET_ONLY there are two possible
    combinations - (ABC, ACB). */

    if (num_active_reactants == 0) {
      mcell_error("Reaction between %s and %s and %s listed, but all marked "
                  "TARGET_ONLY.",
                  rx->players[0]->sym->name, rx->players[1]->sym->name,
                  rx->players[2]->sym->name);

    } else if (num_active_reactants == 3) {
      /* basic case */
      pb_factor =
          (grid_density * grid_density * time_unit) / 6.0;
    } else if (num_active_reactants == 2) {
      pb_factor =
          (grid_density * grid_density * time_unit) / 4.0;
    } else if (num_active_reactants == 1) {
      pb_factor =
          (grid_density * grid_density * time_unit) / 2.0;
    }

    /* NOTE: the reaction rate should be in units of
          (um)^4 * #^(-2) * s^(-1),
          otherwise the units conversion is necessary
    */
  }

  return pb_factor;
}