AddCoupledSolidKinSpeciesAction::AddCoupledSolidKinSpeciesAction(const InputParameters & params)
  : Action(params),
    _primary_species(getParam<std::vector<NonlinearVariableName>>("primary_species")),
    _secondary_species(getParam<std::vector<AuxVariableName>>("secondary_species")),
    _kinetic_species_involved(_primary_species.size()),
    _weights(_primary_species.size()),
    _input_reactions(getParam<std::string>("kin_reactions")),
    _logk(getParam<std::vector<Real>>("log10_keq")),
    _r_area(getParam<std::vector<Real>>("specific_reactive_surface_area")),
    _ref_kconst(getParam<std::vector<Real>>("kinetic_rate_constant")),
    _e_act(getParam<std::vector<Real>>("activation_energy")),
    _gas_const(getParam<Real>("gas_constant")),
    _ref_temp(getParam<std::vector<Real>>("reference_temperature")),
    _sys_temp(getParam<std::vector<VariableName>>("system_temperature"))
{
  // Note: as the reaction syntax has changed, check to see if the old syntax has
  // been used and throw an informative error. The number of = signs should be one
  // more than the number of commas, while the smallest number of spaces possible is 2
  bool old_syntax = false;
  if (std::count(_input_reactions.begin(), _input_reactions.end(), '=') !=
      std::count(_input_reactions.begin(), _input_reactions.end(), ',') + 1)
    old_syntax = true;

  if (std::count(_input_reactions.begin(), _input_reactions.end(), ' ') < 2)
    old_syntax = true;

  if (old_syntax)
    mooseError("Old solid kinetic reaction syntax present.\nReactions should now be comma "
               "separated, and must have spaces between species and +/-/= operators.\n"
               "See #9972 for details");

  // Parse the kinetic reactions
  pcrecpp::RE re_reactions("(.+?)" // A single reaction (any character until the comma delimiter)
                           "(?:,\\s*|$)" // comma or end of string
                           ,
                           pcrecpp::RE_Options().set_extended(true));

  pcrecpp::RE re_terms("(\\S+)");
  pcrecpp::RE re_coeff_and_species("(?: \\(? (.*?) \\)? )" // match the leading coefficent
                                   "([A-Za-z].*)"          // match the species
                                   ,
                                   pcrecpp::RE_Options().set_extended(true));

  pcrecpp::StringPiece input(_input_reactions);
  pcrecpp::StringPiece single_reaction, term;
  std::string single_reaction_str;

  // Parse reaction network to extract each individual reaction
  while (re_reactions.FindAndConsume(&input, &single_reaction_str))
    _reactions.push_back(single_reaction_str);

  _num_reactions = _reactions.size();

  if (_num_reactions == 0)
    mooseError("No solid kinetic reaction provided!");

  // Start parsing each reaction
  for (unsigned int i = 0; i < _num_reactions; ++i)
  {
    single_reaction = _reactions[i];

    // Capture all of the terms
    std::string species, coeff_str;
    Real coeff;
    int sign = 1;
    bool secondary = false;

    std::vector<Real> local_stos;
    std::vector<VariableName> local_species_list;

    // Find every single term in this reaction (species and operators)
    while (re_terms.FindAndConsume(&single_reaction, &term))
    {
      // Separating the stoichiometric coefficients from species
      if (re_coeff_and_species.PartialMatch(term, &coeff_str, &species))
      {
        if (coeff_str.length())
          coeff = std::stod(coeff_str);
        else
          coeff = 1.0;

        coeff *= sign;

        if (secondary)
          _solid_kinetic_species.push_back(species);
        else
        {
          local_stos.push_back(coeff);
          local_species_list.push_back(species);
        }
      }
      // Finding the operators and assign value of -1.0 to "-" sign
      else if (term == "+" || term == "=" || term == "-")
      {
        if (term == "-")
        {
          sign = -1;
          term = "+";
        }

        if (term == "=")
          secondary = true;
      }
      else
        mooseError("Error parsing term: ", term.as_string());
    }

    _stos.push_back(local_stos);
    _primary_species_involved.push_back(local_species_list);
  }

  // Start picking out primary species and coupled primary species and assigning
  // corresponding stoichiomentric coefficients
  for (unsigned int i = 0; i < _primary_species.size(); ++i)
    for (unsigned int j = 0; j < _num_reactions; ++j)
    {
      for (unsigned int k = 0; k < _primary_species_involved[j].size(); ++k)
        if (_primary_species_involved[j][k] == _primary_species[i])
        {
          _weights[i].push_back(_stos[j][k]);
          _kinetic_species_involved[i].push_back(_solid_kinetic_species[j]);
        }
    }

  // Print out details of the solid kinetic reactions to the console
  _console << "Solid kinetic reactions:\n";
  for (unsigned int i = 0; i < _num_reactions; ++i)
    _console << "  Reaction " << i + 1 << ": " << _reactions[i] << "\n";
  _console << "\n";

  // Check that all secondary species read from the reaction network have been added
  // as AuxVariables. Note: can't sort the _solid_kinetic_species vector as it throws
  // out the species and coefficient vectors so use std::is_permutation
  if (!std::is_permutation(
          _secondary_species.begin(), _secondary_species.end(), _solid_kinetic_species.begin()))
    mooseError("All solid kinetic species must be added as secondary species");

  // Check that the size of property vectors is equal to the number of reactions
  if (_logk.size() != _num_reactions)
    mooseError("The number of values entered for log10_keq is not equal to the number of solid "
               "kinetic reactions");
  if (_r_area.size() != _num_reactions)
    mooseError("The number of values entered for specific_reactive_surface_area is not equal to "
               "the number of solid kinetic reactions");
  if (_ref_kconst.size() != _num_reactions)
    mooseError("The number of values entered for kinetic_rate_constant is not equal to the number "
               "of solid kinetic reactions");
  if (_e_act.size() != _num_reactions)
    mooseError("The number of values entered for activation_energy is not equal to the number of "
               "solid kinetic reactions");
  if (_ref_temp.size() != _num_reactions)
    mooseError("The number of values entered for reference_temperature is not equal to the number "
               "of solid kinetic reactions");
}
void
AddCoupledEqSpeciesKernelsAction::act()
{
  // Reading primary species and reaction network from the input file
  std::vector<NonlinearVariableName> vars =
      getParam<std::vector<NonlinearVariableName>>("primary_species");
  std::string reactions = getParam<std::string>("reactions");

  // Getting ready for the parsing system
  pcrecpp::RE re_reactions(
      "(.*?)" // the reaction network (any character until the equilibrium coefficient appears)
      "\\s"   // word boundary
      "("     // start capture
      "-?"    // optional minus sign
      "\\d+(?:\\.\\d*)?" // digits followed by optional decimal and more 0 or more digits
      ")"
      "\\b"        // word boundary
      "(?:\\s+|$)" // eat the whitespace
      ,
      pcrecpp::RE_Options().set_extended(true));

  pcrecpp::RE re_terms("(\\S+)");
  pcrecpp::RE re_coeff_and_species("(?: \\(? (.*?) \\)? )" // match the leading coefficent
                                   "([A-Za-z].*)"          // match the species
                                   ,
                                   pcrecpp::RE_Options().set_extended(true));

  pcrecpp::StringPiece input(reactions);

  pcrecpp::StringPiece single_reaction, term;
  Real equal_coeff;

  std::vector<std::vector<bool>> primary_participation(vars.size());
  std::vector<string> eq_species;
  std::vector<Real> weight;
  std::vector<Real> eq_const;
  std::vector<std::vector<Real>> sto_u(vars.size());
  std::vector<std::vector<std::vector<Real>>> sto_v(vars.size());
  std::vector<std::vector<std::vector<VariableName>>> coupled_v(vars.size());

  std::vector<std::vector<Real>> stos;
  std::vector<std::vector<std::string>> primary_species_involved;

  unsigned int n_reactions = 0;

  // Start parsing
  // Going into every single reaction
  std::ostringstream oss;

  while (re_reactions.FindAndConsume(&input, &single_reaction, &equal_coeff))
  {
    n_reactions += 1;
    oss << "\n\n" << n_reactions << "_th reaction: " << single_reaction << std::endl;

    eq_const.push_back(equal_coeff);
    oss << "\nEquilibrium: " << eq_const[n_reactions - 1] << std::endl;

    // capture all of the terms
    std::string species, coeff_str;
    Real coeff;
    int sign = 1;
    bool secondary = false;

    std::vector<Real> local_stos;
    std::vector<std::string> local_species_list;

    // Going to find every single term in this reaction, sto_species combos and operators
    while (re_terms.FindAndConsume(&single_reaction, &term))
    {
      // Separating the sto from species
      if (re_coeff_and_species.PartialMatch(term, &coeff_str, &species))
      {
        if (coeff_str.length())
        {
          std::istringstream iss(coeff_str);
          iss >> coeff;
        }
        else
          coeff = 1.0;

        coeff *= sign;

        if (secondary)
          eq_species.push_back(species);
        else
        {
          local_stos.push_back(coeff);
          local_species_list.push_back(species);
          oss << "\nSpecies: " << species << "\n"
              << "Coeff: " << coeff << std::endl;
        }
      }
      // Finding the operators and assign value of -1.0 to "-" sign
      else if (term == "+" || term == "=" || term == "-")