#include "CreateFile_IDF.h"

//Note: Consider Refactor Ideas:
//- Call i-TreeCoolAir, using 3 pixels (ref, noTree, Tree) and write cell timeseries to get as input for EWP
//- Field: Material:RoofVegetation option
//- Field: Material: Fields: Thermal Absorptance, Solar Absorptance, Visible Absorptance
//- Field: WindowMaterial:Shade or :Blind
//- Field: Building: InternalMass should be the total surface area exposed to air in building
//- Field: Outputs, HVAC HeatEmissionsReportMonthly:HVAC SYSTEM TOTAL HEAT REJECTION ENERGY [J]
//- Advanced Construction
//- Field: ZoneProperty:LocalEnvironment
//- Field: SurfaceProperty:LocalEnvironment links each wall to items above
//- Field: SurfaceProperty:IncidentSolarMultiplier to quickly simulate effect of something like roof overhang option
//- Field: SurfaceProperty:SurroundingSurfaces, Temperature Schedule, Reflectance Schedule
//- Field: SurfaceProperty:GroundSurfaces, Temperature Schedule, Reflectance Schedule
//- Field: ZoneHVAC:IdealLoadsAirSystem,ZoneHVAC_ForcedAir,,IdealZoneSupply is 100% efficient,saves modeling a specific HVAC 
//Note: E+ macro expandObjects.exe by default searches for in.idf and Energy+.idd in working directory, which can be redefined.  
//Note: ... To redefine, in PowerShell: 
//Note: > Start - Process - FilePath ExpandObjects.exe `
//Note: >>  - WorkingDirectory "C:\iTree\projects\a_TestCase\Floors01_Trees03_MN\input\ExpIDF_V2" `
//Note: >>  - NoNewWindow - Wait

//References:
//City-Scale Building Anthropogenic Heating during Heat Waves: https://www.mdpi.com/2073-4433/11/11/1206
//Yuya Takane, Yukihiro Kikegawa, Masayuki Hara, C. Sue B. Grimmond, Urban warming and future air-conditioning use in an Asian megacity: importance of positive feedback, npj Climate and Atmospheric Science, 10.1038/s41612-019-0096-2, 2, 1, (2019)
//How much can air conditioning increase air temperatures for a city like Paris, France? https://rmets.onlinelibrary.wiley.com/doi/10.1002/joc.3415
//Vincent Viguié, Aude Lemonsu, Stéphane Hallegatte, Anne-Lise Beaulant, Colette Marchadier, Valéry Masson, Grégoire Pigeon, Jean-Luc Salagnac, Early adaptation to heat waves and future reduction of air-conditioning energy use in Paris, Environmental Research Letters, 10.1088/1748-9326/ab6a24, 15, 7, (075006), (2020)

//Note: Building reference wall entered as if facing in -y direction, along x-axis, lower left corner at x=y=0 
//Note: IDF rotates Cartesian grid to allow inputs of Wall_Reference_OutsideFace_Azimuth_N_0_deg facing -y direction
//Note: Points 1-3 ensure the building and surrounding objects are rotated properly in Cartesian space
//Note: 1. EnergyPlus IDF section GlobalGeometryRules uses Coordinate System = Relative, with y-axis pointing north
//Note: 2. CoolBuilding HydroPlusConfig.xml starts Wall_Reference_Length_XCoordinate_m at x=y=0, facing -y direction
//Note: 3. EnergyPlus IDF section Building uses North Axis {deg} = Wall_Reference_OutsideFace_Azimuth_N_0_deg - 180
//Note: If Wall_Reference_OutsideFace_Azimuth_N_0_deg = 90, building_rotation_azimuth_deg = 270
//Note: If Wall_Reference_OutsideFace_Azimuth_N_0_deg = 270, building_rotation_azimuth_deg = 90

//Notes: BigLader: https://bigladdersoftware.com/epx/docs/8-0/input-output-reference/page-011.html

//formatNumberToString function declared for convering number to string with defined precision
string formatNumberToString(double number_to_convert, int precision_target);
//readTreeData function declared for reading tree data from HydroPlusConfig.xml
map<int, treeParameters> readTreeData(Inputs* input, int Tree_ID_Count);

