LCOV - code coverage report
Current view: top level - EnergyPlus - CommandLineInterface.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 266 460 57.8 %
Date: 2024-08-24 18:31:18 Functions: 4 6 66.7 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : // C++ Headers
      49             : #include <thread>
      50             : 
      51             : // CLI Headers
      52             : #include <CLI/CLI11.hpp>
      53             : 
      54             : // Project headers
      55             : #include <EnergyPlus/CommandLineInterface.hh>
      56             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      57             : #include <EnergyPlus/DataStringGlobals.hh>
      58             : #include <EnergyPlus/DisplayRoutines.hh>
      59             : #include <EnergyPlus/EnergyPlus.hh>
      60             : #include <EnergyPlus/FileSystem.hh>
      61             : #include <EnergyPlus/PluginManager.hh>
      62             : #include <EnergyPlus/UtilityRoutines.hh>
      63             : 
      64             : namespace EnergyPlus {
      65             : 
      66             : namespace CommandLineInterface {
      67             : 
      68         796 :     int ProcessArgs(EnergyPlusData &state, const std::vector<std::string> &args)
      69             :     {
      70             :         using size_type = std::string::size_type;
      71             : 
      72             :         // Expand long-name options using "=" sign in to two arguments
      73             :         // and expand multiple short options into separate arguments
      74         796 :         std::vector<std::string> arguments;
      75             : 
      76         796 :         std::string const dash("-");
      77             : 
      78        7194 :         for (const auto &inputArg : args) {
      79             : 
      80        6398 :             size_type const doubleDashPosition = inputArg.find("--");
      81        6398 :             size_type const equalsPosition = inputArg.find("=");
      82             : 
      83        6398 :             if (doubleDashPosition == 0 && equalsPosition != std::string::npos) { // --option=value
      84           0 :                 arguments.push_back(inputArg.substr(0, equalsPosition));
      85           0 :                 arguments.push_back(inputArg.substr(equalsPosition + 1, inputArg.size() - 1));
      86        6398 :             } else if (doubleDashPosition == 0 && inputArg.size() == 2) {
      87             :                 // Filter it out, it's a bash-like separator (end of the command options, before positionals arguments are accepted)
      88        6398 :             } else if ((inputArg.size() > 2) && (inputArg[0] == '-') && (inputArg[1] != '-')) { // -abc style
      89           0 :                 for (size_type c = 1; c < inputArg.size(); ++c) {
      90           0 :                     arguments.push_back(dash + inputArg[c]);
      91             :                 }
      92             :             } else { // ?
      93        6398 :                 arguments.push_back(inputArg);
      94             :             }
      95         796 :         }
      96             : 
      97             :         // erase the first element, which is the name of the program
      98         796 :         const std::string programName = std::move(arguments.front());
      99         796 :         arguments.erase(arguments.begin());
     100             : 
     101         796 :         size_type const argCount = arguments.size();
     102         796 :         bool const legacyMode = (argCount == 0);
     103             : 
     104             :         // Set path of EnergyPlus program path (if we aren't overriding it)
     105         796 :         if (!state.dataGlobal->installRootOverride) {
     106         796 :             state.dataStrGlobals->exeDirectoryPath = FileSystem::getParentDirectoryPath(FileSystem::getAbsolutePath(FileSystem::getProgramPath()));
     107             :         }
     108             : 
     109        1592 :         CLI::App app{"energyplus", programName};
     110             :         // opt.add("", false, 0, 0, "Display version information", "-v", "--version");
     111         796 :         app.set_version_flag("-v,--version", EnergyPlus::DataStringGlobals::VerString);
     112             : 
     113             :         std::string const description = fmt::format(R"({}
     114             : PythonLinkage: {}
     115             : Built on Platform: {}
     116             : )",
     117         796 :                                                     state.dataStrGlobals->VerStringVar,
     118         796 :                                                     PluginManagement::pythonStringForUsage(state),
     119         796 :                                                     DataStringGlobals::BuildPlatformString);
     120         796 :         app.description(description);
     121             : 
     122         796 :         auto *annualOpt = app.add_flag("-a,--annual", state.dataGlobal->AnnualSimulation, "Force annual simulation");
     123             : 
     124         796 :         app.add_flag("-D,--design-day", state.dataGlobal->DDOnlySimulation, "Force design-day-only simulation")->excludes(annualOpt);
     125             : 
     126         796 :         app.add_option("-d,--output-directory", state.dataStrGlobals->outDirPath, "Output directory path (default: current directory)")
     127        2388 :             ->option_text("DIR")
     128         796 :             ->required(false);
     129             :         // ->check(CLI::ExistingDirectory) // We don't require it to exist, we make it if needed
     130             : 
     131         796 :         if (legacyMode) {
     132           0 :             state.dataStrGlobals->inputIddFilePath = "Energy+.idd";
     133             :         } else {
     134         796 :             state.dataStrGlobals->inputIddFilePath = state.dataStrGlobals->exeDirectoryPath / "Energy+.idd";
     135             :         }
     136        2388 :         app.add_option(
     137         796 :                "-i,--idd", state.dataStrGlobals->inputIddFilePath, "Input data dictionary path (default: Energy+.idd in executable directory)")
     138             :             ->required(false)
     139             :             ->option_text("IDD")
     140         796 :             ->check(CLI::ExistingFile);
     141             : 
     142         796 :         bool runEPMacro = false;
     143         796 :         app.add_flag("-m,--epmacro", runEPMacro, "Run EPMacro prior to simulation");
     144             : 
     145         796 :         std::string prefixOutName = "eplus";
     146         796 :         app.add_option("-p,--output-prefix", prefixOutName, "Prefix for output file names (default: eplus)")->required(false)->option_text("PRE");
     147             : 
     148         796 :         app.add_flag("-r,--readvars", state.dataGlobal->runReadVars, "Run ReadVarsESO after simulation");
     149             : 
     150         796 :         app.add_flag("-c,--convert", state.dataGlobal->outputEpJSONConversion, "Output IDF->epJSON or epJSON->IDF, dependent on input file type");
     151             : 
     152        1592 :         app.add_flag("--convert-only",
     153         796 :                      state.dataGlobal->outputEpJSONConversionOnly,
     154             :                      "Only convert IDF->epJSON or epJSON->IDF, dependent on input file type. No simulation");
     155             : 
     156         796 :         std::string suffixType = "L";
     157         796 :         std::string const suffixHelp = R"help(Suffix style for output file names (default: L)
     158             :    L: Legacy (e.g., eplustbl.csv)
     159             :    C: Capital (e.g., eplusTable.csv)
     160             :    D: Dash (e.g., eplus-table.csv))help";
     161             :         app.add_option("-s,--output-suffix", suffixType, suffixHelp)
     162        1592 :             ->option_text("SUFFIX")
     163             :             ->required(false)
     164         796 :             ->check(CLI::IsMember({"L", "C", "D"}, CLI::ignore_case));
     165             : 
     166             :         // TODO: maybe delay validation to output a better error message?
     167         796 :         const int MAX_N = static_cast<int>(std::thread::hardware_concurrency());
     168             :         app.add_option("-j,--jobs",
     169         796 :                        state.dataGlobal->numThread,
     170             :                        "Multi-thread with N threads; 1 thread with no arg. (Currently only for G-Function generation)")
     171             :             ->option_text("N")
     172             :             // ->check(CLI::Range(1, MAX_N)  // Tempted to just do that... much simpler
     173             :             // ->check(CLI::Number)
     174        1592 :             ->transform([MAX_N, &state](std::string input) -> std::string {
     175           0 :                 int number_of_threads = -1;
     176           0 :                 bool const converted = CLI::detail::lexical_cast(input, number_of_threads);
     177           0 :                 if (!converted) {
     178             :                     // CLI::ValidationError
     179           0 :                     return fmt::format("Argument should be an integer, not '{}'", input);
     180             :                 }
     181           0 :                 if (number_of_threads <= 0) {
     182           0 :                     DisplayString(state, "Invalid value for -j arg. Defaulting to 1.");
     183           0 :                     return "1";
     184             :                 }
     185           0 :                 if (number_of_threads > MAX_N) {
     186           0 :                     DisplayString(state,
     187           0 :                                   fmt::format("Invalid value for -j arg. Value exceeds num available. Defaulting to num available. -j {}", MAX_N));
     188           0 :                     return std::to_string(MAX_N);
     189             :                 }
     190           0 :                 return input;
     191             :             });
     192             : 
     193         796 :         state.files.inputWeatherFilePath.filePath = "in.epw";
     194             :         // Note: we can't do check(CLI::ExistingFile) here since passing a non-existing /path/to/myfile.epw file
     195             :         // when there exists a /path/to/myfile.stat would mean the Weather File Statistics are still parsed and reported to the tabular output
     196             :         // We still report a message + fail later if DDOnlySimulation is false
     197             :         auto *weatherPathOpt =
     198        1592 :             app.add_option("-w,--weather", state.files.inputWeatherFilePath.filePath, "Weather file path (default: in.epw in current directory)")
     199             :                 ->required(false)
     200         796 :                 ->option_text("EPW");
     201             : 
     202         796 :         bool runExpandObjects = false;
     203         796 :         app.add_flag("-x,--expandobjects", runExpandObjects, "Run ExpandObjects prior to simulation");
     204             : 
     205             :         // Positional
     206         796 :         state.dataStrGlobals->inputFilePath = "in.idf";
     207        1592 :         app.add_option("input_file", state.dataStrGlobals->inputFilePath, "Input file (default: in.idf in current directory)")
     208             :             ->required(false)
     209         796 :             ->check(CLI::ExistingFile);
     210             : 
     211             :         // Catching it myself, so I can print the arguments vector before it's mutated
     212        7194 :         bool debugCLI = std::any_of(args.begin(), args.end(), [](const auto &arg) { return arg == "--debug-cli"; });
     213         796 :         if (debugCLI) {
     214             :             {
     215           0 :                 fmt::print("ProcessArgs: received args\n");
     216           0 :                 int na = 0;
     217           0 :                 for (const auto &a : args) {
     218           0 :                     fmt::print("* {}: '{}'\n", na++, a);
     219           0 :                 }
     220             :             }
     221             :             {
     222           0 :                 fmt::print("\nAfter massaging/expanding of args\n");
     223           0 :                 int na = 0;
     224           0 :                 for (const auto &a : arguments) {
     225           0 :                     fmt::print("* {}: '{}'\n", na++, a);
     226           0 :                 }
     227             :             }
     228           0 :             fmt::print("\n");
     229             :         }
     230             :         // bool debugCLI = false;
     231         796 :         app.add_flag("--debug-cli", debugCLI, "Print the result of the CLI assignments to the console and exit")->group(""); // Empty group to hide it
     232             : 
     233         796 :         app.footer("Example: energyplus -w weather.epw -r input.idf");
     234             : 
     235         796 :         const bool eplusRunningViaAPI = state.dataGlobal->eplusRunningViaAPI;
     236             : 
     237             :         try {
     238             :             // app.parse(argc, argv);
     239             :             // CLI11 when passing argc, argv creates a vector<string> but **in reverse** order:
     240             :             // https://github.com/CLIUtils/CLI11/blob/291c58789c031208f08f4f261a858b5b7083e8e2/include/CLI/impl/App_inl.hpp#L476-L488
     241         796 :             std::reverse(arguments.begin(), arguments.end());
     242         796 :             app.parse(arguments);
     243           0 :         } catch (const CLI::Success &e) {
     244           0 :             int const return_code = app.exit(e);
     245           0 :             if (eplusRunningViaAPI) {
     246           0 :                 return static_cast<int>(ReturnCodes::SuccessButHelper);
     247             :             } else {
     248           0 :                 exit(return_code);
     249             :             }
     250           0 :         } catch (const CLI::ParseError &e) {
     251           0 :             int const return_code = app.exit(e);
     252           0 :             if (eplusRunningViaAPI) {
     253           0 :                 return static_cast<int>(ReturnCodes::Failure);
     254             :             } else {
     255           0 :                 exit(return_code);
     256             :             }
     257           0 :         }
     258             : 
     259         796 :         if (debugCLI) {
     260           0 :             fmt::print(stderr,
     261             :                        R"debug(
     262             : state.dataGlobal->AnnualSimulation = {},
     263             : state.dataGlobal->DDOnlySimulation = {},
     264             : state.dataStrGlobals->outDirPath = '{:g}',
     265             : state.dataStrGlobals->inputIddFilePath= '{:g}',
     266             : 
     267             : runEPMacro = {},
     268             : prefixOutName = {},
     269             : 
     270             : state.dataGlobal->runReadVars={},
     271             : state.dataGlobal->outputEpJSONConversion={},
     272             : state.dataGlobal->outputEpJSONConversionOnly={},
     273             : 
     274             : suffixType={},
     275             : 
     276             : state.dataGlobal->numThread={},
     277             : state.files.inputWeatherFilePath.filePath='{:g}',
     278             : state.dataStrGlobals->inputFilePath='{:g}',
     279             : )debug",
     280           0 :                        state.dataGlobal->AnnualSimulation,
     281           0 :                        state.dataGlobal->DDOnlySimulation,
     282           0 :                        state.dataStrGlobals->outDirPath,
     283           0 :                        state.dataStrGlobals->inputIddFilePath,
     284             : 
     285             :                        runEPMacro,
     286             :                        prefixOutName,
     287           0 :                        state.dataGlobal->runReadVars,
     288           0 :                        state.dataGlobal->outputEpJSONConversion,
     289           0 :                        state.dataGlobal->outputEpJSONConversionOnly,
     290             :                        suffixType,
     291           0 :                        state.dataGlobal->numThread,
     292           0 :                        state.files.inputWeatherFilePath.filePath,
     293           0 :                        state.dataStrGlobals->inputFilePath);
     294             : 
     295           0 :             fmt::print(stderr, "--debug-cli passed: exiting early\n");
     296             : 
     297           0 :             exit(0);
     298             :         }
     299             : 
     300             :         // Convert all paths to native paths
     301         796 :         state.dataStrGlobals->inputFilePath = FileSystem::makeNativePath(state.dataStrGlobals->inputFilePath);
     302         796 :         state.files.inputWeatherFilePath.filePath = FileSystem::makeNativePath(state.files.inputWeatherFilePath.filePath);
     303         796 :         state.dataStrGlobals->inputIddFilePath = FileSystem::makeNativePath(state.dataStrGlobals->inputIddFilePath);
     304         796 :         state.dataStrGlobals->outDirPath = FileSystem::makeNativePath(state.dataStrGlobals->outDirPath);
     305             : 
     306         796 :         state.dataStrGlobals->inputFilePathNameOnly = FileSystem::getFileName(state.dataStrGlobals->inputFilePath);
     307         796 :         state.dataStrGlobals->inputDirPath = FileSystem::getParentDirectoryPath(state.dataStrGlobals->inputFilePath);
     308             : 
     309             :         {
     310         796 :             FileSystem::FileTypes const fileType = FileSystem::getFileType(state.dataStrGlobals->inputFilePath);
     311         796 :             state.dataGlobal->isEpJSON = FileSystem::is_all_json_type(fileType);
     312         796 :             switch (fileType) {
     313         796 :             case FileSystem::FileTypes::IDF:
     314             :             case FileSystem::FileTypes::IMF:
     315             :             case FileSystem::FileTypes::EpJSON:
     316             :             case FileSystem::FileTypes::JSON:
     317         796 :                 break;
     318           0 :             case FileSystem::FileTypes::CBOR:
     319           0 :                 DisplayString(state, "CBOR input format is experimental and unsupported.");
     320           0 :                 break;
     321           0 :             case FileSystem::FileTypes::MsgPack:
     322           0 :                 DisplayString(state, "MsgPack input format is experimental and unsupported.");
     323           0 :                 break;
     324           0 :             case FileSystem::FileTypes::UBJSON:
     325           0 :                 DisplayString(state, "UBJSON input format is experimental and unsupported.");
     326           0 :                 break;
     327           0 :             case FileSystem::FileTypes::BSON:
     328           0 :                 DisplayString(state, "BSON input format is experimental and unsupported.");
     329           0 :                 break;
     330           0 :             default:
     331           0 :                 DisplayString(state,
     332           0 :                               fmt::format("ERROR: Input file must have IDF, IMF, or epJSON extension: {:g}", state.dataStrGlobals->inputFilePath));
     333           0 :                 if (eplusRunningViaAPI) {
     334           0 :                     return static_cast<int>(ReturnCodes::Failure);
     335             :                 } else {
     336           0 :                     exit(EXIT_FAILURE);
     337             :                 }
     338             :             }
     339             :         }
     340             : 
     341         796 :         if (!state.dataStrGlobals->outDirPath.empty()) {
     342             :             // Create directory if it doesn't already exist
     343         796 :             FileSystem::makeDirectory(state.dataStrGlobals->outDirPath);
     344             :         }
     345             : 
     346             :         // File naming scheme
     347        1592 :         fs::path outputFilePrefixFullPath = state.dataStrGlobals->outDirPath / prefixOutName;
     348             : 
     349        1592 :         fs::path outputEpmdetFilePath;
     350        1592 :         fs::path outputEpmidfFilePath;
     351             : 
     352        1592 :         fs::path outputExpidfFilePath;
     353        1592 :         fs::path outputExperrFilePath;
     354             : 
     355        1592 :         std::string normalSuffix;
     356        1592 :         std::string tableSuffix;
     357        1592 :         std::string mapSuffix;
     358        1592 :         std::string zszSuffix;
     359        1592 :         std::string spszSuffix;
     360        1592 :         std::string sszSuffix;
     361        1592 :         std::string meterSuffix;
     362        1592 :         std::string sqliteSuffix;
     363        1592 :         std::string adsSuffix;
     364        1592 :         std::string screenSuffix;
     365        1592 :         std::string shdSuffix;
     366             : 
     367        1592 :         std::string const errorFollowUp = "Type 'energyplus --help' for usage.";
     368             :         {
     369         796 :             std::transform(suffixType.begin(), suffixType.end(), suffixType.begin(), ::toupper);
     370             : 
     371         796 :             if (suffixType == "L") {
     372             : 
     373         796 :                 normalSuffix = "out";
     374         796 :                 tableSuffix = "tbl";
     375         796 :                 mapSuffix = "map";
     376         796 :                 zszSuffix = "zsz";
     377         796 :                 spszSuffix = "spsz";
     378         796 :                 sszSuffix = "ssz";
     379         796 :                 meterSuffix = "mtr";
     380         796 :                 sqliteSuffix = "sqlite";
     381         796 :                 adsSuffix = "ADS";
     382         796 :                 screenSuffix = "screen";
     383         796 :                 shdSuffix = "shading";
     384             : 
     385           0 :             } else if (suffixType == "D") {
     386             : 
     387           0 :                 normalSuffix = "";
     388           0 :                 tableSuffix = "-table";
     389           0 :                 mapSuffix = "-map";
     390           0 :                 zszSuffix = "-zsz";
     391           0 :                 spszSuffix = "-spsz";
     392           0 :                 sszSuffix = "-ssz";
     393           0 :                 meterSuffix = "-meter";
     394           0 :                 sqliteSuffix = "-sqlite";
     395           0 :                 adsSuffix = "-ads";
     396           0 :                 screenSuffix = "-screen";
     397           0 :                 shdSuffix = "-shading";
     398             : 
     399           0 :             } else if (suffixType == "C") {
     400             : 
     401           0 :                 normalSuffix = "";
     402           0 :                 tableSuffix = "Table";
     403           0 :                 mapSuffix = "Map";
     404           0 :                 zszSuffix = "Zsz";
     405           0 :                 spszSuffix = "Spsz";
     406           0 :                 sszSuffix = "Ssz";
     407           0 :                 meterSuffix = "Meter";
     408           0 :                 sqliteSuffix = "Sqlite";
     409           0 :                 adsSuffix = "Ads";
     410           0 :                 screenSuffix = "Screen";
     411           0 :                 shdSuffix = "Shading";
     412             :             }
     413             :             // No else needed, this is validated at CLI level above
     414             :         }
     415             : 
     416             :         // Helper to construct output file path
     417       60496 :         auto composePath = [&outputFilePrefixFullPath](const std::string &suffix) -> fs::path {
     418       60496 :             return FileSystem::appendSuffixToPath(outputFilePrefixFullPath, suffix);
     419         796 :         };
     420             : 
     421             :         // EnergyPlus files
     422         796 :         state.files.audit.filePath = composePath(normalSuffix + ".audit");
     423         796 :         state.files.bnd.filePath = composePath(normalSuffix + ".bnd");
     424         796 :         state.files.dxf.filePath = composePath(normalSuffix + ".dxf");
     425         796 :         state.files.eio.filePath = composePath(normalSuffix + ".eio");
     426         796 :         state.files.endFile.filePath = composePath(normalSuffix + ".end");
     427         796 :         state.files.outputErrFilePath = composePath(normalSuffix + ".err");
     428         796 :         state.files.eso.filePath = composePath(normalSuffix + ".eso");
     429             : 
     430         796 :         state.files.json.outputJsonFilePath = composePath(normalSuffix + ".json");
     431         796 :         state.files.json.outputTSZoneJsonFilePath = composePath(normalSuffix + "_detailed_zone.json");
     432         796 :         state.files.json.outputTSHvacJsonFilePath = composePath(normalSuffix + "_detailed_HVAC.json");
     433         796 :         state.files.json.outputTSJsonFilePath = composePath(normalSuffix + "_timestep.json");
     434         796 :         state.files.json.outputYRJsonFilePath = composePath(normalSuffix + "_yearly.json");
     435         796 :         state.files.json.outputMNJsonFilePath = composePath(normalSuffix + "_monthly.json");
     436         796 :         state.files.json.outputDYJsonFilePath = composePath(normalSuffix + "_daily.json");
     437         796 :         state.files.json.outputHRJsonFilePath = composePath(normalSuffix + "_hourly.json");
     438         796 :         state.files.json.outputSMJsonFilePath = composePath(normalSuffix + "_runperiod.json");
     439         796 :         state.files.json.outputCborFilePath = composePath(normalSuffix + ".cbor");
     440         796 :         state.files.json.outputTSZoneCborFilePath = composePath(normalSuffix + "_detailed_zone.cbor");
     441         796 :         state.files.json.outputTSHvacCborFilePath = composePath(normalSuffix + "_detailed_HVAC.cbor");
     442         796 :         state.files.json.outputTSCborFilePath = composePath(normalSuffix + "_timestep.cbor");
     443         796 :         state.files.json.outputYRCborFilePath = composePath(normalSuffix + "_yearly.cbor");
     444         796 :         state.files.json.outputMNCborFilePath = composePath(normalSuffix + "_monthly.cbor");
     445         796 :         state.files.json.outputDYCborFilePath = composePath(normalSuffix + "_daily.cbor");
     446         796 :         state.files.json.outputHRCborFilePath = composePath(normalSuffix + "_hourly.cbor");
     447         796 :         state.files.json.outputSMCborFilePath = composePath(normalSuffix + "_runperiod.cbor");
     448         796 :         state.files.json.outputMsgPackFilePath = composePath(normalSuffix + ".msgpack");
     449         796 :         state.files.json.outputTSZoneMsgPackFilePath = composePath(normalSuffix + "_detailed_zone.msgpack");
     450         796 :         state.files.json.outputTSHvacMsgPackFilePath = composePath(normalSuffix + "_detailed_HVAC.msgpack");
     451         796 :         state.files.json.outputTSMsgPackFilePath = composePath(normalSuffix + "_timestep.msgpack");
     452         796 :         state.files.json.outputYRMsgPackFilePath = composePath(normalSuffix + "_yearly.msgpack");
     453         796 :         state.files.json.outputMNMsgPackFilePath = composePath(normalSuffix + "_monthly.msgpack");
     454         796 :         state.files.json.outputDYMsgPackFilePath = composePath(normalSuffix + "_daily.msgpack");
     455         796 :         state.files.json.outputHRMsgPackFilePath = composePath(normalSuffix + "_hourly.msgpack");
     456         796 :         state.files.json.outputSMMsgPackFilePath = composePath(normalSuffix + "_runperiod.msgpack");
     457             : 
     458         796 :         state.files.mtd.filePath = composePath(normalSuffix + ".mtd");
     459         796 :         state.files.mdd.filePath = composePath(normalSuffix + ".mdd");
     460         796 :         state.files.mtr.filePath = composePath(normalSuffix + ".mtr");
     461         796 :         state.files.rdd.filePath = composePath(normalSuffix + ".rdd");
     462         796 :         state.dataStrGlobals->outputShdFilePath = composePath(normalSuffix + ".shd");
     463         796 :         state.files.dfs.filePath = composePath(normalSuffix + ".dfs");
     464         796 :         state.dataStrGlobals->outputGLHEFilePath = composePath(normalSuffix + ".glhe");
     465         796 :         state.files.edd.filePath = composePath(normalSuffix + ".edd");
     466         796 :         state.dataStrGlobals->outputIperrFilePath = composePath(normalSuffix + ".iperr");
     467         796 :         state.files.sln.filePath = composePath(normalSuffix + ".sln");
     468         796 :         state.files.sci.filePath = composePath(normalSuffix + ".sci");
     469         796 :         state.files.wrl.filePath = composePath(normalSuffix + ".wrl");
     470         796 :         state.dataStrGlobals->outputSqlFilePath = composePath(normalSuffix + ".sql");
     471         796 :         state.files.debug.filePath = composePath(normalSuffix + ".dbg");
     472         796 :         state.dataStrGlobals->outputPerfLogFilePath = composePath(normalSuffix + "_perflog.csv");
     473         796 :         state.dataStrGlobals->outputTblCsvFilePath = composePath(tableSuffix + ".csv");
     474         796 :         state.dataStrGlobals->outputTblHtmFilePath = composePath(tableSuffix + ".htm");
     475         796 :         state.dataStrGlobals->outputTblTabFilePath = composePath(tableSuffix + ".tab");
     476         796 :         state.dataStrGlobals->outputTblTxtFilePath = composePath(tableSuffix + ".txt");
     477         796 :         state.dataStrGlobals->outputTblXmlFilePath = composePath(tableSuffix + ".xml");
     478         796 :         state.files.outputMapTabFilePath = composePath(mapSuffix + ".tab");
     479         796 :         state.files.outputMapCsvFilePath = composePath(mapSuffix + ".csv");
     480         796 :         state.files.outputMapTxtFilePath = composePath(mapSuffix + ".txt");
     481         796 :         state.files.outputZszCsvFilePath = composePath(zszSuffix + ".csv");
     482         796 :         state.files.outputZszTabFilePath = composePath(zszSuffix + ".tab");
     483         796 :         state.files.outputZszTxtFilePath = composePath(zszSuffix + ".txt");
     484         796 :         state.files.outputSpszCsvFilePath = composePath(spszSuffix + ".csv");
     485         796 :         state.files.outputSpszTabFilePath = composePath(spszSuffix + ".tab");
     486         796 :         state.files.outputSpszTxtFilePath = composePath(spszSuffix + ".txt");
     487         796 :         state.files.outputSszCsvFilePath = composePath(sszSuffix + ".csv");
     488         796 :         state.files.outputSszTabFilePath = composePath(sszSuffix + ".tab");
     489         796 :         state.files.outputSszTxtFilePath = composePath(sszSuffix + ".txt");
     490         796 :         state.dataStrGlobals->outputAdsFilePath = composePath(adsSuffix + ".out");
     491         796 :         state.files.shade.filePath = composePath(shdSuffix + ".csv");
     492         796 :         if (suffixType == "L") {
     493             :             // out/sqlite.err
     494         796 :             state.dataStrGlobals->outputSqliteErrFilePath = state.dataStrGlobals->outDirPath / fs::path{sqliteSuffix + ".err"};
     495             :         } else {
     496             :             // if 'D':  out/eplus-sqlite.err
     497           0 :             state.dataStrGlobals->outputSqliteErrFilePath = composePath(sqliteSuffix + ".err");
     498             :         }
     499             : 
     500         796 :         state.files.screenCsv.filePath = composePath(screenSuffix + ".csv");
     501             :         // TODO, why are these relative paths?
     502         796 :         state.files.delightIn.filePath = "eplusout.delightin";
     503         796 :         state.dataStrGlobals->outputDelightOutFilePath = "eplusout.delightout";
     504             : 
     505             :         // TODO: why is this relative?
     506         796 :         state.files.iniFile.filePath = "Energy+.ini";
     507             : 
     508             :         // Stat next to epw
     509             :         // Careful! fs::path::replace_extension will **mutate the original object**
     510         796 :         state.files.inStatFilePath.filePath = state.files.inputWeatherFilePath.filePath;
     511         796 :         state.files.inStatFilePath.filePath.replace_extension(".stat");
     512             :         // Or is it better to provide a helper that does not mutate like this?
     513             :         // state.files.inStatFilePath.filePath = FileSystem::replaceFileExtension(state.inputWeatherFilePath.filePath, ".stat");
     514             : 
     515         796 :         state.dataStrGlobals->eplusADSFilePath = state.dataStrGlobals->inputDirPath / "eplusADS.inp";
     516             : 
     517             :         // Readvars files
     518         796 :         state.files.csv.filePath = composePath(normalSuffix + ".csv");
     519         796 :         state.files.mtr_csv.filePath = composePath(meterSuffix + ".csv");
     520         796 :         state.dataStrGlobals->outputRvauditFilePath = composePath(normalSuffix + ".rvaudit");
     521             : 
     522             :         // EPMacro files
     523         796 :         outputEpmdetFilePath = composePath(normalSuffix + ".epmdet");
     524         796 :         outputEpmidfFilePath = composePath(normalSuffix + ".epmidf");
     525             : 
     526             :         // ExpandObjects files
     527         796 :         outputExpidfFilePath = composePath(normalSuffix + ".expidf");
     528         796 :         outputExperrFilePath = composePath(normalSuffix + ".experr");
     529             : 
     530             :         // Read path from INI file if it exists
     531             : 
     532             :         // Check for IDD and IDF files
     533         796 :         if (FileSystem::fileExists(state.files.iniFile.filePath)) {
     534           0 :             EnergyPlus::InputFile iniFile = state.files.iniFile.try_open();
     535           0 :             if (!iniFile.good()) {
     536           0 :                 DisplayString(state, fmt::format("ERROR: Could not open file {} for input (read).", iniFile.filePath));
     537           0 :                 if (eplusRunningViaAPI) {
     538           0 :                     return static_cast<int>(ReturnCodes::Failure);
     539             :                 } else {
     540           0 :                     exit(EXIT_FAILURE);
     541             :                 }
     542             :             }
     543           0 :             state.dataStrGlobals->CurrentWorkingFolder = iniFile.filePath;
     544             :             // Relying on compiler to supply full path name here
     545             :             // TODO: not sure I understand this block
     546             :             // const int TempIndx = index(state.dataStrGlobals->CurrentWorkingFolder, state.dataStrGlobals->pathChar, true);
     547             :             // if (TempIndx == std::string::npos) {
     548             :             // state.dataStrGlobals->CurrentWorkingFolder = "";
     549             :             //} else {
     550             :             // state.dataStrGlobals->CurrentWorkingFolder.erase(TempIndx + 1);
     551             :             //}
     552             :             //       Get directories from ini file
     553           0 :             std::string programPathStr;
     554           0 :             ReadINIFile(iniFile, "program", "dir", programPathStr);
     555           0 :             state.dataStrGlobals->ProgramPath = fs::path(programPathStr);
     556             : 
     557           0 :             state.dataStrGlobals->inputIddFilePath = state.dataStrGlobals->ProgramPath / "Energy+.idd";
     558           0 :         }
     559             : 
     560             :         // Check if specified files exist
     561         796 :         if (!FileSystem::fileExists(state.dataStrGlobals->inputFilePath)) {
     562           0 :             DisplayString(
     563           0 :                 state, fmt::format("ERROR: Could not find input data file: {}.", FileSystem::getAbsolutePath(state.dataStrGlobals->inputFilePath)));
     564           0 :             DisplayString(state, errorFollowUp);
     565           0 :             if (eplusRunningViaAPI) {
     566           0 :                 return static_cast<int>(ReturnCodes::Failure);
     567             :             } else {
     568           0 :                 exit(EXIT_FAILURE);
     569             :             }
     570             :         }
     571             : 
     572         796 :         if ((weatherPathOpt->count() > 0) && !state.dataGlobal->DDOnlySimulation) {
     573           6 :             if (!FileSystem::fileExists(state.files.inputWeatherFilePath.filePath)) {
     574           0 :                 DisplayString(
     575             :                     state,
     576           0 :                     fmt::format("ERROR: Could not find weather file: {}.", FileSystem::getAbsolutePath(state.files.inputWeatherFilePath.filePath)));
     577           0 :                 DisplayString(state, errorFollowUp);
     578           0 :                 if (eplusRunningViaAPI) {
     579           0 :                     return static_cast<int>(ReturnCodes::Failure);
     580             :                 } else {
     581           0 :                     exit(EXIT_FAILURE);
     582             :                 }
     583             :             }
     584             :         }
     585             : 
     586             :         // TODO: might be able to convert epJSON->IDF, run preprocessors, then go back IDF->epJSON
     587             : 
     588             :         // Preprocessors (These will likely move to a new file)
     589         796 :         if (runEPMacro) {
     590           2 :             fs::path epMacroPath = (state.dataStrGlobals->exeDirectoryPath / "EPMacro").replace_extension(FileSystem::exeExtension);
     591           1 :             if (!FileSystem::fileExists(epMacroPath)) {
     592           0 :                 DisplayString(state, fmt::format("ERROR: Could not find EPMacro executable: {}.", FileSystem::getAbsolutePath(epMacroPath)));
     593           0 :                 if (eplusRunningViaAPI) {
     594           0 :                     return static_cast<int>(ReturnCodes::Failure);
     595             :                 } else {
     596           0 :                     exit(EXIT_FAILURE);
     597             :                 }
     598             :             }
     599           2 :             std::string epMacroCommand = "\"" + FileSystem::toString(epMacroPath) + "\"";
     600           1 :             bool inputFilePathdIn = (FileSystem::getAbsolutePath(state.dataStrGlobals->inputFilePath) == FileSystem::getAbsolutePath("in.imf"));
     601             : 
     602           1 :             if (!inputFilePathdIn) {
     603           1 :                 FileSystem::linkFile(state.dataStrGlobals->inputFilePath, "in.imf");
     604             :             }
     605           1 :             DisplayString(state, "Running EPMacro...");
     606           1 :             FileSystem::systemCall(epMacroCommand);
     607           1 :             if (!inputFilePathdIn) {
     608           1 :                 FileSystem::removeFile("in.imf");
     609             :             }
     610           1 :             FileSystem::moveFile("audit.out", outputEpmdetFilePath);
     611           1 :             FileSystem::moveFile("out.idf", outputEpmidfFilePath);
     612           1 :             state.dataStrGlobals->inputFilePath = outputEpmidfFilePath;
     613           1 :         }
     614             : 
     615         796 :         if (runExpandObjects) {
     616             :             fs::path expandObjectsPath =
     617          58 :                 (state.dataStrGlobals->exeDirectoryPath / fs::path("ExpandObjects")).replace_extension(FileSystem::exeExtension);
     618          29 :             if (!FileSystem::fileExists(expandObjectsPath)) {
     619           0 :                 DisplayString(state,
     620           0 :                               fmt::format("ERROR: Could not find ExpandObjects executable: {}.", FileSystem::getAbsolutePath(expandObjectsPath)));
     621           0 :                 if (eplusRunningViaAPI) {
     622           0 :                     return static_cast<int>(ReturnCodes::Failure);
     623             :                 } else {
     624           0 :                     exit(EXIT_FAILURE);
     625             :                 }
     626             :             }
     627          58 :             std::string expandObjectsCommand = "\"" + FileSystem::toString(expandObjectsPath) + "\"";
     628          29 :             bool inputFilePathdIn = (FileSystem::getAbsolutePath(state.dataStrGlobals->inputFilePath) == FileSystem::getAbsolutePath("in.idf"));
     629             : 
     630             :             // check if IDD actually exists since ExpandObjects still requires it
     631          29 :             if (!FileSystem::fileExists(state.dataStrGlobals->inputIddFilePath)) {
     632           0 :                 DisplayString(state,
     633           0 :                               fmt::format("ERROR: Could not find input data dictionary: {}.",
     634           0 :                                           FileSystem::getAbsolutePath(state.dataStrGlobals->inputIddFilePath)));
     635           0 :                 DisplayString(state, errorFollowUp);
     636           0 :                 if (eplusRunningViaAPI) {
     637           0 :                     return static_cast<int>(ReturnCodes::Failure);
     638             :                 } else {
     639           0 :                     exit(EXIT_FAILURE);
     640             :                 }
     641             :             }
     642             : 
     643             :             bool iddFilePathdEnergy =
     644          29 :                 (FileSystem::getAbsolutePath(state.dataStrGlobals->inputIddFilePath) == FileSystem::getAbsolutePath("Energy+.idd"));
     645             : 
     646          29 :             if (!inputFilePathdIn) {
     647          29 :                 FileSystem::linkFile(state.dataStrGlobals->inputFilePath, "in.idf");
     648             :             }
     649          29 :             if (!iddFilePathdEnergy) {
     650          29 :                 FileSystem::linkFile(state.dataStrGlobals->inputIddFilePath, "Energy+.idd");
     651             :             }
     652             : 
     653          29 :             FileSystem::systemCall(expandObjectsCommand);
     654          29 :             if (!inputFilePathdIn) {
     655          29 :                 FileSystem::removeFile("in.idf");
     656             :             }
     657          29 :             if (!iddFilePathdEnergy) {
     658          29 :                 FileSystem::removeFile("Energy+.idd");
     659             :             }
     660             : 
     661          29 :             FileSystem::moveFile("expandedidf.err", outputExperrFilePath);
     662          29 :             if (FileSystem::fileExists("expanded.idf")) {
     663          29 :                 FileSystem::moveFile("expanded.idf", outputExpidfFilePath);
     664          29 :                 state.dataStrGlobals->inputFilePath = outputExpidfFilePath;
     665             :             }
     666          29 :         }
     667             : 
     668         796 :         return static_cast<int>(ReturnCodes::Success);
     669         796 :     }
     670             : 
     671             :     // Fix This is Fortranic code that needs to be brought up to C++ style
     672             :     //     All the index and len and strip should be eliminated and replaced by string calls only where needed
     673             :     //     I/o with std::string should not be pulling in trailing blanks so stripping should not be needed, etc.
     674             :     //     Rewinding is a big performance hit and should be avoided if possible
     675             :     //     Case-insensitive comparison is much faster than converting strings to upper or lower case
     676             :     //     Each strip and case conversion is a heap hit and should be avoided if possible
     677           0 :     void ReadINIFile(InputFile &inputFile,               // Unit number of the opened INI file
     678             :                      std::string const &Heading,         // Heading for the parameters ('[heading]')
     679             :                      std::string const &KindofParameter, // Kind of parameter to be found (String)
     680             :                      std::string &DataOut                // Output from the retrieval
     681             :     )
     682             :     {
     683             : 
     684             :         // SUBROUTINE INFORMATION:
     685             :         //       AUTHOR         Linda K. Lawrie
     686             :         //       DATE WRITTEN   September 1997
     687             :         //       MODIFIED       na
     688             :         //       RE-ENGINEERED  na
     689             : 
     690             :         // PURPOSE OF THIS SUBROUTINE:
     691             :         // This routine reads the .ini file and retrieves
     692             :         // the path names for the files from it.
     693             : 
     694             :         // METHODOLOGY EMPLOYED:
     695             :         // Duplicate the kind of reading the Windows "GetINISetting" would
     696             :         // do.
     697             : 
     698             :         // REFERENCES:
     699             :         // na
     700             : 
     701             :         // Using/Aliasing
     702             :         using namespace EnergyPlus;
     703             :         using namespace DataStringGlobals;
     704             : 
     705             :         // Locals
     706             :         // SUBROUTINE ARGUMENT DEFINITIONS:
     707             : 
     708             :         // SUBROUTINE PARAMETER DEFINITIONS:
     709             : 
     710             :         // INTERFACE BLOCK SPECIFICATIONS
     711             :         // na
     712             : 
     713             :         // DERIVED TYPE DEFINITIONS
     714             :         // na
     715             : 
     716             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     717             : 
     718           0 :         std::string Param;
     719             :         std::string::size_type ILB;
     720             :         std::string::size_type IRB;
     721             :         std::string::size_type IEQ;
     722             :         std::string::size_type IPAR;
     723             :         std::string::size_type IPOS;
     724             :         std::string::size_type ILEN;
     725             : 
     726             :         // Formats
     727             : 
     728           0 :         DataOut.clear();
     729             : 
     730             :         // I tried ADJUSTL(TRIM(KindofParameter)) and got an internal compiler error
     731             : 
     732           0 :         Param = KindofParameter;
     733           0 :         strip(Param);
     734           0 :         ILEN = len(Param);
     735           0 :         inputFile.rewind();
     736           0 :         bool Found = false;
     737           0 :         bool NewHeading = false;
     738             : 
     739           0 :         while (inputFile.good() && !Found) {
     740           0 :             EnergyPlus::InputFile::ReadResult const readResult = inputFile.readLine();
     741             : 
     742           0 :             if (readResult.eof) {
     743           0 :                 break;
     744             :             }
     745             : 
     746           0 :             if (readResult.data.empty()) {
     747           0 :                 continue;
     748             :             } // Ignore Blank Lines
     749             : 
     750           0 :             std::string LINEOut;
     751           0 :             ConvertCaseToLower(readResult.data, LINEOut); // Turn line into lower case
     752             :             //        LINE=LINEOut
     753             : 
     754           0 :             if (!has(LINEOut, Heading)) continue;
     755             : 
     756             :             //                                  See if [ and ] are on line
     757           0 :             ILB = index(LINEOut, '[');
     758           0 :             IRB = index(LINEOut, ']');
     759           0 :             if (ILB == std::string::npos && IRB == std::string::npos) continue;
     760           0 :             if (!has(LINEOut, '[' + Heading + ']')) continue; // Must be really correct heading line
     761             : 
     762             :             //                                  Heading line found, now looking for Kind
     763           0 :             while (inputFile.good() && !NewHeading) {
     764           0 :                 const auto innerReadResult = inputFile.readLine(); // readLine returns a ReadResult<std::string>, hence no & (THIS_AUTO_OK)
     765           0 :                 if (innerReadResult.eof) {
     766           0 :                     break;
     767             :                 }
     768             : 
     769           0 :                 std::string line = innerReadResult.data;
     770           0 :                 strip(line);
     771             : 
     772           0 :                 if (line.empty()) continue; // Ignore Blank Lines
     773             : 
     774           0 :                 ConvertCaseToLower(line, LINEOut); // Turn line into lower case
     775             :                 //         LINE=LINEOut
     776             : 
     777           0 :                 ILB = index(LINEOut, '[');
     778           0 :                 IRB = index(LINEOut, ']');
     779           0 :                 NewHeading = (ILB != std::string::npos && IRB != std::string::npos);
     780             : 
     781             :                 //                                  Should be a parameter line
     782             :                 //                                  KindofParameter = string
     783           0 :                 IEQ = index(LINEOut, '=');
     784           0 :                 IPAR = index(LINEOut, Param);
     785           0 :                 if (IEQ == std::string::npos) continue;
     786           0 :                 if (IPAR == std::string::npos) continue;
     787           0 :                 if (IPAR != 0) continue;
     788           0 :                 if (!has(LINEOut, Param + '=')) continue; // needs to be param=
     789             : 
     790             :                 //                                  = found and parameter found.
     791           0 :                 if (IPAR > IEQ) continue;
     792             : 
     793             :                 //                                  parameter = found
     794             :                 //                                  Set output string to start with non-blank character
     795             : 
     796           0 :                 DataOut = stripped(line.substr(IEQ + 1));
     797           0 :                 Found = true;
     798           0 :                 break;
     799           0 :             }
     800           0 :         }
     801             : 
     802           0 :         if (Param == "dir") {
     803           0 :             IPOS = len(DataOut);
     804           0 :             if (IPOS != 0) {
     805             :                 // Non-blank make sure last position is valid path character
     806             :                 //  (Set in DataStringGlobals)
     807             : 
     808           0 :                 if (DataOut[IPOS - 1] != pathChar) {
     809           0 :                     DataOut += pathChar;
     810             :                 }
     811             :             }
     812             :         }
     813           0 :     }
     814             : 
     815         794 :     int runReadVarsESO(EnergyPlusData &state)
     816             :     {
     817        1588 :         fs::path readVarsPath = (state.dataStrGlobals->exeDirectoryPath / "ReadVarsESO").replace_extension(FileSystem::exeExtension);
     818             : 
     819         794 :         if (!FileSystem::fileExists(readVarsPath)) {
     820           0 :             readVarsPath = (state.dataStrGlobals->exeDirectoryPath / "PostProcess" / "ReadVarsESO").replace_extension(FileSystem::exeExtension);
     821           0 :             if (!FileSystem::fileExists(readVarsPath)) {
     822             :                 // should report the error differently if the user is calling into E+ through EXE or DLL
     823           0 :                 if (state.dataGlobal->eplusRunningViaAPI) {
     824           0 :                     DisplayString(
     825             :                         state,
     826             :                         "ERROR: Could not find ReadVarsESO executable.  When calling through C API, make sure to call setEnergyPlusRootDirectory");
     827             :                 } else {
     828           0 :                     DisplayString(state, fmt::format("ERROR: Could not find ReadVarsESO executable: {}.", FileSystem::getAbsolutePath(readVarsPath)));
     829             :                 }
     830           0 :                 return static_cast<int>(ReturnCodes::Failure);
     831             :             }
     832             :         }
     833             : 
     834        1588 :         fs::path const RVIfile = (state.dataStrGlobals->inputDirPath / state.dataStrGlobals->inputFilePathNameOnly).replace_extension(".rvi");
     835        1588 :         fs::path const MVIfile = (state.dataStrGlobals->inputDirPath / state.dataStrGlobals->inputFilePathNameOnly).replace_extension(".mvi");
     836             : 
     837         794 :         const bool rviFileExists = FileSystem::fileExists(RVIfile);
     838         794 :         if (!rviFileExists) {
     839         641 :             std::ofstream ofs{RVIfile};
     840         641 :             if (!ofs.good()) {
     841           0 :                 ShowFatalError(state, format("EnergyPlus: Could not open file \"{}\" for output (write).", RVIfile));
     842             :             } else {
     843         641 :                 ofs << FileSystem::toString(state.files.eso.filePath) << '\n';
     844         641 :                 ofs << FileSystem::toString(state.files.csv.filePath) << '\n';
     845             :             }
     846         641 :         }
     847             : 
     848         794 :         const bool mviFileExists = FileSystem::fileExists(MVIfile);
     849         794 :         if (!mviFileExists) {
     850         781 :             std::ofstream ofs{MVIfile};
     851         781 :             if (!ofs.good()) {
     852           0 :                 ShowFatalError(state, format("EnergyPlus: Could not open file \"{}\" for output (write).", RVIfile));
     853             :             } else {
     854         781 :                 ofs << FileSystem::toString(state.files.mtr.filePath) << '\n';
     855         781 :                 ofs << FileSystem::toString(state.files.mtr_csv.filePath) << '\n';
     856             :             }
     857         781 :         }
     858             : 
     859             :         // We quote the paths in case we have spaces
     860             :         // "/Path/to/ReadVarEso" "/Path/to/folder with spaces/file.rvi" unlimited
     861        1588 :         std::string const readVarsRviCommand = "\"" + FileSystem::toString(readVarsPath) + "\" \"" + FileSystem::toString(RVIfile) + "\" unlimited";
     862        1588 :         std::string const readVarsMviCommand = "\"" + FileSystem::toString(readVarsPath) + "\" \"" + FileSystem::toString(MVIfile) + "\" unlimited";
     863             : 
     864             :         // systemCall will be responsible to handle to above command on Windows versus Unix
     865         794 :         FileSystem::systemCall(readVarsRviCommand);
     866         794 :         FileSystem::systemCall(readVarsMviCommand);
     867             : 
     868         794 :         if (!rviFileExists) {
     869         641 :             FileSystem::removeFile(RVIfile);
     870             :         }
     871             : 
     872         794 :         if (!mviFileExists) {
     873         781 :             FileSystem::removeFile(MVIfile);
     874             :         }
     875             : 
     876         794 :         FileSystem::moveFile("readvars.audit", state.dataStrGlobals->outputRvauditFilePath);
     877         794 :         return static_cast<int>(ReturnCodes::Success);
     878         794 :     }
     879             : 
     880             : } // namespace CommandLineInterface
     881             : } // namespace EnergyPlus

Generated by: LCOV version 1.14