LCOV - code coverage report
Current view: top level - EnergyPlus - CommandLineInterface.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 270 481 56.1 %
Date: 2023-01-17 19:17:23 Functions: 5 6 83.3 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, 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 <ezOptionParser.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             :     using namespace ez;
      69             : 
      70         771 :     int ProcessArgs(EnergyPlusData &state, int argc, const char *argv[])
      71             :     {
      72             :         typedef std::string::size_type size_type;
      73             : 
      74             :         // Expand long-name options using "=" sign in to two arguments
      75             :         // and expand multiple short options into separate arguments
      76        1542 :         std::vector<std::string> arguments;
      77             : 
      78        6969 :         for (int i = 0; i < argc; ++i) {
      79             : 
      80       12396 :             std::string inputArg(argv[i]);
      81             : 
      82       12396 :             std::string const dash("-");
      83        6198 :             size_type const doubleDashPosition = inputArg.find("--");
      84        6198 :             size_type const equalsPosition = inputArg.find("=");
      85             : 
      86        6198 :             if (doubleDashPosition == 0 && equalsPosition != std::string::npos) { // --option=value
      87           0 :                 arguments.push_back(inputArg.substr(0, equalsPosition));
      88           0 :                 arguments.push_back(inputArg.substr(equalsPosition + 1, inputArg.size() - 1));
      89        6198 :             } else if ((inputArg.size() > 2) && (inputArg[0] == '-') && (inputArg[1] != '-')) { // -abc style
      90           0 :                 for (size_type c = 1; c < inputArg.size(); ++c) {
      91           0 :                     arguments.push_back(dash + inputArg[c]);
      92             :                 }
      93             :             } else { // ?
      94        6198 :                 arguments.push_back(inputArg);
      95             :             }
      96             :         }
      97             : 
      98             :         // Fix This is problematic for a few reasons:
      99             :         //  Using ezOptionParser with a raw C-string interface is asking for trouble: Find something taking std::string if possible
     100             :         //  Passing out pointers returned by c_str() is bad form:
     101             :         //   They are pointers to internally-managed memory in std::string
     102             :         //   They are invalid as soon as the string goes out of scope or is modified
     103             :         //   In this case the strings may be in scope and unmodified until parse is done but this is red flag usage
     104             :         // convert to vector of C strings for option parser
     105        1542 :         std::vector<const char *> cStrArgs;
     106         771 :         cStrArgs.reserve(arguments.size());
     107        6969 :         for (size_type i = 0; i < arguments.size(); ++i) {
     108        6198 :             cStrArgs.push_back(arguments[i].c_str());
     109             :         }
     110             : 
     111         771 :         size_type const argCount = cStrArgs.size();
     112             : 
     113         771 :         bool const legacyMode = (argCount == 1);
     114             : 
     115             :         // Define options
     116        1542 :         ezOptionParser opt;
     117             : 
     118         771 :         opt.overview = state.dataStrGlobals->VerStringVar;
     119         771 :         opt.overview.append("\nPythonLinkage: " + PluginManagement::pythonStringForUsage(state));
     120         771 :         opt.overview.append("\nBuilt on Platform: " + DataStringGlobals::BuildPlatformString);
     121             : 
     122         771 :         opt.syntax = "energyplus [options] [input-file]";
     123             : 
     124         771 :         opt.add("", 0, 0, 0, "Force annual simulation", "-a", "--annual");
     125             : 
     126         771 :         opt.add("", 0, 1, 0, "Output directory path (default: current directory)", "-d", "--output-directory");
     127             : 
     128         771 :         opt.add("", 0, 0, 0, "Force design-day-only simulation", "-D", "--design-day");
     129             : 
     130         771 :         opt.add("", 0, 0, 0, "Display help information", "-h", "--help");
     131             : 
     132         771 :         opt.add("Energy+.idd", 0, 1, 0, "Input data dictionary path (default: Energy+.idd in executable directory)", "-i", "--idd");
     133             : 
     134         771 :         opt.add("", 0, 0, 0, "Run EPMacro prior to simulation", "-m", "--epmacro");
     135             : 
     136         771 :         opt.add("", 0, 1, 0, "Prefix for output file names (default: eplus)", "-p", "--output-prefix");
     137             : 
     138         771 :         opt.add("", 0, 0, 0, "Run ReadVarsESO after simulation", "-r", "--readvars");
     139             : 
     140         771 :         opt.add("", 0, 0, 0, "Output IDF->epJSON or epJSON->IDF, dependent on input file type", "-c", "--convert");
     141             : 
     142         771 :         opt.add("", 0, 0, 0, "Only convert IDF->epJSON or epJSON->IDF, dependent on input file type. No simulation", "--convert-only");
     143             : 
     144         771 :         opt.add("L",
     145             :                 0,
     146             :                 1,
     147             :                 0,
     148             :                 "Suffix style for output file names (default: L)\n   L: Legacy (e.g., eplustbl.csv)\n   C: Capital (e.g., eplusTable.csv)\n   D: "
     149             :                 "Dash (e.g., eplus-table.csv)",
     150             :                 "-s",
     151             :                 "--output-suffix");
     152             : 
     153         771 :         opt.add("", false, 0, 0, "Display version information", "-v", "--version");
     154             : 
     155         771 :         opt.add("1", false, 1, 0, "Multi-thread with N threads; 1 thread with no arg. (Currently only for G-Function generation)", "-j", "--jobs");
     156             : 
     157         771 :         opt.add("in.epw", false, 1, 0, "Weather file path (default: in.epw in current directory)", "-w", "--weather");
     158             : 
     159         771 :         opt.add("", false, 0, 0, "Run ExpandObjects prior to simulation", "-x", "--expandobjects");
     160             : 
     161         771 :         opt.example = "energyplus -w weather.epw -r input.idf";
     162             : 
     163        1542 :         std::string errorFollowUp = "Type 'energyplus --help' for usage.";
     164             : 
     165             :         // Parse arguments
     166         771 :         opt.parse(argCount, &cStrArgs[0]);
     167             : 
     168             :         // print arguments parsed (useful for debugging)
     169             :         //        std::string pretty;
     170             :         //        opt.prettyPrint(pretty);
     171             :         //        std::cout << pretty << std::endl;
     172             : 
     173        1542 :         std::string usage;
     174         771 :         opt.getUsage(usage);
     175             : 
     176             :         // Set path of EnergyPlus program path (if we aren't overriding it)
     177         771 :         if (!state.dataGlobal->installRootOverride) {
     178         771 :             state.dataStrGlobals->exeDirectoryPath = FileSystem::getParentDirectoryPath(FileSystem::getAbsolutePath(FileSystem::getProgramPath()));
     179             :         }
     180             : 
     181             :         // ezOptionParser doesn't have a getPath, so we get a string, then convert it to path
     182             :         // I'm limiting the scope of the std::string temporaries I used to avoid mistakes
     183             :         {
     184        1542 :             std::string inputWeatherFileName;
     185         771 :             opt.get("-w")->getString(inputWeatherFileName);
     186         771 :             state.files.inputWeatherFilePath.filePath = fs::path{inputWeatherFileName};
     187             :         }
     188             : 
     189             :         {
     190             :             // TODO: should this be in IOFiles as an InputFile?
     191        1542 :             std::string inputIddFileName;
     192         771 :             opt.get("-i")->getString(inputIddFileName);
     193         771 :             state.dataStrGlobals->inputIddFilePath = fs::path{inputIddFileName};
     194             :         }
     195             : 
     196         771 :         if (!opt.isSet("-i") && !legacyMode) {
     197         771 :             state.dataStrGlobals->inputIddFilePath = state.dataStrGlobals->exeDirectoryPath / state.dataStrGlobals->inputIddFilePath;
     198             :         }
     199             : 
     200             :         {
     201        1542 :             std::string outDirPathName;
     202         771 :             opt.get("-d")->getString(outDirPathName);
     203         771 :             state.dataStrGlobals->outDirPath = fs::path{outDirPathName};
     204             :         }
     205             : 
     206         771 :         state.dataGlobal->runReadVars = opt.isSet("-r");
     207             : 
     208         771 :         state.dataGlobal->DDOnlySimulation = opt.isSet("-D");
     209             : 
     210         771 :         state.dataGlobal->AnnualSimulation = opt.isSet("-a");
     211             : 
     212         771 :         state.dataGlobal->outputEpJSONConversion = opt.isSet("-c");
     213             : 
     214         771 :         state.dataGlobal->outputEpJSONConversionOnly = opt.isSet("--convert-only");
     215             : 
     216         771 :         bool eplusRunningViaAPI = state.dataGlobal->eplusRunningViaAPI;
     217             : 
     218         771 :         opt.get("-j")->getInt(state.dataGlobal->numThread);
     219             : 
     220         771 :         if (state.dataGlobal->numThread == 0) {
     221           0 :             DisplayString(state, "Invalid value for -j arg. Defaulting to 1.");
     222           0 :             state.dataGlobal->numThread = 1;
     223         771 :         } else if (state.dataGlobal->numThread > (int)std::thread::hardware_concurrency()) {
     224           0 :             DisplayString(state,
     225           0 :                           fmt::format("Invalid value for -j arg. Value exceeds num available. Defaulting to num available. -j {}",
     226           0 :                                       (int)std::thread::hardware_concurrency()));
     227           0 :             state.dataGlobal->numThread = (int)std::thread::hardware_concurrency();
     228             :         }
     229             : 
     230             :         // Process standard arguments
     231         771 :         if (opt.isSet("-h")) {
     232           0 :             DisplayString(state, usage);
     233           0 :             if (eplusRunningViaAPI) {
     234             :                 // we need another return code to let runEnergyPlusAsLibrary know it should not try to run anything
     235           0 :                 return static_cast<int>(ReturnCodes::SuccessButHelper);
     236             :             } else {
     237           0 :                 exit(EXIT_SUCCESS);
     238             :             }
     239             :         }
     240             : 
     241         771 :         if (opt.isSet("-v")) {
     242           0 :             DisplayString(state, state.dataStrGlobals->VerStringVar);
     243           0 :             if (eplusRunningViaAPI) {
     244             :                 // we need another return code to let runEnergyPlusAsLibrary know it should not try to run anything
     245           0 :                 return static_cast<int>(ReturnCodes::SuccessButHelper);
     246             :             } else {
     247           0 :                 exit(EXIT_SUCCESS);
     248             :             }
     249             :         }
     250             : 
     251         771 :         if (opt.lastArgs.size() == 1) {
     252         771 :             state.dataStrGlobals->inputFilePath = fs::path{*opt.lastArgs[0]};
     253           0 :         } else if (opt.lastArgs.empty()) {
     254           0 :             state.dataStrGlobals->inputFilePath = "in.idf";
     255             :         }
     256             : 
     257             :         // Convert all paths to native paths
     258         771 :         state.dataStrGlobals->inputFilePath = FileSystem::makeNativePath(state.dataStrGlobals->inputFilePath);
     259         771 :         state.files.inputWeatherFilePath.filePath = FileSystem::makeNativePath(state.files.inputWeatherFilePath.filePath);
     260         771 :         state.dataStrGlobals->inputIddFilePath = FileSystem::makeNativePath(state.dataStrGlobals->inputIddFilePath);
     261         771 :         state.dataStrGlobals->outDirPath = FileSystem::makeNativePath(state.dataStrGlobals->outDirPath);
     262             : 
     263        1542 :         std::vector<std::string> badOptions;
     264         771 :         if (opt.lastArgs.size() > 1u) {
     265           0 :             bool invalidOptionFound = false;
     266           0 :             for (size_type i = 0; i < opt.lastArgs.size(); ++i) {
     267           0 :                 std::string const &arg(*opt.lastArgs[i]);
     268           0 :                 if (arg.substr(0, 1) == "-") {
     269           0 :                     invalidOptionFound = true;
     270           0 :                     DisplayString(state, "ERROR: Invalid option: " + arg);
     271             :                 }
     272             :             }
     273           0 :             if (invalidOptionFound) {
     274           0 :                 DisplayString(state, errorFollowUp);
     275           0 :                 if (eplusRunningViaAPI) {
     276           0 :                     return static_cast<int>(ReturnCodes::Failure);
     277             :                 } else {
     278           0 :                     exit(EXIT_FAILURE);
     279             :                 }
     280             :             } else {
     281           0 :                 DisplayString(state, "ERROR: Multiple input files specified:");
     282           0 :                 for (size_type i = 0; i < opt.lastArgs.size(); ++i) {
     283           0 :                     std::string const &arg(*opt.lastArgs[i]);
     284           0 :                     DisplayString(state, format("  Input file #{}: {}", i + 1, arg));
     285             :                 }
     286           0 :                 DisplayString(state, errorFollowUp);
     287           0 :                 if (eplusRunningViaAPI) {
     288           0 :                     return static_cast<int>(ReturnCodes::Failure);
     289             :                 } else {
     290           0 :                     exit(EXIT_FAILURE);
     291             :                 }
     292             :             }
     293             :         }
     294             : 
     295         771 :         state.dataStrGlobals->inputFilePathNameOnly = FileSystem::getFileName(state.dataStrGlobals->inputFilePath);
     296         771 :         state.dataStrGlobals->inputDirPath = FileSystem::getParentDirectoryPath(state.dataStrGlobals->inputFilePath);
     297             : 
     298             :         {
     299         771 :             FileSystem::FileTypes const fileType = FileSystem::getFileType(state.dataStrGlobals->inputFilePath);
     300         771 :             state.dataGlobal->isEpJSON = FileSystem::is_all_json_type(fileType);
     301         771 :             switch (fileType) {
     302         771 :             case FileSystem::FileTypes::IDF:
     303             :             case FileSystem::FileTypes::IMF:
     304             :             case FileSystem::FileTypes::EpJSON:
     305             :             case FileSystem::FileTypes::JSON:
     306         771 :                 break;
     307           0 :             case FileSystem::FileTypes::CBOR:
     308           0 :                 DisplayString(state, "CBOR input format is experimental and unsupported.");
     309           0 :                 break;
     310           0 :             case FileSystem::FileTypes::MsgPack:
     311           0 :                 DisplayString(state, "MsgPack input format is experimental and unsupported.");
     312           0 :                 break;
     313           0 :             case FileSystem::FileTypes::UBJSON:
     314           0 :                 DisplayString(state, "UBJSON input format is experimental and unsupported.");
     315           0 :                 break;
     316           0 :             case FileSystem::FileTypes::BSON:
     317           0 :                 DisplayString(state, "BSON input format is experimental and unsupported.");
     318           0 :                 break;
     319           0 :             default:
     320           0 :                 DisplayString(state, "ERROR: Input file must have IDF, IMF, or epJSON extension.");
     321           0 :                 if (eplusRunningViaAPI) {
     322           0 :                     return static_cast<int>(ReturnCodes::Failure);
     323             :                 } else {
     324           0 :                     exit(EXIT_FAILURE);
     325             :                 }
     326             :             }
     327             :         }
     328             : 
     329         771 :         bool runExpandObjects = opt.isSet("-x");
     330         771 :         bool runEPMacro = opt.isSet("-m");
     331             : 
     332         771 :         if (opt.isSet("-d")) {
     333             :             // Create directory if it doesn't already exist
     334         771 :             FileSystem::makeDirectory(state.dataStrGlobals->outDirPath);
     335             :         }
     336             : 
     337             :         // File naming scheme
     338        1542 :         fs::path outputFilePrefixFullPath;
     339         771 :         if (opt.isSet("-p")) {
     340           0 :             std::string prefixOutName;
     341           0 :             opt.get("-p")->getString(prefixOutName);
     342           0 :             fs::path prefix = FileSystem::makeNativePath(fs::path(prefixOutName)); // Why is this needed?
     343           0 :             outputFilePrefixFullPath = state.dataStrGlobals->outDirPath / prefixOutName;
     344             :         } else {
     345         771 :             outputFilePrefixFullPath = state.dataStrGlobals->outDirPath / "eplus";
     346             :         }
     347             : 
     348        1542 :         fs::path outputEpmdetFilePath;
     349        1542 :         fs::path outputEpmidfFilePath;
     350             : 
     351        1542 :         fs::path outputExpidfFilePath;
     352        1542 :         fs::path outputExperrFilePath;
     353             : 
     354        1542 :         std::string normalSuffix;
     355        1542 :         std::string tableSuffix;
     356        1542 :         std::string mapSuffix;
     357        1542 :         std::string zszSuffix;
     358        1542 :         std::string sszSuffix;
     359        1542 :         std::string meterSuffix;
     360        1542 :         std::string sqliteSuffix;
     361        1542 :         std::string adsSuffix;
     362        1542 :         std::string screenSuffix;
     363        1542 :         std::string shdSuffix;
     364             : 
     365        1542 :         std::string suffixType;
     366             :         {
     367         771 :             opt.get("-s")->getString(suffixType);
     368         771 :             std::transform((suffixType).begin(), (suffixType).end(), (suffixType).begin(), ::toupper);
     369             : 
     370         771 :             if (suffixType == "L") {
     371             : 
     372         771 :                 normalSuffix = "out";
     373         771 :                 tableSuffix = "tbl";
     374         771 :                 mapSuffix = "map";
     375         771 :                 zszSuffix = "zsz";
     376         771 :                 sszSuffix = "ssz";
     377         771 :                 meterSuffix = "mtr";
     378         771 :                 sqliteSuffix = "sqlite";
     379         771 :                 adsSuffix = "ADS";
     380         771 :                 screenSuffix = "screen";
     381         771 :                 shdSuffix = "shading";
     382             : 
     383           0 :             } else if (suffixType == "D") {
     384             : 
     385           0 :                 normalSuffix = "";
     386           0 :                 tableSuffix = "-table";
     387           0 :                 mapSuffix = "-map";
     388           0 :                 zszSuffix = "-zsz";
     389           0 :                 sszSuffix = "-ssz";
     390           0 :                 meterSuffix = "-meter";
     391           0 :                 sqliteSuffix = "-sqlite";
     392           0 :                 adsSuffix = "-ads";
     393           0 :                 screenSuffix = "-screen";
     394           0 :                 shdSuffix = "-shading";
     395             : 
     396           0 :             } else if (suffixType == "C") {
     397             : 
     398           0 :                 normalSuffix = "";
     399           0 :                 tableSuffix = "Table";
     400           0 :                 mapSuffix = "Map";
     401           0 :                 zszSuffix = "Zsz";
     402           0 :                 sszSuffix = "Ssz";
     403           0 :                 meterSuffix = "Meter";
     404           0 :                 sqliteSuffix = "Sqlite";
     405           0 :                 adsSuffix = "Ads";
     406           0 :                 screenSuffix = "Screen";
     407           0 :                 shdSuffix = "Shading";
     408             : 
     409             :             } else {
     410           0 :                 DisplayString(state, "ERROR: Unrecognized argument for output suffix style: " + suffixType);
     411           0 :                 DisplayString(state, errorFollowUp);
     412           0 :                 if (eplusRunningViaAPI) {
     413           0 :                     return static_cast<int>(ReturnCodes::Failure);
     414             :                 } else {
     415           0 :                     exit(EXIT_FAILURE);
     416             :                 }
     417             :             }
     418             :         }
     419             : 
     420             :         // Helper to construct output file path
     421       56283 :         auto composePath = [&outputFilePrefixFullPath](const std::string &suffix) -> fs::path {
     422       56283 :             return fs::path(outputFilePrefixFullPath.string() + suffix);
     423         771 :         };
     424             : 
     425             :         // EnergyPlus files
     426         771 :         state.files.audit.filePath = composePath(normalSuffix + ".audit");
     427         771 :         state.files.bnd.filePath = composePath(normalSuffix + ".bnd");
     428         771 :         state.files.dxf.filePath = composePath(normalSuffix + ".dxf");
     429         771 :         state.files.eio.filePath = composePath(normalSuffix + ".eio");
     430         771 :         state.files.endFile.filePath = composePath(normalSuffix + ".end");
     431         771 :         state.files.outputErrFilePath = composePath(normalSuffix + ".err");
     432         771 :         state.files.eso.filePath = composePath(normalSuffix + ".eso");
     433             : 
     434         771 :         state.files.json.outputJsonFilePath = composePath(normalSuffix + ".json");
     435         771 :         state.files.json.outputTSZoneJsonFilePath = composePath(normalSuffix + "_detailed_zone.json");
     436         771 :         state.files.json.outputTSHvacJsonFilePath = composePath(normalSuffix + "_detailed_HVAC.json");
     437         771 :         state.files.json.outputTSJsonFilePath = composePath(normalSuffix + "_timestep.json");
     438         771 :         state.files.json.outputYRJsonFilePath = composePath(normalSuffix + "_yearly.json");
     439         771 :         state.files.json.outputMNJsonFilePath = composePath(normalSuffix + "_monthly.json");
     440         771 :         state.files.json.outputDYJsonFilePath = composePath(normalSuffix + "_daily.json");
     441         771 :         state.files.json.outputHRJsonFilePath = composePath(normalSuffix + "_hourly.json");
     442         771 :         state.files.json.outputSMJsonFilePath = composePath(normalSuffix + "_runperiod.json");
     443         771 :         state.files.json.outputCborFilePath = composePath(normalSuffix + ".cbor");
     444         771 :         state.files.json.outputTSZoneCborFilePath = composePath(normalSuffix + "_detailed_zone.cbor");
     445         771 :         state.files.json.outputTSHvacCborFilePath = composePath(normalSuffix + "_detailed_HVAC.cbor");
     446         771 :         state.files.json.outputTSCborFilePath = composePath(normalSuffix + "_timestep.cbor");
     447         771 :         state.files.json.outputYRCborFilePath = composePath(normalSuffix + "_yearly.cbor");
     448         771 :         state.files.json.outputMNCborFilePath = composePath(normalSuffix + "_monthly.cbor");
     449         771 :         state.files.json.outputDYCborFilePath = composePath(normalSuffix + "_daily.cbor");
     450         771 :         state.files.json.outputHRCborFilePath = composePath(normalSuffix + "_hourly.cbor");
     451         771 :         state.files.json.outputSMCborFilePath = composePath(normalSuffix + "_runperiod.cbor");
     452         771 :         state.files.json.outputMsgPackFilePath = composePath(normalSuffix + ".msgpack");
     453         771 :         state.files.json.outputTSZoneMsgPackFilePath = composePath(normalSuffix + "_detailed_zone.msgpack");
     454         771 :         state.files.json.outputTSHvacMsgPackFilePath = composePath(normalSuffix + "_detailed_HVAC.msgpack");
     455         771 :         state.files.json.outputTSMsgPackFilePath = composePath(normalSuffix + "_timestep.msgpack");
     456         771 :         state.files.json.outputYRMsgPackFilePath = composePath(normalSuffix + "_yearly.msgpack");
     457         771 :         state.files.json.outputMNMsgPackFilePath = composePath(normalSuffix + "_monthly.msgpack");
     458         771 :         state.files.json.outputDYMsgPackFilePath = composePath(normalSuffix + "_daily.msgpack");
     459         771 :         state.files.json.outputHRMsgPackFilePath = composePath(normalSuffix + "_hourly.msgpack");
     460         771 :         state.files.json.outputSMMsgPackFilePath = composePath(normalSuffix + "_runperiod.msgpack");
     461             : 
     462         771 :         state.files.mtd.filePath = composePath(normalSuffix + ".mtd");
     463         771 :         state.files.mdd.filePath = composePath(normalSuffix + ".mdd");
     464         771 :         state.files.mtr.filePath = composePath(normalSuffix + ".mtr");
     465         771 :         state.files.rdd.filePath = composePath(normalSuffix + ".rdd");
     466         771 :         state.dataStrGlobals->outputShdFilePath = composePath(normalSuffix + ".shd");
     467         771 :         state.files.dfs.filePath = composePath(normalSuffix + ".dfs");
     468         771 :         state.dataStrGlobals->outputGLHEFilePath = composePath(normalSuffix + ".glhe");
     469         771 :         state.files.edd.filePath = composePath(normalSuffix + ".edd");
     470         771 :         state.dataStrGlobals->outputIperrFilePath = composePath(normalSuffix + ".iperr");
     471         771 :         state.files.sln.filePath = composePath(normalSuffix + ".sln");
     472         771 :         state.files.sci.filePath = composePath(normalSuffix + ".sci");
     473         771 :         state.files.wrl.filePath = composePath(normalSuffix + ".wrl");
     474         771 :         state.dataStrGlobals->outputSqlFilePath = composePath(normalSuffix + ".sql");
     475         771 :         state.files.debug.filePath = composePath(normalSuffix + ".dbg");
     476         771 :         state.dataStrGlobals->outputPerfLogFilePath = composePath(normalSuffix + "_perflog.csv");
     477         771 :         state.dataStrGlobals->outputTblCsvFilePath = composePath(tableSuffix + ".csv");
     478         771 :         state.dataStrGlobals->outputTblHtmFilePath = composePath(tableSuffix + ".htm");
     479         771 :         state.dataStrGlobals->outputTblTabFilePath = composePath(tableSuffix + ".tab");
     480         771 :         state.dataStrGlobals->outputTblTxtFilePath = composePath(tableSuffix + ".txt");
     481         771 :         state.dataStrGlobals->outputTblXmlFilePath = composePath(tableSuffix + ".xml");
     482         771 :         state.files.outputMapTabFilePath = composePath(mapSuffix + ".tab");
     483         771 :         state.files.outputMapCsvFilePath = composePath(mapSuffix + ".csv");
     484         771 :         state.files.outputMapTxtFilePath = composePath(mapSuffix + ".txt");
     485         771 :         state.files.outputZszCsvFilePath = composePath(zszSuffix + ".csv");
     486         771 :         state.files.outputZszTabFilePath = composePath(zszSuffix + ".tab");
     487         771 :         state.files.outputZszTxtFilePath = composePath(zszSuffix + ".txt");
     488         771 :         state.files.outputSszCsvFilePath = composePath(sszSuffix + ".csv");
     489         771 :         state.files.outputSszTabFilePath = composePath(sszSuffix + ".tab");
     490         771 :         state.files.outputSszTxtFilePath = composePath(sszSuffix + ".txt");
     491         771 :         state.dataStrGlobals->outputAdsFilePath = composePath(adsSuffix + ".out");
     492         771 :         state.files.shade.filePath = composePath(shdSuffix + ".csv");
     493         771 :         if (suffixType == "L") {
     494             :             // out/sqlite.err
     495         771 :             state.dataStrGlobals->outputSqliteErrFilePath = state.dataStrGlobals->outDirPath / fs::path{sqliteSuffix + ".err"};
     496             :         } else {
     497             :             // if 'D':  out/eplus-sqlite.err
     498           0 :             state.dataStrGlobals->outputSqliteErrFilePath = composePath(sqliteSuffix + ".err");
     499             :         }
     500             : 
     501         771 :         state.files.screenCsv.filePath = composePath(screenSuffix + ".csv");
     502             :         // TODO, why are these relative paths?
     503         771 :         state.files.delightIn.filePath = "eplusout.delightin";
     504         771 :         state.dataStrGlobals->outputDelightOutFilePath = "eplusout.delightout";
     505             : 
     506             :         // TODO: why is this relative?
     507         771 :         state.files.iniFile.filePath = "Energy+.ini";
     508             : 
     509             :         // Stat next to epw
     510             :         // Careful! fs::path::replace_extension will **mutate the original object**
     511         771 :         state.files.inStatFilePath.filePath = state.files.inputWeatherFilePath.filePath;
     512         771 :         state.files.inStatFilePath.filePath.replace_extension(".stat");
     513             :         // Or is it better to provide a helper that does not mutate like this?
     514             :         // state.files.inStatFilePath.filePath = FileSystem::replaceFileExtension(state.inputWeatherFilePath.filePath, ".stat");
     515             : 
     516         771 :         state.dataStrGlobals->eplusADSFilePath = state.dataStrGlobals->inputDirPath / "eplusADS.inp";
     517             : 
     518             :         // Readvars files
     519         771 :         state.files.csv.filePath = composePath(normalSuffix + ".csv");
     520         771 :         state.files.mtr_csv.filePath = composePath(meterSuffix + ".csv");
     521         771 :         state.dataStrGlobals->outputRvauditFilePath = composePath(normalSuffix + ".rvaudit");
     522             : 
     523             :         // EPMacro files
     524         771 :         outputEpmdetFilePath = composePath(normalSuffix + ".epmdet");
     525         771 :         outputEpmidfFilePath = composePath(normalSuffix + ".epmidf");
     526             : 
     527             :         // ExpandObjects files
     528         771 :         outputExpidfFilePath = composePath(normalSuffix + ".expidf");
     529         771 :         outputExperrFilePath = composePath(normalSuffix + ".experr");
     530             : 
     531             :         // Handle bad options
     532         771 :         if (!opt.gotExpected(badOptions)) {
     533           0 :             for (size_type i = 0; i < badOptions.size(); ++i) {
     534           0 :                 DisplayString(state, "ERROR: Unexpected number of arguments for option " + badOptions[i]);
     535             :             }
     536           0 :             DisplayString(state, errorFollowUp);
     537           0 :             if (eplusRunningViaAPI) {
     538           0 :                 return static_cast<int>(ReturnCodes::Failure);
     539             :             } else {
     540           0 :                 exit(EXIT_FAILURE);
     541             :             }
     542             :         }
     543             : 
     544             :         // This is a placeholder in case there are required options in the future
     545         771 :         if (!opt.gotRequired(badOptions)) {
     546           0 :             for (size_type i = 0; i < badOptions.size(); ++i) {
     547           0 :                 DisplayString(state, "ERROR: Missing required option " + badOptions[i]);
     548             :             }
     549           0 :             DisplayString(state, errorFollowUp);
     550           0 :             if (eplusRunningViaAPI) {
     551           0 :                 return static_cast<int>(ReturnCodes::Failure);
     552             :             } else {
     553           0 :                 exit(EXIT_FAILURE);
     554             :             }
     555             :         }
     556             : 
     557         771 :         if (opt.firstArgs.size() > 1 || opt.unknownArgs.size() > 0) {
     558           0 :             for (size_type i = 1; i < opt.firstArgs.size(); ++i) {
     559           0 :                 std::string const &arg(*opt.firstArgs[i]);
     560           0 :                 DisplayString(state, "ERROR: Invalid option: " + arg);
     561             :             }
     562           0 :             for (size_type i = 0; i < opt.unknownArgs.size(); ++i) {
     563           0 :                 std::string const &arg(*opt.unknownArgs[i]);
     564           0 :                 DisplayString(state, "ERROR: Invalid option: " + arg);
     565             :             }
     566           0 :             DisplayString(state, errorFollowUp);
     567           0 :             if (eplusRunningViaAPI) {
     568           0 :                 return static_cast<int>(ReturnCodes::Failure);
     569             :             } else {
     570           0 :                 exit(EXIT_FAILURE);
     571             :             }
     572             :         }
     573             : 
     574             :         // Error for cases where both design-day and annual simulation switches are set
     575         771 :         if (state.dataGlobal->DDOnlySimulation && state.dataGlobal->AnnualSimulation) {
     576           0 :             DisplayString(state, "ERROR: Cannot force both design-day and annual simulations. Set either '-D' or '-a', but not both.");
     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             :         // Read path from INI file if it exists
     586             : 
     587             :         // Check for IDD and IDF files
     588         771 :         if (FileSystem::fileExists(state.files.iniFile.filePath)) {
     589           0 :             EnergyPlus::InputFile iniFile = state.files.iniFile.try_open();
     590           0 :             if (!iniFile.good()) {
     591           0 :                 DisplayString(state, "ERROR: Could not open file " + iniFile.filePath.string() + " for input (read).");
     592           0 :                 if (eplusRunningViaAPI) {
     593           0 :                     return static_cast<int>(ReturnCodes::Failure);
     594             :                 } else {
     595           0 :                     exit(EXIT_FAILURE);
     596             :                 }
     597             :             }
     598           0 :             state.dataStrGlobals->CurrentWorkingFolder = iniFile.filePath;
     599             :             // Relying on compiler to supply full path name here
     600             :             // TODO: not sure I understand this block
     601             :             // const auto TempIndx = index(state.dataStrGlobals->CurrentWorkingFolder, state.dataStrGlobals->pathChar, true);
     602             :             // if (TempIndx == std::string::npos) {
     603             :             // state.dataStrGlobals->CurrentWorkingFolder = "";
     604             :             //} else {
     605             :             // state.dataStrGlobals->CurrentWorkingFolder.erase(TempIndx + 1);
     606             :             //}
     607             :             //       Get directories from ini file
     608           0 :             std::string programPathStr;
     609           0 :             ReadINIFile(iniFile, "program", "dir", programPathStr);
     610           0 :             state.dataStrGlobals->ProgramPath = fs::path(programPathStr);
     611             : 
     612           0 :             state.dataStrGlobals->inputIddFilePath = state.dataStrGlobals->ProgramPath / "Energy+.idd";
     613             :         }
     614             : 
     615             :         // Check if specified files exist
     616         771 :         if (!FileSystem::fileExists(state.dataStrGlobals->inputFilePath)) {
     617           0 :             DisplayString(
     618           0 :                 state, "ERROR: Could not find input data file: " + FileSystem::getAbsolutePath(state.dataStrGlobals->inputFilePath).string() + ".");
     619           0 :             DisplayString(state, errorFollowUp);
     620           0 :             if (eplusRunningViaAPI) {
     621           0 :                 return static_cast<int>(ReturnCodes::Failure);
     622             :             } else {
     623           0 :                 exit(EXIT_FAILURE);
     624             :             }
     625             :         }
     626             : 
     627         771 :         if (opt.isSet("-w") && !state.dataGlobal->DDOnlySimulation) {
     628           2 :             if (!FileSystem::fileExists(state.files.inputWeatherFilePath.filePath)) {
     629           0 :                 DisplayString(
     630             :                     state,
     631           0 :                     "ERROR: Could not find weather file: " + FileSystem::getAbsolutePath(state.files.inputWeatherFilePath.filePath).string() + ".");
     632           0 :                 DisplayString(state, errorFollowUp);
     633           0 :                 if (eplusRunningViaAPI) {
     634           0 :                     return static_cast<int>(ReturnCodes::Failure);
     635             :                 } else {
     636           0 :                     exit(EXIT_FAILURE);
     637             :                 }
     638             :             }
     639             :         }
     640             : 
     641             :         // TODO: might be able to convert epJSON->IDF, run preprocessors, then go back IDF->epJSON
     642             : 
     643             :         // Preprocessors (These will likely move to a new file)
     644         771 :         if (runEPMacro) {
     645           2 :             fs::path epMacroPath = (state.dataStrGlobals->exeDirectoryPath / "EPMacro").replace_extension(FileSystem::exeExtension);
     646           1 :             if (!FileSystem::fileExists(epMacroPath)) {
     647           0 :                 DisplayString(state, "ERROR: Could not find EPMacro executable: " + FileSystem::getAbsolutePath(epMacroPath).string() + ".");
     648           0 :                 if (eplusRunningViaAPI) {
     649           0 :                     return static_cast<int>(ReturnCodes::Failure);
     650             :                 } else {
     651           0 :                     exit(EXIT_FAILURE);
     652             :                 }
     653             :             }
     654           2 :             std::string epMacroCommand = "\"" + epMacroPath.string() + "\"";
     655           1 :             bool inputFilePathdIn = (FileSystem::getAbsolutePath(state.dataStrGlobals->inputFilePath) == FileSystem::getAbsolutePath("in.imf"));
     656             : 
     657           1 :             if (!inputFilePathdIn) {
     658           1 :                 FileSystem::linkFile(state.dataStrGlobals->inputFilePath, "in.imf");
     659             :             }
     660           1 :             DisplayString(state, "Running EPMacro...");
     661           1 :             FileSystem::systemCall(epMacroCommand);
     662           1 :             if (!inputFilePathdIn) {
     663           1 :                 FileSystem::removeFile("in.imf");
     664             :             }
     665           1 :             FileSystem::moveFile("audit.out", outputEpmdetFilePath);
     666           1 :             FileSystem::moveFile("out.idf", outputEpmidfFilePath);
     667           1 :             state.dataStrGlobals->inputFilePath = outputEpmidfFilePath;
     668             :         }
     669             : 
     670         771 :         if (runExpandObjects) {
     671             :             fs::path expandObjectsPath =
     672          58 :                 (state.dataStrGlobals->exeDirectoryPath / fs::path("ExpandObjects")).replace_extension(FileSystem::exeExtension);
     673          29 :             if (!FileSystem::fileExists(expandObjectsPath)) {
     674           0 :                 DisplayString(state,
     675           0 :                               "ERROR: Could not find ExpandObjects executable: " + FileSystem::getAbsolutePath(expandObjectsPath).string() + ".");
     676           0 :                 if (eplusRunningViaAPI) {
     677           0 :                     return static_cast<int>(ReturnCodes::Failure);
     678             :                 } else {
     679           0 :                     exit(EXIT_FAILURE);
     680             :                 }
     681             :             }
     682          58 :             std::string expandObjectsCommand = "\"" + expandObjectsPath.string() + "\"";
     683          29 :             bool inputFilePathdIn = (FileSystem::getAbsolutePath(state.dataStrGlobals->inputFilePath) == FileSystem::getAbsolutePath("in.idf"));
     684             : 
     685             :             // check if IDD actually exists since ExpandObjects still requires it
     686          29 :             if (!FileSystem::fileExists(state.dataStrGlobals->inputIddFilePath)) {
     687           0 :                 DisplayString(state,
     688           0 :                               "ERROR: Could not find input data dictionary: " +
     689           0 :                                   FileSystem::getAbsolutePath(state.dataStrGlobals->inputIddFilePath).string() + ".");
     690           0 :                 DisplayString(state, errorFollowUp);
     691           0 :                 if (eplusRunningViaAPI) {
     692           0 :                     return static_cast<int>(ReturnCodes::Failure);
     693             :                 } else {
     694           0 :                     exit(EXIT_FAILURE);
     695             :                 }
     696             :             }
     697             : 
     698             :             bool iddFilePathdEnergy =
     699          29 :                 (FileSystem::getAbsolutePath(state.dataStrGlobals->inputIddFilePath) == FileSystem::getAbsolutePath("Energy+.idd"));
     700             : 
     701          29 :             if (!inputFilePathdIn) {
     702          29 :                 FileSystem::linkFile(state.dataStrGlobals->inputFilePath, "in.idf");
     703             :             }
     704          29 :             if (!iddFilePathdEnergy) {
     705          29 :                 FileSystem::linkFile(state.dataStrGlobals->inputIddFilePath, "Energy+.idd");
     706             :             }
     707             : 
     708          29 :             FileSystem::systemCall(expandObjectsCommand);
     709          29 :             if (!inputFilePathdIn) {
     710          29 :                 FileSystem::removeFile("in.idf");
     711             :             }
     712          29 :             if (!iddFilePathdEnergy) {
     713          29 :                 FileSystem::removeFile("Energy+.idd");
     714             :             }
     715             : 
     716          29 :             FileSystem::moveFile("expandedidf.err", outputExperrFilePath);
     717          29 :             if (FileSystem::fileExists("expanded.idf")) {
     718          29 :                 FileSystem::moveFile("expanded.idf", outputExpidfFilePath);
     719          29 :                 state.dataStrGlobals->inputFilePath = outputExpidfFilePath;
     720             :             }
     721             :         }
     722             : 
     723         771 :         return static_cast<int>(ReturnCodes::Success);
     724             :     }
     725             : 
     726             :     // Fix This is Fortranic code that needs to be brought up to C++ style
     727             :     //     All the index and len and strip should be eliminated and replaced by string calls only where needed
     728             :     //     I/o with std::string should not be pulling in trailing blanks so stripping should not be needed, etc.
     729             :     //     Rewinding is a big performance hit and should be avoided if possible
     730             :     //     Case-insensitive comparison is much faster than converting strings to upper or lower case
     731             :     //     Each strip and case conversion is a heap hit and should be avoided if possible
     732           0 :     void ReadINIFile(InputFile &inputFile,               // Unit number of the opened INI file
     733             :                      std::string const &Heading,         // Heading for the parameters ('[heading]')
     734             :                      std::string const &KindofParameter, // Kind of parameter to be found (String)
     735             :                      std::string &DataOut                // Output from the retrieval
     736             :     )
     737             :     {
     738             : 
     739             :         // SUBROUTINE INFORMATION:
     740             :         //       AUTHOR         Linda K. Lawrie
     741             :         //       DATE WRITTEN   September 1997
     742             :         //       MODIFIED       na
     743             :         //       RE-ENGINEERED  na
     744             : 
     745             :         // PURPOSE OF THIS SUBROUTINE:
     746             :         // This routine reads the .ini file and retrieves
     747             :         // the path names for the files from it.
     748             : 
     749             :         // METHODOLOGY EMPLOYED:
     750             :         // Duplicate the kind of reading the Windows "GetINISetting" would
     751             :         // do.
     752             : 
     753             :         // REFERENCES:
     754             :         // na
     755             : 
     756             :         // Using/Aliasing
     757             :         using namespace EnergyPlus;
     758             :         using namespace DataStringGlobals;
     759             : 
     760             :         // Locals
     761             :         // SUBROUTINE ARGUMENT DEFINITIONS:
     762             : 
     763             :         // SUBROUTINE PARAMETER DEFINITIONS:
     764             : 
     765             :         // INTERFACE BLOCK SPECIFICATIONS
     766             :         // na
     767             : 
     768             :         // DERIVED TYPE DEFINITIONS
     769             :         // na
     770             : 
     771             :         // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
     772             : 
     773           0 :         std::string Param;
     774             :         std::string::size_type ILB;
     775             :         std::string::size_type IRB;
     776             :         std::string::size_type IEQ;
     777             :         std::string::size_type IPAR;
     778             :         std::string::size_type IPOS;
     779             :         std::string::size_type ILEN;
     780             : 
     781             :         // Formats
     782             : 
     783           0 :         DataOut.clear();
     784             : 
     785             :         // I tried ADJUSTL(TRIM(KindofParameter)) and got an internal compiler error
     786             : 
     787           0 :         Param = KindofParameter;
     788           0 :         strip(Param);
     789           0 :         ILEN = len(Param);
     790           0 :         inputFile.rewind();
     791           0 :         bool Found = false;
     792           0 :         bool NewHeading = false;
     793             : 
     794           0 :         while (inputFile.good() && !Found) {
     795           0 :             EnergyPlus::InputFile::ReadResult const readResult = inputFile.readLine();
     796             : 
     797           0 :             if (readResult.eof) {
     798           0 :                 break;
     799             :             }
     800             : 
     801           0 :             if (readResult.data.empty()) {
     802           0 :                 continue;
     803             :             } // Ignore Blank Lines
     804             : 
     805           0 :             std::string LINEOut;
     806           0 :             ConvertCaseToLower(readResult.data, LINEOut); // Turn line into lower case
     807             :             //        LINE=LINEOut
     808             : 
     809           0 :             if (!has(LINEOut, Heading)) continue;
     810             : 
     811             :             //                                  See if [ and ] are on line
     812           0 :             ILB = index(LINEOut, '[');
     813           0 :             IRB = index(LINEOut, ']');
     814           0 :             if (ILB == std::string::npos && IRB == std::string::npos) continue;
     815           0 :             if (!has(LINEOut, '[' + Heading + ']')) continue; // Must be really correct heading line
     816             : 
     817             :             //                                  Heading line found, now looking for Kind
     818           0 :             while (inputFile.good() && !NewHeading) {
     819           0 :                 const auto innerReadResult = inputFile.readLine();
     820           0 :                 if (innerReadResult.eof) {
     821           0 :                     break;
     822             :                 }
     823             : 
     824           0 :                 auto line = innerReadResult.data;
     825           0 :                 strip(line);
     826             : 
     827           0 :                 if (line.empty()) continue; // Ignore Blank Lines
     828             : 
     829           0 :                 ConvertCaseToLower(line, LINEOut); // Turn line into lower case
     830             :                 //         LINE=LINEOut
     831             : 
     832           0 :                 ILB = index(LINEOut, '[');
     833           0 :                 IRB = index(LINEOut, ']');
     834           0 :                 NewHeading = (ILB != std::string::npos && IRB != std::string::npos);
     835             : 
     836             :                 //                                  Should be a parameter line
     837             :                 //                                  KindofParameter = string
     838           0 :                 IEQ = index(LINEOut, '=');
     839           0 :                 IPAR = index(LINEOut, Param);
     840           0 :                 if (IEQ == std::string::npos) continue;
     841           0 :                 if (IPAR == std::string::npos) continue;
     842           0 :                 if (IPAR != 0) continue;
     843           0 :                 if (!has(LINEOut, Param + '=')) continue; // needs to be param=
     844             : 
     845             :                 //                                  = found and parameter found.
     846           0 :                 if (IPAR > IEQ) continue;
     847             : 
     848             :                 //                                  parameter = found
     849             :                 //                                  Set output string to start with non-blank character
     850             : 
     851           0 :                 DataOut = stripped(line.substr(IEQ + 1));
     852           0 :                 Found = true;
     853           0 :                 break;
     854             :             }
     855             :         }
     856             : 
     857           0 :         if (Param == "dir") {
     858           0 :             IPOS = len(DataOut);
     859           0 :             if (IPOS != 0) {
     860             :                 // Non-blank make sure last position is valid path character
     861             :                 //  (Set in DataStringGlobals)
     862             : 
     863           0 :                 if (DataOut[IPOS - 1] != pathChar) {
     864           0 :                     DataOut += pathChar;
     865             :                 }
     866             :             }
     867             :         }
     868           0 :     }
     869             : 
     870         769 :     int runReadVarsESO(EnergyPlusData &state)
     871             :     {
     872        1538 :         fs::path readVarsPath = (state.dataStrGlobals->exeDirectoryPath / "ReadVarsESO").replace_extension(FileSystem::exeExtension);
     873             : 
     874         769 :         if (!FileSystem::fileExists(readVarsPath)) {
     875           0 :             readVarsPath = (state.dataStrGlobals->exeDirectoryPath / "PostProcess" / "ReadVarsESO").replace_extension(FileSystem::exeExtension);
     876           0 :             if (!FileSystem::fileExists(readVarsPath)) {
     877             :                 // should report the error differently if the user is calling into E+ through EXE or DLL
     878           0 :                 if (state.dataGlobal->eplusRunningViaAPI) {
     879           0 :                     DisplayString(
     880             :                         state,
     881             :                         "ERROR: Could not find ReadVarsESO executable.  When calling through C API, make sure to call setEnergyPlusRootDirectory");
     882             :                 } else {
     883           0 :                     DisplayString(state, "ERROR: Could not find ReadVarsESO executable: " + FileSystem::getAbsolutePath(readVarsPath).string() + ".");
     884             :                 }
     885           0 :                 return static_cast<int>(ReturnCodes::Failure);
     886             :             }
     887             :         }
     888             : 
     889        1538 :         fs::path const RVIfile = (state.dataStrGlobals->inputDirPath / state.dataStrGlobals->inputFilePathNameOnly).replace_extension(".rvi");
     890        1538 :         fs::path const MVIfile = (state.dataStrGlobals->inputDirPath / state.dataStrGlobals->inputFilePathNameOnly).replace_extension(".mvi");
     891             : 
     892         769 :         const auto rviFileExists = FileSystem::fileExists(RVIfile);
     893         769 :         if (!rviFileExists) {
     894        1236 :             std::ofstream ofs{RVIfile};
     895         618 :             if (!ofs.good()) {
     896           0 :                 ShowFatalError(state, "EnergyPlus: Could not open file \"" + RVIfile.string() + "\" for output (write).");
     897             :             } else {
     898         618 :                 ofs << state.files.eso.filePath.string() << '\n';
     899         618 :                 ofs << state.files.csv.filePath.string() << '\n';
     900             :             }
     901             :         }
     902             : 
     903         769 :         const auto mviFileExists = FileSystem::fileExists(MVIfile);
     904         769 :         if (!mviFileExists) {
     905        1512 :             std::ofstream ofs{MVIfile};
     906         756 :             if (!ofs.good()) {
     907           0 :                 ShowFatalError(state, "EnergyPlus: Could not open file \"" + RVIfile.string() + "\" for output (write).");
     908             :             } else {
     909         756 :                 ofs << state.files.mtr.filePath.string() << '\n';
     910         756 :                 ofs << state.files.mtr_csv.filePath.string() << '\n';
     911             :             }
     912             :         }
     913             : 
     914             :         // We quote the paths in case we have spaces
     915             :         // "/Path/to/ReadVarEso" "/Path/to/folder with spaces/file.rvi" unlimited
     916        1538 :         std::string const readVarsRviCommand = "\"" + readVarsPath.string() + "\" \"" + RVIfile.string() + "\" unlimited";
     917        1538 :         std::string const readVarsMviCommand = "\"" + readVarsPath.string() + "\" \"" + MVIfile.string() + "\" unlimited";
     918             : 
     919             :         // systemCall will be responsible to handle to above command on Windows versus Unix
     920         769 :         FileSystem::systemCall(readVarsRviCommand);
     921         769 :         FileSystem::systemCall(readVarsMviCommand);
     922             : 
     923         769 :         if (!rviFileExists) {
     924         618 :             FileSystem::removeFile(RVIfile);
     925             :         }
     926             : 
     927         769 :         if (!mviFileExists) {
     928         756 :             FileSystem::removeFile(MVIfile);
     929             :         }
     930             : 
     931         769 :         FileSystem::moveFile("readvars.audit", state.dataStrGlobals->outputRvauditFilePath);
     932         769 :         return static_cast<int>(ReturnCodes::Success);
     933             :     }
     934             : 
     935             : } // namespace CommandLineInterface
     936        2313 : } // namespace EnergyPlus

Generated by: LCOV version 1.13