//Create_IDF_file function initalizes ....
void CreateFile_IDF::Create_IDF_file(Inputs* input, string scenarioCoolBuilding)
{
    //outputDirectory = input->SimulationStringParams["OutputFolder_Path"]
    string outputDirectory = input->SimulationStringParams["OutputFolder_Path"];
    //path_IDF_Outputfile is defined as IDF output file
    string path_IDF_Outputfile = outputDirectory + "/iTreeCoolBuilding_" + scenarioCoolBuilding + ".idf";
    ofstream outputFile_IDF(path_IDF_Outputfile);
    if (!outputFile_IDF.is_open()) {
        cout << "Error: Cannot open output outputFile_IDF: " << path_IDF_Outputfile << ".\n" << endl;
        abort;
    }

    //Wall_Reference_OutsideFace_Azimuth_N_0_deg and other HydroPlusConfig.xml parameters read
    double Wall_Reference_OutsideFace_Azimuth_N_0_deg = input->CoolBuildingNumericalParams["Wall_Reference_OutsideFace_Azimuth_N_0_deg"];
    double Wall_Reference_Length_XCoordinate_m = input->CoolBuildingNumericalParams["Wall_Reference_Length_XCoordinate_m"];
    double Wall_NonReference_Length_YCoordinate_m = input->CoolBuildingNumericalParams["Wall_NonReference_Length_YCoordinate_m"];
    double Wall_Height_m = input->CoolBuildingNumericalParams["Wall_Height_m"];
    double Roof_Height_Max_m = input->CoolBuildingNumericalParams["Roof_Height_Max_m"];
    bool Roof_Gable_Above_Wall_Reference_Flag = input->CoolBuildingNumericalParams["Roof_Gable_Above_Wall_Reference_Flag"];
    double Window_to_Wall_Area_Ratio = input->CoolBuildingNumericalParams["Window_to_Wall_Area_Ratio"];
    int Windows_per_Wall = input->CoolBuildingNumericalParams["Windows_per_Wall"];

    double AirChangesPerHour = input->CoolBuildingNumericalParams["AirChangesPerHour"];
    double Building_NumberOfFloors = input->CoolBuildingNumericalParams["Building_NumberOfFloors"];
    double Occupants_NumberOfPeople = input->CoolBuildingNumericalParams["Occupants_NumberOfPeople"];
    double SetPoint_Humidistat_LowerRH_percent = input->CoolBuildingNumericalParams["SetPoint_Humidistat_LowerRH_percent"];
    double SetPoint_Humidistat_UpperRH_percent = input->CoolBuildingNumericalParams["SetPoint_Humidistat_UpperRH_percent"];
    double SetPoint_Thermostat_Cooling_C = input->CoolBuildingNumericalParams["SetPoint_Thermostat_Cooling_C"];
    double SetPoint_Thermostat_Heating_C = input->CoolBuildingNumericalParams["SetPoint_Thermostat_Heating_C"];

    //ErrorCheck_CoolBuilding_parameters called to check if HydroPlusConfig.xml parameters are valid
    CreateFile_IDF::ErrorCheck_CoolBuilding_parameters(input);

    //Tree_ID_Count is read from HydroPlusConfig.xml element Tree_ID_Count  within CoolBuildingNumericalParams
    int Tree_ID_Count = input->CoolBuildingNumericalParams["Tree_ID_Count"];
    //If scenarioCoolBuilding = NoTree then Tree_ID_Count = 0
    if (scenarioCoolBuilding == "NoTree") {
        Tree_ID_Count = 0;
    }
    //treeParametersLibrary initialized as instance of treeParameters struct
    map <int, treeParameters> treeParametersLibrary;
    //try calling map<int, treeParameters> treeParametersLibrary = readTreeData(input) to read in the tree data
    try {
        //treeParametersLibrary dyanamically takes readTreeData content
        treeParametersLibrary = readTreeData(input, Tree_ID_Count);
        /*Use for debugging input
        //For it loop from treeParametersLibrary.begin() to not treeParametersLibrary.end()
        for (auto it = treeParametersLibrary.begin(); it != treeParametersLibrary.end(); ++it) {
            //id is first key in it, treeParametersLibrary
            int id = it->first;
            //treeParameters struct params is second key in it, treeParametersLibrary library
            const treeParameters& params = it->second;
            cout << "  Trunk X: " << params.Tree_Trunk_X_m << "\n";
            cout << "  Shape #: " << params.Tree_Canopy_Shape_Option << "\n";
        }
        */
    }
    //Cath error if unable to read tree data
    catch (const exception& e) {
        cerr << "Warning: error processing tree data from HydroPlusConfig.xml file: " << e.what() << endl;
    }

    //HydroPlusConfig_StartDay_str_YYYYMMDD, HydroPlusConfig_StopDay_str_YYYYMMDD are strings of HydroPlusConfig.xml StartDate_YYYYMMDD, StopDate_YYYYMMDD
    string HydroPlusConfig_StartDay_str_YYYYMMDD = to_string(input->SimulationNumericalParams["StartDate_YYYYMMDD"]);
    string HydroPlusConfig_StopDay_str_YYYYMMDD = to_string(input->SimulationNumericalParams["StopDate_YYYYMMDD"]);
    //HydroPlusConfig_StartMonth_MM, etc. integer forms of HydroPlusConfig_StartDay_str_YYYYMMDD, substring identifies string start and length 
    int HydroPlusConfig_StartMonth_MM = atoi(HydroPlusConfig_StartDay_str_YYYYMMDD.substr(4, 2).c_str());
    int HydroPlusConfig_StartDay_DD = atoi(HydroPlusConfig_StartDay_str_YYYYMMDD.substr(6, 2).c_str());
    int HydroPlusConfig_StopMonth_MM = atoi(HydroPlusConfig_StopDay_str_YYYYMMDD.substr(4, 2).c_str());
    int HydroPlusConfig_StopDay_DD = atoi(HydroPlusConfig_StopDay_str_YYYYMMDD.substr(6, 2).c_str());

    //map materialLibrary as instance of buildingMaterial class (Key and Name are redundant for now)
//Note: map has Key, followed by its Name, Roughness, Thickness {m}, Conductivity {W/m-k}, Density {kg/m3}, Specific Heat {J/kg-K}, Thermal Absorptance, Solar Absorptance, Visible Absorptance (frac)
    map<string, buildingMaterial> materialLibrary = {
    {"F07 25mm stucco", {"F07 25mm stucco", "Smooth", 0.0254, 0.72, 1856, 840, 0.9, 0.7, 0.7}},
    {"F12 Asphalt shingles", {"F12 Asphalt shingles", "VeryRough", 0.0032, 0.04, 1120, 1260, 0.9, 0.7, 0.7}},
    {"F16 Acoustic tile", {"F16 Acoustic tile", "MediumSmooth", 0.0191, 0.06, 368, 590, 0.9, 0.7, 0.7}},
    {"G01 16mm gypsum board", {"G01 16mm gypsum board", "MediumSmooth", 0.0159, 0.16, 800, 1090, 0.9, 0.7, 0.7}},
    {"G01a 19mm gypsum board", {"G01a 19mm gypsum board", "MediumSmooth", 0.019, 0.16, 800, 1090, 0.9, 0.7, 0.7}},
    {"G02 16mm plywood", {"G02 16mm plywood", "Smooth", 0.0159, 0.12, 544, 1210, 0.9, 0.7, 0.7}},
    {"G05 25mm wood", {"G05 25mm wood", "MediumSmooth", 0.0254, 0.15, 608, 1630, 0.9, 0.7, 0.7}},
    {"M14a 100mm heavyweight concrete", {"M14a 100mm heavyweight concrete", "MediumRough", 0.1016, 1.95, 2240, 900, 0.9, 0.7, 0.7}},
    {"I01 114mm batt insulation", {"I01 114mm batt insulation", "VeryRough", 0.0894, 0.05, 19, 960, 0.9, 0.7, 0.7}},
    {"I07 431mm batt insulation", {"I07 431mm batt insulation", "VeryRough", 0.4314, 0.05, 19, 960, 0.9, 0.7, 0.7}},
    {"I01 25mm insulation board", {"I01 25mm insulation board", "MediumRough", 0.0254, 0.03, 43, 1210, 0.9, 0.7, 0.7}}
    };

    //map materialNoMassLibrary as instance of buildingMaterialNoMass class (Key and Name are redundant for now)
    //Note: map has Key, followed by its Name, Roughness, Thermal Resistance {m2-K/W}, Thermal Absorptance, Solar Absorptance, Visible Absorptance (frac)
    map<string, buildingMaterialNoMass> materialNoMassLibrary = {
    {"Building Roof Insulation ASHRAE", {"Building Roof Insulation ASHRAE", "MediumRough", 20.11, 0.9, 0.7, 0.7}}
    };

    //map buildingMaterialAirGap as instance of materialAirGapLibrary class (Key and Name are redundant for now)
    //Note: map has Key, followed by its Name, Thermal Resistance {m2-K/W}
    map<string, buildingMaterialAirGap> materialAirGapLibrary = {
    {"F04 Wall air space resistance", {"F04 Wall air space resistance", 0.15}},
    {"F05 Ceiling air space resistance", {"F05 Ceiling air space resistance", 0.18}}
    };

    //map materialLibrary as instance of windowMaterialGlazing class (Key and Name are redundant for now)
    //Note: map has Key, followed by its Name, Roughness, Thickness {m}, Conductivity {W/m-k}, Density {kg/m3}, Specific Heat {J/kg-K}
    map<string, windowMaterialGlazing> materialWindowGlazingLibrary = {
    {"CLEAR 6MM", {"CLEAR 6MM", "SpectralAverage", "", 0.006, 0.775, 0.071, 0.071, 0.881, 0.080, 0.080, 0.0, 0.84, 0.84, 0.9}},
    };

    //map materialWindowGasLibrary as instance of windowMaterialGas class (Key and Name are redundant for now)
    //Note: map has Key, followed by its Name, Gas Type, Thickness {m}
    map<string, windowMaterialGas> materialWindowGasLibrary = {
    {"AIR 6MM", {"AIR 6MM", "Air", 0.0063}}
    };

    //map constructionLibrary as instance of buildingConstruction class (Key and Name are redundant for now)
    //Note: map has Key, followed by its Name, and different layers pulled from material maps above
    map<string, buildingConstruction> constructionLibrary = {
    {"iTE Building Floor", { "iTE Building Floor", {"F16 Acoustic tile", "F05 Ceiling air space resistance", "M14a 100mm heavyweight concrete"}}},
    {"i-Tree Building Window", { "i-Tree Building Window", {"CLEAR 6MM", "AIR 6MM", "CLEAR 6MM"}}},
    {"i-Tree Building Partition", { "i-Tree Building Partition", {"G01a 19mm gypsum board", "F04 Wall air space resistance", "G01a 19mm gypsum board"}}},
    {"i-Tree Building Wall", { "i-Tree Building Wall", {"F07 25mm stucco", "I01 25mm insulation board", "G01 16mm gypsum board", "I01 114mm batt insulation", "G01 16mm gypsum board"}}},
    {"i-Tree Building Roof", { "i-Tree Building Roof", {"F12 Asphalt shingles", "G02 16mm plywood", "I07 431mm batt insulation"}}},
    {"i-Tree Building Furnishing", { "i-Tree Building Furnishing", {"G05 25mm wood"}}}
    };

    //map outputVariableLibrary as instance of idfOutputVariables class (Key and Name are redundant for now)
    //Note: map is initially output variables shared by all EnergyPlus_HVAC systems: IdealLoadsAirSystem, HVACTemplate:Zone:PTHP, etc. 
    //Note: map has Key, followed by its value, and different variable names and reporting frequencies
    //Note: Consider refactor to change * wild card to Wall and Roof names by updating dictionary once Azimuth is known 
    map<string, idfOutputVariables> outputVariableLibrary = {
        {"Site Outdoor Air Drybulb Temperature", {"*", "Site Outdoor Air Drybulb Temperature", "Hourly"}},
        {"People Occupant Count", {"*", "People Occupant Count", "Hourly"}},
        {"Zone Air Temperature", {"*", "Zone Air Temperature", "Hourly"}},
        {"Site Diffuse Solar Radiation Rate per Area", {"*", "Site Diffuse Solar Radiation Rate per Area", "Hourly"}},
        {"Site Direct Solar Radiation Rate per Area", {"*", "Site Direct Solar Radiation Rate per Area", "Hourly"}},
        {"Site Wind Speed", {"*", "Site Wind Speed", "Hourly"}},
        {"Site Wind Direction", {"*", "Site Wind Direction", "Hourly"}},
        {"Site Solar Azimuth Angle", {"*", "Site Solar Azimuth Angle", "Hourly"}},
        {"Site Solar Altitude Angle", {"*", "Site Solar Altitude Angle", "Hourly"}},
        {"Site Solar Hour Angle", {"*", "Site Solar Hour Angle", "Hourly"}},
        {"Site Sky Temperature", {"*", "Site Sky Temperature", "Hourly"}},
        {"Site Snow on Ground Status", {"*", "Site Snow on Ground Status", "Hourly"}},
        {"Zone Infiltration Mass Flow Rate", {"*", "Zone Infiltration Mass Flow Rate", "Hourly"}},
        {"Zone Air Relative Humidity", {"*", "Zone Air Relative Humidity", "Hourly"}},
        {"Zone Air Heat Balance Outdoor Air Transfer Rate", {"*", "Zone Air Heat Balance Outdoor Air Transfer Rate", "Hourly"}},
        {"Site Outdoor Air Relative Humidity", {"*", "Site Outdoor Air Relative Humidity", "Hourly"}}
    };
    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem" then add appropriate outputs from MDD options
    if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem") {
        outputVariableLibrary.insert({
            {"Zone Ideal Loads Zone Total Heating Rate", {"*", "Zone Ideal Loads Zone Total Heating Rate", "Hourly"}},
            {"Zone Ideal Loads Zone Total Cooling Rate", {"*", "Zone Ideal Loads Zone Total Cooling Rate", "Hourly"}}
            });
    }
    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP" then add appropriate outputs from MDD options
    else if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP") {
        outputVariableLibrary.insert({
            {"Zone Packaged Terminal Heat Pump Total Heating Rate", {"*", "Zone Packaged Terminal Heat Pump Total Heating Rate", "Hourly"}},
            {"Zone Packaged Terminal Heat Pump Total Cooling Rate", {"*", "Zone Packaged Terminal Heat Pump Total Cooling Rate", "Hourly"}},
            {"Zone Packaged Terminal Heat Pump Sensible Heating Rate", {"*", "Zone Packaged Terminal Heat Pump Sensible Heating Rate", "Hourly"}},
            {"Zone Packaged Terminal Heat Pump Sensible Cooling Rate", {"*", "Zone Packaged Terminal Heat Pump Sensible Cooling Rate", "Hourly"}},
            {"Zone Packaged Terminal Heat Pump Latent Heating Rate", {"*", "Zone Packaged Terminal Heat Pump Latent Heating Rate", "Hourly"}},
            {"Zone Packaged Terminal Heat Pump Latent Cooling Rate", {"*", "Zone Packaged Terminal Heat Pump Latent Cooling Rate", "Hourly"}},
            {"Zone Packaged Terminal Heat Pump Electricity Rate", {"*", "Zone Packaged Terminal Heat Pump Electricity Rate", "Hourly"}},
            {"Air System Relief Air Total Heat Loss Energy", {"*", "Air System Relief Air Total Heat Loss Energy", "Hourly"}},
            {"HVAC System Total Heat Rejection Energy", {"*", "HVAC System Total Heat Rejection Energy", "Hourly"}},
            {"Site Total Surface Heat Emission to Air", {"*", "Site Total Surface Heat Emission to Air", "Hourly"}},
            {"Site Total Zone Exfiltration Heat Loss", {"*", "Site Total Zone Exfiltration Heat Loss", "Hourly"}},
            {"Site Total Zone Exhaust Air Heat Loss", {"*", "Site Total Zone Exhaust Air Heat Loss", "Hourly"}}
            });
    }

    // Version
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: VERSION ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    vector<string> IDF_content_strings = { "Version,", "    25.1;"};
    vector<string> IDF_comment_strings = { "", "!- Version Identifier"};
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // SimulationControl
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SIMULATIONCONTROL ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem" then neglect system sizing calculations
    if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem") {
        IDF_content_strings = { "SimulationControl,", "    No,", "    No,", "    No,", "    No,", "    Yes,", "    No,", "    1;" };
    }
    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP" then complete system sizing calculations
    else if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP") {
        IDF_content_strings = { "SimulationControl,", "    Yes,", "    No,", "    No,", "    No,", "    Yes,", "    No,", "    1;" };
    }
    IDF_comment_strings = { "", "!- Do Zone Sizing Calculation", "!- Do System Sizing Calculation", "!- Do Plant Sizing Calculation", "!- Run Simulation for Sizing Periods", "!- Run Simulation for Weather File Run Periods", "!- Do HVAC Sizing Simulation for Sizing Periods", "!- Maximum Number of HVAC Sizing Simulation Passes" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Building
    //Note: Consider refactor so HydroPlusConfig.xml has element Terrain: Country, Suburbs, City, Urban or Ocean
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: BUILDING ===========" << endl;
    //building_rotation_azimuth_deg is fmod((Wall_Reference_OutsideFace_Azimuth_N_0_deg - 180 + 360), 360) ...
    //Note: ... fmod floating point modulus divided by 360 ensures values do not exceed 360 deg
    //Note: Building reference wall entered as if facing in -y direction, along x-axis, lower left corner at x=y=0 
    //Note: IDF rotates Cartesian grid to allow inputs of Wall_Reference_OutsideFace_Azimuth_N_0_deg facing -y direction
    //Note: Building rotation will force rotation of all zones; only use zone rotation if it is not aligned with building
    //Note: Points 1-3 ensure the building and surrounding objects are rotated properly in Cartesian space
    //Note: 1. EnergyPlus IDF section GlobalGeometryRules uses Coordinate System = Relative, with y-axis pointing north
    //Note: 2. CoolBuilding HydroPlusConfig.xml starts Wall_Reference_Length_XCoordinate_m at x=y=0, facing -y direction
    //Note: 3. EnergyPlus IDF section Building uses North Axis {deg} = Wall_Reference_OutsideFace_Azimuth_N_0_deg - 180
    //Note: If Wall_Reference_OutsideFace_Azimuth_N_0_deg = 90, building_rotation_azimuth_deg = 270
    //Note: If Wall_Reference_OutsideFace_Azimuth_N_0_deg = 270, building_rotation_azimuth_deg = 90
    double building_rotation_azimuth_deg = fmod((Wall_Reference_OutsideFace_Azimuth_N_0_deg - 180 + 360), 360);
    //building_azimuth_deg created with formatNumberToString(building_rotation_azimuth_deg, 1) with blank spaces and ,
    string building_azimuth_deg = "    " + formatNumberToString(building_rotation_azimuth_deg, 1) + ",";
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    //Note: Terrain (Country, Suburbs, City, Urban or Ocean) modifies Site Wind Speed and perhaps other Site weather: 
    IDF_content_strings = { "Building,", "    i-Tree Building,", building_azimuth_deg, "    City,", "    0.04,", "    0.4,", "    FullExterior,", "    25,", "    ;"};
    IDF_comment_strings = { "", "!- Name", "!- North Axis {deg}", "!- Terrain", "!- Loads Convergence Tolerance Value {W}", "!- Temperature Convergence Tolerance Value {deltaC}", "!- Solar Distribution", "!- Maximum Number of Warmup Days", "!- Minimum Number of Warmup Days" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // ShadowCalculation
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SHADOWCALCULATION ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "ShadowCalculation,", "    PolygonClipping,", "    Periodic,", "    7,", "    1000000,","    SutherlandHodgman,", "    ,", "    SimpleSkyDiffuseModeling,", "    Yes,", "    No,", "    No;" };
    IDF_comment_strings = { "", "!- Shading Calculation Method", "!- Shading Calculation Update Frequency Method", "!- Shading Calculation Update Frequency", "!- Maximum Figures in Shadow Overlap Calculations", "!- Polygon Clipping Algorithm", "!- Pixel Counting Resolution", "!- Sky Diffuse Modeling Algorithm", "!- Output External Shading Calculation Results", "!- Disable Self-Shading Within Shading Zone Groups", "!- Disable Self-Shading From Shading Zone Groups to Other Zones" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // SurfaceConvectionAlgorithm:Inside
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SURFACECONVECTIONALGORITHM:INSIDE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "SurfaceConvectionAlgorithm:Inside,", "    TARP;"};
    IDF_comment_strings = { "", "!- Algorithm" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // SurfaceConvectionAlgorithm:Outside
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SURFACECONVECTIONALGORITHM:OUTSIDE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "SurfaceConvectionAlgorithm:Outside,", "    DOE-2;" };
    IDF_comment_strings = { "", "!- Algorithm" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // ZoneAirHeatBalanceAlgorithm
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONEAIRHEATBALANCEALGORITHM ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "ZoneAirHeatBalanceAlgorithm,", "    AnalyticalSolution,", "    No;"};
    IDF_comment_strings = { "", "!- Algorithm", "!- Do Space Heat Balance for Sizing"};
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Timestep
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: TIMESTEP ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    //Note: Value of 6 for Number of Timesteps per Hour is typical for achieving stability
    IDF_content_strings = { "Timestep,", "    6;" };
    IDF_comment_strings = { "", "!- Number of Timesteps per Hour" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Site:Location,
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SITE:LOCATION ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    string Place_Location = "    " + input->CoolBuildingStringParams["Place_Location"] + ",";
    string Latitude_Location = "    " + input->CoolBuildingStringParams["Latitude_Location"] + ",";
    string Longitude_Location = "    " + input->CoolBuildingStringParams["Longitude_Location"] + ",";
    string TimeZoneOffset_Location = "    " + input->CoolBuildingStringParams["TimeZoneOffset_Location"] + ",";
    string Elevation_Location = "    " + input->CoolBuildingStringParams["Elevation_Location"] + ";";
    IDF_content_strings = { "Site:Location,", Place_Location, Latitude_Location, Longitude_Location, TimeZoneOffset_Location, Elevation_Location };
    IDF_comment_strings = { "", "!- Name", "!- Latitude {deg}", "!- Longitude {deg}", "!- Time Zone {hr}", "!- Elevation {m}" };;
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // SizingPeriod:DesignDay,
    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP" then complete system sizing calculations
    if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP") {
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SITE:DESIGNDAY ===========" << endl;

        //Heating design values determined using Min with call to findIndex_Meteorological_Min_or_Max
        //Index_Vector = Inputs::findIndex_Meteorological_Min_or_Max(input->Tair_C, "Min")
        //Note: size_t allows for large integer values; findIndex_Meteorological_Min_or_Max function searches vector
        size_t Index_Vector = Inputs::findIndex_Meteorological_Min_or_Max(input->Tair_C, "Min");
        //Index_Window_Vector = Inputs::findIndex_Min_or_Max_in_Window(input->Tair_C, Index_Vector, "Max", 24, 1);
        //Note: Consider refactor of 24 hr window to search, 1 hr presumed time step for EnergyPlus
        size_t Index_Window_Vector = Inputs::findIndex_Min_or_Max_in_Window(input->Tair_C, Index_Vector, "Max", 24, 1);

        //Tair_Design_C  = input->Tair_C[Index_Vector]
        double Tair_Design_C = input->Tair_C[Index_Vector];
        ///Tair_Design_Window_Max_C = input->Tair_C[Index_Window_Vector]
        //Note: Maximum temperature during the day with the coldest temperature
        double Tair_Design_Window_Max_C = input->Tair_C[Index_Window_Vector];

        //ostringstream and setprecision to convert from 6 digit to 2 digit string
        ostringstream ss_Tair_Design_Max_C;
        ss_Tair_Design_Max_C << fixed << setprecision(2) << Tair_Design_Window_Max_C;
        string ss_Tair_Design_Max_C_str = ss_Tair_Design_Max_C.str();

        //Tair_Range_C = Tair_Design_Window_Max_C - Tair_Design_C; used to set Daily Dry-bulb Temperature Range
        double Tair_Range_C = Tair_Design_Window_Max_C - Tair_Design_C;
        //ostringstream and setprecision to convert from 6 digit to 2 digit string
        ostringstream ss_Tair_Range_C;
        ss_Tair_Range_C << fixed << setprecision(2) << Tair_Range_C;
        string Tair_Range_C_str = ss_Tair_Range_C.str();

        //Tdew_Design_C  = input->Tdew_C[Index_Vector]
        double Tdew_Design_C = input->Tdew_C[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 2 digit string
        ostringstream ss_Tdew_Design_C;
        ss_Tdew_Design_C << fixed << setprecision(2) << Tdew_Design_C;
        string Tdew_Design_C_str = ss_Tdew_Design_C.str();

        //AtmPres_Design_Pa = input->AtmPres_kPa[Index_Vector] * 1000; conversion from kPa to Pa
        double AtmPres_Design_Pa = input->AtmPres_kPa[Index_Vector] * 1000;
        //ostringstream and setprecision to convert from 6 digit to 0 digit string
        ostringstream ss_AtmPres_Design_Pa;
        ss_AtmPres_Design_Pa << fixed << setprecision(0) << AtmPres_Design_Pa;
        string AtmPres_Design_Pa_str = ss_AtmPres_Design_Pa.str();

        //WindSpeed_Design_mps  = input->WindSpd_mps[Index_Vector]
        double WindSpd_Design_mps = input->WindSpd_mps[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 1 digit string
        ostringstream ss_WindSpeed_Design_mps;
        ss_WindSpeed_Design_mps << fixed << setprecision(1) << WindSpd_Design_mps;
        string WindSpeed_Design_mps_str = ss_WindSpeed_Design_mps.str();

        //WindDirection_Design_deg = input->WindDir_deg[Index_Vector]
        double WindDir_Design_deg = input->WindDir_deg[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 0 digit string
        ostringstream ss_WindDirection_Design_deg;
        ss_WindDirection_Design_deg << fixed << setprecision(0) << WindDir_Design_deg;
        string WindDirection_Design_deg_str = ss_WindDirection_Design_deg.str();

        //Month_0_Julian_or_1_to_12_MM extracted from YYYYMMDD, next 2 spaces
        int Month_Design_MM = stoi(to_string(input->SimulationDate_YYYYMMDD[Index_Vector]).substr(4, 2));
        //Day_of_Month_or_Julian_if_Month_0_DD extracted from YYYYMMDD, next 2 spaces
        int Day_Design_DD = stoi(to_string(input->SimulationDate_YYYYMMDD[Index_Vector]).substr(6, 2));

        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        string Place_Location_Design = "    " + input->CoolBuildingStringParams["Place_Location"] + "_Heating,";
        string Month_Design = "    " + to_string(Month_Design_MM) + ",";
        string Day_Design = "    " + to_string(Day_Design_DD) + ",";
        //Note: For heating design, DryBulbTemperature_Design_Max_C is the maximum temperature on coldest day
        string DryBulbTemperature_Design_Max_C = "    " + ss_Tair_Design_Max_C_str + ",";
        string DailyDryBulbTemperature_Design_Range_C = "    " + Tair_Range_C_str + ",";
        string DewPointTemperature_Design_Max_C = "    " + Tdew_Design_C_str + ",";
        string BarometricPressure_Design_Pa = "    " + AtmPres_Design_Pa_str + ",";
        string WindSpeed_Design_mps = "    " + WindSpeed_Design_mps_str + ",";
        string WindDirection_Design_deg = "    " + WindDirection_Design_deg_str + ",";
        string SolarModel_Indicator = "    ASHRAEClearSky,";
        //SkyClearness_frac typically 0.0 for Winter Design Days
        string SkyClearness_frac = "    0.0;";
        IDF_content_strings = { "SizingPeriod:DesignDay,", Place_Location_Design, Month_Design, Day_Design, "    WinterDesignDay,", DryBulbTemperature_Design_Max_C, DailyDryBulbTemperature_Design_Range_C, "    DefaultMultipliers,", "    ,", "    DewPoint,", DewPointTemperature_Design_Max_C, "    ,", "    ,", "    ,", "    ,", BarometricPressure_Design_Pa, WindSpeed_Design_mps, WindDirection_Design_deg, "    No,", "    No,", "    No,", SolarModel_Indicator, "    ,", "    ,", "    ,", "    ,", SkyClearness_frac };
        IDF_comment_strings = { "", "!- Name", "!- Month", "!- Day of Month", "!- Day Type", "!- Maximum Dry-Bulb Temperature {C}", "!- Daily Dry-Bulb Temperature Range {deltaC}", "!- Dry-Bulb Temperature Range Modifier Type", "!- Dry-Bulb Temperature Range Modifier Day Schedule Name", "!- Humidity Condition Type", "!- Wetbulb or DewPoint at Maximum Dry-Bulb {C}", "!- Humidity Condition Day Schedule Name", "!- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}", "!- Enthalpy at Maximum Dry-Bulb {J/kg}", "!- Daily Wet-Bulb Temperature Range {deltaC}", "!- Barometric Pressure {Pa}", "!- Wind Speed {m/s}", "!- Wind Direction {deg}", "!- Rain Indicator", "!- Snow Indicator", "!- Daylight Saving Time Indicator", "!- Solar Model Indicator", "!- Beam Solar Day Schedule Name", "!- Diffuse Solar Day Schedule Name", "!- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}", "!- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}", "!- Sky Clearness" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        //Cooling design values determined using Max with call to findIndex_Meteorological_Min_or_Max
        //Index_Vector = Inputs::findIndex_Meteorological_Min_or_Max(input->Tair_C, "Max")
        Index_Vector = Inputs::findIndex_Meteorological_Min_or_Max(input->Tair_C, "Max");
        //Index_Window_Vector = Inputs::findIndex_Min_or_Max_in_Window(input->Tair_C Index_Vector, "Min", 24, 1);
        //Note: Consider refactor of 24 hr window to search, 1 hr presumed time step for EnergyPlus
        Index_Window_Vector = Inputs::findIndex_Min_or_Max_in_Window(input->Tair_C, Index_Vector, "Min", 24, 1);

        //Tair_Design_C  = input->Tair_C[Index_Vector]
        Tair_Design_C = input->Tair_C[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 2 digit string; .str("") clears the contents
        ostringstream ss_Tair_Design_C;
        ss_Tair_Design_C << fixed << setprecision(2) << Tair_Design_C;
        string Tair_Design_C_str = ss_Tair_Design_C.str();

        double Tair_Design_Window_Min_C = input->Tair_C[Index_Window_Vector];
        Tair_Range_C = Tair_Design_C - Tair_Design_Window_Min_C;
        //ostringstream and setprecision to convert from 6 digit to 2 digit string
        ss_Tair_Range_C.str("");
        ss_Tair_Range_C << fixed << setprecision(2) << Tair_Range_C;
        Tair_Range_C_str = ss_Tair_Range_C.str();

        //Tdew_Design_C  = input->Tdew_C[Index_Vector]
        Tdew_Design_C = input->Tdew_C[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 2 digit string; .str("") clears the contents
        ss_Tdew_Design_C.str("");
        ss_Tdew_Design_C << fixed << setprecision(2) << Tdew_Design_C;
        Tdew_Design_C_str = ss_Tdew_Design_C.str();

        //AtmPres_Design_Pa = input->AtmPres_kPa[Index_Vector] * 1000; conversion from kPa to Pa
        AtmPres_Design_Pa = input->AtmPres_kPa[Index_Vector] * 1000;
        //ostringstream and setprecision to convert from 6 digit to 0 digit string; .str("") clears the contents
        ss_AtmPres_Design_Pa.str("");
        ss_AtmPres_Design_Pa << fixed << setprecision(0) << AtmPres_Design_Pa;
        AtmPres_Design_Pa_str = ss_AtmPres_Design_Pa.str();

        //WindSpeed_Design_mps  = input->WindSpd_mps[Index_Vector]
        WindSpd_Design_mps = input->WindSpd_mps[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 1 digit string; .str("") clears the contents
        ss_WindSpeed_Design_mps.str("");
        ss_WindSpeed_Design_mps << fixed << setprecision(1) << WindSpd_Design_mps;
        WindSpeed_Design_mps_str = ss_WindSpeed_Design_mps.str();

        //WindDirection_Design_deg = input->WindDir_deg[Index_Vector]
        WindDir_Design_deg = input->WindDir_deg[Index_Vector];
        //ostringstream and setprecision to convert from 6 digit to 0 digit string; .str("") clears the contents
        ss_WindDirection_Design_deg.str("");
        ss_WindDirection_Design_deg << fixed << setprecision(0) << WindDir_Design_deg;
        WindDirection_Design_deg_str = ss_WindDirection_Design_deg.str();

        //Month_0_Julian_or_1_to_12_MM extracted from YYYYMMDD, next 2 spaces
        Month_Design_MM = stoi(to_string(input->SimulationDate_YYYYMMDD[Index_Vector]).substr(4, 2));
        //Day_of_Month_or_Julian_if_Month_0_DD extracted from YYYYMMDD, next 2 spaces
        Day_Design_DD = stoi(to_string(input->SimulationDate_YYYYMMDD[Index_Vector]).substr(6, 2));

        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        Place_Location_Design = "    " + input->CoolBuildingStringParams["Place_Location"] + "_Cooling,";
        Month_Design = "    " + to_string(Month_Design_MM) + ",";
        Day_Design = "    " + to_string(Day_Design_DD) + ",";
        //Note: For cooling design, DryBulbTemperature_Design_Max_C is the maximum temperature on hottest day
        DryBulbTemperature_Design_Max_C = "    " + Tair_Design_C_str + ",";
        DailyDryBulbTemperature_Design_Range_C = "    " + Tair_Range_C_str + ",";
        DewPointTemperature_Design_Max_C = "    " + Tdew_Design_C_str + ",";
        BarometricPressure_Design_Pa = "    " + AtmPres_Design_Pa_str + ",";
        WindSpeed_Design_mps = "    " + WindSpeed_Design_mps_str + ",";
        WindDirection_Design_deg = "    " + WindDirection_Design_deg_str + ",";
        SolarModel_Indicator = "    ASHRAEClearSky,";
        //SkyClearness_frac could be inferred from max solar radiation for Summer Design Days
        SkyClearness_frac = "    1.0;";
        IDF_content_strings = { "SizingPeriod:DesignDay,", Place_Location_Design, Month_Design, Day_Design, "    WinterDesignDay,", DryBulbTemperature_Design_Max_C, DailyDryBulbTemperature_Design_Range_C, "    DefaultMultipliers,", "    ,", "    DewPoint,", DewPointTemperature_Design_Max_C, "    ,", "    ,", "    ,", "    ,", BarometricPressure_Design_Pa, WindSpeed_Design_mps, WindDirection_Design_deg, "    No,", "    No,", "    No,", SolarModel_Indicator, "    ,", "    ,", "    ,", "    ,", SkyClearness_frac };
        IDF_comment_strings = { "", "!- Name", "!- Month", "!- Day of Month", "!- Day Type", "!- Maximum Dry-Bulb Temperature {C}", "!- Daily Dry-Bulb Temperature Range {deltaC}", "!- Dry-Bulb Temperature Range Modifier Type", "!- Dry-Bulb Temperature Range Modifier Day Schedule Name", "!- Humidity Condition Type", "!- Wetbulb or DewPoint at Maximum Dry-Bulb {C}", "!- Humidity Condition Day Schedule Name", "!- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}", "!- Enthalpy at Maximum Dry-Bulb {J/kg}", "!- Daily Wet-Bulb Temperature Range {deltaC}", "!- Barometric Pressure {Pa}", "!- Wind Speed {m/s}", "!- Wind Direction {deg}", "!- Rain Indicator", "!- Snow Indicator", "!- Daylight Saving Time Indicator", "!- Solar Model Indicator", "!- Beam Solar Day Schedule Name", "!- Diffuse Solar Day Schedule Name", "!- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}", "!- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}", "!- Sky Clearness" };
        //Note: If SolarModel_Indicator is ASHRAETau, then define taub and taud
        /*
        string AHSRAE_ClearSkyOpticalDepth_BeamIrradiance_taub = "    0.455,";
        string AHSRAE_ClearSkyOpticalDepth_DiffuseIrradiance_taud = "    2.050;";
        IDF_content_strings = { "SizingPeriod:DesignDay,", Place_Location_Design, Month_Design, Day_Design, "    SummerDesignDay,", DryBulbTemperature_Design_Max_C, DailyDryBulbTemperature_Design_Range_C, "    DefaultMultipliers,", "    ,", "    DewPoint,", DewPointTemperature_Design_Max_C, "    ,", "    ,", "    ,", "    ,", BarometricPressure_Design_Pa, WindSpeed_Design_mps, WindDirection_Design_deg, "    No,", "    No,", "    No,", SolarModel_Indicator, "    ,", "    ,", AHSRAE_ClearSkyOpticalDepth_BeamIrradiance_taub, AHSRAE_ClearSkyOpticalDepth_DiffuseIrradiance_taud };
        IDF_comment_strings = { "", "!- Name", "!- Month", "!- Day of Month", "!- Day Type", "!- Maximum Dry-Bulb Temperature {C}", "!- Daily Dry-Bulb Temperature Range {deltaC}", "!- Dry-Bulb Temperature Range Modifier Type", "!- Dry-Bulb Temperature Range Modifier Day Schedule Name", "!- Humidity Condition Type", "!- Wetbulb or DewPoint at Maximum Dry-Bulb {C}", "!- Humidity Condition Day Schedule Name", "!- Humidity Ratio at Maximum Dry-Bulb {kgWater/kgDryAir}", "!- Enthalpy at Maximum Dry-Bulb {J/kg}", "!- Daily Wet-Bulb Temperature Range {deltaC}", "!- Barometric Pressure {Pa}", "!- Wind Speed {m/s}", "!- Wind Direction {deg}", "!- Rain Indicator", "!- Snow Indicator", "!- Daylight Saving Time Indicator", "!- Solar Model Indicator", "!- Beam Solar Day Schedule Name", "!- Diffuse Solar Day Schedule Name", "!- ASHRAE Clear Sky Optical Depth for Beam Irradiance (taub) {dimensionless}", "!- ASHRAE Clear Sky Optical Depth for Diffuse Irradiance (taud) {dimensionless}" };
        */

        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // RunPeriod
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: RUNPERIOD ===========" << endl;
    string HydroPlusConfig_StartMonth_MM_str = "    " + formatNumberToString(HydroPlusConfig_StartMonth_MM,0) + ",";
    string HydroPlusConfig_StartDay_DD_str = "    " + formatNumberToString(HydroPlusConfig_StartDay_DD, 0) + ",";
    string HydroPlusConfig_StopMonth_MM_str = "    " + formatNumberToString(HydroPlusConfig_StopMonth_MM, 0) + ",";
    string HydroPlusConfig_StopDay_DD_str = "    " + formatNumberToString(HydroPlusConfig_StopDay_DD, 0) + ",";
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "RunPeriod,", "    Run Period,", HydroPlusConfig_StartMonth_MM_str, HydroPlusConfig_StartDay_DD_str, "    ,", HydroPlusConfig_StopMonth_MM_str, HydroPlusConfig_StopDay_DD_str, "    ,", "    Sunday,", "    No,", "    No,", "    No,", "    Yes,", "    Yes;" };
    IDF_comment_strings = { "", "!- Name", "!- Begin Month", "!- Begin Day of Month", "!- Begin Year", "!- End Month", "!- End Day of Month", "!- End Year", "!- Day of Week for Start Day", "!- Use Weather File Holidays and Special Days", "!- Use Weather File Daylight Saving Period", "!- Apply Weekend Holiday Rule", "!- Use Weather File Rain Indicators", "!- Use Weather File Snow Indicators" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // WeatherProperty:SkyTemperature
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: WEATHERPROPERTY:SKYTEMPERATURE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "WeatherProperty:SkyTemperature,", "    Run Period,", "    DifferenceScheduleDryBulbValue,", "    Blast Model;"};
    IDF_comment_strings = { "", "!- Name", "!- Calculation Type", "!- Schedule Name"};
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Site:GroundTemperature:BuildingSurface
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SITE:GROUNDTEMPERATURE:BUILDINGSURFACE ===========" << endl;
    //IDF_content_strings set to nothing, then receives blank space for 1st line
    IDF_content_strings = { "Site:GroundTemperature:BuildingSurface,",};
    //IDF_content_strings for January begins with MonthlyAvgTair_C[11] from December, assigning prior month 
    //Note: formatNumberToString function maintains precision, and adds space and comma around string returned
    IDF_content_strings.push_back("    " + formatNumberToString(input->MonthlyAvgTair_C[11], 1) + ",");
    //For loop of month January (0) to <11, assign prior month air temperature to current month ground under building temperature
    for (int month_ID = 0; month_ID < 11; ++month_ID) {
        //isNovember bool set if month_ID is 10, November, the final air temperature to assign as ground temperature for December
        bool isNovember = (month_ID == 10);
        //groundUnderBuildingTemperature_C takes space and output from formatNumberToString(input->MonthlyAvgTair_C[month_ID], 1), needing , or ;
        string groundUnderBuildingTemperature_C = "    " + formatNumberToString(input->MonthlyAvgTair_C[month_ID], 1);
        //groundUnderBuildingTemperature_C adds semicolon or comma depending on ternary operator if isNovember is true
        groundUnderBuildingTemperature_C += isNovember ? ";" : ",";
        //IDF_content_strings push_back groundUnderBuildingTemperature_C
        IDF_content_strings.push_back(groundUnderBuildingTemperature_C);
    }
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_comment_strings = { "", "!- January Ground Temperature {C}", "!- February Ground Temperature {C}", "!- March Ground Temperature {C}", "!- April Ground Temperature {C}", "!- May Ground Temperature {C}", "!- June Ground Temperature {C}", "!- July Ground Temperature {C}", "!- August Ground Temperature {C}", "!- September Ground Temperature {C}", "!- October Ground Temperature {C}", "!- November Ground Temperature {C}", "!- December Ground Temperature {C}" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // ScheduleTypeLimits (two sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SCHEDULETYPELIMITS ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "ScheduleTypeLimits,", "    Any Number;"};
    IDF_comment_strings = { "", "!- Name" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "ScheduleTypeLimits,", "    Fraction,", "    0,", "    1,", "    Continuous;"};
    IDF_comment_strings = { "", "!- Name","!- Lower Limit Value", "!- Upper Limit Value", "!- Numeric Type"};
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Schedule:Compact (multiple sections)
    //Note: Consider refactor to use map library as with Construction
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SCHEDULE:COMPACT ===========" << endl;
    //Through_Month_Day string defined to agree with HydroPlusConfig_StopMonth_MM and HydroPlusConfig_StopDay_DD
    string Through_Month_Day = "    Through: " + formatNumberToString(HydroPlusConfig_StopMonth_MM, 0) + "/" + formatNumberToString(HydroPlusConfig_StopDay_DD, 0) + ",";
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "Schedule:Compact,", "    VentiliationInfiltrationSchedule,", "    Fraction,", Through_Month_Day, "    For: AllDays,", "    Until: 24:00,", "    1;"};
    IDF_comment_strings = { "", "!- Name", "!- Schedule Type Limits Name", "!- Field 1", "!- Field 2", "!- Field 3", "!- Field 4" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //TransmittanceSchedule_Tree_01 defined for 1 type of tree (e.g. deciduous vs evergreen)
    //Note: Consider refactor to allow for leaf on and leaf off if deciduous and for some Beers Law
    string TransmittanceSchedule_Tree_01 = "TransmittanceSchedule_Tree_01";
    //If Tree_ID_Count > 0 then write tree transmittance schedule
    if (Tree_ID_Count > 0) {
        //IDF_content_strings uses [1] to replace 2nd item 
        IDF_content_strings[1] = "    TransmittanceSchedule_Tree_01,";
        //IDF_content_strings uses back to replace last item
        IDF_content_strings.back() = "    0;";
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }
    //IDF_content_strings uses [1] to replace 2nd item 
    IDF_content_strings[1] = "    People_Schedule_Activity,";
    IDF_content_strings[2] = "    Any Number,";
    IDF_content_strings.back() = "    120;";
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //IDF_content_strings uses [1] to replace 2nd item 
    //Note: Consider refactor to allow for input from HydroPlusConfig on options for schedules
    IDF_content_strings[1] = "    People_Schedule_Occupancy,";
    IDF_content_strings[2] = "    Fraction,";
    IDF_content_strings[5] = "    Until: 08:00,";
    IDF_content_strings.back() = "    1,";
    //IDF_content_strings uses insert and end to add items to end
    IDF_content_strings.insert(IDF_content_strings.end(), { "    Until: 16:00", "    0,", "    Until: 24:00", "    1;" });
    IDF_comment_strings.insert(IDF_comment_strings.end(), { "!- Field 5", "!- Field 6", "!- Field 7", "!- Field 8" });
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //IDF_content_strings uses [1] to replace 2nd item 
    IDF_content_strings[1] = "    CoolingSetpoint,";
    IDF_content_strings[2] = "    Any Number,";
    IDF_content_strings[5] = "    Until: 24:00,";
    //IDF_content_strings uses erase and end to remove items from end
    IDF_content_strings.erase(IDF_content_strings.end() - 4, IDF_content_strings.end());
    IDF_comment_strings.erase(IDF_comment_strings.end() - 4, IDF_comment_strings.end());
    IDF_content_strings.back() = "    4;";
    IDF_content_strings.back() = "    " + formatNumberToString(SetPoint_Thermostat_Cooling_C, 1) + ";";
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //IDF_content_strings uses [1] to replace 2nd item 
    IDF_content_strings[1] = "    HeatingSetpoint,";
    IDF_content_strings.back() = "    " + formatNumberToString(SetPoint_Thermostat_Heating_C, 1) + ";";
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //IDF_content_strings uses [1] to replace 2nd item 
    IDF_content_strings[1] = "    Blast Model,";
    IDF_content_strings.back() = "    6;";
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem" then set thermostat and humidistat schedules
    //Note: Consider refactor to remove humidistat, as this is rare in residential and not replicated in other E+ HVAC sytems
    if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem") {
        //IDF_content_strings uses [1] to replace 2nd item 
        IDF_content_strings[1] = "    ThermostatSchedule,";
        IDF_content_strings.back() = "    4;";
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        //IDF_content_strings uses [1] to replace 2nd item 
        IDF_content_strings[1] = "    HumidifySetpoint,";
        IDF_content_strings.back() = "    " + formatNumberToString(SetPoint_Humidistat_LowerRH_percent, 1) + ";";
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        //IDF_content_strings uses [1] to replace 2nd item 
        IDF_content_strings[1] = "    DehumidifySetpoint,";
        IDF_content_strings.back() = "    " + formatNumberToString(SetPoint_Humidistat_UpperRH_percent, 1) + ";";
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // Material (multiple sections)
    //Note: Jeff, Aly, and Aaron at BigLadder would consult to provide ASHRAE type material properties for locations and periods ...
    //Note: ... such as thickness_m, conductivity_W_per_mK, density_kg_per_m3, specific_heat_J_per_kgK
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: MATERIAL ===========" << endl;
    //For pair pointer iterates through all items in materialLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : materialLibrary) {
        //key pointer string access pair.first item in materialLibrary, the key
        const string& key = pair.first;
        //buildingMaterial struct material pointer access pair.second item in materialLibrary, the items such as name, etc. 
        const buildingMaterial& material = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("Material,");
        IDF_content_strings.push_back("    " + material.name + ",");
        IDF_content_strings.push_back("    " + material.roughness + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thickness_m, 4) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.conductivity_W_per_mK, 2) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.density_kg_per_m3, 0) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.specific_heat_J_per_kgK, 0) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thermal_absorptance_frac, 2) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.solar_absorptance_frac, 2) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.visible_absorptance_frac, 2) + ";");
        IDF_comment_strings = { "", "!- Name", "!- Roughness", "!- Thickness {m}", "!- Conductivity {W/m-K}", "!- Density {kg/m3}", "!- Specific Heat {J/kg-K}", "!- Thermal Absorptance", "!- Solar Absorptance", "!- Visible Absorptance" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // Material:NoMass (potentially multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: MATERIAL:NOMASS ===========" << endl;
    //For pair pointer iterates through all items in materialNoMassLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : materialNoMassLibrary) {
        //key pointer string access pair.first item in materialNoMassLibrary, the key
        const string& key = pair.first;
        //buildingMaterialNoMass struct material pointer access pair.second item in materialNoMassLibrary, the items such as name, etc. 
        const buildingMaterialNoMass& material = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("Material:NoMass,");
        IDF_content_strings.push_back("    " + material.name + ",");
        IDF_content_strings.push_back("    " + material.roughness + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thermal_resistance_m2K_per_W, 2) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thermal_absorptance_frac, 2) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.solar_absorptance_frac, 2) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.visible_absorptance_frac, 2) + ";");
        IDF_comment_strings = { "", "!- Name", "!- Roughness", "!- Thermal Resistance {m2-K/W}", "!- Thermal Absorptance", "!- Solar Absorptance", "!- Visible Absorptance" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // Material:AirGap (multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: MATERIAL:AIRGAP ===========" << endl;
    //For pair pointer iterates through all items in materialAirGapLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : materialAirGapLibrary) {
        //key pointer string access pair.first item in materialAirGapLibrary, the key
        const string& key = pair.first;
        //buildingMaterialNoMass struct material pointer access pair.second item in materialAirGapLibrary, the items such as name, etc. 
        const buildingMaterialAirGap& material = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("Material:AirGap,");
        IDF_content_strings.push_back("    " + material.name + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thermal_resistance_m2K_per_W, 2) + ";");
        IDF_comment_strings = { "", "!- Name", "!- Thermal Resistance {m2-K/W}" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // WindowMaterial:Glazing (potentially multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: WINDOWMATERIAL:GLAZING ===========" << endl;
    //For pair pointer iterates through all items in materialAirGapLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : materialWindowGlazingLibrary) {
        //key pointer string access pair.first item in materialWindowGlazingLibrary, the key
        const string& key = pair.first;
        //buildingMaterialNoMass struct material pointer access pair.second item in materialWindowGlazingLibrary, the items such as name, etc. 
        const windowMaterialGlazing& material = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("WindowMaterial:Glazing,");
        IDF_content_strings.push_back("    " + material.name + ",");
        IDF_content_strings.push_back("    " + material.optical_data_type + ",");
        IDF_content_strings.push_back("    " + material.window_glass_spectral_data_set_name + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thickness_m, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.solar_transmittance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.front_side_solar_reflectance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.back_side_solar_reflectance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.visible_transmittance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.front_side_visible_reflectance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.back_side_visible_reflectance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.infrared_transmittance, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.front_side_infrared_emissivity, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.back_side_infrared_emissivity, 3) + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.conductivity_W_per_mK, 3) + ";");
        IDF_comment_strings = { "", "!- Name", "!- Optical Data Type", "!- Window Glass Spectral Data Set Name", "!- Thickness {m}", "!- Solar Transmittance at Normal Incidence", "!- Front Side Solar Reflectance at Normal Incidence", "!- Back Side Solar Reflectance at Normal Incidence", "!- Visible Transmittance at Normal Incidence", "!- Front Side Visible Reflectance at Normal Incidence", "!- Back Side Visible Reflectance at Normal Incidence", "!- Infrared Transmittance at Normal Incidence", "!- Front Side Infrared Hemispherical Emissivity", "!- Back Side Infrared Hemispherical Emissivity", "!- Conductivity {W/m-K}" };;
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // WindowMaterial:Gas (potentially multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: WINDOWMATERIAL:GAS ===========" << endl;
    //For pair pointer iterates through all items in materialWindowGasLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : materialWindowGasLibrary) {
        //key pointer string access pair.first item in materialWindowGasLibrary, the key
        const string& key = pair.first;
        //buildingMaterialNoMass struct material pointer access pair.second item in materialWindowGasLibrary, the items such as name, etc. 
        const windowMaterialGas& material = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("WindowMaterial:Gas,");
        IDF_content_strings.push_back("    " + material.name + ",");
        IDF_content_strings.push_back("    " + material.gas_type + ",");
        IDF_content_strings.push_back("    " + formatNumberToString(material.thickness_m, 4) + ";");
        IDF_comment_strings = { "", "!- Name", "!- Gas Type", "!- Thickness {m}"};
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // Construction (multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: CONSTRUCTION ===========" << endl;
    //For pair pointer iterates through all items in constructionLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : constructionLibrary) {
        //key pointer string access pair.first item in constructionLibrary, the key
        const string& key = pair.first;
        //buildingMaterialNoMass struct material pointer access pair.second item in constructionLibrary, the items such as name, etc. 
        const buildingConstruction& material = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("Construction,");
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("    " + material.name + ",");
        //IDF_comment_strings initiated with empty string and Name, then define dynamically
        IDF_comment_strings = { "", "!- Name" };
        //For loop i from 0 through all material.layers.size
        for (size_t i = 0; i < material.layers.size(); ++i) {
            //layer string takes material.layers[i]
            string layer = "    " + material.layers[i];
            //If i == material.layers.size() - 1 then last item, and attach semicolon
            if (i == material.layers.size() - 1) {
                layer += ";";
            }
            //Else not last item, and attach comma
            else {
                layer += ",";
            }
            //IDF_content_strings push_back layer
            IDF_content_strings.push_back(layer);
            if (i == 0) {
                IDF_comment_strings.push_back("!- Outside Layer");
            }
            else {
                IDF_comment_strings.push_back("!- Layer " + to_string(i + 1));
            }
        }
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // GlobalGeometryRules 
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: GLOBALGEOMETRYRULES ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "GlobalGeometryRules,", "    LowerLeftCorner,", "    Clockwise,", "    Relative;" };
    IDF_comment_strings = { "", "!- Starting Vertex Position", "!- Vertex Entry Direction", "!- Coordinate System" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Zone 
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "Zone,", "    ThermalZoneOfBuilding,", "    0.0,", "    0.0,", "    0.0,", "    0.0,", "    ,", "    1;" };
    IDF_comment_strings = { "", "!- Name", "!- Direction of Relative North {deg}", "!- X Origin {m}", "!- Y Origin {m}", "!- Z Origin {m}", "!- Type", "!- Multiplier"};
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // BuildingSurface:Detailed
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: BUILDINGSURFACE:DETAILED ===========" << endl;
    //Floor variables defined 
    //lowerLeft_X_m = 0 for 1st vertex when viewed from outside building, under floor
    double lowerLeft_X_m = 0;
    //lowerLeft_Y_m = Wall_NonReference_Length_YCoordinate_m for 1st vertex when viewed from outside building, under floor
    double lowerLeft_Y_m = Wall_NonReference_Length_YCoordinate_m;
    //floor_delta_X_m and floor_delta_Y_m used to compute 2nd, 3rd, and 4th vertices
    double floor_delta_X_m = Wall_Reference_Length_XCoordinate_m;
    double floor_delta_Y_m = Wall_NonReference_Length_YCoordinate_m;
    double height_min_m = 0;
    double height_max_m = 0;
    string Heading_Subsection_str = "BuildingSurface:Detailed";
    string floorName = "Building Slab";
    string surfaceType = "Floor";
    string constructionName = "iTE Building Floor";
    string buildingSurfaceName = "";
    //sunExposed for floor is NoSun 
    string sunExposed = "NoSun";
    //windExposed for floor is NoWind
    string windExposed = "NoWind";
    //outsideBoundaryCondition for floor is Ground
    string outsideBoundaryCondition = "Ground";
    //isGable for floor is false
    bool isGable = false;
    //viewFactorGround for floor is 0 
    double viewFactorGround = 0;
    int numberOfVertices = 4;
    //Initialize other variables used in calculateBuildingVertices for wall, roof and window
    int AssemblyFace_azimuth = 0;
    double assembly_delta_X_m = 0;
    double assembly_delta_Y_m = 0;
    double height_roof_m = Roof_Height_Max_m;
    double roof_delta_X_m = 0;
    double roof_delta_Y_m = 0;
    double Window_LeftEdge_m = 0;
    double Window_Length_m = 0;
    //floorVertices defined with calculateBuildingVertices function
    auto floorVertices = calculateBuildingVertices(numberOfVertices, lowerLeft_X_m, lowerLeft_Y_m, assembly_delta_X_m, assembly_delta_Y_m, floor_delta_X_m, floor_delta_Y_m, roof_delta_X_m, roof_delta_Y_m, height_min_m, AssemblyFace_azimuth, isGable, height_max_m, height_roof_m);
    //writeBuildingSurface called for Floor
    writeBuildingSurface(outputFile_IDF, Heading_Subsection_str, floorName, surfaceType, constructionName, outsideBoundaryCondition, buildingSurfaceName, viewFactorGround, sunExposed, windExposed, floorVertices);

    //Wall variables defined 
    height_min_m = 0.0;
    height_max_m = Wall_Height_m;
    surfaceType = "Wall";
    constructionName = "i-Tree Building Wall";
    viewFactorGround = 0.5;
    sunExposed = "SunExposed";
    windExposed = "WindExposed";
    //floor_delta_Y_m is is reset to 0 for wall, roof or window assembly
    floor_delta_X_m = 0;
    floor_delta_Y_m = 0;
    //outsideBoundaryCondition for wall and roof is Outside 
    outsideBoundaryCondition = "Outdoors";
    int j = 0;
    //For loop i = 0 to 3, rotating through all walls, starting with 1st wall facing in y direction, 2nd wall facing in x direction, 3rd wall facing in -y direction, 4th wall facing in -x direction
    //Note: Building reference wall entered as if facing in -y direction, along x-axis, lower left corner at x=y=0 
    //Note: IDF rotates Cartesian grid to allow inputs of Wall_Reference_OutsideFace_Azimuth_N_0_deg facing -y direction
    //Note: Points 1-3 ensure the building and surrounding objects are rotated properly in Cartesian space
    //Note: 1. EnergyPlus IDF section GlobalGeometryRules uses Coordinate System = Relative, with y-axis pointing north
    //Note: 2. CoolBuilding HydroPlusConfig.xml starts Wall_Reference_Length_XCoordinate_m at x=y=0, facing -y direction
    //Note: 3. EnergyPlus IDF section Building uses North Axis {deg} = Wall_Reference_OutsideFace_Azimuth_N_0_deg - 180
    //Note: If Wall_Reference_OutsideFace_Azimuth_N_0_deg = 90, building_rotation_azimuth_deg = 270
    //Note: If Wall_Reference_OutsideFace_Azimuth_N_0_deg = 270, building_rotation_azimuth_deg = 90
    for (size_t i = 0; i < 4; ++i) {
        //AssemblyFace_azimuth static_cast int rounded from building_rotation_azimuth_deg + i * 90, ...
        //... computed using fmod floating point modulus function, divided by 360.0 to ensure no degree exceeds 360
        AssemblyFace_azimuth = static_cast<int>(round(fmod(building_rotation_azimuth_deg + i * 90, 360.0)));
        //wallName constructed of string and to_string AssemblyFace_azimuth
        string wallName = "Wall Facing " + to_string(AssemblyFace_azimuth) + " deg";

        //outputVariableLibrary emplace to update with specific wallName, rather than wildCard * variable
        outputVariableLibrary.emplace("Surface Outside Face Sunlit Fraction " + to_string(AssemblyFace_azimuth),
            idfOutputVariables{ wallName, "Surface Outside Face Sunlit Fraction", "Hourly" });
        outputVariableLibrary.emplace("Surface Outside Face Temperature " + to_string(AssemblyFace_azimuth),
            idfOutputVariables{ wallName, "Surface Outside Face Temperature", "Hourly" });
        outputVariableLibrary.emplace("Surface Outside Face Incident Solar Radiation Rate per Area " + to_string(AssemblyFace_azimuth),
            idfOutputVariables{ wallName, "Surface Outside Face Incident Solar Radiation Rate per Area", "Hourly" });

        //isGable uses ternary ? to assign Roof_Gable_Above_Wall_Reference_Flag if i modulus 2 is 0, else Roof_Gable_Above_Wall_Reference_Flag
        isGable = (i % 2 == 0) ? Roof_Gable_Above_Wall_Reference_Flag : !Roof_Gable_Above_Wall_Reference_Flag;
        //numberOfVertices uses ternary ? to assign 5 if isGable is true, else 4
        numberOfVertices = isGable ? 5 : 4;
        //assemblyGeometry class has instance Wallgeometry created to contain output from function calculateAssemblyGeometry
        assemblyGeometry Wallgeometry = calculateAssemblyGeometry(i, Wall_Reference_Length_XCoordinate_m, Wall_NonReference_Length_YCoordinate_m, Window_LeftEdge_m, Window_Length_m);
        //lowerLeft_X_m and lowerLeft_Y_m defined for 1st vertex when viewed from outside building
        lowerLeft_X_m = Wallgeometry.lowerLeft_X_m;
        lowerLeft_Y_m = Wallgeometry.lowerLeft_Y_m;
        //assembly_delta_X_m and assembly_delta_Y_m used to compute 2nd, 3rd, and 4th vertices
        assembly_delta_X_m = Wallgeometry.assembly_delta_X_m;
        assembly_delta_Y_m = Wallgeometry.assembly_delta_Y_m;

        //wallVertices defined with calculateBuildingVertices function
        auto wallVertices = calculateBuildingVertices(numberOfVertices, lowerLeft_X_m, lowerLeft_Y_m, assembly_delta_X_m, assembly_delta_Y_m, floor_delta_X_m, floor_delta_Y_m, roof_delta_X_m, roof_delta_Y_m, height_min_m, AssemblyFace_azimuth, isGable, height_max_m, height_roof_m);
        //writeBuildingSurface called
        writeBuildingSurface(outputFile_IDF, Heading_Subsection_str, wallName, surfaceType, constructionName, outsideBoundaryCondition, buildingSurfaceName, viewFactorGround, sunExposed, windExposed, wallVertices);
    }

    //Roof variables defined 
    height_min_m = Wall_Height_m;
    height_max_m = Roof_Height_Max_m;
    surfaceType = "Roof";
    constructionName = "i-Tree Building Roof";
    viewFactorGround = 0;
    numberOfVertices = 4;
    for (size_t i = 0; i < 4; ++i) {
        //AssemblyFace_azimuth static_cast int rounded from building_rotation_azimuth_deg + i * 90, ...
        //... computed using fmod floating point modulus function, divided by 360.0 to ensure no degree exceeds 360
        AssemblyFace_azimuth = static_cast<int>(round(fmod(building_rotation_azimuth_deg + i * 90, 360.0)));
        //isGable uses ternary ? to assign Roof_Gable_Above_Wall_Reference_Flag if i modulus 2 is 0, else Roof_Gable_Above_Wall_Reference_Flag
        isGable = (i % 2 == 0) ? Roof_Gable_Above_Wall_Reference_Flag : !Roof_Gable_Above_Wall_Reference_Flag;
        //If isGable is false then write roof vertices
        if (!isGable) {
            //roofName constructed of string and to_string AssemblyFace_azimuth
            string roofName = "Roof Facing " + to_string(AssemblyFace_azimuth) + " deg";

            //outputVariableLibrary emplace to update with specific wallName, rather than wildCard * variable
            outputVariableLibrary.emplace("Surface Outside Face Sunlit Fraction " + to_string(AssemblyFace_azimuth),
                idfOutputVariables{ roofName, "Surface Outside Face Sunlit Fraction" , "Hourly" });
            outputVariableLibrary.emplace("Surface Outside Face Temperature " + to_string(AssemblyFace_azimuth),
                idfOutputVariables{ roofName, "Surface Outside Face Temperature", "Hourly" });
            outputVariableLibrary.emplace("Surface Outside Face Incident Solar Radiation Rate per Area " + to_string(AssemblyFace_azimuth),
                idfOutputVariables{ roofName, "Surface Outside Face Incident Solar Radiation Rate per Area", "Hourly" });

            //assemblyGeometry class has instance Roofgeometry created to contain output from function calculateAssemblyGeometry
            assemblyGeometry Roofgeometry = calculateAssemblyGeometry(i, Wall_Reference_Length_XCoordinate_m, Wall_NonReference_Length_YCoordinate_m, Window_LeftEdge_m, Window_Length_m);
            //lowerLeft_X_m and lowerLeft_Y_m defined for 1st vertex when viewed from outside building
            lowerLeft_X_m = Roofgeometry.lowerLeft_X_m;
            lowerLeft_Y_m = Roofgeometry.lowerLeft_Y_m;
            //assembly_delta_X_m and assembly_delta_Y_m used to compute 2nd, 3rd, and 4th vertices
            assembly_delta_X_m = Roofgeometry.assembly_delta_X_m;
            assembly_delta_Y_m = Roofgeometry.assembly_delta_Y_m;
            //roof_delta_X_m  and roof_delta_Y_m used to compute 2nd, 3rd, and 4th vertices of roof
            roof_delta_X_m = Roofgeometry.roof_delta_X_m;
            roof_delta_Y_m = Roofgeometry.roof_delta_Y_m;
            auto roofVertices = calculateBuildingVertices(numberOfVertices, lowerLeft_X_m, lowerLeft_Y_m, assembly_delta_X_m, assembly_delta_Y_m, floor_delta_X_m, floor_delta_Y_m, roof_delta_X_m, roof_delta_Y_m, height_min_m, Wall_Reference_OutsideFace_Azimuth_N_0_deg, isGable, height_max_m, height_roof_m);
            writeBuildingSurface(outputFile_IDF, Heading_Subsection_str, roofName, surfaceType, constructionName, outsideBoundaryCondition, buildingSurfaceName, viewFactorGround, sunExposed, windExposed, roofVertices);
        }
    }

    //Window variables defined 
    //If Windows_per_Wall > 0 there are windows to add to IDF
    if (Windows_per_Wall > 0) {
        //FenestrationSurface:Detailed
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: FENESTRATIONSURFACE:DETAILED ===========" << endl;
        Heading_Subsection_str = "FenestrationSurface:Detailed";
        //If Window_to_Wall_Area_Ratio < 0 then set to 0.1 to avoid division by zero below
        if (Window_to_Wall_Area_Ratio < 0) { Window_to_Wall_Area_Ratio = 0.1; }
        surfaceType = "Window";
        constructionName = "i-Tree Building Window";
        viewFactorGround = 0.5;
        numberOfVertices = 4;
        //isGable flag set to false to ensure only 4 vertices are assigned to windows
        isGable = false;
        roof_delta_X_m = roof_delta_Y_m = 0;
        //floor_delta_Y_m is is reset to 0 for wall and roof assembly
        floor_delta_Y_m = 0;
        //outsideBoundaryCondition for wall and roof is Outside 
        outsideBoundaryCondition = "Outdoors";
        //For loop i = 0 to 3, rotating through all walls, starting with 1st wall facing in y direction, 2nd wall facing in x direction, 3rd wall facing in -y direction, 4th wall facing in -x direction
        //Note: IDF will rotate cartesian grid such that -y direction = Wall_Reference_OutsideFace_Azimuth_N_0_deg
        //Note: If wall faces in -y direction then wall y = 0, If wall faces in -x direction then wall x = 0
        for (size_t i = 0; i < 4; ++i) {
            //AssemblyFace_azimuth static_cast int rounded from building_rotation_azimuth_deg + i * 90, ...
            //... computed using fmod floating point modulus function, divided by 360.0 to ensure no degree exceeds 360
            AssemblyFace_azimuth = static_cast<int>(round(fmod(building_rotation_azimuth_deg + i * 90, 360.0)));
            //widowName constructed of string and to_string AssemblyFace_azimuth
            buildingSurfaceName = "Wall Facing " + to_string(AssemblyFace_azimuth) + " deg";
            //Wall_Length_m uses ternary ? to assign Wall_Reference_Length_XCoordinate_m if i modulus 2 is 0, else Wall_NonReference_Length_YCoordinate_m
            double Wall_Length_m = (i % 2 == 0) ? Wall_Reference_Length_XCoordinate_m : Wall_NonReference_Length_YCoordinate_m;
            //Coefficient_a is pow((Windows_per_Wall / Window_to_Wall_Area_Ratio), 0.5), defined to find window length and height, derived below ...
            //Note: Coefficient_a = ([(Wall_Length_m * Wall_Height_m * Window_to_Wall_Area_Ratio)/Windows_per_Wall] / ...
            //Note: ... [[(Wall_Length_m * Window_to_Wall_Area_Ratio)/Windows_per_Wall] * [(Wall_Height_m * Window_to_Wall_Area_Ratio)/Windows_per_Wall]])^0.5
            //Note: Coefficient_a = ([1/Windows_per_Wall]/[[1/Windows_per_Wall] * [Window_to_Wall_Area_Ratio/Windows_per_Wall]])^0.5
            //Note: Coefficient_a = ([Windows_per_Wall^2/Windows_per_Wall]/[Window_to_Wall_Area_Ratio])^0.5
            //Note: Coefficient_a = (Windows_per_Wall/Window_to_Wall_Area_Ratio)^0.5
            double Coefficient_a = pow((Windows_per_Wall / Window_to_Wall_Area_Ratio), 0.5);
            //Window_Length_m is Coefficient_a * (Wall_Length_m * Window_to_Wall_Area_Ratio) / Windows_per_Wall;
            double Window_Length_m = Coefficient_a * (Wall_Length_m * Window_to_Wall_Area_Ratio) / Windows_per_Wall;
            //Window_Height_m is Coefficient_a * (Wall_Height_m * Window_to_Wall_Area_Ratio) / Windows_per_Wall;
            double Window_Height_m = Coefficient_a * (Wall_Height_m * Window_to_Wall_Area_Ratio) / Windows_per_Wall;
            //For loop j 0 to Window_per_Wall_Ratio, rotating through windows per wall, starting with 1
            for (size_t j = 0; j < Windows_per_Wall; ++j) {
                //windowName constructed of strings and to_string j+1 counter and AssemblyFace_azimuth
                string windowName = "Window " + to_string(j + 1) + " Facing " + to_string(AssemblyFace_azimuth) + " deg";
                //Window_Center_XY_m (m) for 1st window is Wall_Length_m / (Windows_per_Wall * 2), relative to left edge of wall
                //Note: If Windows_per_Wall = 1, then only window centered at 1/2 distance on wall 
                //Note: If Windows_per_Wall = 2, then 1st window centered at 1/4 distance on wall, and can extend between 0 and 50%
                //Note: If Windows_per_Wall = 3, then 1st window centered at 1/6 distance on wall, and can extend between 0 and 33%
                double Window_Center_XY_m = Wall_Length_m / (Windows_per_Wall * 2);
                if (j > 0) {
                    //Window_Center_XY_m (m) for > 1st window is j * (Wall_Length_m / Windows_per_Wall) + (Wall_Length_m / (Windows_per_Wall * 2)) 
                    //Note: If Windows_per_Wall = 2, then 2nd window centered at 3/4 distance on wall, and can extend between 75 and 100%
                    //Note: If Windows_per_Wall = 3, then 2nd window centered at 3/6 distance on wall, and can extend between 33 and 66%
                    Window_Center_XY_m = j * (Wall_Length_m / Windows_per_Wall) + (Wall_Length_m / (Windows_per_Wall * 2));
                }
                //Window_LeftEdge_m (m) is Window_Center_XY_m - Window_Length_m/2, relative to left edge of wall
                Window_LeftEdge_m = Window_Center_XY_m - Window_Length_m / 2;
                //assemblyGeometry class has instance Windowgeometry created to contain output from function calculateAssemblyGeometry
                assemblyGeometry Windowgeometry = calculateAssemblyGeometry(i, Wall_Reference_Length_XCoordinate_m, Wall_NonReference_Length_YCoordinate_m, Window_LeftEdge_m, Window_Length_m);
                //lowerLeft_X_m and lowerLeft_Y_m defined for 1st vertex when viewed from outside building
                lowerLeft_X_m = Windowgeometry.lowerLeft_X_m;
                lowerLeft_Y_m = Windowgeometry.lowerLeft_Y_m;
                //assembly_delta_X_m and assembly_delta_Y_m used to compute 2nd, 3rd, and 4th vertices
                assembly_delta_X_m = Windowgeometry.window_delta_X_m;
                assembly_delta_Y_m = Windowgeometry.window_delta_Y_m;

                //Window_Center_Z_m (m) is Wall_Height_m / 2, relative to bottom edge of wall
                double Window_Center_Z_m = Wall_Height_m / 2;
                //height_min_m (m) is Window_Center_Z_m - Window_Height_m / 2.0, Window lower edge
                height_min_m = Window_Center_Z_m - Window_Height_m / 2.0;
                //height_max_m (m) is Window_Center_Z_m + Window_Height_m / 2.0, Window upper edge
                height_max_m = Window_Center_Z_m + Window_Height_m / 2.0;
                //windowVertices defined with calculateBuildingVertices function, turning isGable into False
                auto windowVertices = calculateBuildingVertices(numberOfVertices, lowerLeft_X_m, lowerLeft_Y_m, assembly_delta_X_m, assembly_delta_Y_m, floor_delta_X_m, floor_delta_Y_m, roof_delta_X_m, roof_delta_Y_m, height_min_m, AssemblyFace_azimuth, isGable, height_max_m, height_roof_m);
                //writeBuildingSurface called
                writeBuildingSurface(outputFile_IDF, Heading_Subsection_str, windowName, surfaceType, constructionName, outsideBoundaryCondition, buildingSurfaceName, viewFactorGround, sunExposed, windExposed, windowVertices);
            }
        }
    }

    // InternalMass (multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: INTERNALMASS ===========" << endl;
    //surfaceAreaBuildingPartitionsMass_m2 (m2) defined as 50% of floor area, represents likely wall area within house
    double surfaceAreaBuildingPartitionsMass_m2 = 0.5 * Building_NumberOfFloors * Wall_Reference_Length_XCoordinate_m * Wall_NonReference_Length_YCoordinate_m;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "InternalMass,", "    PartitionsMass,", "    i-Tree Building Partition,", "    ThermalZoneOfBuilding,", "    ," };
    IDF_content_strings.push_back("    " + formatNumberToString(surfaceAreaBuildingPartitionsMass_m2, 1) + ";");
    IDF_comment_strings = { "", "!- Name", "!- Construction Name", "!- Zone or ZoneList Name", "!- Space or SpaceList Name", "!- Surface Area {m2}" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //surfaceAreaBuildingPartitionsMass_m2 (m2) defined as 35% of floor area, represents likely wall area within house
    double surfaceAreaBuildingFurnishingsMass_m2 = 0.35 * Building_NumberOfFloors * Wall_Reference_Length_XCoordinate_m * Wall_NonReference_Length_YCoordinate_m;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "InternalMass,", "    FunitureMass,", "    i-Tree Building Furnishing,", "    ThermalZoneOfBuilding,", "    ," };
    IDF_content_strings.push_back("    " + formatNumberToString(surfaceAreaBuildingFurnishingsMass_m2, 1) + ";");
    IDF_comment_strings = { "", "!- Name", "!- Construction Name", "!- Zone or ZoneList Name", "!- Space or SpaceList Name", "!- Surface Area {m2}" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Shading:Building:Detailed (multiple sections)
    if (Tree_ID_Count > 0) {
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: SHADING:BUILDING:DETAILED ===========" << endl;
        //numSegments initialized to 20, but could be decreased or increased if resolution needed 
        int numSegments = 20;
        int segmentsPerWedge = 1;
        //For tree_ID from 1 to Tree_ID_Count
        for (int tree_ID = 1; tree_ID <= Tree_ID_Count; ++tree_ID) {
            //treeParams is instance of treeParameters struct, assigned treeParametersLibrary[tree_ID] for current tree
            const treeParameters& treeParams = treeParametersLibrary[tree_ID];
            //transmisttanceSchedule defined as TransmittanceSchedule_Tree_01 for tree type, either deciduous or evergreen
            string transmittanceSchedule = "TransmittanceSchedule_Tree_01";
            //If treeParams.Tree_Canopy_Shape_Option = 3 then ellipsoid and 2 facets (lower and upper) needed per wedge
            if (treeParams.Tree_Canopy_Shape_Option == 3) { segmentsPerWedge = 2;}
            //treeShape set to to_string(treeParams.Tree_Canopy_Shape_Option)
            string treeShape = to_string(treeParams.Tree_Canopy_Shape_Option);
            //Note: Consider refactor with loop to write tree trunk after canopy, but make shape column for all trunks ...
            //Note: ... calculateTreeVertices for trunk will need to ensure shape is column & segmentsPerWedge=1
            //For wedgePart from 0 to segmentsPerWedge, which is only > 1 if ellipsoid 
            for (int wedgePart = 0; wedgePart < segmentsPerWedge; ++wedgePart) {
                //For segmentNumber from 1 to numSegments
                for (int segmentNumber = 1; segmentNumber <= numSegments; ++segmentNumber) {
                    //treeVertices auto detected as vector<idfVertex> as return from calculateTreeVertices(treeParams, numSegments)
                    auto treeVertices = calculateTreeVertices(treeParams, wedgePart, segmentNumber, numSegments);
                    //treeName defined as Tree_ and string of tree_ID
                    string treeName = "Tree_ID_" + to_string(tree_ID) + "_Shape_" + treeShape + "_Segment_" + to_string(segmentNumber);
                    //If segmentsPerWedge > 1 then add wedgePart as upper or lower to treeName
                    if (segmentsPerWedge > 1) {
                        //wedgeType defined as _upper or _lower based on ternary operator for wedgePart == 0
                        string wedgeType = (wedgePart == 0) ? "_lower" : "_upper";
                        //treeName defined as Tree_ and string of tree_ID
                        treeName = treeName + wedgeType;
                    }
                    //writeTreeSurface called with outputFile_IDF, treeName, transmittanceSchedule, treeVertices
                    writeTreeSurface(outputFile_IDF, treeName, transmittanceSchedule, treeVertices);
                }
            }
        }
    }

    // People 
    //Note: Consider refactor to add ClothingInsulationSchedule for winter and summer and get Fanger vote on thermal comfort
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: PEOPLE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "People,", "    HumanOccupancy,", "    ThermalZoneOfBuilding,", "    People_Schedule_Occupancy,", "    People,", "    #,", "    ,", "    ,", "    0.3,", "    autocalculate,", "    People_Schedule_Activity,", "    0.0000000382,", "    No,", "    EnclosureAveraged,", "    ,", "    ,", "    ClothingInsulationSchedule;"};
    //IDF_content_strings[5] replace # with Occupants_NumberOfPeople
    IDF_content_strings[5] = ("    " + formatNumberToString(Occupants_NumberOfPeople, 0) + ",");
    IDF_comment_strings = { "", "!- Name", "!- Zone or ZoneList or Space or SpaceList Name", "!- Number of People Schedule Name", "!- Number of People Calculation Method", "!- Number of People", "!- People per Floor Area {person/m2}", "!- Floor Area per Person {m2/person}", "!- Fraction Radiant", "!- Sensible Heat Fraction", "!- Activity Level Schedule Name", "!- Carbon Dioxide Generation Rate {m3/s-W}", "!- Enable ASHRAE 55 Comfort Warnings", "!- Mean Radiant Temperature Calculation Type", "!- Surface Name/Angle Factor List Name", "!- Work Efficiency Schedule Name", "!- Clothing Insulation Calculation Method" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // ZoneInfiltration:DesignFlowRate 
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONEINFILTRATION:DESIGNFLOWRATE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "ZoneInfiltration:DesignFlowRate,", "    House Infiltration,", "    ThermalZoneOfBuilding,", "    VentiliationInfiltrationSchedule,", "    AirChanges/Hour,", "    ,", "    ,", "    ,", "    #,", "    1,", "    ,", "    ,", "    ;" };
    //IDF_content_strings[8] replace # with AirChangesPerHour
    IDF_content_strings[8] = ("    " + formatNumberToString(AirChangesPerHour, 2) + ",");
    IDF_comment_strings = { "", "!- Name", "!- Zone or ZoneList or Space or SpaceList Name", "!- Schedule Name", "!- Design Flow Rate Calculation Method", "!- Design Flow Rate {m3/s}", "!- Flow Rate per Floor Area {m3/s-m2}", "!- Flow Rate per Exterior Surface Area {m3/s-m2}", "!- Air Changes per Hour {1/hr}", "!- Constant Term Coefficient", "!- Temperature Term Coefficient", "!- Velocity Term Coefficient", "!- Velocity Squared Term Coefficient" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // ZoneVentilation:DesignFlowRate
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONEVENTILATION:DESIGNFLOWRATE ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "ZoneVentilation:DesignFlowRate,", "    House Ventilation,", "    ThermalZoneOfBuilding,", "    VentiliationInfiltrationSchedule,", "    Flow/Zone,", "    0.0375,", "    ,", "    ,", "    #,", "    Natural,", "    ,", "    1,", "    1,", "    ,", "    ,", "    ,", "    -100,", "    ,", "    100,", "    ,", "    -100,", "    ,", "    -100,", "    ,", "    100,", "    ,", "    40;"};
    //IDF_content_strings[8] replace # with AirChangesPerHour
    IDF_content_strings[8] = ("    " + formatNumberToString(AirChangesPerHour, 2) + ",");
    IDF_comment_strings = { "", "!- Name", "!- Zone or ZoneList or Space or SpaceList Name", "!- Schedule Name", "!- Design Flow Rate Calculation Method", "!- Design Flow Rate {m3/s}", "!- Flow Rate per Floor Area {m3/s-m2}", "!- Flow Rate per Person {m3/s-person}", "!- Air Changes per Hour {1/hr}", "!- Ventilation Type", "!- Fan Pressure Rise {Pa}", "!- Fan Total Efficiency", "!- Constant Term Coefficient", "!- Temperature Term Coefficient", "!- Velocity Term Coefficient", "!- Velocity Squared Term Coefficient", "!- Minimum Indoor Temperature {C}", "!- Minimum Indoor Temperature Schedule Name", "!- Maximum Indoor Temperature {C}", "!- Maximum Indoor Temperature Schedule Name", "!- Delta Temperature {deltaC}", "!- Delta Temperature Schedule Name", "!- Minimum Outdoor Temperature {C}", "!- Minimum Outdoor Temperature Schedule Name", "!- Maximum Outdoor Temperature {C}", "!- Maximum Outdoor Temperature Schedule Name", "!- Maximum Wind Speed {m/s}" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem" then use ZoneHVAC:IdealLoadsAirSystem setup
    if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "IdealLoadsAirSystem") {
        // ZoneHVAC:IdealLoadsAirSystem
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONEHVAC:IDEALLOADSAIRSYSTEM ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "ZoneHVAC:IdealLoadsAirSystem,", "    ZoneHVAC_ForcedAir,", "    ,", "    IdealZoneSupply,", "    ,", "    ,", "    50,", "    13,", "    0.0156,", "    0.005,", "    NoLimit,", "    ,", "    ,", "    NoLimit,", "    ,", "    ,", "    ,", "    ,", "    Humidistat,", "    0.7,", "    Humidistat,", "    ,", "    ,", "    None,", "    NoEconomizer,", "    None,", "    0.7,", "    0.65;" };
        IDF_comment_strings = { "", "!- Name", "!- Availability Schedule Name", "!- Zone Supply Air Node Name", "!- Zone Exhaust Air Node Name", "!- System Inlet Air Node Name", "!- Maximum Heating Supply Air Temperature {C}", "!- Minimum Cooling Supply Air Temperature {C}", "!- Maximum Heating Supply Air Humidity Ratio {kgWater/kgDryAir}", "!- Minimum Cooling Supply Air Humidity Ratio {kgWater/kgDryAir}", "!- Heating Limit", "!- Maximum Heating Air Flow Rate {m3/s}", "!- Maximum Sensible Heating Capacity {W}", "!- Cooling Limit", "!- Maximum Cooling Air Flow Rate {m3/s}", "!- Maximum Total Cooling Capacity {W}", "!- Heating Availability Schedule Name", "!- Cooling Availability Schedule Name", "!- Dehumidification Control Type", "!- Cooling Sensible Heat Ratio {dimensionless}", "!- Humidification Control Type", "!- Design Specification Outdoor Air Object Name", "!- Outdoor Air Inlet Node Name", "!- Demand Controlled Ventilation Type", "!- Outdoor Air Economizer Type", "!- Heat Recovery Type", "!- Sensible Heat Recovery Effectiveness {dimensionless}", "!- Latent Heat Recovery Effectiveness {dimensionless}" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        // ZoneHVAC:EquipmentList
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONEHVAC:EQUIPMENTLIST ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "ZoneHVAC:EquipmentList,", "    ZoneHVAC Equipment,", "    SequentialLoad,", "    ZoneHVAC:IdealLoadsAirSystem,", "    ZoneHVAC_ForcedAir,", "    1,", "    1,", "    ,", "    ;" };
        IDF_comment_strings = { "", "!- Name", "!- Load Distribution Scheme", "!- Zone Equipment 1 Object Type", "!- Zone Equipment 1 Name", "!- Zone Equipment 1 Cooling Sequence", "!- Zone Equipment 1 Heating or No-Load Sequence", "!- Zone Equipment 1 Sequential Cooling Fraction Schedule Name", "!- Zone Equipment 1 Sequential Heating Fraction Schedule Name" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        // ZoneHVAC:EquipmentConnections
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONEHVAC:EQUIPMENTCONNECTIONS ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "ZoneHVAC:EquipmentConnections,", "    ThermalZoneOfBuilding,", "    ZoneHVAC Equipment,", "    IdealZoneSupply,", "    ,", "    IdealZoneAir,", "    IdealZoneReturn;" };
        IDF_comment_strings = { "", "!- Zone Name", "!- Zone Conditioning Equipment List Name", "!- Zone Air Inlet Node or NodeList Name", "!- Zone Air Exhaust Node or NodeList Name", "!- Zone Air Node Name", "!- Zone Return Air Node or NodeList Name" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        // ZoneControl:Thermostat
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONECONTROL:THERMOSTAT ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "ZoneControl:Thermostat,", "    Thermostat,", "    ThermalZoneOfBuilding,", "    ThermostatSchedule,", "    ThermostatSetpoint:DualSetpoint,", "    Thermostat Setpoints;" };
        IDF_comment_strings = { "", "!- Name", "!- Zone or ZoneList Name", "!- Control Type Schedule Name", "!- Control 1 Object Type", "!- Control 1 Name" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        // ThermostatSetpoint:DualSetpoint
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: THERMOSTATSETPOINT:DUALSETPOINT ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "ThermostatSetpoint:DualSetpoint,", "    Thermostat Setpoints,", "    HeatingSetpoint,", "    CoolingSetpoint;" };
        IDF_comment_strings = { "", "!- Name", "!- Heating Setpoint Temperature Schedule Name", "!- Cooling Setpoint Temperature Schedule Name" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        // ZoneControl:Humidistat
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: ZONECONTROL:HUMIDISTAT ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "ZoneControl:Humidistat,", "    Humidity Set Point,", "    ThermalZoneOfBuilding,", "    HumidifySetpoint,", "    DehumidifySetpoint;" };
        IDF_comment_strings = { "", "!- Name", "!- Zone Name", "!- Humidifying Relative Humidity Setpoint Schedule Name", "!- Dehumidifying Relative Humidity Setpoint Schedule Name" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    //If CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP" then use HVACTemplate:Zone:PTHP & HVACTemplate::Thermostat
    else if (input->CoolBuildingStringParams["EnergyPlus_HVAC"] == "HVACTemplate:Zone:PTHP") {
        // HVACTemplate:Zone:PTHP
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: HVACTEMPLATE:ZONE:PTHP ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "HVACTemplate:Zone:PTHP,", "    ThermalZoneOfBuilding,", "    Thermostat,", "    autosize,", "    autosize,", "    ,", "    ,", "    ,", "    Flow/Person,", "    0.00944,", "    ,", "    ,", "    ,", "    ,", "    DrawThrough,", "    0.7,", "    75,", "    0.9,", "    SingleSpeedDX,", "    ,", "    autosize,", "    autosize,", "    3,", "    SingleSpeedDXHeatPump,", "    ,", "    autosize,", "    2.75,", "    -8,", "    5,", "    ReverseCycle,", "    Timed,", "    0.058333,", "    Electric,", "    ,", "    autosize,", "    21,", "    0.8,", "    ,", "    ,", "    SupplyAirTemperature,", "    14,", "    11.11,", "    SupplyAirTemperature,", "    50,", "    30,", "    ,", "    ,", "    None,", "    ,", "    autosize,", "    None;" };
        IDF_comment_strings = { "", "!- Zone Name", "!- Template Thermostat Name", "!- Cooling Supply Air Flow Rate {m3/s}", "!- Heating Supply Air Flow Rate {m3/s}", "!- No Load Supply Air Flow Rate {m3/s}", "!- Zone Heating Sizing Factor", "!- Zone Cooling Sizing Factor", "!- Outdoor Air Method", "!- Outdoor Air Flow Rate per Person {m3/s}", "!- Outdoor Air Flow Rate per Zone Floor Area {m3/s-m2}", "!- Outdoor Air Flow Rate per Zone {m3/s}", "!- System Availability Schedule Name", "!- Supply Fan Operating Mode Schedule Name", "!- Supply Fan Placement", "!- Supply Fan Total Efficiency", "!- Supply Fan Delta Pressure {Pa}", "!- Supply Fan Motor Efficiency", "!- Cooling Coil Type", "!- Cooling Coil Availability Schedule Name", "!- Cooling Coil Gross Rated Total Capacity {W}", "!- Cooling Coil Gross Rated Sensible Heat Ratio", "!- Cooling Coil Gross Rated COP {W/W}", "!- Heat Pump Heating Coil Type", "!- Heat Pump Heating Coil Availability Schedule Name", "!- Heat Pump Heating Coil Gross Rated Capacity {W}", "!- Heat Pump Heating Coil Gross Rated COP {W/W}", "!- Heat Pump Heating Minimum Outdoor Dry-Bulb Temperature {C}", "!- Heat Pump Defrost Maximum Outdoor Dry-Bulb Temperature {C}", "!- Heat Pump Defrost Strategy", "!- Heat Pump Defrost Control", "!- Heat Pump Defrost Time Period Fraction", "!- Supplemental Heating Coil Type", "!- Supplemental Heating Coil Availability Schedule Name", "!- Supplemental Heating Coil Capacity {W}", "!- Supplemental Heating Coil Maximum Outdoor Dry-Bulb Temperature {C}", "!- Supplemental Gas Heating Coil Efficiency", "!- Supplemental Gas Heating Coil Parasitic Electric Load {W}", "!- Dedicated Outdoor Air System Name", "!- Zone Cooling Design Supply Air Temperature Input Method", "!- Zone Cooling Design Supply Air Temperature {C}", "!- Zone Cooling Design Supply Air Temperature Difference {deltaC}", "!- Zone Heating Design Supply Air Temperature Input Method", "!- Zone Heating Design Supply Air Temperature {C}", "!- Zone Heating Design Supply Air Temperature Difference {deltaC}", "!- Design Specification Outdoor Air Object Name", "!- Design Specification Zone Air Distribution Object Name", "!- Baseboard Heating Type", "!- Baseboard Heating Availability Schedule Name", "!- Baseboard Heating Capacity {W}", "!- Capacity Control Method;" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

        // HVACTemplate:Thermostat
        outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: HVACTEMPLATE:THERMOSTAT ===========" << endl;
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings = { "HVACTemplate:Thermostat,", "    Thermostat,", "    HeatingSetpoint,", "    ,", "    CoolingSetpoint,", "    ;" };
        IDF_comment_strings = { "", "!- Name", "!- Heating Setpoint Schedule Name", "!- Constant Heating Setpoint {C}", "!- Cooling Setpoint Schedule Name", "!- Constant Heating Setpoint {C}" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // Output:VariableDictionary
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: OUTPUT:VARIABLEDICTIONARY ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "Output:VariableDictionary,", "    IDF;" };
    IDF_comment_strings = { "", "!- Key Field" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Output:Table:SummaryReports
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: OUTPUT:TABLE:SUMMARYREPORTS ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "Output:Table:SummaryReports,", "    AllSummaryAndMonthly,", "    HeatEmissionsReportMonthly;" };
    IDF_comment_strings = { "", "!- Report 1 Name", "!- Report 2 Name" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    // Output:Variable (multiple sections)
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: OUTPUT:VARIABLE ===========" << endl;
    //For pair pointer iterates through all items in outputVariableLibrary
    //Note: items selected in alphabetical order
    for (const auto& pair : outputVariableLibrary) {
        //key pointer string access pair.first item in outputVariableLibrary, the key
        const string& key = pair.first;
        //idfOutputVariablesstruct outputIDF pointer access pair.second item in outputVariableLibrary, the items such as name, etc. 
        const idfOutputVariables& outputIDF = pair.second;
        //IDF_content_strings emptied
        IDF_content_strings = {};
        //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
        IDF_content_strings.push_back("Output:Variable,");
        IDF_content_strings.push_back("    " + outputIDF.key_value + ",");
        IDF_content_strings.push_back("    " + outputIDF.variable_name + ",");
        IDF_content_strings.push_back("    " + outputIDF.reporting_freq + ";");
        IDF_comment_strings = { "", "!- Key Value", "!- Variable Name", "!- Reporting Frequency" };
        //writeContentAndComments called with outputFile_IDF, content, comment
        writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
    }

    // OutputControl:Files
    //Note: Yes = Output CSV; Yes = Output Tabular
    //Note: Set to Yes for CSV, ESO, Tabular, RDD
    //Note: Optionally change No to Yes below, and modify filesToDelete in EnergyLoad_Calc.cpp, to write the output
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: OUTPUTCONTROL:FILES ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "OutputControl:Files,", "    Yes,", "    No,", "    Yes,", "    No,", "    Yes,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    Yes,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No,", "    No;" };
    IDF_comment_strings = { "", "!- Output CSV", "!- Output MTR", "!- Output ESO", "!- Output EIO", "!- Output Tabular", "!- Output SQLite", "!- Output JSON", "!- Output AUDIT", "!- Output Space Sizing", "!- Output Zone Sizing", "!- Output System Sizing", "!- Output DXF", "!- Output BND", "!- Output RDD", "!- Output MDD", "!- Output MTD", "!- Output END", "!- Output SHD", "!- Output DFS", "!- Output GLHE", "!- Output DelightIn", "!- Output DelightELdmp", "!- Output DelightDFdmp", "!- Output EDD", "!- Output DBG", "!- Output PerfLog", "!- Output SLN", "!- Output SCI", "!- Output WRL", "!- Output Screen", "!- Output ExtShd", "!- Output Tarcog" };
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //39. Output:Diagnostics
    outputFile_IDF << "!-   ===========  ALL OBJECTS IN CLASS: OUTPUT:DIAGNOSTICS ===========" << endl;
    //IDF_content_strings and IDF_comment_strings vectors defined with IDF variables and comments
    IDF_content_strings = { "Output:Diagnostics,", "    DisplayAllWarnings;" };
    IDF_comment_strings = { "", "!- Key 1" };
    //Note: Warning may occurr: Warning ** Temperature out of range [-100. to 200.] (PsyPsatFnTemp)
    //Note: It is caused by wet-bulb calculation error in EnergyPlus when dry bulb temperature and humidity ratio are low
    //writeContentAndComments called with outputFile_IDF, content, comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);

    //outputFile_IDF closed
    outputFile_IDF.close();
    cout << "CoolBuilding IDF file written successfully!" << endl;

}

//formatNumberToString will return string from number and target precision 
string formatNumberToString(double number_to_convert, int precision_target) {
    //ostringstream object oss allows fine control on conversion of number to string
    ostringstream oss;
    //oss defined as fixed setprecision of precision_target places and attaches number_to_convert
    oss << fixed << setprecision(precision_target) << number_to_convert;
    //Return the formatted string
    return oss.str();
}

//formatVertex converts x,y,z double to string with explicit comma
string formatVertex(const idfVertex& vertices) {
    //ostringstream object oss allows fine control on conversion of number to string
    ostringstream oss;
    //oss defined as fixed setprecision of 2 places and attaches vertices x, y, and z separated by commas
    oss << fixed << setprecision(2) << vertices.x << ", " << vertices.y << ", " << vertices.z;
    //Return the formatted string
    return oss.str();
}

//calculateBuildingVertices vector generates x,y,z coordinate using idfVertex struct
vector<idfVertex> calculateBuildingVertices(int numVertices, double lowerLeft_X_m, double lowerLeft_Y_m, double assembly_delta_X_m, double assembly_delta_Y_m, double floor_delta_X_m, double floor_delta_Y_m, double roof_delta_X_m, double roof_delta_Y_m, double height_min_m, int AssemblyFace_azimuth, bool isGable, double height_max_m, double height_roof_m) {
    vector<idfVertex> vertices;
    //Note: Calculate the vertices for the floor, walls, or roof based on the parameters
    //Note: Add the vertices in clockwise order starting from the lower-left corner, facing into building
    //If not isGable then write standard rectangular wall or floor or roof
    if (!isGable) {
        // LowerLeft
        vertices.push_back({ lowerLeft_X_m, lowerLeft_Y_m, height_min_m });
        // UpperLeft
        vertices.push_back({ lowerLeft_X_m + roof_delta_X_m, lowerLeft_Y_m + roof_delta_Y_m - floor_delta_Y_m, height_max_m });
        // UpperRight
        vertices.push_back({ lowerLeft_X_m + assembly_delta_X_m + roof_delta_X_m + floor_delta_X_m, lowerLeft_Y_m + assembly_delta_Y_m + roof_delta_Y_m - floor_delta_Y_m, height_max_m });
        // UpperLeft
        vertices.push_back({ lowerLeft_X_m + assembly_delta_X_m + floor_delta_X_m, lowerLeft_Y_m + assembly_delta_Y_m, height_min_m });
    }
    else {
        // LowerLeft
        vertices.push_back({ lowerLeft_X_m, lowerLeft_Y_m, height_min_m });
        // UpperLeft
        vertices.push_back({ lowerLeft_X_m, lowerLeft_Y_m, height_max_m });
        // Gable peak
        vertices.push_back({ lowerLeft_X_m + assembly_delta_X_m/2.0, lowerLeft_Y_m + assembly_delta_Y_m/2.0, height_roof_m }); 
        // UpperRight
        vertices.push_back({ lowerLeft_X_m + assembly_delta_X_m, lowerLeft_Y_m + assembly_delta_Y_m, height_max_m });
        // UpperLeft
        vertices.push_back({ lowerLeft_X_m + assembly_delta_X_m, lowerLeft_Y_m + assembly_delta_Y_m, height_min_m });
    }
    return vertices;
}

// Function implementation for wall geometry
assemblyGeometry calculateAssemblyGeometry(int i, double Wall_Reference_Length_XCoordinate_m, double Wall_NonReference_Length_YCoordinate_m, double Window_LeftEdge_m, double Window_Length_m) {
    assemblyGeometry Genericgeometry;

    //If i = 0 then define lower left x and y while looking into building for wall facing in y direction
    //Note: If wall faces in y direction then lower left x = Wall_Reference_Length_XCoordinate_m y = Wall_NonReference_Length_YCoordinate_m
    if (i == 0) {
        Genericgeometry.lowerLeft_X_m = Wall_Reference_Length_XCoordinate_m - Window_LeftEdge_m;
        Genericgeometry.lowerLeft_Y_m = Wall_NonReference_Length_YCoordinate_m;
        Genericgeometry.assembly_delta_X_m = -Wall_Reference_Length_XCoordinate_m;
        Genericgeometry.assembly_delta_Y_m = 0;
        Genericgeometry.roof_delta_X_m = 0;
        Genericgeometry.roof_delta_Y_m = -Wall_NonReference_Length_YCoordinate_m * 0.5;
        Genericgeometry.window_delta_X_m = -Window_Length_m;
        Genericgeometry.window_delta_Y_m = 0;
    }
    //If i = 1 then define lower left x and y while looking into building for wall facing in x direction
    //Note: If wall faces in x direction then lower left x = Wall_NonReference_Length_YCoordinate_m y = 0
    else if (i == 1) {
        Genericgeometry.lowerLeft_X_m = Wall_Reference_Length_XCoordinate_m;
        Genericgeometry.lowerLeft_Y_m = 0 + Window_LeftEdge_m;
        Genericgeometry.assembly_delta_X_m = 0;
        Genericgeometry.assembly_delta_Y_m = Wall_NonReference_Length_YCoordinate_m;
        Genericgeometry.roof_delta_X_m = -Wall_Reference_Length_XCoordinate_m * 0.5;
        Genericgeometry.roof_delta_Y_m = 0;
        Genericgeometry.window_delta_X_m = 0;
        Genericgeometry.window_delta_Y_m = Window_Length_m;
    }
    //If i = 2 then define lower left x and y while looking into building for wall facing in -y direction
    //Note: If wall faces in -y direction then lower left x = 0 y = 0
    else if (i == 2) {
        Genericgeometry.lowerLeft_X_m = 0 + Window_LeftEdge_m;
        Genericgeometry.lowerLeft_Y_m = 0;
        Genericgeometry.assembly_delta_X_m = Wall_Reference_Length_XCoordinate_m;
        Genericgeometry.assembly_delta_Y_m = 0;
        Genericgeometry.roof_delta_X_m = 0;
        Genericgeometry.roof_delta_Y_m = Wall_NonReference_Length_YCoordinate_m * 0.5;
        Genericgeometry.window_delta_X_m = Window_Length_m;
        Genericgeometry.window_delta_Y_m = 0;
    }
    //If i = 3 then define lower left x and y while looking into building for wall facing in -x direction
    //Note: If wall faces in -x direction then lower left x = 0 y = Wall_NonReference_Length_YCoordinate_m
    else if (i == 3) {
        Genericgeometry.lowerLeft_X_m = 0;
        Genericgeometry.lowerLeft_Y_m = Wall_NonReference_Length_YCoordinate_m - Window_LeftEdge_m;
        Genericgeometry.assembly_delta_X_m = 0;
        Genericgeometry.assembly_delta_Y_m = -Wall_NonReference_Length_YCoordinate_m;
        Genericgeometry.roof_delta_X_m = Wall_Reference_Length_XCoordinate_m * 0.5;
        Genericgeometry.roof_delta_Y_m = 0;
        Genericgeometry.window_delta_X_m = 0;
        Genericgeometry.window_delta_Y_m = -Window_Length_m;
    }

    return Genericgeometry;
}

//CreateFile_IDF::writeBuildingSurface function writes IDF building content and comments
void CreateFile_IDF::writeBuildingSurface(ofstream& outputFile_IDF, const string Heading_Subsection_str, const string& assemblyName, const string& surfaceType, const string& constructionType, string outsideBoundaryCondition, string buildingSurfaceName, double viewFactorGround, string sunExposed, string windExposed, const vector<idfVertex>& vertices) {
    //IDF_content_strings and IDF_comment_strings initialized as vector strings
    vector<string> IDF_content_strings = {};
    vector<string> IDF_comment_strings = {};
    //IDF_content_strings push_back with Heading_Subsection_str 
    IDF_content_strings.push_back(Heading_Subsection_str + ",");
    IDF_comment_strings.push_back("");
    //IDF_content_strings push_back with assemblyName 
    IDF_content_strings.push_back("    " + assemblyName + ",");
    IDF_comment_strings.push_back("!- Name");
    //IDF_content_strings push_back with surfaceType 
    IDF_content_strings.push_back("    " + surfaceType + ",");
    IDF_comment_strings.push_back("!- Surface Type");
    //IDF_content_strings push_back with constructionType 
    IDF_content_strings.push_back("    " + constructionType + ",");
    IDF_comment_strings.push_back("!- Construction Name");

    //If surfaceType not Window then
    if (surfaceType != "Window") {
        //IDF_content_strings push_back with ThermalZoneOfBuilding 
        IDF_content_strings.push_back("    ThermalZoneOfBuilding,");
        IDF_comment_strings.push_back("!- Zone Name");
        //IDF_content_strings push_back with NO Space Name 
        IDF_content_strings.push_back("    ,");
        IDF_comment_strings.push_back("!- Space Name");
        //IDF_content_strings push_back with outsideBoundaryCondition 
        IDF_content_strings.push_back("    " + outsideBoundaryCondition + ",");
        IDF_comment_strings.push_back("!- Outside Boundary Condition");
    }
    //Else If surfaceType is Window then
    else {
        //IDF_content_strings push_back with buildingSurfaceName 
        IDF_content_strings.push_back("    " + buildingSurfaceName + ",");
        IDF_comment_strings.push_back("!- Building Surface Name");
    }

    //IDF_content_strings push_back with NO Outside Boundary Condition Object 
    IDF_content_strings.push_back("    ,");
    IDF_comment_strings.push_back("!- Outside Boundary Condition Object");
     
    //If surfaceType not Window then
    if (surfaceType != "Window") {
        //IDF_content_strings push_back with sunExposed 
        IDF_content_strings.push_back("    " + sunExposed + ",");
        IDF_comment_strings.push_back("!- Sun Exposure");
        //IDF_content_strings push_back with windExposed 
        IDF_content_strings.push_back("    " + windExposed + ",");
        IDF_comment_strings.push_back("!- Wind Exposure");
    }
    //IDF_content_strings push_back with viewFactorGround using formatNumberToString function
    IDF_content_strings.push_back("    " + formatNumberToString(viewFactorGround, 1) + ",");
    IDF_comment_strings.push_back("!- View Factor to Ground");

    //If surfaceType is Window then
    if (surfaceType == "Window") {
        //IDF_content_strings push_back with NO Frame and Divider Name 
        IDF_content_strings.push_back("    ,");
        IDF_comment_strings.push_back("!- Frame and Divider Name");
        //IDF_content_strings push_back with NO Multiplier 
        IDF_content_strings.push_back("    ,");
        IDF_comment_strings.push_back("!- Multiplier");
    }
    //IDF_content_strings push_back with to_string of vertices.size() to get Number of Vertices
    IDF_content_strings.push_back("    " + to_string(vertices.size()) + ",");
    IDF_comment_strings.push_back("!- Number of Vertices");

    //For i from 0 to vertices.size()
    for (size_t i = 0; i < vertices.size(); ++i) {
        //If (i < vertices.size() - 1) then i is less than the last member of vertices
        if (i < vertices.size() - 1) {
            //IDF_content_strings push_back with formatVertex(vertices[i]) of x,y,z vertices as string from double, ending with comma
            IDF_content_strings.push_back("    " + formatVertex(vertices[i]) + ",");
        }
        //If (i == vertices.size() - 1) then i is equal to the last member of vertices
        else {
            //IDF_content_strings push_back with formatVertex(vertices[i]) of x,y,z vertices as string from double, ending with semicolon
            IDF_content_strings.push_back("    " + formatVertex(vertices[i]) + ";");
        }
        //IDF_comment_strings push_back with Vertex, ending all comments
        IDF_comment_strings.push_back("!- Vertex " + to_string(i + 1) + " x,y,z");
    }
    //writeContentAndCommentsfunction called, sending outputFile_IDF and vector strings of content and comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
}

//CreateFile_IDF::writeTreeSurface function writes IDF tree content and comments
void CreateFile_IDF::writeTreeSurface(ofstream& outputFile_IDF, const string& treeName, const string& transmittanceSchedule, const vector<idfVertex>& vertices) {
    //IDF_content_strings and IDF_comment_strings initialized as vector strings
    vector<string> IDF_content_strings = {};
    vector<string> IDF_comment_strings = {};

    //IDF_content_strings push_back with Shading:Building:Detailed 
    IDF_content_strings.push_back("Shading:Building:Detailed,");
    IDF_comment_strings.push_back("");
    //IDF_content_strings push_back with treeName and segment number as to_string(i + 1) 
    IDF_content_strings.push_back("    " + treeName + ",");
    IDF_comment_strings.push_back("!- Name");
    //IDF_content_strings push_back with transmittanceSchedule 
    IDF_content_strings.push_back("    " + transmittanceSchedule + ",");
    IDF_comment_strings.push_back("!- Transmittance Schedule Name");
    //IDF_content_strings push_back with to_string of vertices.size() to get Number of Vertices
    IDF_content_strings.push_back("    " + to_string(vertices.size()) + ",");
    IDF_comment_strings.push_back("!- Number of Vertices");
    //For i from 0 to vertices.size()
    for (size_t i = 0; i < vertices.size(); ++i) {
        //If (i < vertices.size() - 1) then i is less than the last member of vertices
        if (i < vertices.size() - 1) {
            //IDF_content_strings push_back with formatVertex(vertices[i]) of x,y,z vertices as string from double, ending with comma
            IDF_content_strings.push_back("    " + formatVertex(vertices[i]) + ",");
        }
        //If (i == vertices.size() - 1) then i is equal to the last member of vertices
        else {
            //IDF_content_strings push_back with formatVertex(vertices[i]) of x,y,z vertices as string from double, ending with semicolon
            IDF_content_strings.push_back("    " + formatVertex(vertices[i]) + ";");
        }
        //IDF_comment_strings push_back with Vertex, ending all comments
        IDF_comment_strings.push_back("!- Vertex " + to_string(i + 1) + " x,y,z");
    }
    //writeContentAndCommentsfunction called, sending outputFile_IDF and vector strings of content and comment
    writeContentAndComments(outputFile_IDF, IDF_content_strings, IDF_comment_strings);
}

//CreateFile_IDF::writeContentAndComments function writes IDF generic content and comments
void CreateFile_IDF::writeContentAndComments(ofstream& outputFile_IDF, const vector<string>& content, const vector<string>& comment) {
    //commentColumn defined at 50 columns, where comments can start
    int commentColumn = 50; 
    //writeline function autodeduces variable w pointer to outputFile_IDF & commentColumn, and receives strings content and comment
    //Note: const makes content and comment immutable
    auto writeLine = [&outputFile_IDF, commentColumn](const string& content, const string& comment) {
        //outputFile_IDF receives content string
        outputFile_IDF << content;
        //If comment not empty then enter
        if (!comment.empty()) {
            //padding created as commentColumn minuse content.length
            int padding = commentColumn - content.length();
            //If padding > 0 then enter
            if (padding > 0) {
                //outputFile_IDF receives setw(padding) and a space
                outputFile_IDF << setw(padding) << " ";
            }
            //outputFile_IDF receives comment
            outputFile_IDF << comment;
        }
        //outputFile_IDF receives line return
        outputFile_IDF << "\n";
    };

    //If content.size() not equal to comment.size() then send error message with 1st item in content to help ID error
    if (content.size() != comment.size()) {
        cerr << "\n" << endl;
        cerr << "Error:" + content[0] + " content and comment vectors must be of the same length." << endl;
        cerr << "Aborting: This error causes the program to abort." << endl;
        cerr << "Explanation: If the content and comment vectors are different lengths the IDF file cannot be created." << endl;
        cerr << "Correction: Correct the length of the content or comment vectors for " + content[0] + " " + content[1] + "." << endl;
        abort;
    }

    //For i from 0 to content.size call writeLine sending content and comment element, size_t to align i and content
    for (size_t i = 0; i < content.size(); ++i) {
        //writeLine function called with content and comment element i
        writeLine(content[i], comment[i]);
    }

    //outputFile receives blank line at the end
    outputFile_IDF << "\n";
}

//readTreeData function reads tree data into map using treeParameters struct 
map<int, treeParameters> readTreeData(Inputs* input, int Tree_ID_Count) {
    //treeParametersLibrary as instance of treeParameters struct
    map<int, treeParameters> treeParametersLibrary;
    //For loop of id from 1 through Tree_ID_Count
    for (int id = 1; id <= Tree_ID_Count; ++id) {
        //keyBuilder defined as stringstream
        stringstream keyBuilder;
        //keyBuilder constructed dynamically for each parameter, using HydroPlusConfig.xml element names
        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Trunk_XCoordinate_m";
        //Tree_Trunk_X_m defined from HydroPlusConfig.xml element Tree_ID_01_Trunk_XCoordinate_m
        double Tree_Trunk_X_m = input->CoolBuildingNumericalParams[keyBuilder.str()];
        //keyBuilder.str reset and cleared
        keyBuilder.str(""); keyBuilder.clear();

        //Repeat several times through the three steps above for keyBuilder and other HydroPlusConfig.xml elements
        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Trunk_YCoordinate_m";
        double Tree_Trunk_Y_m = input->CoolBuildingNumericalParams[keyBuilder.str()];
        keyBuilder.str(""); keyBuilder.clear();

        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Trunk_Diameter_atBH_m";
        double Tree_Diameter_atBH_m = input->CoolBuildingNumericalParams[keyBuilder.str()];
        keyBuilder.str(""); keyBuilder.clear();

        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Canopy_BaseHeight_m";
        double Tree_Canopy_Base_Height_m = input->CoolBuildingNumericalParams[keyBuilder.str()];
        keyBuilder.str(""); keyBuilder.clear();

        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Canopy_TopHeight_m";
        double Tree_Canopy_Top_Height_m = input->CoolBuildingNumericalParams[keyBuilder.str()];
        keyBuilder.str(""); keyBuilder.clear();

        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Canopy_Diameter_m";
        double Tree_Canopy_Diameter_m = input->CoolBuildingNumericalParams[keyBuilder.str()];
        keyBuilder.str(""); keyBuilder.clear();

        keyBuilder << "Tree_ID_" << (id < 10 ? "0" : "") << id << "_Canopy_ShapeOption";
        int Tree_Canopy_Shape_Option = static_cast<int>(input->CoolBuildingNumericalParams[keyBuilder.str()]);

        //treeParametersLibrary[id] instance of struct populated with the tree parameters as map
        treeParametersLibrary[id] = { Tree_Trunk_X_m, Tree_Trunk_Y_m, Tree_Diameter_atBH_m, Tree_Canopy_Base_Height_m, Tree_Canopy_Top_Height_m, Tree_Canopy_Diameter_m, Tree_Canopy_Shape_Option };
    }
    //return treeParametersLibrary instance of struct
    return treeParametersLibrary;
}

// Function to calculate vertices of a tree
vector<idfVertex> calculateTreeVertices(const treeParameters& params, int wedgePart, int segmentNumber, int numSegments) {
    vector<idfVertex> vertices;

    // Trunk dimensions
    double Tree_trunk_base_m = 0.0; // Base of the trunk
    double Tree_trunk_top_m = params.Tree_Canopy_Base_Height_m; // Top of the trunk
    double Tree_trunk_radius_m = params.Tree_Diameter_atBH_m / 2.0;

    // Center of the trunk
    double Tree_center_X_m = params.Tree_Trunk_X_m;
    double Tree_center_Y_m = params.Tree_Trunk_Y_m;

    // Generate canopy vertices
    double Tree_canopy_base_m = params.Tree_Canopy_Base_Height_m;
    double Tree_canopy_top_m = params.Tree_Canopy_Top_Height_m;
    //Tree_canopy_radius_m equals Tree_Canopy_Diameter_m / 2.0
    double Tree_canopy_radius_m = params.Tree_Canopy_Diameter_m / 2.0;
    int numberVertices;

    //Calculate current segment angle in radians
    double segmentAngle_rad = segmentNumber * (2 * M_PI / numSegments);
    //Calculate next segment angle in radians(wrapping around to 0 for last segment) 
    double segmentAngle_next_rad = fmod((segmentNumber + 1), numSegments) * (2 * M_PI / numSegments);

    //Calculate first point coordinates using trigonometry
    double Tree_surface_Xa_m = Tree_center_X_m + Tree_canopy_radius_m * cos(segmentAngle_rad);
    double Tree_surface_Ya_m = Tree_center_Y_m + Tree_canopy_radius_m * sin(segmentAngle_rad);
    //Calculate second point coordinates
    double Tree_surface_Xb_m = Tree_center_X_m + Tree_canopy_radius_m * cos(segmentAngle_next_rad);
    double Tree_surface_Yb_m = Tree_center_Y_m + Tree_canopy_radius_m * sin(segmentAngle_next_rad);

    switch (params.Tree_Canopy_Shape_Option) {
    //case Tree_Canopy_Shape_Option = 1, then shape = Cone 
    case 1: { 
        vertices.push_back({ Tree_surface_Xa_m, Tree_surface_Ya_m, Tree_canopy_base_m });
        vertices.push_back({ Tree_center_X_m, Tree_center_Y_m, Tree_canopy_top_m });
        vertices.push_back({ Tree_surface_Xb_m, Tree_surface_Yb_m, Tree_canopy_base_m });
        break;
    }
    //case Tree_Canopy_Shape_Option = 2, then shape = Column 
    case 2: {
        vertices.push_back({ Tree_surface_Xa_m, Tree_surface_Ya_m, Tree_canopy_base_m });
        vertices.push_back({ Tree_surface_Xa_m, Tree_surface_Ya_m, Tree_canopy_top_m });
        vertices.push_back({ Tree_surface_Xb_m, Tree_surface_Yb_m, Tree_canopy_top_m });
        vertices.push_back({ Tree_surface_Xb_m, Tree_surface_Yb_m, Tree_canopy_base_m });
        break;
    }
    //case Tree_Canopy_Shape_Option = 3, then shape = Ellipsoid 
    case 3: { 
        //If wedgePart is 0 then create lower half of wege
        if (wedgePart == 0) {
            vertices.push_back({ Tree_center_X_m, Tree_center_Y_m, Tree_canopy_base_m });
            vertices.push_back({ Tree_surface_Xa_m, Tree_surface_Ya_m, Tree_canopy_top_m / 2.0 });
            vertices.push_back({ Tree_surface_Xb_m, Tree_surface_Yb_m, Tree_canopy_top_m / 2.0 });
        }
        //Else If wedgePart is 1 then create upper half of wege
        else {
            vertices.push_back({ Tree_surface_Xa_m, Tree_surface_Ya_m, Tree_canopy_top_m / 2.0 });
            vertices.push_back({ Tree_center_X_m, Tree_center_Y_m, Tree_canopy_top_m });
            vertices.push_back({ Tree_surface_Xb_m, Tree_surface_Yb_m, Tree_canopy_top_m / 2.0 });
        }
        break;
    }
    default:
        cerr << "Invalid Tree Canopy Shape Option." << endl;
    }

    return vertices;
}
//ErrorCheck_CoolBuilding_parameters funtion to check HydroPlusConfig.xml parameter values
void CreateFile_IDF::ErrorCheck_CoolBuilding_parameters(Inputs* input) {

    //parametersToCheckForNegative is a vector of the HydroPlusConfig.xml elements for CoolBuilding
    const vector<string> parametersToCheckForNegative = {
        "Wall_Reference_OutsideFace_Azimuth_N_0_deg",
        "Wall_Reference_Length_XCoordinate_m",
        "Wall_NonReference_Length_YCoordinate_m",
        "Wall_Height_m",
        "Roof_Height_Max_m",
        "Roof_Gable_Above_Wall_Reference_Flag",
        "Window_to_Wall_Area_Ratio",
        "Windows_per_Wall",
        "AirChangesPerHour",
        "Building_NumberOfFloors",
        "Occupants_NumberOfPeople",
        "SetPoint_Humidistat_LowerRH_percent",
        "SetPoint_Humidistat_UpperRH_percent",
        "Tree_ID_01_Trunk_Diameter_atBH_m",

    };

    //For loop auto detect param_HydroPlusConfig in parametersToCheckForNegative vector
    for (const auto& param_HydroPlusConfig : parametersToCheckForNegative) {
        //If input->CoolBuildingNumericalParams[param_HydroPlusConfig] < 0 then issue warning and abort
        if (input->CoolBuildingNumericalParams[param_HydroPlusConfig] < 0) {
            cerr << "Warning: The HydroPlusConfig.xml element " << param_HydroPlusConfig << " has a value < 0.\n";
            cerr << "Aborting: This warning triggers the HydroPlus simulation to abort.\n";
            cerr << "Explanation: When this element is < 0, the model cannot experience our shared reality.\n";
            cerr << "Correction: Provide an agreeable value for this element in the HydroPlusConfig.xml file.\n";
            abort();
        }
    }

    //parameterRelationships if vector tuple of the HydroPlusConfig.xml elements that have a < relationship
    const vector<tuple<string, string>> parameterRelationships = {
        {"Roof_Height_Max_m", "Wall_Height_m"},
        {"SetPoint_Humidistat_UpperRH_percent", "SetPoint_Humidistat_LowerRH_percent"},
        {"SetPoint_Thermostat_Cooling_C", "SetPoint_Thermostat_Heating_C"}
    };

    //For tuple relationship pointer in parameterRelationships relationships
    for (const tuple<string, string>& relationship : parameterRelationships) {
        //highParam defined as get<0>(relationship) in tuple, 1st item
        const string& highParam = get<0>(relationship);
        //lowParam defined as get<1>(relationship) in tuple, 2nd item
        const string& lowParam = get<1>(relationship);
        if (input->CoolBuildingNumericalParams[highParam] < input->CoolBuildingNumericalParams[lowParam]) {
            cerr << "Warning: The HydroPlusConfig.xml element " << highParam << " is less than " << lowParam << ".\n";
            cerr << "Aborting: This warning triggers the HydroPlus simulation to abort.\n";
            cerr << "Explanation: When this element relationship is violated, the model cannot experience our shared reality.\n";
            cerr << "Correction: Provide agreeable values for these elements in the HydroPlusConfig.xml file.\n";
            abort();
        }
    }
}
