RooRealVar* LoadParameters(string filename, string label, string parname )
{

  RooRealVar *newVar = 0;

  // now read in the parameters from the file.  Stored in the file as                                                                                                  
  // parameter manager   |   name   | initial val  | min  | max  | step                                                                                                
  fstream parameter_file(filename.c_str(), ios::in);
  if (! parameter_file) {
    cout << "Error:  Couldn't open parameters file " << filename << endl;
    return false;
  }
  
  string name;
  string category;
  double initial_val, min, max, step;
  
  char c;
  Bool_t foundPar = kFALSE;
  while (true) {
    
    // skip white spaces and look for a #                                                                                                                              
    while(parameter_file.get(c)) {
      if      (isspace(c) || c == '\n')
        continue;
      else if (c == '#')
        while (c != '\n') parameter_file.get(c);
      else {
        parameter_file.putback(c);
        break;
      }
    }
    if (parameter_file.fail())
      break;
    
    parameter_file >> category >> name >> initial_val >> min >> max >> step;
    if (parameter_file.fail())
      break;
    
    if (category == label && parname == name) {
      // create a new fit parameter         
      newVar = new RooRealVar(name.c_str(),name.c_str(),initial_val,min, max);
      if (step == 0) {
        newVar->setConstant(kTRUE);  
      }
      
      break;
    }
  } //end while loop

  if (!newVar) {
    cout << "Could not load parameter " << parname << " from file " << filename << " , category " << label << endl;
    assert(newVar);
  }

  return newVar;
}
std::vector<std::string> read_parameters(std::string filename) {
    std::vector<std::string> inputs;
    std::string line;
    int linenum = 0;

// Read input file with defined solver parameters
// First - CFL, second - tmax, third - input file name (*.bkcfd), fourth - output file name (*.txt), fifth - debug mode boolean

    std::ifstream parameter_file(filename);
    if (parameter_file.is_open()) {
        while (getline (parameter_file,line)) {
            if (line[0] != '%') {
                inputs.push_back(line);
                linenum++;
            }
        }
        parameter_file.close();
    }
    else {
        std::cout << "Cannot Open File" << '\n';
    }
    return inputs;
}
int main(int argc, char** argv) {

  if (argc != 2)
    return Usage(std::string(argv[0]));

  // Initialize logging.
  const bool log_to_file = false;

  if (log_to_file) {

    const int nb_rolling_logfiles = 2;
    const int max_logfile_size = 1000000;
    plog::init(plog::verbose, "wave_propagator.log", max_logfile_size, nb_rolling_logfiles);

  } else {

    static plog::ColorConsoleAppender<plog::TxtFormatter> console_appender;
    plog::init(plog::verbose, &console_appender);

  }

  LOG_INFO << "**** 2D/3D wave propagator in heterogeneous media ****\n";

  char const* const parameter_filename_c = argv[1];
  const std::string parameter_filename = std::string(parameter_filename_c);

  RectilinearGrid3D propagation_grid;
  MultiDimensionalStorage4D variable_storage;
  MathematicalParser math_parser;
  std::vector<OutputEvent> output_events;
  TimeloopManager timeloop_manager;
  WaveSolverOptions wave_solver_options = WaveSolverOptions();;

  LOG_INFO << "Reading parameter file \"" << parameter_filename << "\"...";

  std::ifstream parameter_file(parameter_filename.c_str(), std::ifstream::in);
  const int nx_padding = 10;

  ParseParameterFile(nx_padding, &parameter_file, &propagation_grid, 
                     &variable_storage, &math_parser, &output_events,
                     &timeloop_manager, &wave_solver_options);

  parameter_file.close();

  math_parser.PrintConstants();

  const int nb_iter = timeloop_manager.nb_iter();

  // Variables information (hardcoded for now, in variable_definitions.hpp).
  const int nb_variables = variable::NB_VARIABLES;
  
  std::stringstream var_name_msg;

  var_name_msg << "List of variables: [";
  for (int ivar = 0; ivar < nb_variables; ++ivar)
    var_name_msg << " " << variable::VARIABLE_NAMES[ivar];
  var_name_msg << " ]\n";

  LOG_DEBUG << var_name_msg.str();

  const RealT dx = propagation_grid.dx_fast();
  const RealT dy = propagation_grid.dx_medium();
  const RealT dz = propagation_grid.dx_slow();

  RealT* pressure_0 = variable_storage.RawDataSlowDimension(variable::PRESSURE_0);
  RealT* pressure_1 = variable_storage.RawDataSlowDimension(variable::PRESSURE_1);
  RealT* laplace_p = variable_storage.RawDataSlowDimension(variable::LAPLACE_PRESSURE);
  RealT* velocity = variable_storage.RawDataSlowDimension(variable::VELOCITY);

  // Init output events.
  for (auto it = output_events.begin(); it != output_events.end(); ++it)
    it->Create(nb_iter, variable_storage, propagation_grid);

  RealT* sponge_fast = VectorRawData<RealT>(wave_solver_options.sponge_fast());
  RealT* sponge_medium = VectorRawData<RealT>(wave_solver_options.sponge_medium());
  RealT* sponge_slow = VectorRawData<RealT>(wave_solver_options.sponge_slow());

  std::ofstream sponge_fast_out("sponge_fast.txt", std::ofstream::out);
  std::ofstream sponge_medium_out("sponge_medium.txt", std::ofstream::out);
  std::ofstream sponge_slow_out("sponge_slow.txt", std::ofstream::out);

  wave_solver_options.DumpAllSponges(&sponge_fast_out,
                                     &sponge_medium_out,
                                     &sponge_slow_out);

  sponge_fast_out.close();
  sponge_medium_out.close();
  sponge_slow_out.close();

  const RealT dt = timeloop_manager.dt();
  RealT t = 0.0;

  // Main time loop. All numerics (except time step computation) is in
  // here.
  for (int iter = 0; iter < timeloop_manager.nb_iter(); ++iter) {

    t += dt;
    math_parser.AddConstant("t", t);

    const int stencil_radius = 1;

    const int n_fast = variable_storage.dimensions().at(0);
    const int n_fast_padding = variable_storage.padding_fast();
    const int n_medium = variable_storage.dimensions().at(1);
    const int n_slow = variable_storage.dimensions().at(2);

    timer_start = Timer::Now();

    // Apply sponge.
    ApplySpongeLayer_0(n_fast, n_fast_padding, n_medium, n_slow,
                       sponge_fast, sponge_medium, sponge_slow, 
                       pressure_1);

    ComputeLaplacian_0(n_fast, n_fast_padding, n_medium, n_slow,
                       stencil_radius, dx, dy, dz, 
                       pressure_0, laplace_p);
     
    // Advance pressure.
    AdvanceWavePressure_0(n_fast, n_fast_padding, n_medium, n_slow, 
                          stencil_radius, dt,
                          velocity, laplace_p, pressure_0, pressure_1);

    // Apply sponge.
    ApplySpongeLayer_0(n_fast, n_fast_padding, n_medium, n_slow,
                       sponge_fast, sponge_medium, sponge_slow, 
                       pressure_1);

    timer_stop = Timer::Now();
    
    timings_numerics.push_back(Timer::DiffTime(timer_start, timer_stop));

    for (auto it = output_events.begin(); it != output_events.end(); ++it) {

      const bool event_happens = 
        ((iter % it->rhythm() == 0) && (it->rhythm() >= 0)) ||
        ((iter == timeloop_manager.nb_iter() - 1) && (it->rhythm() == - 1));

      if (event_happens) {

        it->Execute(iter, propagation_grid, &math_parser, &variable_storage);

      }
    }

    // Exchange pressure arrays.
    std::swap(pressure_0, pressure_1);

  }

  // Variables cleanup.
  variable_storage.DeAllocate();

  for (auto it = output_events.begin(); it != output_events.end(); ++it)
    it->Destroy();

  const RealT minimum_time = 
    *std::min_element(timings_numerics.begin(), timings_numerics.end());
  assert(0.0 < minimum_time);

  const size_t nb_grid_points = 
    propagation_grid.n_fast() * propagation_grid.n_medium() * propagation_grid.n_slow();

  LOG_INFO << "****************************************";
  std::cout << "\n";
  Timer::PrintTimings(timings_numerics, "            Wave propagation", &std::cout);
  std::cout << "            Wave propagation : " 
            << std::scientific << (RealT)nb_grid_points / (minimum_time * 1.0e-3)
            << " grid updates per second";
  std::cout << "\n";
  std::cout << "\n";
  LOG_INFO << "****************************************";

  return 0;
}