Skip to content
Snippets Groups Projects
Select Git revision
  • ha_mcp
  • master default protected
  • pac_composite_target_mce
  • ramsey_k_order
  • 4.6
  • occbin
  • uop
  • rework_pac
  • aux_vars_fix
  • created_preprocessor_repo
10 results

ConfigFile.cc

Blame
  • Forked from Dynare / preprocessor
    807 commits behind the upstream repository.
    ConfigFile.cc 28.61 KiB
    /*
     * Copyright © 2010-2021 Dynare Team
     *
     * This file is part of Dynare.
     *
     * Dynare is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * Dynare is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with Dynare.  If not, see <https://www.gnu.org/licenses/>.
     */
    
    #include <iostream>
    #include <fstream>
    #include <utility>
    #include <vector>
    #include <filesystem>
    
    #include "ConfigFile.hh"
    
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #include <boost/algorithm/string/trim.hpp>
    #include <boost/algorithm/string/split.hpp>
    #include <boost/tokenizer.hpp>
    #pragma GCC diagnostic pop
    
    using namespace std;
    
    Hook::Hook(string global_init_file_arg)
    {
      if (global_init_file_arg.empty())
        {
          cerr << "ERROR: The Hook must have a Global Initialization File argument." << endl;
          exit(EXIT_FAILURE);
        }
      hooks["global_init_file"] = move(global_init_file_arg);
    }
    
    Path::Path(vector<string> includepath_arg)
    {
      if (includepath_arg.empty())
        {
          cerr << "ERROR: The Path must have an Include argument." << endl;
          exit(EXIT_FAILURE);
        }
      paths["include"] = move(includepath_arg);
    }
    
    SlaveNode::SlaveNode(string computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg, string userName_arg,
                         string password_arg, string remoteDrive_arg, string remoteDirectory_arg,
                         string programPath_arg, string programConfig_arg, string matlabOctavePath_arg, bool singleCompThread_arg,
                         int numberOfThreadsPerJob_arg, string operatingSystem_arg) :
      computerName{move(computerName_arg)},
      port{move(port_arg)},
      minCpuNbr{minCpuNbr_arg},
      maxCpuNbr{maxCpuNbr_arg},
      userName{move(userName_arg)},
      password{move(password_arg)},
      remoteDrive{move(remoteDrive_arg)},
      remoteDirectory{move(remoteDirectory_arg)},
      programPath{move(programPath_arg)},
      programConfig{move(programConfig_arg)},
      matlabOctavePath{move(matlabOctavePath_arg)},
      singleCompThread{singleCompThread_arg},
      numberOfThreadsPerJob{numberOfThreadsPerJob_arg},
      operatingSystem{move(operatingSystem_arg)}
    {
      if (computerName.empty())
        {
          cerr << "ERROR: The node must have a ComputerName." << endl;
          exit(EXIT_FAILURE);
        }
    
      if (!operatingSystem.empty())
        if (operatingSystem.compare("windows") != 0 && operatingSystem.compare("unix") != 0)
          {
            cerr << "ERROR: The OperatingSystem must be either 'unix' or 'windows' (Case Sensitive)." << endl;
            exit(EXIT_FAILURE);
          }
    }
    
    Cluster::Cluster(member_nodes_t member_nodes_arg) :
      member_nodes{move(member_nodes_arg)}
    {
      if (member_nodes.empty())
        {
          cerr << "ERROR: The cluster must have at least one member node." << endl;
          exit(EXIT_FAILURE);
        }
    }
    
    ConfigFile::ConfigFile(bool parallel_arg, bool parallel_test_arg,
                           bool parallel_slave_open_mode_arg, bool parallel_use_psexec_arg,
                           string cluster_name_arg) :
      parallel{parallel_arg}, parallel_test{parallel_test_arg},
      parallel_slave_open_mode{parallel_slave_open_mode_arg},
      parallel_use_psexec{parallel_use_psexec_arg},
      cluster_name{move(cluster_name_arg)}
    {
    }
    
    void
    ConfigFile::getConfigFileInfo(const string &config_file)
    {
      using namespace boost;
      ifstream configFile;
    
      if (config_file.empty())
        {
          string defaultConfigFile;
          // Test OS and try to open default file
    #if defined(_WIN32) || defined(__CYGWIN32__)
          if (getenv("APPDATA") == nullptr)
            {
              if (parallel || parallel_test)
                cerr << "ERROR: ";
              else
                cerr << "WARNING: ";
              cerr << "APPDATA environment variable not found." << endl;
    
              if (parallel || parallel_test)
                exit(EXIT_FAILURE);
            }
          else
            {
              defaultConfigFile += getenv("APPDATA");
              defaultConfigFile += "\\dynare.ini";
            }
    #else
          if (getenv("HOME") == nullptr)
            {
              if (parallel || parallel_test)
                cerr << "ERROR: ";
              else
                cerr << "WARNING: ";
              cerr << "HOME environment variable not found." << endl;
              if (parallel || parallel_test)
                exit(EXIT_FAILURE);
            }
          else
            {
              defaultConfigFile += getenv("HOME");
              defaultConfigFile += "/.dynare";
            }
    #endif
          configFile.open(defaultConfigFile, fstream::in);
          if (!configFile.is_open())
            if (parallel || parallel_test)
              {
                cerr << "ERROR: Could not open the default config file (" << defaultConfigFile << ")" << endl;
                exit(EXIT_FAILURE);
              }
            else
              return;
        }
      else
        {
          configFile.open(config_file, fstream::in);
          if (!configFile.is_open())
            {
              cerr << "ERROR: Couldn't open file " << config_file << endl;;
              exit(EXIT_FAILURE);
            }
        }
    
      string name, computerName, port, userName, password, remoteDrive,
        remoteDirectory, programPath, programConfig, matlabOctavePath,
        operatingSystem, global_init_file;
      vector<string> includepath;
      int minCpuNbr{0}, maxCpuNbr{0};
      int numberOfThreadsPerJob{1};
      bool singleCompThread{false};
      member_nodes_t member_nodes;
    
      bool inHooks{false}, inNode{false}, inCluster{false}, inPaths{false};
    
      while (configFile.good())
        {
          string line;
          getline(configFile, line);
          trim(line);
          if (line.empty() || !line.compare(0, 1, "#"))
            continue;
    
          if (!line.compare("[node]")
              || !line.compare("[cluster]")
              || !line.compare("[hooks]")
              || !line.compare("[paths]"))
            {
              if (!global_init_file.empty())
                // we were just in [hooks]
                addHooksConfFileElement(global_init_file);
              else if (!includepath.empty())
                // we were just in [paths]
                addPathsConfFileElement(includepath);
              else
                // we were just in [node] or [cluster]
                addParallelConfFileElement(inNode, inCluster, member_nodes, name,
                                           computerName, port, minCpuNbr, maxCpuNbr, userName,
                                           password, remoteDrive, remoteDirectory,
                                           programPath, programConfig, matlabOctavePath, singleCompThread,
                                           numberOfThreadsPerJob, operatingSystem);
    
              //! Reset communication vars / option defaults
              if (!line.compare("[hooks]"))
                {
                  inHooks = true;
                  inNode = false;
                  inCluster = false;
                  inPaths = false;
                }
              else if (!line.compare("[node]"))
                {
                  inHooks = false;
                  inNode = true;
                  inCluster = false;
                  inPaths = false;
                }
              else if (!line.compare("[paths]"))
                {
                  inHooks = false;
                  inNode = false;
                  inCluster = false;
                  inPaths = true;
                }
              else
                {
                  inHooks = false;
                  inNode = false;
                  inCluster = true;
                  inPaths = false;
                }
    
              name = userName = computerName = port = password = remoteDrive
                = remoteDirectory = programPath = programConfig = matlabOctavePath
                = operatingSystem = global_init_file = "";
              includepath.clear();
              minCpuNbr = maxCpuNbr = 0;
              numberOfThreadsPerJob = 1;
              singleCompThread = false;
              member_nodes.clear();
            }
          else
            {
              vector<string> tokenizedLine;
              split(tokenizedLine, line, is_any_of("="));
              if (tokenizedLine.size() != 2)
                {
                  cerr << "ERROR (in config file): Options should be formatted as 'option = value'." << endl;
                  exit(EXIT_FAILURE);
                }
              trim(tokenizedLine.front());
              trim(tokenizedLine.back());
    
              if (inHooks)
                if (!tokenizedLine.front().compare("GlobalInitFile"))
                  if (global_init_file.empty())
                    global_init_file = tokenizedLine.back();
                  else
                    {
                      cerr << "ERROR: May not have more than one GlobalInitFile option in [hooks] block." << endl;
                      exit(EXIT_FAILURE);
                    }
                else
                  {
                    cerr << "ERROR: Unrecognized option " << tokenizedLine.front() << " in [hooks] block." << endl;
                    exit(EXIT_FAILURE);
                  }
              else if (inPaths)
                if (!tokenizedLine.front().compare("Include"))
                  if (includepath.empty())
                    {
                      vector<string> tokenizedPath;
                      split(tokenizedPath, tokenizedLine.back(), is_any_of(":"), token_compress_on);
                      for (auto &it : tokenizedPath)
                        if (!it.empty())
                          {
                            trim(it);
                            includepath.push_back(it);
                          }
                    }
                  else
                    {
                      cerr << "ERROR: May not have more than one Include option in [paths] block." << endl;
                      exit(EXIT_FAILURE);
                    }
                else
                  {
                    cerr << "ERROR: Unrecognized option " << tokenizedLine.front() << " in [paths] block." << endl;
                    exit(EXIT_FAILURE);
                  }
              else
                if (!tokenizedLine.front().compare("Name"))
                  name = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("CPUnbr"))
                  {
                    vector<string> tokenizedCpuNbr;
                    split(tokenizedCpuNbr, tokenizedLine.back(), is_any_of(":"));
                    try
                      {
                        if (tokenizedCpuNbr.size() == 1)
                          {
                            minCpuNbr = 1;
                            maxCpuNbr = stoi(tokenizedCpuNbr.front());
                          }
                        else if (tokenizedCpuNbr.size() == 2
                                 && tokenizedCpuNbr[0].at(0) == '['
                                 && tokenizedCpuNbr[1].at(tokenizedCpuNbr[1].size()-1) == ']')
                          {
                            tokenizedCpuNbr[0].erase(0, 1);
                            tokenizedCpuNbr[1].erase(tokenizedCpuNbr[1].size()-1, 1);
                            minCpuNbr = stoi(tokenizedCpuNbr[0]);
                            maxCpuNbr = stoi(tokenizedCpuNbr[1]);
                          }
                      }
                    catch (const invalid_argument &)
                      {
                        cerr << "ERROR: Could not convert value to integer for CPUnbr." << endl;
                        exit(EXIT_FAILURE);
                      }
    
                    if (minCpuNbr <= 0 || maxCpuNbr <= 0)
                      {
                        cerr << "ERROR: Syntax for the CPUnbr option is as follows:" << endl
                             << "       1) CPUnbr = <int>" << endl
                             << "    or 2) CPUnbr = [<int>:<int>]" << endl
                             << "       where <int> is an Integer > 0." << endl;
                        exit(EXIT_FAILURE);
                      }
    
                    minCpuNbr--;
                    maxCpuNbr--;
                    if (minCpuNbr > maxCpuNbr)
                      {
                        int tmp = maxCpuNbr;
                        maxCpuNbr = minCpuNbr;
                        minCpuNbr = tmp;
                      }
                  }
                else if (!tokenizedLine.front().compare("Port"))
                  port = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("ComputerName"))
                  computerName = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("UserName"))
                  userName = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("Password"))
                  password = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("RemoteDrive"))
                  remoteDrive = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("RemoteDirectory"))
                  remoteDirectory = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("DynarePath")
                         || !tokenizedLine.front().compare("ProgramPath"))
                  programPath = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("ProgramConfig"))
                  programConfig = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("MatlabOctavePath"))
                  matlabOctavePath = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("NumberOfThreadsPerJob"))
                  numberOfThreadsPerJob = stoi(tokenizedLine.back());
                else if (!tokenizedLine.front().compare("SingleCompThread"))
                  if (tokenizedLine.back().compare("true") == 0)
                    singleCompThread = true;
                  else if (tokenizedLine.back().compare("false") == 0)
                    singleCompThread = false;
                  else
                    {
                      cerr << "ERROR (in config file): The value passed to SingleCompThread may only be 'true' or 'false'." << endl;
                      exit(EXIT_FAILURE);
                    }
                else if (!tokenizedLine.front().compare("OperatingSystem"))
                  operatingSystem = tokenizedLine.back();
                else if (!tokenizedLine.front().compare("Members"))
                  {
                    char_separator<char> sep(" ,;", "()", drop_empty_tokens);
                    tokenizer<char_separator<char>> tokens(tokenizedLine.back(), sep);
                    bool begin_weight = false;
                    string node_name;
                    for (const auto &token : tokens)
                      {
                        if (token.compare("(") == 0)
                          {
                            begin_weight = true;
                            continue;
                          }
                        else if (token.compare(")") == 0)
                          {
                            node_name.clear();
                            begin_weight = false;
                            continue;
                          }
    
                        if (!begin_weight)
                          {
                            if (!node_name.empty())
                              if (member_nodes.find(node_name) != member_nodes.end())
                                {
                                  cerr << "ERROR (in config file): Node entered twice in specification of cluster." << endl;
                                  exit(EXIT_FAILURE);
                                }
                              else
                                member_nodes[node_name] = 1.0;
                            node_name = token;
                          }
                        else
                          try
                            {
                              auto weight = stod(token);
                              if (weight <= 0)
                                {
                                  cerr << "ERROR (in config file): Misspecification of weights passed to Members option." << endl;
                                  exit(EXIT_FAILURE);
                                }
                              member_nodes[node_name] = weight;
                            }
                          catch (const invalid_argument &)
                            {
                              cerr << "ERROR (in config file): Misspecification of weights passed to Members option." << endl;
                              exit(EXIT_FAILURE);
                            }
                      }
                    if (!node_name.empty())
                      if (member_nodes.find(node_name) == member_nodes.end())
                        member_nodes[node_name] = 1.0;
                      else
                        {
                          cerr << "ERROR (in config file): Node entered twice in specification of cluster." << endl;
                          exit(EXIT_FAILURE);
                        }
                  }
                else
                  {
                    cerr << "ERROR (in config file): Option " << tokenizedLine.front() << " is invalid." << endl;
                    exit(EXIT_FAILURE);
                  }
            }
        }
    
      if (!global_init_file.empty())
        addHooksConfFileElement(global_init_file);
      else if (!includepath.empty())
        addPathsConfFileElement(includepath);
      else
        addParallelConfFileElement(inNode, inCluster, member_nodes, name,
                                   computerName, port, minCpuNbr, maxCpuNbr, userName,
                                   password, remoteDrive, remoteDirectory,
                                   programPath, programConfig, matlabOctavePath, singleCompThread,
                                   numberOfThreadsPerJob, operatingSystem);
    
      configFile.close();
    }
    
    void
    ConfigFile::addHooksConfFileElement(string global_init_file)
    {
      if (global_init_file.empty())
        {
          cerr << "ERROR: The global initialization file must be passed to the GlobalInitFile option." << endl;
          exit(EXIT_FAILURE);
        }
      else
        hooks.emplace_back(move(global_init_file));
    }
    
    void
    ConfigFile::addPathsConfFileElement(vector<string> includepath)
    {
      if (includepath.empty())
        {
          cerr << "ERROR: The path to be included must be passed to the Include option." << endl;
          exit(EXIT_FAILURE);
        }
      else
        paths.emplace_back(move(includepath));
    }
    
    void
    ConfigFile::addParallelConfFileElement(bool inNode, bool inCluster, const member_nodes_t &member_nodes, const string &name,
                                           const string &computerName, const string &port, int minCpuNbr, int maxCpuNbr,
                                           const string &userName, const string &password, const string &remoteDrive,
                                           const string &remoteDirectory, const string &programPath, const string &programConfig,
                                           const string &matlabOctavePath, bool singleCompThread, int numberOfThreadsPerJob,
                                           const string &operatingSystem)
    {
      //! ADD NODE
      if (inNode)
        if (!member_nodes.empty())
          {
            cerr << "Invalid option passed to [node]." << endl;
            exit(EXIT_FAILURE);
          }
        else
          if (name.empty() || slave_nodes.find(name) != slave_nodes.end())
            {
              cerr << "ERROR: Every node must be assigned a unique name." << endl;
              exit(EXIT_FAILURE);
            }
          else
            slave_nodes.emplace(name, SlaveNode{computerName, port, minCpuNbr, maxCpuNbr, userName,
                                                  password, remoteDrive, remoteDirectory, programPath, programConfig,
                                                  matlabOctavePath, singleCompThread, numberOfThreadsPerJob,
                                                  operatingSystem});
      //! ADD CLUSTER
      else if (inCluster)
        if (minCpuNbr > 0 || maxCpuNbr > 0 || !userName.empty()
            || !password.empty() || !remoteDrive.empty() || !remoteDirectory.empty()
            || !programPath.empty() || !programConfig.empty()
            || !matlabOctavePath.empty() || !operatingSystem.empty())
          {
            cerr << "Invalid option passed to [cluster]." << endl;
            exit(EXIT_FAILURE);
          }
        else
          if (name.empty() || clusters.find(name) != clusters.end())
            {
              cerr << "ERROR: The cluster must be assigned a unique name." << endl;
              exit(EXIT_FAILURE);
            }
          else
            {
              if (clusters.empty())
                firstClusterName = name;
              clusters.emplace(name, Cluster{member_nodes});
            }
    }
    
    void
    ConfigFile::checkPass(WarningConsolidation &warnings) const
    {
      bool global_init_file_declared = false;
      for (const auto &hook : hooks)
        {
          for (const auto &mapit : hook.get_hooks())
            if (mapit.first.compare("global_init_file") == 0)
              if (global_init_file_declared == true)
                {
                  cerr << "ERROR: Only one global initialization file may be provided." << endl;
                  exit(EXIT_FAILURE);
                }
              else
                global_init_file_declared = true;
        }
    
      if (!parallel && !parallel_test)
        return;
    
      //! Check Slave Nodes
      if (slave_nodes.empty())
        {
          cerr << "ERROR: At least one node must be defined in the config file." << endl;
          exit(EXIT_FAILURE);
        }
    
      for (const auto &slave_node : slave_nodes)
        {
    #if !defined(_WIN32) && !defined(__CYGWIN32__)
          //For Linux/Mac, check that cpuNbr starts at 0
          if (slave_node.second.minCpuNbr != 0)
            warnings << "WARNING: On Unix-based operating systems, you cannot specify the CPU that is "
                     << "used in parallel processing. This will be adjusted for you such that the "
                     << "same number of CPUs are used." << endl;
    #endif
          if (!slave_node.second.port.empty())
            try
              {
                stoi(slave_node.second.port);
              }
            catch (const invalid_argument &)
              {
                cerr << "ERROR (node " << slave_node.first << "): the port must be an integer." << endl;
                exit(EXIT_FAILURE);
              }
          if (!slave_node.second.computerName.compare("localhost")) // We are working locally
            {
              if (!slave_node.second.remoteDrive.empty())
                {
                  cerr << "ERROR (node " << slave_node.first << "): the RemoteDrive option may not be passed for a local node." << endl;
                  exit(EXIT_FAILURE);
                }
              if (!slave_node.second.remoteDirectory.empty())
                {
                  cerr << "ERROR (node " << slave_node.first << "): the RemoteDirectory option may not be passed for a local node." << endl;
                  exit(EXIT_FAILURE);
                }
            }
          else
            {
              if (slave_node.second.userName.empty())
                {
                  cerr << "ERROR (node " << slave_node.first << "): the UserName option must be passed for every remote node." << endl;
                  exit(EXIT_FAILURE);
                }
              if (slave_node.second.operatingSystem.compare("windows") == 0)
                {
                  if (slave_node.second.password.empty())
                    {
                      cerr << "ERROR (node " << slave_node.first << "): the Password option must be passed under Windows for every remote node." << endl;
                      exit(EXIT_FAILURE);
                    }
                  if (slave_node.second.remoteDrive.empty())
                    {
                      cerr << "ERROR (node " << slave_node.first << "): the RemoteDrive option must be passed under Windows for every remote node." << endl;
                      exit(EXIT_FAILURE);
                    }
                }
    #if defined(_WIN32) || defined(__CYGWIN32__)
              if (slave_node.second.operatingSystem.empty())
                {
                  if (slave_node.second.password.empty())
                    {
                      cerr << "ERROR (node " << slave_node.first << "): the Password option must be passed under Windows for every remote node." << endl;
                      exit(EXIT_FAILURE);
                    }
                  if (slave_node.second.remoteDrive.empty())
                    {
                      cerr << "ERROR (node " << slave_node.first << "): the RemoteDrive option must be passed under Windows for every remote node." << endl;
                      exit(EXIT_FAILURE);
                    }
                }
    #endif
              if (slave_node.second.remoteDirectory.empty())
                {
                  cerr << "ERROR (node " << slave_node.first << "): the RemoteDirectory must be specified for every remote node." << endl;
                  exit(EXIT_FAILURE);
                }
            }
        }
    
      //! Check Clusters
      if (clusters.empty())
        {
          cerr << "ERROR: At least one cluster must be defined in the config file." << endl;
          exit(EXIT_FAILURE);
        }
    
      if (!cluster_name.empty() && clusters.find(cluster_name) == clusters.end())
        {
          cerr << "ERROR: Cluster Name " << cluster_name << " was not found in the config file." << endl;
          exit(EXIT_FAILURE);
        }
    
      for (const auto &cluster : clusters)
        for (const auto &itmn : cluster.second.member_nodes)
          if (slave_nodes.find(itmn.first) == slave_nodes.end())
            {
              cerr << "Error: node " << itmn.first << " specified in cluster " << cluster.first << " was not found" << endl;
              exit(EXIT_FAILURE);
            }
    }
    
    void
    ConfigFile::transformPass()
    {
      if (!parallel && !parallel_test)
        return;
    
    #if !defined(_WIN32) && !defined(__CYGWIN32__)
      //For Linux/Mac, check that cpuNbr starts at 0
      for (auto &it : slave_nodes)
        if (it.second.minCpuNbr != 0)
          {
            it.second.maxCpuNbr = it.second.maxCpuNbr - it.second.minCpuNbr;
            it.second.minCpuNbr = 0;
          }
    #endif
    
      auto cluster_it = cluster_name.empty() ? clusters.find(firstClusterName) : clusters.find(cluster_name);
    
      double weight_denominator{0.0};
      for (const auto &it : cluster_it->second.member_nodes)
        weight_denominator += it.second;
    
      for (auto &member_node : cluster_it->second.member_nodes)
        member_node.second /= weight_denominator;
    }
    
    vector<filesystem::path>
    ConfigFile::getIncludePaths() const
    {
      vector<filesystem::path> include_paths;
      for (auto path : paths)
        for (const auto &mapit : path.get_paths())
          for (const auto &vecit : mapit.second)
            include_paths.emplace_back(vecit);
      return include_paths;
    }
    
    void
    ConfigFile::writeHooks(ostream &output) const
    {
      for (auto hook : hooks)
        for (const auto &mapit : hook.get_hooks())
          output << "options_." << mapit.first << " = '" << mapit.second << "';" << endl;
    }
    
    void
    ConfigFile::writeCluster(ostream &output) const
    {
      if (!parallel && !parallel_test)
        return;
    
      auto cluster_it = cluster_name.empty() ? clusters.find(firstClusterName) : clusters.find(cluster_name);
    
      int i{1};
      for (const auto &slave_node : slave_nodes)
        {
          bool slave_node_in_member_nodes = false;
          for (const auto &itmn : cluster_it->second.member_nodes)
            if (!slave_node.first.compare(itmn.first))
              slave_node_in_member_nodes = true;
    
          if (!slave_node_in_member_nodes)
            continue;
    
          output << "options_.parallel";
          if (i > 1)
            output << "(" << i << ")";
          i++;
          output << " = struct('Local', ";
          if (slave_node.second.computerName.compare("localhost"))
            output << "0, ";
          else
            output << "1, ";
    
          output << "'ComputerName', '" << slave_node.second.computerName << "', "
                 << "'Port', '" << slave_node.second.port << "', "
                 << "'CPUnbr', [" << slave_node.second.minCpuNbr << ":" << slave_node.second.maxCpuNbr << "], "
                 << "'UserName', '" << slave_node.second.userName << "', "
                 << "'Password', '" << slave_node.second.password << "', "
                 << "'RemoteDrive', '" << slave_node.second.remoteDrive << "', "
                 << "'RemoteDirectory', '" << slave_node.second.remoteDirectory << "', "
            // The following should be switched back to “ProgramPath” once we move to Dragonfly
                 << "'DynarePath', '" << slave_node.second.programPath << "', "
                 << "'ProgramConfig', '" << slave_node.second.programConfig << "', "
                 << "'MatlabOctavePath', '" << slave_node.second.matlabOctavePath << "', "
                 << "'OperatingSystem', '" << slave_node.second.operatingSystem << "', "
                 << "'NodeWeight', '" << (cluster_it->second.member_nodes.find(slave_node.first))->second << "', "
                 << "'NumberOfThreadsPerJob', " << slave_node.second.numberOfThreadsPerJob << ", ";
    
          if (slave_node.second.singleCompThread)
            output << "'SingleCompThread', 'true');" << endl;
          else
            output << "'SingleCompThread', 'false');" << endl;
        }
    
      // Default values for the following two are both in DynareMain.cc and matlab/default_option_values.m
      if (parallel_slave_open_mode)
        output << "options_.parallel_info.leaveSlaveOpen = 1;" << endl;
      if (!parallel_use_psexec)
        output << "options_.parallel_use_psexec = false;" << endl;
    
      output << "options_.parallel_info.console_mode= isoctave;" << endl;
    
      output << "InitializeComputationalEnvironment();" << endl;
      if (parallel_test)
        output << "ErrorCode = AnalyseComputationalEnvironment(options_.parallel, options_.parallel_info);" << endl
               << "disp(['AnalyseComputationalEnvironment returned with Error Code: ' num2str(ErrorCode)]);" << endl
               << "diary off;" << endl
               << "return;" << endl;
    }
    
    void
    ConfigFile::writeEndParallel(ostream &output) const
    {
      if ((!parallel && !parallel_test) || !parallel_slave_open_mode)
        return;
    
      output << "if options_.parallel_info.leaveSlaveOpen == 1" << endl
             << "     closeSlave(options_.parallel,options_.parallel_info.RemoteTmpFolder);" << endl
             << "end" << endl;
    }